* [RFC PATCH] mm/mglru: preserve inactive placement when enabling MGLRU
@ 2026-08-13 11:02 Ridong Chen
2026-08-13 11:24 ` Barry Song
0 siblings, 1 reply; 3+ messages in thread
From: Ridong Chen @ 2026-08-13 11:02 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner
Cc: David Hildenbrand, Michal Hocko, Qi Zheng, Shakeel Butt,
Lorenzo Stoakes, Kairui Song, Barry Song, Axel Rasmussen,
Yuanchu Xie, Wei Xu, Brian Geffon,
Jan Alexander Steffens (heftig), Steven Barrett, Yu Zhao,
linux-mm, linux-kernel, Ridong Chen, Ridong Chen
From: Ridong Chen <chenridong@xiaomi.com>
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.
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")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
mm/vmscan.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 94fc4f25e99f..2befc8d7dd3f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -5319,7 +5319,12 @@ 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);
+ /*
+ * Keep a folio from the inactive list inactive:
+ * pass reclaiming=!active so it is not seeded as
+ * active. See lru_gen_folio_seq().
+ */
+ success = lru_gen_add_folio(lruvec, folio, !active);
VM_WARN_ON_ONCE(!success);
if (!--remaining)
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [RFC PATCH] mm/mglru: preserve inactive placement when enabling MGLRU
2026-08-13 11:02 [RFC PATCH] mm/mglru: preserve inactive placement when enabling MGLRU Ridong Chen
@ 2026-08-13 11:24 ` Barry Song
2026-08-13 11:37 ` Barry Song
0 siblings, 1 reply; 3+ messages in thread
From: Barry Song @ 2026-08-13 11:24 UTC (permalink / raw)
To: Ridong Chen
Cc: Andrew Morton, Johannes Weiner, David Hildenbrand, Michal Hocko,
Qi Zheng, Shakeel Butt, Lorenzo Stoakes, Kairui Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Brian Geffon,
Jan Alexander Steffens (heftig), Steven Barrett, Yu Zhao,
linux-mm, linux-kernel, Ridong Chen
On Thu, Aug 13, 2026 at 7:02 PM Ridong Chen <ridong.chen@linux.dev> wrote:
>
> From: Ridong Chen <chenridong@xiaomi.com>
>
> 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.
>
> 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")
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> ---
> mm/vmscan.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index 94fc4f25e99f..2befc8d7dd3f 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -5319,7 +5319,12 @@ 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);
> + /*
> + * Keep a folio from the inactive list inactive:
> + * pass reclaiming=!active so it is not seeded as
> + * active. See lru_gen_folio_seq().
> + */
> + success = lru_gen_add_folio(lruvec, folio, !active);
This is a very interesting use of the reclaim argument, as it is not
intended to serve this MGLRU switch purpose. It is really only meant
for `folio_rotate_reclaimable()`.
However, the change itself seems to be *partially* correct and
*partially* wrong.
One real issue is that inactive is always placed in the oldest
generation, while we have two old generations. Maybe we can ignore
this for now.
but somehow, are we also inverting the cold/hot ordering in the
inactive list?
`lru_to_folio(head)` always takes the tail, but now we are putting the
tail before the head folios.
Because reclaim == true will use list_add_tail().
if (reclaiming)
list_add_tail(&folio->lru, &lrugen->folios[gen][type][zone]);
> VM_WARN_ON_ONCE(!success);
>
> if (!--remaining)
> --
> 2.34.1
>
Thanks
Barry
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC PATCH] mm/mglru: preserve inactive placement when enabling MGLRU
2026-08-13 11:24 ` Barry Song
@ 2026-08-13 11:37 ` Barry Song
0 siblings, 0 replies; 3+ messages in thread
From: Barry Song @ 2026-08-13 11:37 UTC (permalink / raw)
To: Ridong Chen
Cc: Andrew Morton, Johannes Weiner, David Hildenbrand, Michal Hocko,
Qi Zheng, Shakeel Butt, Lorenzo Stoakes, Kairui Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Brian Geffon,
Jan Alexander Steffens (heftig), Steven Barrett, Yu Zhao,
linux-mm, linux-kernel, Ridong Chen
On Thu, Aug 13, 2026 at 7:24 PM Barry Song <baohua@kernel.org> wrote:
>
> On Thu, Aug 13, 2026 at 7:02 PM Ridong Chen <ridong.chen@linux.dev> wrote:
> >
> > From: Ridong Chen <chenridong@xiaomi.com>
> >
> > 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.
> >
> > 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")
> > Assisted-by: Claude:claude-opus-4-8
> > Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
> > ---
> > mm/vmscan.c | 7 ++++++-
> > 1 file changed, 6 insertions(+), 1 deletion(-)
> >
> > diff --git a/mm/vmscan.c b/mm/vmscan.c
> > index 94fc4f25e99f..2befc8d7dd3f 100644
> > --- a/mm/vmscan.c
> > +++ b/mm/vmscan.c
> > @@ -5319,7 +5319,12 @@ 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);
> > + /*
> > + * Keep a folio from the inactive list inactive:
> > + * pass reclaiming=!active so it is not seeded as
> > + * active. See lru_gen_folio_seq().
> > + */
> > + success = lru_gen_add_folio(lruvec, folio, !active);
>
> This is a very interesting use of the reclaim argument, as it is not
> intended to serve this MGLRU switch purpose. It is really only meant
> for `folio_rotate_reclaimable()`.
>
> However, the change itself seems to be *partially* correct and
> *partially* wrong.
>
> One real issue is that inactive is always placed in the oldest
> generation, while we have two old generations. Maybe we can ignore
> this for now.
>
> but somehow, are we also inverting the cold/hot ordering in the
> inactive list?
>
> `lru_to_folio(head)` always takes the tail, but now we are putting the
> tail before the head folios.
>
> Because reclaim == true will use list_add_tail().
>
> if (reclaiming)
> list_add_tail(&folio->lru, &lrugen->folios[gen][type][zone]);
I guess we can fix this by iterating in `fill_evictable()` from head
to tail order.
struct list_head *pos = head->next;
while (pos != head) {
struct folio *folio = list_entry(pos, struct folio, lru);
...
}
Then the oldest generation will maintain the same folio order as the
inactive list.
Thanks
Barry
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-13 11:38 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 11:02 [RFC PATCH] mm/mglru: preserve inactive placement when enabling MGLRU Ridong Chen
2026-08-13 11:24 ` Barry Song
2026-08-13 11:37 ` Barry Song
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.