Re: [RFC PATCH v3 4/6] mm: mglru: improve scan_folios() exhaustion detection
Barry Song <[email protected]>
| Newsgroups | org.kvack.linux-mm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAGsJ_4y_v1PNyAEg1Xj8FciPNSkaoFifGnxM34QuUWFWMup79Q@mail.gmail.com> |
On Fri, Jul 31, 2026 at 4:39 PM Barry Song (Xiaomi) <[email protected]> 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) <[email protected]> > --- > 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; }