Re: [RFC PATCH v3 2/6] mm: mglru: let scan_folios() scan both reclaimable generations
Barry Song <[email protected]>
| Newsgroups | org.kvack.linux-mm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAGsJ_4xxO-yAxcynYHdbR-PcYUtKQXKMxq9W2xnm7QasEqg75A@mail.gmail.com> |
On Fri, Jul 31, 2026 at 4:39 PM Barry Song (Xiaomi) <[email protected]> wrote: > > When we have four generations, and scan_folios() exhausts the oldest > one while the second-oldest still contains reclaimable folios, the > current implementation doesn't move on to scan the second-oldest > generation. Instead, it returns, leaving that generation with no > chance to be scanned at the current sc->priority. > > This doesn't seem right. Rather than breaking out and starting a > larger next iteration, let's let scan_folios() continue scanning the > second-oldest generation directly. > > Signed-off-by: Barry Song (Xiaomi) <[email protected]> > --- > mm/vmscan.c | 13 +++++++++++-- > 1 file changed, 11 insertions(+), 2 deletions(-) > > diff --git a/mm/vmscan.c b/mm/vmscan.c > index ce027c271e9b..31947fa60f18 100644 > --- a/mm/vmscan.c > +++ b/mm/vmscan.c > @@ -4718,6 +4718,7 @@ 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; > + unsigned long min_seq = lrugen->min_seq[type]; > > VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH); > VM_WARN_ON_ONCE(!list_empty(list)); > @@ -4725,8 +4726,8 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec, > if (get_nr_gens(lruvec, type) == MIN_NR_GENS) > return 0; > > - gen = lru_gen_from_seq(lrugen->min_seq[type]); > - > +next_gen: > + gen = lru_gen_from_seq(min_seq); > for (i = MAX_NR_ZONES; i > 0; i--) { > LIST_HEAD(moved); > int skipped_zone = 0; > @@ -4768,6 +4769,14 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec, > break; > } > > + /* > + * This generation is exhausted across all zones, but the scan > + * target has not been reached yet. Continue with the next > + * reclaimable generation. > + */ > + if (i == 0 && ++min_seq + MIN_NR_GENS <= lrugen->max_seq) > + goto next_gen; https://sashiko.dev/#/patchset/20260731083843.37811-1-baohua%40kernel.org “Does this cause all folios from the next generation to be skipped? When we increment the local min_seq and jump to next_gen, the global lrugen->min_seq[type] remains unchanged. When sort_folio() is called for these newer-generation folios, won't it misidentify them as concurrently promoted? mm/vmscan.c:sort_folio() { ... /* promoted */ if (gen != lru_gen_from_seq(lrugen->min_seq[type])) { list_move(&folio->lru, &lrugen->folios[gen][type][zone]); return true; } ... } Because gen now differs from the global lrugen->min_seq[type], won't this unconditionally return true, moving the folio to the head of the list instead of isolating it, and wasting CPU cycles while falsely inflating the sorted metric?” Good catch. One way to fix it is to update lrugen->min_seq[type] as well in scan_folios(): /* * This generation is exhausted across all zones, but the scan * target has not been reached yet. Continue with the next * reclaimable generation. */ if (!reached_scan_target && ++min_seq + MIN_NR_GENS <= lrugen->max_seq) { reset_ctrl_pos(lruvec, type, true); WRITE_ONCE(lrugen->min_seq[type], min_seq); goto next_gen; } But this seems overly complicated, so I'd like to drop this patch in RFC v4 and instead rely on the outer loop to retry the remaining reclaimable generations. The current issue is that when a reclaim type has four generations, scan_folios() may finish scanning the oldest generation while the second-oldest generation still contains reclaimable folios. However, it won't continue scanning that generation in the same reclaim round. Thanks Barry