Re: [PATCH] mm/mglru: preserve inactive placement when enabling MGLRU
Ridong Chen <[email protected]>
| Newsgroups | org.kvack.linux-mm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On 8/20/2026 3:50 PM, Barry Song wrote: > On Thu, Aug 20, 2026 at 3:27 PM Ridong Chen <[email protected]> wrote: >> >> From: Ridong Chen <[email protected]> >> >> When the LRU is switched to MGLRU (echo y > /sys/kernel/mm/lru_gen/ >> enabled), fill_evictable() re-inserts every folio via >> lru_gen_add_folio(..., false). With reclaiming hardcoded to false, an >> inactive anonymous folio (no PG_active, not in the swapcache) takes the >> "gen = MIN_NR_GENS" branch in lru_gen_folio_seq() and is seeded at >> seq = max_seq - 1, which lru_gen_is_active() treats as active. Its >> inactive placement is lost and NR_INACTIVE_ANON is folded into >> NR_ACTIVE_ANON. >> >> Pass reclaiming=!active so a folio from an inactive list is seeded into >> an older generation. Folios from the active list carry PG_active and >> hit the first branch either way, so they are unchanged. >> >> reclaiming also selects the insertion end in lru_gen_add_folio(): >> list_add_tail() for inactive folios, list_add() for active ones. Both >> the legacy LRU and a MGLRU generation keep the hottest folios at the >> head and the coldest at the tail, and reclaim takes from the tail. To >> preserve that order the folio must be taken from the end matching the >> insertion end, so take inactive folios from the head and active folios >> from the tail; otherwise hot/cold would be reversed within the >> generation. >> >> Tested on x86_64, next-20260812, 2G VM + 1G swap, ~1.5G anon pushed onto >> the inactive list before enabling MGLRU: >> >> Active(anon) Inactive(anon) >> before switch (legacy) 2952 1548792 kB >> after `echo y`, unpatched 1552052 0 kB >> after `echo y`, patched 15144 1536636 kB >> >> Inactive file folios stay inactive either way (NR_INACTIVE_FILE is >> preserved). >> >> Fixes: 354ed5974429 ("mm: multi-gen LRU: kill switch") >> Suggested-by: Barry Song <[email protected]> >> Assisted-by: Claude:claude-opus-4-8 >> Signed-off-by: Ridong Chen <[email protected]> >> --- >> mm/vmscan.c | 29 +++++++++++++++++++++++++++-- >> 1 file changed, 27 insertions(+), 2 deletions(-) >> >> diff --git a/mm/vmscan.c b/mm/vmscan.c >> index 94fc4f25e99f..7be4cec9a838 100644 >> --- a/mm/vmscan.c >> +++ b/mm/vmscan.c >> @@ -5311,7 +5311,23 @@ static bool fill_evictable(struct lruvec *lruvec) >> >> while (!list_empty(head)) { >> bool success; >> - struct folio *folio = lru_to_folio(head); >> + struct folio *folio; >> + >> + /* >> + * Both the legacy LRU and a MGLRU generation keep the >> + * hottest folios at the head and the coldest at the >> + * tail, and reclaim takes from the tail. To preserve >> + * that order, the end we take from must match the end >> + * lru_gen_add_folio() inserts at: inactive folios use >> + * reclaiming=true (list_add_tail), so take from the >> + * head; active folios use reclaiming=false (list_add), >> + * so take from the tail. Taking from the wrong end >> + * would reverse hot/cold within the generation. >> + */ > > too many words, maybe just: > > lru_gen_add_folio() uses list_add_tail() rather than list_add() > when reclaiming is true. Match its ordering to avoid cold/hot > Inversion. > >> + if (active) >> + folio = lru_to_folio(head); >> + else >> + folio = list_first_entry(head, struct folio, lru); >> >> VM_WARN_ON_ONCE_FOLIO(folio_test_unevictable(folio), folio); >> VM_WARN_ON_ONCE_FOLIO(folio_test_active(folio) != active, folio); >> @@ -5319,7 +5335,16 @@ static bool fill_evictable(struct lruvec *lruvec) >> VM_WARN_ON_ONCE_FOLIO(folio_lru_gen(folio) != -1, folio); >> >> lruvec_del_folio(lruvec, folio); >> - success = lru_gen_add_folio(lruvec, folio, false); >> + /* >> + * With reclaiming=false, lru_gen_folio_seq() would seed >> + * an inactive folio near max_seq, which >> + * lru_gen_is_active() reports as active, so its inactive >> + * placement would be lost. Pass reclaiming=!active to >> + * seed it into the oldest generation instead. This >> + * reuses reclaiming beyond its folio_rotate_reclaimable() >> + * meaning; it also picks list_add_tail() above. >> + */ > > Maybe that's too verbose. How about: > > "Borrow reclaiming=true to place inactive folios in the older gens" > >> + success = lru_gen_add_folio(lruvec, folio, !active); >> VM_WARN_ON_ONCE(!success); >> >> if (!--remaining) > > This is admittedly a bit ugly, but it seems to be the simplest > approach. Since switching MGLRU on and off is not a common scenario, > we probably don't want to over-engineer it. So, with the above change: > > Acked-by: Barry Song <[email protected]> Thanks. Will update. -- Best regards Ridong