* [PATCH mm-unstable v3 0/2] mm/vmscan: fix NR_ISOLATED accounting and throttling for MGLRU
@ 2026-08-18 12:48 Hui Zhu
2026-08-18 12:48 ` [PATCH mm-unstable v3 1/2] mm/vmscan: fix missing NR_ISOLATED counter update in MGLRU reclaim path Hui Zhu
2026-08-18 12:48 ` [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction Hui Zhu
0 siblings, 2 replies; 8+ messages in thread
From: Hui Zhu @ 2026-08-18 12:48 UTC (permalink / raw)
To: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Johannes Weiner,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, Baolin Wang,
linux-mm, linux-kernel
Cc: Hui Zhu
From: Hui Zhu <zhuhui@kylinos.cn>
The legacy reclaim path updates the NR_ISOLATED_ANON/FILE node
counters around isolation and throttles direct reclaimers via
too_many_isolated() when isolated folios pile up. The MGLRU eviction
path does neither: evict_folios() isolates folios without touching
the counters and never consults too_many_isolated().
Patch 1 updates NR_ISOLATED_ANON/FILE around isolation in
evict_folios(), reusing the existing nr_isolated. Without this the
counters stay at zero while MGLRU reclaim is active, so compaction's
too_many_isolated() cannot see the pages MGLRU has isolated.
Patch 2 extracts the throttling loop from shrink_inactive_list() into
throttle_is_throttled() and calls it from evict_folios() as well, keeping
the two reclaim paths unified, in line with what was done previously
for writeback reclaim. This way the MGLRU eviction path is throttled
when isolated folios pile up instead of thrashing the shrinking LRU
lists - the scenario the too_many_isolated() check exists for. A
dying task fakes reclaim progress exactly like the legacy path so it
exits reclaim quickly.
Testing
=======
Test on 8G RAM qemu.
The reproducer confines stress-ng workers in a 192M memcg and swaps
through dm-delay (300ms write latency) so pageout is slow and isolated
folios pile up; the workload is intentionally extreme to force the
throttle path.
Throttle events are counted via the mm_vmscan_throttled tracepoint.
The test scripts and test log is in [1].
Test 1, reclaim throttling, parallel direct reclaim in the memcg:
before after
throttle events (ISOLATED) 0 512488
- from kswapd 0 0
nr_isolated_anon peak 0 1450
- samples above the
too_many_isolated threshold 0/1086 293/1057
pgscan_direct 1253539938 148299282
pswpout 3383673 6502879
Without the series MGLRU reclaim spins on the shrinking LRU lists
while nr_isolated_* stays at 0 and nothing is throttled: pgscan_direct
runs to 1.25 billion with only 3.3M pages swapped out. With the
series the counters are updated, isolated folios cross the
too_many_isolated threshold (293 of 1057 samples) and direct
reclaimers are throttled; scanning drops by an order of magnitude
while pswpout goes up, i.e. the reclaimers back off and let the slow
swap device finish writing out the isolated folios instead of
thrashing. kswapd stays exempt.
Test 2, counters visible to compaction, same pressure plus
compact_memory in parallel:
before after
nr_isolated peak 59 2271
The "before" 59 is compaction's own transient isolation; reclaim's
isolation is invisible. With patch 1 it becomes visible to
compaction's too_many_isolated(). (Compaction's own throttling
threshold, (inactive + active) / 16, is ~23k pages on this box and
needs more pile-up than the box can generate; test 1 exercises the
same throttling mechanism end to end on the reclaim side.)
[1] https://gist.github.com/teawater/d3968aac92eb6bd1378beb54a82933f4
Changelog:
v3:
Accoding to the commens of Baolin, remove the redundant nr_isolated
check before restoring the NR_ISOLATED_* counters in evict_folios().
rename the extracted helper to throttle_is_throttled() to avoid
confusion with the existing wake_throttle_isolated() naming space.
Use for_each_evictable_type() in the MGLRU throttle to check each
evictable type's isolation instead of only the type returned by
get_type_to_scan(), since isolate_folios() may fall back to the
other type.
Re-run the tests and update the test log.
v2:
Accoding to the commens of Kairun, Rebased on mm-unstable.
Split into two patches; patch 2 is new and adds the
too_many_isolated() throttling to the MGLRU eviction path, which v1
did not cover.
Add test infomations.
Hui Zhu (2):
mm/vmscan: fix missing NR_ISOLATED counter update in MGLRU reclaim
path
mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction
mm/vmscan.c | 76 ++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 64 insertions(+), 12 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH mm-unstable v3 1/2] mm/vmscan: fix missing NR_ISOLATED counter update in MGLRU reclaim path
2026-08-18 12:48 [PATCH mm-unstable v3 0/2] mm/vmscan: fix NR_ISOLATED accounting and throttling for MGLRU Hui Zhu
@ 2026-08-18 12:48 ` Hui Zhu
2026-08-18 21:33 ` Barry Song
2026-08-18 12:48 ` [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction Hui Zhu
1 sibling, 1 reply; 8+ messages in thread
From: Hui Zhu @ 2026-08-18 12:48 UTC (permalink / raw)
To: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Johannes Weiner,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, Baolin Wang,
linux-mm, linux-kernel
Cc: Hui Zhu
From: Hui Zhu <zhuhui@kylinos.cn>
MGLRU evict_folios() isolates folios from the LRU without updating
the NR_ISOLATED_ANON/FILE counters, unlike the legacy
shrink_inactive_list() path. This causes compaction's
too_many_isolated() check to under-count isolated pages when MGLRU
reclaim is active.
Add NR_ISOLATED counter updates in evict_folios(): increment after
isolate_folios() and decrement after all retry passes complete, using
the existing nr_isolated which holds the original isolated count.
Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
---
mm/vmscan.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index c1404a59523d..98226bb021f3 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4892,6 +4892,9 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness,
&list, &isolated, &type, &type_scanned);
nr_isolated = isolated;
+ if (nr_isolated)
+ __mod_node_page_state(pgdat, NR_ISOLATED_ANON + type,
+ nr_isolated);
/* Scanning may have emptied the oldest gen, flush it */
if (scanned)
@@ -4954,6 +4957,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
goto retry;
}
+ mod_node_page_state(pgdat, NR_ISOLATED_ANON + type, -nr_isolated);
+
if (nr_isolated > total_reclaimed)
mod_lruvec_state(lruvec, PGROTATE_ANON + type,
nr_isolated - total_reclaimed);
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction
2026-08-18 12:48 [PATCH mm-unstable v3 0/2] mm/vmscan: fix NR_ISOLATED accounting and throttling for MGLRU Hui Zhu
2026-08-18 12:48 ` [PATCH mm-unstable v3 1/2] mm/vmscan: fix missing NR_ISOLATED counter update in MGLRU reclaim path Hui Zhu
@ 2026-08-18 12:48 ` Hui Zhu
2026-08-18 22:06 ` Barry Song
1 sibling, 1 reply; 8+ messages in thread
From: Hui Zhu @ 2026-08-18 12:48 UTC (permalink / raw)
To: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Johannes Weiner,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, Baolin Wang,
linux-mm, linux-kernel
Cc: Hui Zhu
From: Hui Zhu <zhuhui@kylinos.cn>
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.
If a fatal signal is pending, fake reclaim progress the same way the
legacy path does, so the dying task exits reclaim quickly instead of
being held in the throttle.
Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
---
mm/vmscan.c | 71 ++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 59 insertions(+), 12 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 98226bb021f3..6fe8824430ac 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1819,7 +1819,7 @@ bool folio_isolate_lru(struct folio *folio)
* the LRU list will go small and be scanned faster than necessary, leading to
* unnecessary swapping, thrashing and OOM.
*/
-static bool too_many_isolated(struct pglist_data *pgdat, int file,
+static bool too_many_isolated(struct pglist_data *pgdat, bool file,
struct scan_control *sc)
{
unsigned long inactive, isolated;
@@ -1856,6 +1856,37 @@ static bool too_many_isolated(struct pglist_data *pgdat, int file,
return too_many;
}
+/*
+ * Throttle reclaim if too many isolated folios are piling up. If this makes
+ * no progress, the caller is probably looping on unevictable folios, so give
+ * up. Returns true to tell the caller to stop reclaiming, and sets @fatal
+ * if the task received a fatal signal while waiting, so that the caller can
+ * bail out faster.
+ */
+static bool throttle_is_throttled(struct pglist_data *pgdat, bool file,
+ struct scan_control *sc, bool *fatal)
+{
+ bool stalled = false;
+
+ *fatal = false;
+ while (unlikely(too_many_isolated(pgdat, file, sc))) {
+ if (stalled)
+ return true;
+
+ /* wait a bit for the reclaimer. */
+ stalled = true;
+ reclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED);
+
+ /* We are about to die and free our memory. Return now. */
+ if (fatal_signal_pending(current)) {
+ *fatal = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
/*
* move_folios_to_lru() moves folios from private @list to appropriate LRU list.
*
@@ -1992,19 +2023,14 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan,
bool file = is_file_lru(lru);
enum node_stat_item item;
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
- bool stalled = false;
-
- while (unlikely(too_many_isolated(pgdat, file, sc))) {
- if (stalled)
- return 0;
-
- /* wait a bit for the reclaimer. */
- stalled = true;
- reclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED);
+ bool fatal;
+ if (throttle_is_throttled(pgdat, file, sc, &fatal)) {
/* We are about to die and free our memory. Return now. */
- if (fatal_signal_pending(current))
+ if (fatal)
return SWAP_CLUSTER_MAX;
+
+ return 0;
}
lru_add_drain();
@@ -4877,12 +4903,33 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
enum node_stat_item item;
struct reclaim_stat stat;
struct lru_gen_mm_walk *walk;
- int scanned, reclaimed;
+ int i, scanned, reclaimed;
int isolated = 0, nr_isolated = 0, type, type_scanned;
unsigned long total_reclaimed = 0;
bool skip_retry = false;
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+ bool fatal;
+
+ /*
+ * The type to isolate is unknown until isolation, and
+ * isolate_folios() may fall back to the other type. Throttle if
+ * any evictable type has too many isolated folios.
+ */
+ for_each_evictable_type(i, swappiness) {
+ if (throttle_is_throttled(pgdat, i, sc, &fatal)) {
+ /*
+ * We are about to die and free our memory. Like the
+ * legacy path, pretend some pages were reclaimed so
+ * reclaim unwinds quickly instead of looping back
+ * into the throttle.
+ */
+ if (fatal)
+ sc->nr_reclaimed += SWAP_CLUSTER_MAX;
+
+ return 0;
+ }
+ }
lruvec_lock_irq(lruvec);
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH mm-unstable v3 1/2] mm/vmscan: fix missing NR_ISOLATED counter update in MGLRU reclaim path
2026-08-18 12:48 ` [PATCH mm-unstable v3 1/2] mm/vmscan: fix missing NR_ISOLATED counter update in MGLRU reclaim path Hui Zhu
@ 2026-08-18 21:33 ` Barry Song
0 siblings, 0 replies; 8+ messages in thread
From: Barry Song @ 2026-08-18 21:33 UTC (permalink / raw)
To: Hui Zhu
Cc: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Johannes Weiner,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, Baolin Wang,
linux-mm, linux-kernel, Hui Zhu
On Tue, Aug 18, 2026 at 8:49 PM Hui Zhu <hui.zhu@linux.dev> wrote:
>
> From: Hui Zhu <zhuhui@kylinos.cn>
>
> MGLRU evict_folios() isolates folios from the LRU without updating
> the NR_ISOLATED_ANON/FILE counters, unlike the legacy
> shrink_inactive_list() path. This causes compaction's
> too_many_isolated() check to under-count isolated pages when MGLRU
> reclaim is active.
>
> Add NR_ISOLATED counter updates in evict_folios(): increment after
> isolate_folios() and decrement after all retry passes complete, using
> the existing nr_isolated which holds the original isolated count.
>
> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
> Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
> ---
LGTM, thanks!
Reviewed-by: Barry Song <baohua@kernel.org>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction
2026-08-18 12:48 ` [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction Hui Zhu
@ 2026-08-18 22:06 ` Barry Song
2026-08-19 2:15 ` Baolin Wang
0 siblings, 1 reply; 8+ messages in thread
From: Barry Song @ 2026-08-18 22:06 UTC (permalink / raw)
To: Hui Zhu
Cc: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Johannes Weiner,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, Baolin Wang,
linux-mm, linux-kernel, Hui Zhu
On Tue, Aug 18, 2026 at 8:49 PM Hui Zhu <hui.zhu@linux.dev> wrote:
>
> From: Hui Zhu <zhuhui@kylinos.cn>
>
> 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.
BTW, if we are hitting isolated > inactive with MGLRU, it probably
means the generations are quite imbalanced—we are running out of
reclaimable generations. In that case, we may actually want
reclamation to proceed with aging instead.
> If a fatal signal is pending, fake reclaim progress the same way the
> legacy path does, so the dying task exits reclaim quickly instead of
> being held in the throttle.
>
> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
> ---
> mm/vmscan.c | 71 ++++++++++++++++++++++++++++++++++++++++++++---------
> 1 file changed, 59 insertions(+), 12 deletions(-)
>
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index 98226bb021f3..6fe8824430ac 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -1819,7 +1819,7 @@ bool folio_isolate_lru(struct folio *folio)
> * the LRU list will go small and be scanned faster than necessary, leading to
> * unnecessary swapping, thrashing and OOM.
> */
> -static bool too_many_isolated(struct pglist_data *pgdat, int file,
> +static bool too_many_isolated(struct pglist_data *pgdat, bool file,
> struct scan_control *sc)
> {
> unsigned long inactive, isolated;
> @@ -1856,6 +1856,37 @@ static bool too_many_isolated(struct pglist_data *pgdat, int file,
> return too_many;
> }
>
> +/*
> + * Throttle reclaim if too many isolated folios are piling up. If this makes
> + * no progress, the caller is probably looping on unevictable folios, so give
> + * up. Returns true to tell the caller to stop reclaiming, and sets @fatal
> + * if the task received a fatal signal while waiting, so that the caller can
> + * bail out faster.
> + */
> +static bool throttle_is_throttled(struct pglist_data *pgdat, bool file,
> + struct scan_control *sc, bool *fatal)
> +{
> + bool stalled = false;
> +
> + *fatal = false;
TBH, I find the name quite weird :-)
Best Regards
Barry
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction
2026-08-18 22:06 ` Barry Song
@ 2026-08-19 2:15 ` Baolin Wang
2026-08-19 9:29 ` Kairui Song
0 siblings, 1 reply; 8+ messages in thread
From: Baolin Wang @ 2026-08-19 2:15 UTC (permalink / raw)
To: Barry Song, Hui Zhu
Cc: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Johannes Weiner,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, linux-mm,
linux-kernel, Hui Zhu
On 8/19/26 6:06 AM, Barry Song wrote:
> On Tue, Aug 18, 2026 at 8:49 PM Hui Zhu <hui.zhu@linux.dev> wrote:
>>
>> From: Hui Zhu <zhuhui@kylinos.cn>
>>
>> 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.
Yes, that's a valid concern. So I think we can check the isolation of
both types for MGLRU to avoid this case:
static bool check_need_throttle()
{
bool need_throttle = true;
for_each_evictable_type(i, swappiness) {
if (!too_many_isolated(pgdat, i, sc))
need_throttle = false;
}
return need_throttle;
}
In evict_folios():
......
while (unlikely(check_need_throttle())) {
if (stalled)
return 0;
/* wait a bit for the reclaimer. */
stalled = true;
reclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED);
/* We are about to die and free our memory. Return now. */
if (fatal_signal_pending(current))
return SWAP_CLUSTER_MAX;
}
> BTW, if we are hitting isolated > inactive with MGLRU, it probably
> means the generations are quite imbalanced—we are running out of
> reclaimable generations. In that case, we may actually want
> reclamation to proceed with aging instead.
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.
>> If a fatal signal is pending, fake reclaim progress the same way the
>> legacy path does, so the dying task exits reclaim quickly instead of
>> being held in the throttle.
>>
>> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
>> ---
>> mm/vmscan.c | 71 ++++++++++++++++++++++++++++++++++++++++++++---------
>> 1 file changed, 59 insertions(+), 12 deletions(-)
>>
>> diff --git a/mm/vmscan.c b/mm/vmscan.c
>> index 98226bb021f3..6fe8824430ac 100644
>> --- a/mm/vmscan.c
>> +++ b/mm/vmscan.c
>> @@ -1819,7 +1819,7 @@ bool folio_isolate_lru(struct folio *folio)
>> * the LRU list will go small and be scanned faster than necessary, leading to
>> * unnecessary swapping, thrashing and OOM.
>> */
>> -static bool too_many_isolated(struct pglist_data *pgdat, int file,
>> +static bool too_many_isolated(struct pglist_data *pgdat, bool file,
>> struct scan_control *sc)
>> {
>> unsigned long inactive, isolated;
>> @@ -1856,6 +1856,37 @@ static bool too_many_isolated(struct pglist_data *pgdat, int file,
>> return too_many;
>> }
>>
>> +/*
>> + * Throttle reclaim if too many isolated folios are piling up. If this makes
>> + * no progress, the caller is probably looping on unevictable folios, so give
>> + * up. Returns true to tell the caller to stop reclaiming, and sets @fatal
>> + * if the task received a fatal signal while waiting, so that the caller can
>> + * bail out faster.
>> + */
>> +static bool throttle_is_throttled(struct pglist_data *pgdat, bool file,
>> + struct scan_control *sc, bool *fatal)
>> +{
>> + bool stalled = false;
>> +
>> + *fatal = false;
>
> TBH, I find the name quite weird :-)
Yes, that is not what I meant. :) What I mean is to use a readable
variable to return instead of 'true' or 'false':
static bool throttle_isolated(struct pglist_data *pgdat, bool file,
struct scan_control *sc, bool *fatal)
{
bool stalled = false;
*fatal = false;
while (unlikely(too_many_isolated(pgdat, file, sc))) {
if (stalled)
return stalled;
/* wait a bit for the reclaimer. */
stalled = true;
reclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED);
/* We are about to die and free our memory. Return now. */
if (fatal_signal_pending(current)) {
*fatal = true;
return stalled;
}
}
return stalled;
}
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction
2026-08-19 2:15 ` Baolin Wang
@ 2026-08-19 9:29 ` Kairui Song
2026-08-19 22:01 ` Barry Song
0 siblings, 1 reply; 8+ messages in thread
From: Kairui Song @ 2026-08-19 9:29 UTC (permalink / raw)
To: Hui Zhu, Baolin Wang, Barry Song
Cc: Andrew Morton, Qi Zheng, Shakeel Butt, Axel Rasmussen,
Yuanchu Xie, Wei Xu, Johannes Weiner, David Hildenbrand,
Michal Hocko, Lorenzo Stoakes, linux-mm, linux-kernel, Hui Zhu
On Wed, Aug 19, 2026 at 10:15 AM Baolin Wang
<baolin.wang@linux.alibaba.com> wrote:
> On 8/19/26 6:06 AM, Barry Song wrote:
> > On Tue, Aug 18, 2026 at 8:49 PM Hui Zhu <hui.zhu@linux.dev> wrote:
> >>
> >> From: Hui Zhu <zhuhui@kylinos.cn>
> >>
> >> 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@linux.dev/
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 :)
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction
2026-08-19 9:29 ` Kairui Song
@ 2026-08-19 22:01 ` Barry Song
0 siblings, 0 replies; 8+ messages in thread
From: Barry Song @ 2026-08-19 22:01 UTC (permalink / raw)
To: Kairui Song
Cc: Hui Zhu, Baolin Wang, Andrew Morton, Qi Zheng, Shakeel Butt,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Johannes Weiner,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, linux-mm,
linux-kernel, Hui Zhu
On Wed, Aug 19, 2026 at 5:30 PM Kairui Song <ryncsn@gmail.com> wrote:
>
> On Wed, Aug 19, 2026 at 10:15 AM Baolin Wang
> <baolin.wang@linux.alibaba.com> wrote:
> > On 8/19/26 6:06 AM, Barry Song wrote:
> > > On Tue, Aug 18, 2026 at 8:49 PM Hui Zhu <hui.zhu@linux.dev> wrote:
> > >>
> > >> From: Hui Zhu <zhuhui@kylinos.cn>
> > >>
> > >> 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.
I don't know how to throttle a type, since the throttle applies to a
thread rather than a type. Once the thread goes to sleep, it has no way
to fall back to the other type until it wakes up again.
>
> >
> > 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@linux.dev/
>
> 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.
Yep, I think we can probably find a way:
* Throttle if the total number of isolated file and anon folios is too
high compared with their combined totals.
* Throttle if both file and anon have too many isolated folios compared
with their respective totals.
Either approach sounds good to me. Also I agree that inactive
accounting is really unreliable in MGLRU.
>
> Another thing btw is that the function name throttle_is_throttled
> reads a bit strange to me :)
Best Regards
Barry
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-19 22:01 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-18 12:48 [PATCH mm-unstable v3 0/2] mm/vmscan: fix NR_ISOLATED accounting and throttling for MGLRU Hui Zhu
2026-08-18 12:48 ` [PATCH mm-unstable v3 1/2] mm/vmscan: fix missing NR_ISOLATED counter update in MGLRU reclaim path Hui Zhu
2026-08-18 21:33 ` Barry Song
2026-08-18 12:48 ` [PATCH mm-unstable v3 2/2] mm/vmscan: apply too_many_isolated() throttling to MGLRU eviction Hui Zhu
2026-08-18 22:06 ` Barry Song
2026-08-19 2:15 ` Baolin Wang
2026-08-19 9:29 ` Kairui Song
2026-08-19 22:01 ` Barry Song
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®