Re: [RFC PATCH 0/1] mm/mglru: MADV_RANDOM page cache never ages and thrashes the oldest generation

From: Kairui Song

Date: Sun Nov 01 2026 - 13:03:30 EST


On Mon, Nov 2, 2026 at 12:04 AM Maxim Muzafarov <maxmuzaf@xxxxxxxxx> wrote:
>
> Hi,

Hello Maxim!

> It looks like I've stumbled upon an awkward behaviour. When MGLRU is

You are not alone, I encountered same issue too :)

> enabled, page cache faulted in through an MADV_RANDOM mapping goes into
> the oldest generation, and the refaults land there at the same rate as
> well. In my tests bpftrace shows the generations stay at seq 0-3, with
> no aging attempts and no new generations during the workload run, and
> /sys/kernel/debug/lru_gen_full shows for generation seq 0 15758245
> pages evicted and 15580028 pages refaulted, which is 98.87%.
>
> At the same time, a file read through a normal mmap lands one
> generation higher and none of its pages are evicted. It stays cached
> until the end of the workload, and the randomly read working set
> shrinks. With lru_gen disabled, the CLRU evicts the file
> instead, and throughput recovers 20-30 seconds after the file was read.
> I would expect the same behaviour for MGLRU.
>
> I first saw this slowdown on a single-node OpenSearch 3.6 (Lucene 10.4)
> HNSW vector search workload [1]. The node had about 2.8 GiB of page
> cache for a 2.9 GiB index, and Lucene maps the .vec and .vex files with
> MADV_RANDOM. Kernel 7.2.10-200.fc44.aarch64.
>
> Search latency and throughput on a single-node OpenSearch:
>
> MGLRU on MGLRU off
> p50, ms 286.0 73.6
> p90, ms 512.1 135.6
> p99, ms 687.0 191.0
> queries/s 27.4 87.1
>
> Disk reads, same runs:
>
> MGLRU on MGLRU off
> major faults/query 1484 325
> disk reads/query 1484 325
> read size, KiB 4.0 4.0
> disk read, GiB/run 509.6 111.5
>
> This is clearly visible on the page cache chart [2], where "blue" are
> page cache pages mapped into at least one process's address space and
> "orange" the rest of the page cache.
>
> Then I reproduced it with a synthetic HNSW-like query script in python,
> and later with fio alone, on 7.2.10-200.fc44.aarch64, 7.2.14, 7.2.14
> with the MGLRU-FG v4 series [3][4], and on mainline v7.3-rc7.

The missing piece here is refault distance. Both MGLRU and CLRU ignore
hotness on eviction for vma_has_recency() == false case, however, CLRU
still has refault distance protection, while MGLRU doesn't.

I observed a similar issue, which disappeared after we added refault
distance for MGLRU.

There is an early version of FG that have it:
https://lore.kernel.org/linux-mm/20260502-mglru-fg-v1-0-913619b014d9@xxxxxxxxxxx/

See this patch, it too long so I have to split it out and dropped it
for later version:
https://lore.kernel.org/linux-mm/20260502-mglru-fg-v1-30-913619b014d9@xxxxxxxxxxx/

This code snip:
+ /* Set refault as active. */
+ if (distance < total / 2) {
+ folio_set_active(folio);

I may change it to something like
folio_set_lru_refs(LRU_REFS_WORKINGSET) later for non in-fault case
cause this kind of break FG for other cases.

That way, even a random folio will be put to the head directly instead
of keep thrasing on tail. And placing on head or newer gen will trigger aging.

I think I got the problem right? I'll run your benchmark too a bit
later, still tweaking it for some other issues. The refault distance
part needs some rebasing and tweaking as it will have chemical
reactions with many other MGLRU components and it may not work as
expected yet (I also need to use it for an MRU fallback, because, in
theory a pure random case may benifit more from fallback to MRU
stragegy, but your case is a hybrid of hot random and cold non-random,
so a bit tricky to get it right)...

(And we may also loose lru_gen_in_fault for refault case? Not sure...)

But I think we are getting there :), and your report is a really nice case!