Re: [RFC v2 0/3] mm/mglru: proactive aging via memory.aging

From: Barry Song

Date: Sun Jul 26 2026 - 23:55:05 EST


On Tue, Jul 14, 2026 at 8:15 PM Zicheng Wang <wangzicheng@xxxxxxxxx> wrote:
>
> MGLRU inverts the reclaim order when anonymous memory is faulted in
> bulk: anonymous pages sit in the young generations while file pages
> sit in the oldest two, so reclaim evicts hot file pages before cold
> anonymous pages. swappiness cannot correct it. This series adds a
> per-cgroup proactive-aging operation, memory.aging, with the
> observability to drive it as a closed loop, so userspace can rebalance
> a cgroup's generations before reclaim runs.
>
> The problem
> -----------
>
> MGLRU places pages by access path, not hotness. On the current tree
> the entry rules in lru_gen_folio_seq() give:
>
> - faulted-in pages (anonymous pages; the fault path sets the active
> mark) land at the 2nd youngest generation (max_seq - 1);
> - file pages from read()/fadvise() land in the oldest two
> generations: non-workingset at min_seq, workingset at min_seq + 1.
>
> Pictorially, for a full four-generation window::
>
> youngest oldest
> max_seq max_seq-1 min_seq+1 min_seq
> -------------------------------------------
> anon . X . . (faulted in)
> file . . X X (read/fadvise)
> ^
> reclaim scans from here -> evicts file first
>
> Reclaim scans from min_seq, so file pages are evicted before the
> anonymous pages two generations younger, a hot/cold inversion.
>
> swappiness does not fix it: it only selects which type to scan, not
> the ordering within a type, so reclaim can still evict hot pages.
> swappiness=201 (SWAPPINESS_ANON_ONLY) just flips the inversion to the
> other side: no value recovers a correct hot/cold order, because the
> order comes from the access path, not access recency [1]. This is not
> Android-specific; the same file over-reclaim is shown on servers and
> worked around there by forcing an age iteration before reclaim.
>
> Why userspace-driven
> --------------------
>
> The benefit is workload-dependent: file-cache-bound servers gain from
> aging, anon-bound servers do not, so no kernel default is correct for
> all. The kernel also cannot know when to age: on Android the right
> moment is the foreground-to-background transition, when the app's pages
> are cold but their PTE accessed bits are still accurate from foreground
> execution, a framework concept. The kernel therefore provides the
> mechanism; userspace runs the loop.
>

Hi Zicheng,

The problem is that when an app transitions from the foreground to
the background, it becomes inactive. As a result, you clear the PTE
young bits accumulated while it was in the foreground.

We basically need at least two rounds of scanning to determine
whether a folio is hot or cold. With only one round, many folios
that are actually cold will still appear hot because their PTE
young bits remain set. Nobody clears them while the app is
running in the foreground.

So I'm really curious whether aging should be performed while the
app is running in the foreground, rather than after it has switched
to the background. If this significantly improves the distinction
between hot and cold folios, allowing reclamation after the app
moves to the background to reclaim genuinely cold folios, then it
would provide a much stronger argument for an aging interface that
only sorts folios in the LRU without reclaiming them.
I have a feeling we may really need an aging interface that sorts
folios without reclaiming them. We can then use the sorted LRU to
reclaim cold folios whenever reclamation is needed.
But your use cases don't convince me :-)

If the goal is to work around MGLRU's swappiness behavior, we should
fix the swappiness implementation itself rather than introduce an
aging interface as a workaround. If the concern is swappiness=201,
I believe swappiness=200 will be a good choice once my swappiness
fixes are ready:

https://lore.kernel.org/linux-mm/20260726122123.7614-1-baohua@xxxxxxxxxx/

Best Regards
Barry