Re: [PATCH 2/3] mm/mglru: improve scan_folios() exhaustion detection
From: Ridong Chen
Date: Thu Aug 20 2026 - 21:45:04 EST
On 8/20/2026 12:56 PM, Barry Song (Xiaomi) wrote:
Commit 16b475d2ac3c ("mm/mglru: avoid reclaim type fall back when
isolation makes no progress") uses scanned == 0 to determine
whether scan_folios() has exhausted a reclaim type. However,
this is not always sufficient. It is possible for scanned > 0,
while the oldest reclaimable generation is exhausted after the
first scan_folios() call.
We detect early_stop in scan_folios(). If we stop early for any reason,
it means the current reclaim type is not exhausted yet. If early_stop is
never reached, it means we have exhausted the current oldest generation
without hitting any scanning limit.
Another issue is that if the lruvec has 4 generations, we might have
exhausted the oldest generation while the second oldest generation is
still reclaimable. In that case, this type is not exhausted yet.
Add an exhausted output argument to scan_folios() so it can
explicitly report whether the reclaimable lists for the current
type have been exhausted.
Signed-off-by: Barry Song (Xiaomi) <baohua@xxxxxxxxxx>
---
mm/vmscan.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index d5cc30b667ad..1f2e574b0061 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4721,7 +4721,8 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca
static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
struct scan_control *sc, int type, int tier,
- struct list_head *list, int *isolatedp)
+ struct list_head *list, int *isolatedp,
+ bool *exhausted)
{
int i;
int gen;
@@ -4732,12 +4733,15 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
int skipped = 0;
unsigned long remaining = nr_to_scan;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
+ bool early_stop = false;
VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH);
VM_WARN_ON_ONCE(!list_empty(list));
- if (get_nr_gens(lruvec, type) == MIN_NR_GENS)
+ if (get_nr_gens(lruvec, type) == MIN_NR_GENS) {
+ *exhausted = true;
return 0;
+ }
gen = lru_gen_from_seq(lrugen->min_seq[type]);
@@ -4768,8 +4772,10 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
skipped_zone += delta;
}
- if (!--remaining || max(isolated, skipped_zone) >= MIN_LRU_BATCH)
+ if (!--remaining || max(isolated, skipped_zone) >= MIN_LRU_BATCH) {
+ early_stop = true;
break;
+ }
}
if (skipped_zone) {
@@ -4778,8 +4784,10 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
skipped += skipped_zone;
}
- if (!remaining || isolated >= MIN_LRU_BATCH)
+ if (!remaining || isolated >= MIN_LRU_BATCH) {
+ early_stop = true;
break;
+ }
}
item = PGSCAN_KSWAPD + reclaimer_offset(sc);
@@ -4790,6 +4798,13 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
scanned, skipped, isolated,
type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
+ /*
+ * If we didn't stop early, all reclaimable folios in the current
+ * generation have been scanned. We are exhausted if this is the last
+ * reclaimable generation.
+ */
+ *exhausted = !early_stop &&
+ lrugen->min_seq[type] + MIN_NR_GENS == lrugen->max_seq;
*isolatedp = isolated;
return scanned;
}
@@ -4847,11 +4862,12 @@ static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
bool type_fallback_allowed = !is_single_type_reclaim(swappiness);
int type = get_type_to_scan(lruvec, swappiness);
int total_scanned = 0, scanned, tier;
+ bool exhausted;
retry:
tier = get_tier_idx(lruvec, type);
scanned = scan_folios(nr_to_scan, lruvec, sc,
- type, tier, list, isolated);
+ type, tier, list, isolated, &exhausted);
total_scanned += scanned;
if (*isolated) {
@@ -4864,7 +4880,7 @@ static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
* We are running out of the current reclaim type. Fall back to
* the other type if allowed.
*/
- if (!scanned && type_fallback_allowed) {
+ if (exhausted && type_fallback_allowed) {
type = !type;
type_fallback_allowed = false;
goto retry;
LGTM.
Reviewed-by: Ridong Chen <ridong.chen@xxxxxxxxx>
--
Best regards
Ridong