[RFC PATCH 1/4] mm/vmscan: only reclaim slab in node reclaim when over min_slab_pages
From: Ridong Chen
Date: Fri Aug 21 2026 - 04:23:39 EST
From: Ridong Chen <chenridong@xxxxxxxxxx>
Since commit d8ff6fde8e88 ("mm/vmscan: take min_slab_pages into account
when try to call shrink_node"), node reclaim enters shrink_node() when
reclaimable slab is over min_slab_pages OR unmapped page cache is over
min_unmapped_pages.
But the threshold only decides whether to enter shrink_node(), not what
it reclaims. min_slab_pages is documented to gate slab reclaim alone:
"On Zone reclaim slabs will be reclaimed if more than this percentage of
pages in a zone are reclaimable slab pages". Yet once unmapped page
cache alone trips the gate, shrink_node() still invokes the slab
shrinkers and can drive reclaimable slab below min_slab_pages.
Carry the decision into the reclaim path via a scan_control flag and
skip the slab shrinkers when reclaimable slab is already at or below
min_slab_pages, so the limit gates slab reclaim as documented. The flag
defaults to zero, so kswapd, direct reclaim, memcg reclaim, proactive
reclaim and drop_caches are unaffected; only the node reclaim path sets
it.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Ridong Chen <chenridong@xxxxxxxxxx>
---
mm/vmscan.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index c17ac77b08a4..7e65d0ba4a96 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -114,6 +114,12 @@ struct scan_control {
/* zone_reclaim_mode, boost reclaim, cgroup restrictions */
unsigned int may_swap:1;
+ /*
+ * When set, the slab shrinkers are not invoked because reclaimable
+ * slab is already at or below min_slab_pages.
+ */
+ unsigned int skip_slab_reclaim:1;
+
/* Not allow cache_trim_mode to be turned on as part of reclaim? */
unsigned int no_cache_trim_mode:1;
@@ -5120,7 +5126,8 @@ static int shrink_one(struct lruvec *lruvec, struct scan_control *sc)
need_rotate = try_to_shrink_lruvec(lruvec, sc);
- shrink_slab(sc->gfp_mask, pgdat->node_id, memcg, sc->priority);
+ if (!sc->skip_slab_reclaim)
+ shrink_slab(sc->gfp_mask, pgdat->node_id, memcg, sc->priority);
if (!sc->proactive)
vmpressure(sc->gfp_mask, sc->order, memcg, false,
@@ -6237,8 +6244,9 @@ static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc)
shrink_lruvec(lruvec, sc);
- shrink_slab(sc->gfp_mask, pgdat->node_id, memcg,
- sc->priority);
+ if (!sc->skip_slab_reclaim)
+ shrink_slab(sc->gfp_mask, pgdat->node_id, memcg,
+ sc->priority);
/* Record the group's reclaim efficiency */
if (!sc->proactive)
@@ -7940,6 +7948,15 @@ unsigned long node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned i
if (test_and_set_bit_lock(PGDAT_RECLAIM_LOCKED, &pgdat->flags))
return 0;
+ /*
+ * min_slab_pages only gates slab reclaim: when reclaimable slab is
+ * already at or below the limit, leave the shrinkers alone even if we
+ * entered node reclaim to trim unmapped page cache.
+ */
+ sc.skip_slab_reclaim =
+ node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) <=
+ pgdat->min_slab_pages;
+
ret = __node_reclaim(pgdat, nr_pages, &sc);
clear_bit_unlock(PGDAT_RECLAIM_LOCKED, &pgdat->flags);
--
2.34.1