Skip to contentSkip to conversation

mm/vmscan: honour node reclaim limits per type

RFC PATCH In review Ridong Chen 8 participants 27 messages Aug 21 – Aug 26

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover mm/vmscan: honour node reclaim limits per type

From: Ridong Chen <[email protected]>

min_unmapped_pages and min_slab_pages are documented as per-type limits, but node reclaim treats them as one combined gate: once either is exceeded, shrink_node() reclaims slab, file and anon together and pushes the other type below its limit. Per-node proactive reclaim reuses the same gate and fares worse -- with page cache and slab both under their limits it reclaims nothing and returns -EAGAIN on an anon-heavy node [1].

This series gates each type separately via two scan_control flags (skip_slab_reclaim, skip_file_reclaim) set only on the node reclaim path, drops the combined gate, and extends node_reclaim()'s early bail to check anon. The flags default to zero, so kswapd, direct, memcg and drop_caches are unaffected.

Tested on QEMU (x86_64, 2 NUMA nodes), A/B kernels differing only in this series.

Proactive reclaim (echo to node/reclaim) on an anon-heavy node, file and slab under their limits:

  metric              before   after
  -----------------   ------   -----------
  pages reclaimed     0 MiB    254 MiB anon
  return value        -EAGAIN  0

Node reclaim (zone_reclaim_mode) with one type under its limit -- the type under its limit must be left alone:

  type under limit    before          after
  -----------------   -------------   --------
  slab                645 scans       0 scans
  page cache          83 MiB scanned  0 MiB

[1] https://sashiko.dev/#/patchset/[email protected]

Ridong Chen (4):
mm/vmscan: only reclaim slab in node reclaim when over min_slab_pages mm/vmscan: only reclaim file pages in node reclaim when over
min_unmapped_pages
mm/vmscan: drop the combined limit gate in __node_reclaim() mm/vmscan: do not skip node reclaim when only anon is reclaimable

 mm/vmscan.c | 89 ++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 67 insertions(+), 22 deletions(-)
Signature · 1 line
2.34.1
#1 lore raw

RFC PATCH 1/4 mm/vmscan: only reclaim slab in node reclaim when over min_slab_pages

From: Ridong Chen <[email protected]>

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
---
 mm/vmscan.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)
mm/vmscan.c +20 -3
index c17ac77b08a4..7e65d0ba4a96 100644
@@ -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);
 
Signature · 1 line
2.34.1
#2 lore raw

RFC PATCH 2/4 mm/vmscan: only reclaim file pages in node reclaim when over min_unmapped_pages

In reply to Ridong Chen

From: Ridong Chen <[email protected]>

Node reclaim enters shrink_node() when unmapped page cache is over min_unmapped_pages OR reclaimable slab is over min_slab_pages, but the threshold only decides whether to enter shrink_node(), not what it reclaims.

min_unmapped_pages is documented to keep a small amount of unmapped page cache around so that file I/O is not immediately thrown out: "Zone reclaim will only occur if more than this percentage of pages are in a state that zone_reclaim_mode allows to be reclaimed." Yet once slab alone trips the gate, shrink_node() still reclaims file pages and can drive unmapped page cache below min_unmapped_pages, defeating the protection.

Carry the decision into the reclaim path via a scan_control flag set only on the node reclaim path, and honour it on both reclaim implementations:

- traditional LRU: get_scan_count() forces SCAN_ANON, or scans nothing when anon cannot be reclaimed (e.g. no swap), rather than falling back to SCAN_FILE and breaching the floor;

- MGLRU: scan_folios() leaves the file type alone, and isolate_folios() falls back to anon.

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
---
 mm/vmscan.c | 38 +++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)
mm/vmscan.c +35 -3
index 7e65d0ba4a96..1e56973ceb73 100644
@@ -120,6 +120,12 @@ struct scan_control {
 	 */
 	unsigned int skip_slab_reclaim:1;
 
+	/*
+	 * When set, file pages are not reclaimed because unmapped page cache
+	 * is already at or below min_unmapped_pages.
+	 */
+	unsigned int skip_file_reclaim:1;
+
 	/* Not allow cache_trim_mode to be turned on as part of reclaim? */
 	unsigned int no_cache_trim_mode:1;
 
@@ -2581,6 +2587,21 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
 		goto out;
 	}
 
+	/*
+	 * node_reclaim protects unmapped page cache down to
+	 * min_unmapped_pages: skip file pages and reclaim anon only.  As with
+	 * the anon-only case above, if anon cannot be reclaimed there is
+	 * nothing to do without breaching the floor, so scan nothing.
+	 */
+	if (sc->skip_file_reclaim) {
+		if (!can_reclaim_anon_pages(memcg, pgdat->node_id, sc)) {
+			memset(nr, 0, sizeof(*nr) * NR_LRU_LISTS);
+			return;
+		}
+		scan_balance = SCAN_ANON;
+		goto out;
+	}
+
 	/* If we have no swap space, do not bother scanning anon folios. */
 	if (!sc->may_swap || !can_reclaim_anon_pages(memcg, pgdat->node_id, sc)) {
 		scan_balance = SCAN_FILE;
@@ -4749,6 +4770,14 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
 	VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH);
 	VM_WARN_ON_ONCE(!list_empty(list));
 
+	/*
+	 * node_reclaim protects unmapped page cache down to min_unmapped_pages,
+	 * so leave the file type alone; isolate_folios() then falls back to
+	 * anon.
+	 */
+	if (sc->skip_file_reclaim && type == LRU_GEN_FILE)
+		return 0;
+
 	if (get_nr_gens(lruvec, type) == MIN_NR_GENS)
 		return 0;
 
@@ -7949,13 +7978,16 @@ unsigned long node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned i
 		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.
+	 * Each limit only gates its own type of reclaim.  When reclaimable
+	 * slab or unmapped page cache is already at or below its limit, leave
+	 * that type alone even if the other type tripped the gate and brought
+	 * us into node reclaim.
 	 */
 	sc.skip_slab_reclaim =
 		node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) <=
 		pgdat->min_slab_pages;
+	sc.skip_file_reclaim =
+		node_pagecache_reclaimable(pgdat) <= pgdat->min_unmapped_pages;
 
 	ret = __node_reclaim(pgdat, nr_pages, &sc);
 	clear_bit_unlock(PGDAT_RECLAIM_LOCKED, &pgdat->flags);
Signature · 1 line
2.34.1
#3 lore raw

RFC PATCH 3/4 mm/vmscan: drop the combined limit gate in __node_reclaim()

In reply to Ridong Chen

From: Ridong Chen <[email protected]>

__node_reclaim() only ran shrink_node() when unmapped page cache was over min_unmapped_pages OR reclaimable slab was over min_slab_pages.

With slab and file reclaim now gated per type by sc->skip_slab_reclaim and sc->skip_file_reclaim, this combined gate is either redundant or harmful:

- for the NUMA node reclaim caller it is always true, since node_reclaim() only calls in when at least one limit is exceeded;

- for the per-node proactive reclaim caller (which does not go through node_reclaim()'s checks) it wrongly suppressed all reclaim -- anon included -- whenever both page cache and slab happened to sit at or below their limits, even with plenty of reclaimable anon present.

Drop the gate and let the per-type flags decide what to reclaim. The node reclaim path is unchanged; the proactive path can now reclaim anon as requested.

Assisted-by Claude:claude-opus-4-8
---
 mm/vmscan.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
mm/vmscan.c +10 -10
index 1e56973ceb73..5a3f67b3ba32 100644
@@ -7906,16 +7906,16 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat,
 	noreclaim_flag = memalloc_noreclaim_save();
 	set_task_reclaim_state(p, &sc->reclaim_state);
 
-	if (node_pagecache_reclaimable(pgdat) > pgdat->min_unmapped_pages ||
-	    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) > pgdat->min_slab_pages) {
-		/*
-		 * Free memory by calling shrink node with increasing
-		 * priorities until we have enough memory freed.
-		 */
-		do {
-			shrink_node(pgdat, sc);
-		} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
-	}
+	/*
+	 * Free memory by calling shrink node with increasing
+	 * priorities until we have enough memory freed.
+	 *
+	 * What to reclaim is gated per type by sc->skip_slab_reclaim and
+	 * sc->skip_file_reclaim.
+	 */
+	do {
+		shrink_node(pgdat, sc);
+	} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
 
 	set_task_reclaim_state(p, NULL);
 	memalloc_noreclaim_restore(noreclaim_flag);
Signature · 1 line
2.34.1
maintainer Acked-by on v1 3/4
#4 lore raw

Re: [RFC PATCH 3/4] mm/vmscan: drop the combined limit gate in __node_reclaim()

Quoted 16 lines · Ridong Chen

From: Ridong Chen <[email protected]>

__node_reclaim() only ran shrink_node() when unmapped page cache was over min_unmapped_pages OR reclaimable slab was over min_slab_pages.

With slab and file reclaim now gated per type by sc->skip_slab_reclaim and sc->skip_file_reclaim, this combined gate is either redundant or harmful:

- for the NUMA node reclaim caller it is always true, since node_reclaim() only calls in when at least one limit is exceeded;

- for the per-node proactive reclaim caller (which does not go through node_reclaim()'s checks) it wrongly suppressed all reclaim -- anon included -- whenever both page cache and slab happened to sit at or below their limits, even with plenty of reclaimable anon present.

Quoted 6 lines

Drop the gate and let the per-type flags decide what to reclaim. The node reclaim path is unchanged; the proactive path can now reclaim anon as requested.

Assisted-by Claude:claude-opus-4-8

Could we go with this patch and table the rest of the series?

These minimums are specifically for zone_reclaim_mode. I don't know who is using that at this point, and it seems the behavior has been like that for a while with no practical complaints. So my take is, leave it until somebody has a real problem.

Applying those limits to proactive reclaim, on the other hand, wasn't intentional, isn't documented, and can lead to unexpected behavior - considering we have non-zero default values on these knobs.

Since the gate was already redundant for some reason, leaving it in node_reclaim() (zone_reclaim_mode) and killing it in __node_reclaim() (the path shared with proactive reclaim), like you did here, sounds like the best way forward for now to me.

With an updated changelog to that end,

Quoted 15 lines
---
 mm/vmscan.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
mm/vmscan.c +0 -3
index 1e56973ceb73..5a3f67b3ba32 100644
@@ -7906,16 +7906,16 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat,
 	noreclaim_flag = memalloc_noreclaim_save();
 	set_task_reclaim_state(p, &sc->reclaim_state);
 
-	if (node_pagecache_reclaimable(pgdat) > pgdat->min_unmapped_pages ||
-	    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) > pgdat->min_slab_pages) {
-		/*

mm/vmscan.c static unsigned long __node_reclaim(struct pglist_data *pgdat, · line 7911

Quoted 10 lines
mm/vmscan.c +3 -7
-		 * Free memory by calling shrink node with increasing
-		 * priorities until we have enough memory freed.
-		 */
-		do {
-			shrink_node(pgdat, sc);
-		} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
-	}
+	/*
+	 * Free memory by calling shrink node with increasing
+	 * priorities until we have enough memory freed.

Might as well drop this comment. It just says what the code does.

Quoted 10 lines
mm/vmscan.c +7 -0
+	 *
+	 * What to reclaim is gated per type by sc->skip_slab_reclaim and
+	 * sc->skip_file_reclaim.
+	 */
+	do {
+		shrink_node(pgdat, sc);
+	} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
 
 	set_task_reclaim_state(p, NULL);
 	memalloc_noreclaim_restore(noreclaim_flag);
#5 lore raw

Re: [RFC PATCH 3/4] mm/vmscan: drop the combined limit gate in __node_reclaim()

Quoted 35 lines · Johannes Weiner
Quoted 16 lines · Ridong Chen

From: Ridong Chen <[email protected]>

__node_reclaim() only ran shrink_node() when unmapped page cache was over min_unmapped_pages OR reclaimable slab was over min_slab_pages.

With slab and file reclaim now gated per type by sc->skip_slab_reclaim and sc->skip_file_reclaim, this combined gate is either redundant or harmful:

- for the NUMA node reclaim caller it is always true, since

    node_reclaim() only calls in when at least one limit is exceeded;

- for the per-node proactive reclaim caller (which does not go through node_reclaim()'s checks) it wrongly suppressed all reclaim -- anon included -- whenever both page cache and slab happened to sit at or below their limits, even with plenty of reclaimable anon present.

Quoted 6 lines

Drop the gate and let the per-type flags decide what to reclaim. The node reclaim path is unchanged; the proactive path can now reclaim anon as requested.

Assisted-by Claude:claude-opus-4-8

Could we go with this patch and table the rest of the series?

These minimums are specifically for zone_reclaim_mode. I don't know who is using that at this point, and it seems the behavior has been like that for a while with no practical complaints. So my take is, leave it until somebody has a real problem.

Applying those limits to proactive reclaim, on the other hand, wasn't intentional, isn't documented, and can lead to unexpected behavior - considering we have non-zero default values on these knobs.

Quoted 9 lines

Since the gate was already redundant for some reason, leaving it in node_reclaim() (zone_reclaim_mode) and killing it in __node_reclaim() (the path shared with proactive reclaim), like you did here, sounds like the best way forward for now to me.

With an updated changelog to that end,

Thanks. Will update.

Quoted 38 lines
Quoted 15 lines
---
  mm/vmscan.c | 20 ++++++++++----------
  1 file changed, 10 insertions(+), 10 deletions(-)
mm/vmscan.c +0 -3
index 1e56973ceb73..5a3f67b3ba32 100644
@@ -7906,16 +7906,16 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat,
  	noreclaim_flag = memalloc_noreclaim_save();
  	set_task_reclaim_state(p, &sc->reclaim_state);
 
-	if (node_pagecache_reclaimable(pgdat) > pgdat->min_unmapped_pages ||
-	    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) > pgdat->min_slab_pages) {
-		/*

mm/vmscan.c static unsigned long __node_reclaim(struct pglist_data *pgdat, · line 7911

Quoted 10 lines
mm/vmscan.c +3 -7
-		 * Free memory by calling shrink node with increasing
-		 * priorities until we have enough memory freed.
-		 */
-		do {
-			shrink_node(pgdat, sc);
-		} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
-	}
+	/*
+	 * Free memory by calling shrink node with increasing
+	 * priorities until we have enough memory freed.

Might as well drop this comment. It just says what the code does.

Quoted 10 lines
mm/vmscan.c +7 -0
+	 *
+	 * What to reclaim is gated per type by sc->skip_slab_reclaim and
+	 * sc->skip_file_reclaim.
+	 */
+	do {
+		shrink_node(pgdat, sc);
+	} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
 
  	set_task_reclaim_state(p, NULL);
  	memalloc_noreclaim_restore(noreclaim_flag);
Signature · 2 lines
Best regards
Ridong
#6 lore raw

RFC PATCH 4/4 mm/vmscan: do not skip node reclaim when only anon is reclaimable

In reply to Ridong Chen

From: Ridong Chen <[email protected]>

node_reclaim() bailed out with "return 0" whenever both unmapped page cache and reclaimable slab were at or below their limits:

	if (node_pagecache_reclaimable(pgdat) <= pgdat->min_unmapped_pages &&
	    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) <=
	    pgdat->min_slab_pages)
		return 0;

But min_unmapped_pages and min_slab_pages only describe file and slab; they say nothing about anonymous memory, which node reclaim is otherwise happy to reclaim (may_swap is set, swappiness applies). So a node with little page cache and slab but plenty of reclaimable anon was skipped entirely, even though there was anon to free.

Require in addition that anon cannot be reclaimed before bailing out, so the early return only triggers when none of the three types has anything to reclaim. When anon is reclaimable the per-type flags leave file and slab alone, so shrink_node reclaims anon only; checking can_reclaim_anon_pages() here also avoids entering reclaim just to scan without freeing anything when swap and demotion are both unavailable.

Assisted-by Claude:claude-opus-4-8
---
 mm/vmscan.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)
mm/vmscan.c +5 -9
index 5a3f67b3ba32..8c706807da44 100644
@@ -7945,18 +7945,14 @@ unsigned long node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned i
 	};
 
 	/*
-	 * Node reclaim reclaims unmapped file backed pages and
-	 * slab pages if we are over the defined limits.
-	 *
-	 * A small portion of unmapped file backed pages is needed for
-	 * file I/O otherwise pages read by file I/O will be immediately
-	 * thrown out if the node is overallocated. So we do not reclaim
-	 * if less than a specified percentage of the node is used by
-	 * unmapped file backed pages.
+	 * min_unmapped_pages and min_slab_pages only gate file and slab.
+	 * Bail out only when both are under their limits and anon cannot
+	 * be reclaimed either, so we do not skip reclaimable anon.
 	 */
 	if (node_pagecache_reclaimable(pgdat) <= pgdat->min_unmapped_pages &&
 	    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) <=
-	    pgdat->min_slab_pages)
+	    pgdat->min_slab_pages &&
+	    !can_reclaim_anon_pages(NULL, pgdat->node_id, &sc))
 		return 0;
 
 	/*
Signature · 1 line
2.34.1
#7 lore raw
In reply to Ridong Chen
Quoted 9 lines · Ridong Chen

From: Ridong Chen <[email protected]>

min_unmapped_pages and min_slab_pages are documented as per-type limits, but node reclaim treats them as one combined gate: once either is exceeded, shrink_node() reclaims slab, file and anon together and pushes the other type below its limit. Per-node proactive reclaim reuses the same gate and fares worse -- with page cache and slab both under their limits it reclaims nothing and returns -EAGAIN on an anon-heavy node [1].

Quoted 5 lines

This series gates each type separately via two scan_control flags (skip_slab_reclaim, skip_file_reclaim) set only on the node reclaim path, drops the combined gate, and extends node_reclaim()'s early bail to check anon. The flags default to zero, so kswapd, direct, memcg and drop_caches are unaffected.

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

Quoted 33 lines

Tested on QEMU (x86_64, 2 NUMA nodes), A/B kernels differing only in this series.

Proactive reclaim (echo to node/reclaim) on an anon-heavy node, file and slab under their limits:

  metric              before   after
  -----------------   ------   -----------
  pages reclaimed     0 MiB    254 MiB anon
  return value        -EAGAIN  0

Node reclaim (zone_reclaim_mode) with one type under its limit -- the type under its limit must be left alone:

  type under limit    before          after
  -----------------   -------------   --------
  slab                645 scans       0 scans
  page cache          83 MiB scanned  0 MiB

[1] https://sashiko.dev/#/patchset/[email protected]

Ridong Chen (4):
mm/vmscan: only reclaim slab in node reclaim when over min_slab_pages mm/vmscan: only reclaim file pages in node reclaim when over
min_unmapped_pages
mm/vmscan: drop the combined limit gate in __node_reclaim() mm/vmscan: do not skip node reclaim when only anon is reclaimable

 mm/vmscan.c | 89 ++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 67 insertions(+), 22 deletions(-)
Signature · 1 line
2.34.1
Signature · 2 lines
Michal Hocko
SUSE Labs
#8 lore raw

+cc Roman for suggestion.

Quoted 4 lines · Michal Hocko

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

This is exactly the issue with these 'unrelated to your patch but' suggestions from sashiko.

You end up in loops:

AI generated patch ---------------> AI generated review
^ |
| |
| v
AI generated 'unrelated to your patch but'

And _at every stage_ reviewers have to do _additional work_ (with ~50% signal/noise).

This isn't sustainable.

We already had _too much work_ prior to the slopgeddon. Now we have a multiple of that.

Roman - I really think we a way of switching off the 'unrelated to your patch but' stuff per-subsystem would be useful.

Maybe we could figure out a way of funnelling this stuff somewhere separately longer term.

(I have I think 2 slopped fixes to rewrite after the previous what like 7 or 8 this cycle? So forgive the grumpiness :)

--
Cheers, Lorenzo

#9 lore raw
Quoted 26 lines · Lorenzo Stoakes (ARM)

+cc Roman for suggestion.

Quoted 4 lines · Michal Hocko

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

This is exactly the issue with these 'unrelated to your patch but' suggestions from sashiko.

You end up in loops:

AI generated patch ---------------> AI generated review
^ |
| |
| v
AI generated 'unrelated to your patch but'

And _at every stage_ reviewers have to do _additional work_ (with ~50% signal/noise).

This isn't sustainable.

We already had _too much work_ prior to the slopgeddon. Now we have a multiple of that.

Quoted 8 lines

Roman - I really think we a way of switching off the 'unrelated to your patch but' stuff per-subsystem would be useful.

Maybe we could figure out a way of funnelling this stuff somewhere separately longer term.

(I have I think 2 slopped fixes to rewrite after the previous what like 7 or 8 this cycle? So forgive the grumpiness :)

I wouldn't blame Sashiko on this really. Yes it points to a theoretical problem. That is fine. But we should encourage people to not blindly follow that lead and immediately jump at fixing something that is not a real problem. Quite honestly I even haven't looked into patches until it is clear that the usecase is sound. We should enforce this more and leave patches lingering if they are not sufficiently justified.

Signature · 2 lines
Michal Hocko
SUSE Labs
#10 lore raw
Quoted 30 lines · Michal Hocko
Quoted 29 lines · Lorenzo Stoakes (ARM)

+cc Roman for suggestion.

Quoted 4 lines · Michal Hocko

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

This is exactly the issue with these 'unrelated to your patch but' suggestions from sashiko.

You end up in loops:

AI generated patch ---------------> AI generated review
^ |
| |
| v
AI generated 'unrelated to your patch but'

And _at every stage_ reviewers have to do _additional work_ (with ~50% signal/noise).

This isn't sustainable.

We already had _too much work_ prior to the slopgeddon. Now we have a multiple of that.

Roman - I really think we a way of switching off the 'unrelated to your patch but' stuff per-subsystem would be useful.

Quoted 8 lines
Quoted 5 lines

Maybe we could figure out a way of funnelling this stuff somewhere separately longer term.

(I have I think 2 slopped fixes to rewrite after the previous what like 7 or 8 this cycle? So forgive the grumpiness :)

I wouldn't blame Sashiko on this really. Yes it points to a theoretical problem. That is fine. But we should encourage people to not blindly

I mean it's not only this case, it's a pattern I've been observing for a while.

Quoted 4 lines

follow that lead and immediately jump at fixing something that is not a real problem. Quite honestly I even haven't looked into patches until it is clear that the usecase is sound. We should enforce this more and leave patches lingering if they are not sufficiently justified.

Yes this is a needed change in mm, but until we fully transition workflow any patch might still land.

And I still find those kinds of suggestions deeply problematic for reviewer workload.

If you had a person repeatedly say 'hey unrelated to this series but...' you'd very quickly ask them to stop and if they persisted, >/dev/null them.

I get that passive passes are too expensive and it's not that much more work to have sashiko point this stuff out, but it's not that much more work for _it_, it's substantially increasing workload for reviewers.

As I said on a recent call - it's fine as long as you don't care about reviewer/maintainer burnout.

But I do so :)

Quoted 3 lines

--
Michal Hocko
SUSE Labs

--
Cheers, Lorenzo

#11 lore raw
Quoted 34 lines · Lorenzo Stoakes (ARM)
Quoted 33 lines · Michal Hocko
Quoted 32 lines · Lorenzo Stoakes (ARM)

+cc Roman for suggestion.

Quoted 4 lines · Michal Hocko

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

This is exactly the issue with these 'unrelated to your patch but' suggestions from sashiko.

You end up in loops:

AI generated patch ---------------> AI generated review
^ |
| |
| v
AI generated 'unrelated to your patch but'

And _at every stage_ reviewers have to do _additional work_ (with ~50% signal/noise).

This isn't sustainable.

We already had _too much work_ prior to the slopgeddon. Now we have a multiple of that.

Roman - I really think we a way of switching off the 'unrelated to your patch but' stuff per-subsystem would be useful.

Maybe we could figure out a way of funnelling this stuff somewhere separately longer term.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

(I have I think 2 slopped fixes to rewrite after the previous what like 7 or 8 this cycle? So forgive the grumpiness :)

I wouldn't blame Sashiko on this really. Yes it points to a theoretical problem. That is fine. But we should encourage people to not blindly

I mean it's not only this case, it's a pattern I've been observing for a while.

Yes, I know.

Quoted 14 lines
Quoted 4 lines

follow that lead and immediately jump at fixing something that is not a real problem. Quite honestly I even haven't looked into patches until it is clear that the usecase is sound. We should enforce this more and leave patches lingering if they are not sufficiently justified.

Yes this is a needed change in mm, but until we fully transition workflow any patch might still land.

And I still find those kinds of suggestions deeply problematic for reviewer workload.

If you had a person repeatedly say 'hey unrelated to this series but...' you'd very quickly ask them to stop and if they persisted, >/dev/null them.

Quoted 8 lines

I get that passive passes are too expensive and it's not that much more work to have sashiko point this stuff out, but it's not that much more work for _it_, it's substantially increasing workload for reviewers.

As I said on a recent call - it's fine as long as you don't care about reviewer/maintainer burnout.

But I do so :)

Hey, do not get me wrong. I very much care about reviewers as well. I merely wanted to say that we've had peaks of tool driven patches no matter what. Things have eventually normalized but people need to be educated about expectations of maintainers. Sashiko suggesting "but this might need attention as well..." certainly contribute to more noise but keep in mind that AI driven people are going to find a tool of their choice to arm them with ideas to implement and post so focusing on Sashiko is not going to help all that much. My main message is to establish and enforce a notion that patches are going to be ignored if they are not justified properly (even if they might seem technically correct or fixing a theoretical problem).

Signature · 2 lines
Michal Hocko
SUSE Labs
#12 lore raw
Quoted 11 lines · Michal Hocko

Hey, do not get me wrong. I very much care about reviewers as well. I merely wanted to say that we've had peaks of tool driven patches no matter what. Things have eventually normalized but people need to be educated about expectations of maintainers. Sashiko suggesting "but this might need attention as well..." certainly contribute to more noise but keep in mind that AI driven people are going to find a tool of their choice to arm them with ideas to implement and post so focusing on Sashiko is not going to help all that much. My main message is to establish and enforce a notion that patches are going to be ignored if they are not justified properly (even if they might seem technically correct or fixing a theoretical problem).

I couldn't agree more.

We are, of course, furiously agreeing here :>)

Quoted 3 lines

--
Michal Hocko
SUSE Labs

--
Cheers, Lorenzo

#13 lore raw
In reply to Lorenzo Stoakes (ARM)
Quoted 21 lines · Lorenzo Stoakes (ARM)

+cc Roman for suggestion.

Quoted 4 lines · Michal Hocko

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

This is exactly the issue with these 'unrelated to your patch but' suggestions from sashiko.

You end up in loops:

AI generated patch ---------------> AI generated review
^ |
| |
| v
AI generated 'unrelated to your patch but'

And _at every stage_ reviewers have to do _additional work_ (with ~50% signal/noise).

Quoted 7 lines

This isn't sustainable.

We already had _too much work_ prior to the slopgeddon. Now we have a multiple of that.

Roman - I really think we a way of switching off the 'unrelated to your patch but' stuff per-subsystem would be useful.

I thought about it, but switching it off completely looks dubious to me. So maybe moderating it a bit can help? Like not reporting completely unrelated issues or at least do not report them more than once? Even if it's only 50% accurate, 50% chance of missing a critical/high severity bug (other are not reported) sounds bad.

But also I feel like there should be no expectations on fixing pre-existing issues as something _required_ to land a patch. It's always a best effort thing.

Quoted 2 lines

Maybe we could figure out a way of funnelling this stuff somewhere separately longer term.

Yes, there will be a database available to maintainers soon.

Quoted 3 lines

(I have I think 2 slopped fixes to rewrite after the previous what like 7 or 8 this cycle? So forgive the grumpiness :)

No problems :)

#14 lore raw
Quoted 2 lines · Roman Gushchin
Quoted 1 line · Lorenzo Stoakes (ARM)

On Fri, Aug 21, 2026 at 10:31:06AM +0200, Michal Hocko wrote:

...>> We already had _too much work_ prior to the slopgeddon. Now we have a multiple

Quoted 8 lines
Quoted 4 lines

of that.

Roman - I really think we a way of switching off the 'unrelated to your patch but' stuff per-subsystem would be useful.

I thought about it, but switching it off completely looks dubious to me. So maybe moderating it a bit can help? Like not reporting completely unrelated issues or at least do not report them more than once?

Or, reporting them *once*, and not as a reply to the patch!

After all, it's "unrelated to your patch". So then, don't pollute that patch's review thread, right?

That would alleviate the most infuriating part of this cycle, imho.

thanks,

Signature · 1 line
John Hubbard
#15 lore raw
Quoted 13 lines · John Hubbard
Quoted 2 lines · Roman Gushchin
Quoted 1 line · Lorenzo Stoakes (ARM)

On Fri, Aug 21, 2026 at 10:31:06AM +0200, Michal Hocko wrote:

...>> We already had _too much work_ prior to the slopgeddon. Now we have a multiple

Quoted 8 lines
Quoted 4 lines

of that.

Roman - I really think we a way of switching off the 'unrelated to your patch but' stuff per-subsystem would be useful.

I thought about it, but switching it off completely looks dubious to me. So maybe moderating it a bit can help? Like not reporting completely unrelated issues or at least do not report them more than once?

Quoted 6 lines

Or, reporting them *once*, and not as a reply to the patch!

After all, it's "unrelated to your patch". So then, don't pollute that patch's review thread, right?

That would alleviate the most infuriating part of this cycle, imho.

Yes, this sounds good to me, it requires quite some work on the sashiko side, so I can't promise it tomorrow, but hopefully in few weeks.

Thanks

#16 lore raw
In reply to Roman Gushchin
Quoted 1 line · On Fri, 21 Aug 2026 21:35:04 +0000 Roman Gushchin <[email protected]>…

"Lorenzo Stoakes (ARM)" <[email protected]> writes:

[...]

Quoted 4 lines
Quoted 2 lines

Maybe we could figure out a way of funnelling this stuff somewhere separately longer term.

Yes, there will be a database available to maintainers soon.

Awesome, that will be helpful for me!

Thanks,
SJ

[...]

#17 lore raw
In reply to Lorenzo Stoakes (ARM)
Quoted 13 lines · Lorenzo Stoakes (ARM)

+cc Roman for suggestion.

Quoted 4 lines · Michal Hocko

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

This is exactly the issue with these 'unrelated to your patch but' suggestions from sashiko.

You end up in loops:

Quoted 8 lines

AI generated patch ---------------> AI generated review
^ |
| |
| v
AI generated 'unrelated to your patch but'

And _at every stage_ reviewers have to do _additional work_ (with ~50% signal/noise).

We've been trying to reach you about your kernel's pre-existing conditions.

The reviewer piece aside - which I agree with (aggressively) - there's actually some value on the first go-around of this cycle. It has been valuable to me in identifying bugs that are affecting my active development areas - which is simultaneously frustrating and helpful.

Feels like ye olde map cycle problem - just break the chain at some point and let it sit in a database and create a timeout on how often sashiko can nag about a particular pre-existing bug.

If it keeps getting reported on - that means the area is under active development and we should really address it.

Also - bugs is bugs. Ignoring them is double plus bad.

~Gregory

#18 lore raw
In reply to Michal Hocko
Quoted 16 lines · Michal Hocko
Quoted 9 lines · Ridong Chen

From: Ridong Chen <[email protected]>

min_unmapped_pages and min_slab_pages are documented as per-type limits, but node reclaim treats them as one combined gate: once either is exceeded, shrink_node() reclaims slab, file and anon together and pushes the other type below its limit. Per-node proactive reclaim reuses the same gate and fares worse -- with page cache and slab both under their limits it reclaims nothing and returns -EAGAIN on an anon-heavy node [1].

Quoted 5 lines

This series gates each type separately via two scan_control flags (skip_slab_reclaim, skip_file_reclaim) set only on the node reclaim path, drops the combined gate, and extends node_reclaim()'s early bail to check anon. The flags default to zero, so kswapd, direct, memcg and drop_caches are unaffected.

Quoted 5 lines

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

Hi Michal,

Thank you for your reply. I should have made the background much clearer.

Yes, the original issue comes from Sashiko's review. Sashiko found that proactive reclaim fails to reclaim memory when the node's unmapped file or slab pages are below the minimum thresholds, even though there is plenty of anonymous memory available.

After further discussion, we realized that min_unmapped_pages and min_slab_pages may not be used correctly. Apart from the issue above, there are other problems as mentioned by Barry in [2]:

Even when page cache is below min_unmapped_pages, it may still be reclaimed as long as slab is sufficient. Similarly, slab may still be reclaimed even when it is below min_slab_pages.

node_reclaim() cannot reclaim anonymous pages if both page cache and slab are below their respective thresholds, even when there is plenty of anonymous memory available.

To address these issues, I am sending this series to facilitate discussion. Your feedback would be greatly appreciated.

[2] https://lore.kernel.org/linux-mm/CAGsJ_4xCi1TzV0sg=8ZFhfAq7v=k=Q6a10DNDouYOrjcJL-5=A@mail.gmail.com/

Quoted 34 lines
Quoted 33 lines

Tested on QEMU (x86_64, 2 NUMA nodes), A/B kernels differing only in this series.

Proactive reclaim (echo to node/reclaim) on an anon-heavy node, file and slab under their limits:

   metric              before   after
   -----------------   ------   -----------
   pages reclaimed     0 MiB    254 MiB anon
   return value        -EAGAIN  0

Node reclaim (zone_reclaim_mode) with one type under its limit -- the type under its limit must be left alone:

   type under limit    before          after
   -----------------   -------------   --------
   slab                645 scans       0 scans
   page cache          83 MiB scanned  0 MiB

[1] https://sashiko.dev/#/patchset/[email protected]

Ridong Chen (4):
mm/vmscan: only reclaim slab in node reclaim when over min_slab_pages mm/vmscan: only reclaim file pages in node reclaim when over
min_unmapped_pages
mm/vmscan: drop the combined limit gate in __node_reclaim() mm/vmscan: do not skip node reclaim when only anon is reclaimable

  mm/vmscan.c | 89 ++++++++++++++++++++++++++++++++++++++++-------------
  1 file changed, 67 insertions(+), 22 deletions(-)
Signature · 1 line
2.34.1
Signature · 2 lines
Best regards
Ridong
#19 lore raw
Quoted 42 lines · Ridong Chen
Quoted 16 lines · Michal Hocko
Quoted 9 lines · Ridong Chen

From: Ridong Chen <[email protected]>

min_unmapped_pages and min_slab_pages are documented as per-type limits, but node reclaim treats them as one combined gate: once either is exceeded, shrink_node() reclaims slab, file and anon together and pushes the other type below its limit. Per-node proactive reclaim reuses the same gate and fares worse -- with page cache and slab both under their limits it reclaims nothing and returns -EAGAIN on an anon-heavy node [1].

Quoted 5 lines

This series gates each type separately via two scan_control flags (skip_slab_reclaim, skip_file_reclaim) set only on the node reclaim path, drops the combined gate, and extends node_reclaim()'s early bail to check anon. The flags default to zero, so kswapd, direct, memcg and drop_caches are unaffected.

Quoted 5 lines

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

Hi Michal,

Thank you for your reply. I should have made the background much clearer.

Yes, the original issue comes from Sashiko's review. Sashiko found that proactive reclaim fails to reclaim memory when the node's unmapped file or slab pages are below the minimum thresholds, even though there is plenty of anonymous memory available.

After further discussion, we realized that min_unmapped_pages and min_slab_pages may not be used correctly. Apart from the issue above, there are other problems as mentioned by Barry in [2]:

Even when page cache is below min_unmapped_pages, it may still be reclaimed as long as slab is sufficient. Similarly, slab may still be reclaimed even when it is below min_slab_pages.

Quoted 6 lines

node_reclaim() cannot reclaim anonymous pages if both page cache and slab are below their respective thresholds, even when there is plenty of anonymous memory available.

To address these issues, I am sending this series to facilitate discussion. Your feedback would be greatly appreciated.

Those interfaces are relicts from the distant past same as the node reclaim. I wouldn't bother fixing those unless there is a real usecase. Pro-active per node reclaim is a different thing and we should probably divorce it from those min_$foo counters altogether (if they are not yet).

Same as checkpatch.pl, shashiko is giving you hints and you shouldn't simply follow them without a deeper considerations. Consider that there is review capacity required for any patch posted. We do not want to waste that scarce resource.

Signature · 2 lines
Michal Hocko
SUSE Labs
#20 lore raw
Quoted 47 lines · Michal Hocko
Quoted 46 lines · Ridong Chen
Quoted 16 lines · Michal Hocko
Quoted 9 lines · Ridong Chen

From: Ridong Chen <[email protected]>

min_unmapped_pages and min_slab_pages are documented as per-type limits, but node reclaim treats them as one combined gate: once either is exceeded, shrink_node() reclaims slab, file and anon together and pushes the other type below its limit. Per-node proactive reclaim reuses the same gate and fares worse -- with page cache and slab both under their limits it reclaims nothing and returns -EAGAIN on an anon-heavy node [1].

Quoted 5 lines

This series gates each type separately via two scan_control flags (skip_slab_reclaim, skip_file_reclaim) set only on the node reclaim path, drops the combined gate, and extends node_reclaim()'s early bail to check anon. The flags default to zero, so kswapd, direct, memcg and drop_caches are unaffected.

Quoted 5 lines

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

Hi Michal,

Thank you for your reply. I should have made the background much clearer.

Yes, the original issue comes from Sashiko's review. Sashiko found that proactive reclaim fails to reclaim memory when the node's unmapped file or slab pages are below the minimum thresholds, even though there is plenty of anonymous memory available.

After further discussion, we realized that min_unmapped_pages and min_slab_pages may not be used correctly. Apart from the issue above, there are other problems as mentioned by Barry in [2]:

Even when page cache is below min_unmapped_pages, it may still be reclaimed as long as slab is sufficient. Similarly, slab may still be reclaimed even when it is below min_slab_pages.

node_reclaim() cannot reclaim anonymous pages if both page cache and slab are below their respective thresholds, even when there is plenty of anonymous memory available.

Quoted 9 lines
Quoted 2 lines

To address these issues, I am sending this series to facilitate discussion. Your feedback would be greatly appreciated.

Those interfaces are relicts from the distant past same as the node reclaim. I wouldn't bother fixing those unless there is a real usecase. Pro-active per node reclaim is a different thing and we should probably divorce it from those min_$foo counters altogether (if they are not yet).

Yeah, proactive per-node reclaim currently does not divorce from those min_$foo counters.

Did you mean that min_slab_pages and min_unmapped_pages should influence proactive per-node reclaim? If so, perhaps the easiest fix would be something like this:

```
static unsigned long __node_reclaim(struct pglist_data *pgdat,
				    unsigned long nr_pages,
				    struct scan_control *sc)
{
...
if (sc->proactive ||
    node_pagecache_reclaimable(pgdat) > pgdat->min_unmapped_pages ||
    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) > pgdat->min_slab_pages) {
	do {
			shrink_node(pgdat, sc);
	} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
}
...
}
```
Quoted 5 lines

Same as checkpatch.pl, shashiko is giving you hints and you shouldn't simply follow them without a deeper considerations. Consider that there is review capacity required for any patch posted. We do not want to waste that scarce resource.

You are right. I should be more considerate. Sometimes, due to my lack of experience, I cannot come up with a good solution on my own, so I send RFCs to gather professional opinions. Thank you for your time and guidance.

Signature · 2 lines
Best regards
Ridong
#21 lore raw
Quoted 60 lines · Ridong Chen
Quoted 47 lines · Michal Hocko
Quoted 46 lines · Ridong Chen
Quoted 16 lines · Michal Hocko
Quoted 9 lines · Ridong Chen

From: Ridong Chen <[email protected]>

min_unmapped_pages and min_slab_pages are documented as per-type limits, but node reclaim treats them as one combined gate: once either is exceeded, shrink_node() reclaims slab, file and anon together and pushes the other type below its limit. Per-node proactive reclaim reuses the same gate and fares worse -- with page cache and slab both under their limits it reclaims nothing and returns -EAGAIN on an anon-heavy node [1].

Quoted 5 lines

This series gates each type separately via two scan_control flags (skip_slab_reclaim, skip_file_reclaim) set only on the node reclaim path, drops the combined gate, and extends node_reclaim()'s early bail to check anon. The flags default to zero, so kswapd, direct, memcg and drop_caches are unaffected.

Quoted 5 lines

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

Hi Michal,

Thank you for your reply. I should have made the background much clearer.

Yes, the original issue comes from Sashiko's review. Sashiko found that proactive reclaim fails to reclaim memory when the node's unmapped file or slab pages are below the minimum thresholds, even though there is plenty of anonymous memory available.

After further discussion, we realized that min_unmapped_pages and min_slab_pages may not be used correctly. Apart from the issue above, there are other problems as mentioned by Barry in [2]:

Even when page cache is below min_unmapped_pages, it may still be reclaimed as long as slab is sufficient. Similarly, slab may still be reclaimed even when it is below min_slab_pages.

node_reclaim() cannot reclaim anonymous pages if both page cache and slab are below their respective thresholds, even when there is plenty of anonymous memory available.

Quoted 9 lines
Quoted 2 lines

To address these issues, I am sending this series to facilitate discussion. Your feedback would be greatly appreciated.

Those interfaces are relicts from the distant past same as the node reclaim. I wouldn't bother fixing those unless there is a real usecase. Pro-active per node reclaim is a different thing and we should probably divorce it from those min_$foo counters altogether (if they are not yet).

Quoted 5 lines

Yeah, proactive per-node reclaim currently does not divorce from those min_$foo counters.

Did you mean that min_slab_pages and min_unmapped_pages should influence proactive per-node reclaim?

Nope, exactly opposite

Signature · 2 lines
Michal Hocko
SUSE Labs
#22 lore raw
Quoted 61 lines · Michal Hocko
Quoted 60 lines · Ridong Chen
Quoted 56 lines · Michal Hocko
Quoted 42 lines · Ridong Chen
Quoted 16 lines · Michal Hocko
Quoted 9 lines · Ridong Chen

From: Ridong Chen <[email protected]>

min_unmapped_pages and min_slab_pages are documented as per-type limits, but node reclaim treats them as one combined gate: once either is exceeded, shrink_node() reclaims slab, file and anon together and pushes the other type below its limit. Per-node proactive reclaim reuses the same gate and fares worse -- with page cache and slab both under their limits it reclaims nothing and returns -EAGAIN on an anon-heavy node [1].

Quoted 5 lines

This series gates each type separately via two scan_control flags (skip_slab_reclaim, skip_file_reclaim) set only on the node reclaim path, drops the combined gate, and extends node_reclaim()'s early bail to check anon. The flags default to zero, so kswapd, direct, memcg and drop_caches are unaffected.

Quoted 5 lines

You are explaining what but missing the most important part _Why_ do we need to have this addressed? Is this just addressing Sashiko review refernced below? Is there any real usecase where the current behavior matters?

Hi Michal,

Thank you for your reply. I should have made the background much clearer.

Yes, the original issue comes from Sashiko's review. Sashiko found that proactive reclaim fails to reclaim memory when the node's unmapped file or slab pages are below the minimum thresholds, even though there is plenty of anonymous memory available.

After further discussion, we realized that min_unmapped_pages and min_slab_pages may not be used correctly. Apart from the issue above, there are other problems as mentioned by Barry in [2]:

Even when page cache is below min_unmapped_pages, it may still be reclaimed as long as slab is sufficient. Similarly, slab may still be reclaimed even when it is below min_slab_pages.

Quoted 6 lines

node_reclaim() cannot reclaim anonymous pages if both page cache and slab are below their respective thresholds, even when there is plenty of anonymous memory available.

To address these issues, I am sending this series to facilitate discussion. Your feedback would be greatly appreciated.

Those interfaces are relicts from the distant past same as the node reclaim. I wouldn't bother fixing those unless there is a real usecase. Pro-active per node reclaim is a different thing and we should probably divorce it from those min_$foo counters altogether (if they are not yet).

Quoted 8 lines
Quoted 5 lines

Yeah, proactive per-node reclaim currently does not divorce from those min_$foo counters.

Did you mean that min_slab_pages and min_unmapped_pages should influence proactive per-node reclaim?

Nope, exactly opposite

I would really appreciate it if you could clarify this further. Sorry, I'm not sure I fully understand what you meant.

Signature · 2 lines
Best regards
Ridong
#23 lore raw
Quoted 8 lines · Ridong Chen
Quoted 5 lines
Quoted 2 lines

Did you mean that min_slab_pages and min_unmapped_pages should influence proactive per-node reclaim?

Nope, exactly opposite

I would really appreciate it if you could clarify this further. Sorry, I'm not sure I fully understand what you meant.

pro-active (userspace triggered) node reclaim should completely ignore all those historical node_reclaim tunables.

Signature · 2 lines
Michal Hocko
SUSE Labs
#24 lore raw
Quoted 4 lines · Michal Hocko
Quoted 3 lines · Ridong Chen
Quoted 3 lines
Quoted 2 lines

Did you mean that min_slab_pages and min_unmapped_pages should influence proactive per-node reclaim?

I apologize for being incoherent in my last email, it was after a full day of work. What I wanted to say is that min_slab_pages and min_unmapped_pages should *not* influence proactive per-node reclaim.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

Nope, exactly opposite

I would really appreciate it if you could clarify this further. Sorry, I'm not sure I fully understand what you meant.

pro-active (userspace triggered) node reclaim should completely ignore all those historical node_reclaim tunables.

Yeah, this matches my understanding. Apologies again for the confusing reply.

Since Johannes has acked patch 3, perhaps we can simply remove the conditional check in __node_reclaim and keep the shrink loop as follows:

```
@@ -7906,16 +7906,16 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat,
 	noreclaim_flag = memalloc_noreclaim_save();
 	set_task_reclaim_state(p, &sc->reclaim_state);

-	if (node_pagecache_reclaimable(pgdat) > pgdat->min_unmapped_pages ||
-	    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) > pgdat->min_slab_pages) {
-		/*
-		 * Free memory by calling shrink node with increasing
-		 * priorities until we have enough memory freed.
-		 */
-		do {
-			shrink_node(pgdat, sc);
-		} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
-	}
+	/*
+	 * Free memory by calling shrink node with increasing
+	 * priorities until we have enough memory freed.
+	 *
+	 * What to reclaim is gated per type by sc->skip_slab_reclaim and
+	 * sc->skip_file_reclaim.
+	 */
+	do {
+		shrink_node(pgdat, sc);
+	} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);

 	set_task_reclaim_state(p, NULL);
 	memalloc_noreclaim_restore(noreclaim_flag);

```
Signature · 2 lines
Best regards
Ridong
#25 lore raw
Quoted 54 lines · Ridong Chen
Quoted 4 lines · Michal Hocko
Quoted 3 lines · Ridong Chen
Quoted 3 lines
Quoted 2 lines

Did you mean that min_slab_pages and min_unmapped_pages should influence proactive per-node reclaim?

I apologize for being incoherent in my last email, it was after a full day of work. What I wanted to say is that min_slab_pages and min_unmapped_pages should *not* influence proactive per-node reclaim.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

Nope, exactly opposite

I would really appreciate it if you could clarify this further. Sorry, I'm not sure I fully understand what you meant.

pro-active (userspace triggered) node reclaim should completely ignore all those historical node_reclaim tunables.

Yeah, this matches my understanding. Apologies again for the confusing reply.

Since Johannes has acked patch 3, perhaps we can simply remove the conditional check in __node_reclaim and keep the shrink loop as follows:

```

(quoted diff) +10 -10
@@ -7906,16 +7906,16 @@ static unsigned long __node_reclaim(struct
 pglist_data *pgdat,
 	noreclaim_flag = memalloc_noreclaim_save();
 	set_task_reclaim_state(p, &sc->reclaim_state);
 
-	if (node_pagecache_reclaimable(pgdat) > pgdat->min_unmapped_pages ||
-	    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) > pgdat->min_slab_pages) {
-		/*
-		 * Free memory by calling shrink node with increasing
-		 * priorities until we have enough memory freed.
-		 */
-		do {
-			shrink_node(pgdat, sc);
-		} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
-	}
+	/*
+	 * Free memory by calling shrink node with increasing
+	 * priorities until we have enough memory freed.
+	 *
+	 * What to reclaim is gated per type by sc->skip_slab_reclaim and
+	 * sc->skip_file_reclaim.
+	 */
+	do {
+		shrink_node(pgdat, sc);
+	} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
 
 	set_task_reclaim_state(p, NULL);
Quoted 1 line

memalloc_noreclaim_restore(noreclaim_flag);

Could you post a stand alone patch with the full description please?

Signature · 2 lines
Michal Hocko
SUSE Labs
#26 lore raw
Quoted 54 lines · Michal Hocko
Quoted 53 lines · Ridong Chen
Quoted 4 lines · Michal Hocko
Quoted 3 lines · Ridong Chen
Quoted 3 lines
Quoted 2 lines

Did you mean that min_slab_pages and min_unmapped_pages should influence proactive per-node reclaim?

I apologize for being incoherent in my last email, it was after a full day of work. What I wanted to say is that min_slab_pages and min_unmapped_pages should *not* influence proactive per-node reclaim.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

Nope, exactly opposite

I would really appreciate it if you could clarify this further. Sorry, I'm not sure I fully understand what you meant.

pro-active (userspace triggered) node reclaim should completely ignore all those historical node_reclaim tunables.

Yeah, this matches my understanding. Apologies again for the confusing reply.

Since Johannes has acked patch 3, perhaps we can simply remove the conditional check in __node_reclaim and keep the shrink loop as follows:

```

(quoted diff) +10 -10
@@ -7906,16 +7906,16 @@ static unsigned long __node_reclaim(struct
 pglist_data *pgdat,
  	noreclaim_flag = memalloc_noreclaim_save();
  	set_task_reclaim_state(p, &sc->reclaim_state);
 
-	if (node_pagecache_reclaimable(pgdat) > pgdat->min_unmapped_pages ||
-	    node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) > pgdat->min_slab_pages) {
-		/*
-		 * Free memory by calling shrink node with increasing
-		 * priorities until we have enough memory freed.
-		 */
-		do {
-			shrink_node(pgdat, sc);
-		} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
-	}
+	/*
+	 * Free memory by calling shrink node with increasing
+	 * priorities until we have enough memory freed.
+	 *
+	 * What to reclaim is gated per type by sc->skip_slab_reclaim and
+	 * sc->skip_file_reclaim.
+	 */
+	do {
+		shrink_node(pgdat, sc);
+	} while (sc->nr_reclaimed < nr_pages && --sc->priority >= 0);
 
Quoted 4 lines
Quoted 2 lines
(quoted diff) +0 -0
  	set_task_reclaim_state(p, NULL);
  	memalloc_noreclaim_restore(noreclaim_flag);

Could you post a stand alone patch with the full description please?

I have just done so. I would appreciate any comments you might have on it.

Signature · 2 lines
Best regards
Ridong