Re: [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction

From: Kairui Song

Date: Wed Aug 19 2026 - 05:35:35 EST


On Wed, Aug 19, 2026 at 10:15 AM Baolin Wang
<baolin.wang@xxxxxxxxxxxxxxxxx> wrote:
> On 8/19/26 6:06 AM, Barry Song wrote:
> > On Tue, Aug 18, 2026 at 8:49 PM Hui Zhu <hui.zhu@xxxxxxxxx> wrote:
> >>
> >> From: Hui Zhu <zhuhui@xxxxxxxxxx>
> >>
> >> The legacy path throttles direct reclaim in shrink_inactive_list()
> >> when too many isolated folios pile up, but MGLRU's evict_folios()
> >> isolates folios without this check, which can lead to unnecessary
> >> swapping, thrashing and OOM.
> >>
> >> With the NR_ISOLATED counters now updated in evict_folios(), extract
> >> the throttling loop from shrink_inactive_list() into
> >> throttle_is_throttled() and reuse it in evict_folios(). Since the
> >> type to isolate is unknown until isolation and isolate_folios() may
> >> fall back to the other type, check all evictable types with
> >> for_each_evictable_type() and throttle if any of them has too many
> >> isolated folios.
> >>
> >
> > I feel this is unlikely to work. MGLRU behaves quite differently from the
> > active/inactive LRU, and its `NR_INACTIVE_FILE` accounting is also very
> > different.
> >
> > With the active/inactive LRU, shrink_inactive_list() ensures that we
> > always have an inactive list with pages available for reclaim. With MGLRU,
> > however, a generation can legitimately point to an empty list, so this
> > assumption does not hold.
> >
> > try_to_inc_min_seq:
> >
> > /* see the comment on lru_gen_folio */
> > if (swappiness && swappiness <= MAX_SWAPPINESS) {
> > unsigned long seq = lrugen->max_seq - MIN_NR_GENS;
> >
> > if (min_seq[LRU_GEN_ANON] > seq && min_seq[LRU_GEN_FILE] < seq)
> > min_seq[LRU_GEN_ANON] = seq;
> > else if (min_seq[LRU_GEN_FILE] > seq &&
> > min_seq[LRU_GEN_ANON] < seq)
> > min_seq[LRU_GEN_FILE] = seq;
> > }
> >
> > At that point, we have no inactive pages for the type, so the
> > throttle will take effect when the following condition is true:
> >
> > too_many = isolated > inactive;
> >
> > With MGLRU, however, we can still fall back to the other type even
> > when there are no inactive pages for the current type.

Hi All, that's a very insightful concern.

But on a higher level, you can fall back to the other type regardless
of the LRU? That's not something limited to MGLRU I think? I mean
throttling on one type seems like a good idea if the other type is
very reclaimable.

>
> Yes, that's a valid concern. So I think we can check the isolation of
> both types for MGLRU to avoid this case:

...

>
> The typical 'isolated > inactive' case is that we've tried our best with
> aging, but cold pages production can't keep up with isolation speed,
> especially under concurrent reclaim from multiple processes. In this
> case, I think throttling is reasonable.

Just an idea... How about we simplify this: just check the isolated vs
total memory size? At least for MGLRU. MGLRU maintains NR_INACTIVE_*
only as a compatibility shim for reporting meminfo, so it does not
track the immediately reclaimable set the way the active/inactive LRU
does, so the inactive value is a poor trigger for MGLRU: Aging moves
the folios in batches so anonymous reading can be very jumpy. For file
folios, PID can only promote unmapped page cache to the second oldest
generation, meaning most file folios are considered inactive. This has
been causing trouble for our dashboards for a long time and I think we
can fix this by using the tier number intead of gen number as the
inactive / active reading, that 's a different topic though.

And in fact I don't quite get why classical LRU only compares that
isolated number to the inactive count only. The reclamer can move
active folios to the inactive part anytime, so throttling when we are
low on inactive doesn't always seem like a good idea?

I remember this was discussed sometime ago that in the long term we
might better throttle it in a different way, it was once mentioned by
Shakeel:
https://lore.kernel.org/linux-mm/ahncWUAJlPZhNGr8@xxxxxxxxx/

So perhaps we can throttle MGLRU more gently? e.g. just compare to the
total number / MIN_NR_GENS, which means half of the memory is isolated
for MGLRU? This doesn't look too pretty but I think it's better than
using the inactive count for MGLRU. And if we introduce some
gen-balance-based aging (whether that is a good idea is also another
topic), the ideal goal of MGLRU will be evenly ditribute folios among
gens so the current inactive reading will always target half of the
total memory.

Another thing btw is that the function name throttle_is_throttled
reads a bit strange to me :)