Re: [RFC PATCH v3 4/6] mm: mglru: improve scan_folios() exhaustion detection
From: Barry Song
Date: Fri Jul 31 2026 - 19:35:43 EST
On Fri, Jul 31, 2026 at 4:39 PM Barry Song (Xiaomi) <baohua@xxxxxxxxxx> 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.
>
> 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 | 18 +++++++++++++-----
> 1 file changed, 13 insertions(+), 5 deletions(-)
>
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index 0038f33aa318..2e7fef6975b6 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -4707,7 +4707,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;
> @@ -4723,8 +4724,10 @@ 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));
>
> - if (get_nr_gens(lruvec, type) == MIN_NR_GENS)
> + if (get_nr_gens(lruvec, type) == MIN_NR_GENS) {
> + *exhausted = true;
> return 0;
> + }
>
> next_gen:
> gen = lru_gen_from_seq(min_seq);
> @@ -4785,6 +4788,11 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
> scanned, skipped, isolated,
> type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
>
> + /*
> + * This scan exhausted the reclaimable lists before reaching the
> + * scan target or accumulating enough isolated folios.
> + */
> + *exhausted = remaining > 0 && isolated < MIN_LRU_BATCH;
https://sashiko.dev/#/patchset/20260731083843.37811-1-baohua%40kernel.org
"Does this logic properly account for early loop termination when large folios
are skipped?
Looking at the scanning loop earlier in scan_folios():
} else {
list_move(&folio->lru, &moved);
skipped_zone += delta;
}
if (!--remaining || max(isolated, skipped_zone) >= MIN_LRU_BATCH)
break;
If a large folio is skipped, skipped_zone can instantly reach or exceed
MIN_LRU_BATCH. This causes the loop to break early while remaining is still
greater than zero.
Because isolated would also be less than MIN_LRU_BATCH at this point, could
this incorrectly evaluate the exhausted flag to true? This appears like it
would signal to isolate_folios() that the current reclaim type is completely
empty, triggering a premature fallback to the other reclaim type and breaking
the cost-based reclaim balancing logic."
This is a good point. We could break out of a zone once
skipped_zone >= MIN_LRU_BATCH, provided the zone still
contains folios.
It seems I can fix this by checking whether we exited
early after reaching the scan target due to any reason:
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 902d85afc5f6..c0e14048ab7f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4720,6 +4720,7 @@ static int scan_folios(unsigned long nr_to_scan,
struct lruvec *lruvec,
unsigned long remaining = nr_to_scan;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
unsigned long min_seq = lrugen->min_seq[type];
+ bool reached_scan_target = false;
VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH);
VM_WARN_ON_ONCE(!list_empty(list));
@@ -4758,8 +4759,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) {
+ reached_scan_target = true;
break;
+ }
}
if (skipped_zone) {
@@ -4768,8 +4771,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) {
+ reached_scan_target = true;
break;
+ }
}
/*
@@ -4788,11 +4793,7 @@ static int scan_folios(unsigned long
nr_to_scan, struct lruvec *lruvec,
scanned, skipped, isolated,
type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
- /*
- * This scan exhausted the reclaimable lists before reaching the
- * scan target or accumulating enough isolated folios.
- */
- *exhausted = remaining > 0 && isolated < MIN_LRU_BATCH;
+ *exhausted = !reached_scan_target;
*isolatedp = isolated;
return scanned;
}