* [to-be-updated] mm-mglru-skip-zones-above-reclaim_idx-instead-of-rotating-them.patch removed from -mm tree
@ 2026-11-14 21:59 Andrew Morton
0 siblings, 0 replies; 2+ messages in thread
From: Andrew Morton @ 2026-11-14 21:59 UTC (permalink / raw)
To: mm-commits, kasong, akpm
The quilt patch titled
Subject: mm/mglru: skip zones above reclaim_idx instead of rotating them
has been removed from the -mm tree. Its filename was
mm-mglru-skip-zones-above-reclaim_idx-instead-of-rotating-them.patch
This patch was dropped because an updated version will be issued
------------------------------------------------------
From: Kairui Song <kasong@tencent.com>
Subject: mm/mglru: skip zones above reclaim_idx instead of rotating them
Date: Sat, 14 Nov 2026 02:55:30 +0800
scan_folios() used to visit every zone. Folios in zones above
sc->reclaim_idx can never be isolated, so sort_folio() rotated them to
a younger generation, the "ineligible" branch, or, when only one gen
was left, skipped them. This wasted scan work on every pass, and the
rotation kept the protected folios from aging.
Restrict both the scan and the gen selection to the eligible zones,
like the legacy LRU, which only isolates folios in zones up to
reclaim_idx:
- lruvec_populated_min_seq() takes a zone_limit, so the oldest
populated gen is searched only among the zones the scan can
reclaim from. A gen populated only in protected zones no longer
stalls the scan.
- scan_folios() iterates the eligible zones in the descending order
of the legacy zonelist walk, draining the zone at reclaim_idx
first.
- The ineligible branch of sort_folio() is now dead and removed,
with a VM_WARN_ON_ONCE_FOLIO() guarding the new invariant.
try_to_inc_min_seq() still considers all zones, so min_seq[type] is
only bumped once the oldest gen is empty everywhere. At DEF_PRIORITY
the search bound stays min_seq[type], so the scan still starts from
the oldest gen regardless of whether it is populated.
Link: https://lore.kernel.org/20261113185541.70203-6-baohua@kernel.org
Signed-off-by: Kairui Song <kasong@tencent.com>
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Baoquan He <baoquan.he@linux.dev>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: <hui.zhu@linux.dev>
Cc: Kunwu Chan <kunwu.chan@gmail.com>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: wang lian <lianux.mm@gmail.com>
Cc: <liuxiang.277@bytedance.com>
Cc: <lyugaofei@xiaomi.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Qi Zheng <qi.zheng@linux.dev>
Cc: Ridong Chen <ridong.chen@linux.dev>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Kairui Song <ryncsn@gmail.com>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: wangzicheng <wangzicheng@honor.com>
Cc: Wei Xu <weixugc@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Bo Zhang <zhangbo56@xiaomi.com>
Cc: Hui Zhu <zhuhui@kylinos.cn>
Cc: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
Cc: Bo Zhang <zhangbo0325@gmail.com>
---
mm/vmscan.c | 47 +++++++++++++++++++----------------------------
1 file changed, 19 insertions(+), 28 deletions(-)
--- a/mm/vmscan.c~mm-mglru-skip-zones-above-reclaim_idx-instead-of-rotating-them
+++ a/mm/vmscan.c
@@ -3992,7 +3992,7 @@ done:
/* Similar to lruvec_evictable_size, return the oldest populated seq of the type */
static unsigned long lruvec_populated_min_seq(struct lruvec *lruvec, int type,
- unsigned long max_seq)
+ int zone_limit, unsigned long max_seq)
{
int gen, zone;
unsigned long min_seq;
@@ -4003,7 +4003,7 @@ static unsigned long lruvec_populated_mi
min_seq = READ_ONCE(lrugen->min_seq[type]);
while (min_seq <= max_seq) {
gen = lru_gen_from_seq(min_seq);
- for (zone = 0; zone < MAX_NR_ZONES; zone++) {
+ for (zone = 0; zone < zone_limit; zone++) {
if (!list_empty(&lrugen->folios[gen][type][zone]))
return min_seq;
}
@@ -4026,7 +4026,7 @@ static void try_to_inc_min_seq(struct lr
/* Requires at least MIN_NR_GENS gens for basic functionality */
seq = lrugen->max_seq - MIN_NR_GENS;
for_each_evictable_type(type, swappiness) {
- min_seq[type] = lruvec_populated_min_seq(lruvec, type, seq + 1);
+ min_seq[type] = lruvec_populated_min_seq(lruvec, type, MAX_NR_ZONES, seq + 1);
if (min_seq[type] > lrugen->min_seq[type])
seq_inc_flag = true;
}
@@ -4689,6 +4689,7 @@ static bool sort_folio(struct lruvec *lr
struct lru_gen_folio *lrugen = &lruvec->lrugen;
VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio);
+ VM_WARN_ON_ONCE_FOLIO(zone > sc->reclaim_idx, folio);
/* unevictable: let it through and the generic path will cull it */
if (!folio_evictable(folio))
@@ -4705,10 +4706,8 @@ static bool sort_folio(struct lruvec *lr
* longer be promoted or protected, so just isolate them. The one
* exception is lru_gen_reparent_memcg(), which is handled above.
*/
- if (min_gen == max_gen) {
- VM_WARN_ON_ONCE(zone > sc->reclaim_idx);
+ if (min_gen == max_gen)
return false;
- }
/* protected */
if (tier > tier_idx || refs + workingset == BIT(LRU_REFS_WIDTH) + 1) {
@@ -4725,13 +4724,6 @@ static bool sort_folio(struct lruvec *lr
return true;
}
- /* ineligible */
- if (zone > sc->reclaim_idx) {
- gen = folio_inc_gen(lruvec, folio, min_gen);
- list_move_tail(&folio->lru, &lrugen->folios[gen][type][zone]);
- return true;
- }
-
return false;
}
@@ -4763,16 +4755,17 @@ static int scan_folios(unsigned long nr_
struct scan_control *sc, int type, int tier,
struct list_head *list, int *isolatedp)
{
- int i;
enum node_stat_item item;
+ int zone_idx;
int sorted = 0;
int scanned = 0;
int isolated = 0;
int skipped = 0;
int gen, max_gen;
- unsigned long min_seq;
+ unsigned long seq;
unsigned long remaining = nr_to_scan;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
DEFINE_MAX_SEQ(lruvec);
VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH);
@@ -4784,24 +4777,22 @@ static int scan_folios(unsigned long nr_
* when under pressure to respect swappiness more and reduce IO cost.
*/
if (sc->priority == DEF_PRIORITY)
- min_seq = lrugen->min_seq[type];
+ seq = lrugen->min_seq[type];
else
- min_seq = lruvec_populated_min_seq(lruvec, type, lrugen->max_seq);
+ seq = lrugen->max_seq;
- gen = lru_gen_from_seq(min_seq);
+ seq = lruvec_populated_min_seq(lruvec, type, sc->reclaim_idx + 1, seq);
+ gen = lru_gen_from_seq(seq);
max_gen = lru_gen_from_seq(max_seq);
- for (i = MAX_NR_ZONES; i > 0; i--) {
+ /* Scan eligible zones in descending order. */
+ for (zone_idx = sc->reclaim_idx; zone_idx >= 0; zone_idx--) {
LIST_HEAD(moved);
int skipped_zone = 0;
- int zone = (sc->reclaim_idx + i) % MAX_NR_ZONES;
- struct list_head *head = &lrugen->folios[gen][type][zone];
+ struct zone *zone = &pgdat->node_zones[zone_idx];
+ struct list_head *head = &lrugen->folios[gen][type][zone_idx];
- /*
- * If there is only one gen left, rotating the unreclaimable
- * zone become meaningless, just skip them.
- */
- if (gen == max_gen && zone > sc->reclaim_idx)
+ if (!managed_zone(zone))
continue;
while (!list_empty(head)) {
@@ -4811,7 +4802,7 @@ static int scan_folios(unsigned long nr_
VM_WARN_ON_ONCE_FOLIO(folio_test_unevictable(folio), folio);
VM_WARN_ON_ONCE_FOLIO(folio_test_active(folio), folio);
VM_WARN_ON_ONCE_FOLIO(folio_is_file_lru(folio) != type, folio);
- VM_WARN_ON_ONCE_FOLIO(folio_zonenum(folio) != zone, folio);
+ VM_WARN_ON_ONCE_FOLIO(folio_zonenum(folio) != zone_idx, folio);
scanned += delta;
if (sort_folio(lruvec, folio, sc, gen, max_gen, tier))
@@ -4830,7 +4821,7 @@ static int scan_folios(unsigned long nr_
if (skipped_zone) {
list_splice(&moved, head);
- __count_zid_vm_events(PGSCAN_SKIP, zone, skipped_zone);
+ __count_zid_vm_events(PGSCAN_SKIP, zone_idx, skipped_zone);
skipped += skipped_zone;
}
_
Patches currently in -mm which might be from kasong@tencent.com are
mm-mglru-return-for-unavailable-anon-reclaim-earlier.patch
mm-mglru-balance-anon-and-file-scan-budget-by-refault-rate.patch
mm-mglru-lift-youngest-gen-protection-for-higher-priority.patch
^ permalink raw reply [flat|nested] 2+ messages in thread* [to-be-updated] mm-mglru-skip-zones-above-reclaim_idx-instead-of-rotating-them.patch removed from -mm tree
@ 2026-11-13 19:41 Andrew Morton
0 siblings, 0 replies; 2+ messages in thread
From: Andrew Morton @ 2026-11-13 19:41 UTC (permalink / raw)
To: mm-commits, kasong, akpm
The quilt patch titled
Subject: mm/mglru: skip zones above reclaim_idx instead of rotating them
has been removed from the -mm tree. Its filename was
mm-mglru-skip-zones-above-reclaim_idx-instead-of-rotating-them.patch
This patch was dropped because an updated version will be issued
------------------------------------------------------
From: Kairui Song <kasong@tencent.com>
Subject: mm/mglru: skip zones above reclaim_idx instead of rotating them
Date: Thu, 12 Nov 2026 07:25:52 +0800
scan_folios() used to visit every zone. Folios in zones above
sc->reclaim_idx can never be isolated, so sort_folio() rotated them to
a younger generation, the "ineligible" branch, or, when only one gen
was left, skipped them. This wasted scan work on every pass, and the
rotation kept the protected folios from aging.
Restrict both the scan and the gen selection to the eligible zones,
like the legacy LRU, which only isolates folios in zones up to
reclaim_idx:
- lruvec_populated_min_seq() takes a zone_limit, so the oldest
populated gen is searched only among the zones the scan can
reclaim from. A gen populated only in protected zones no longer
stalls the scan.
- scan_folios() iterates the eligible zones in the descending order
of the legacy zonelist walk, draining the zone at reclaim_idx
first.
- The ineligible branch of sort_folio() is now dead and removed,
with a VM_WARN_ON_ONCE_FOLIO() guarding the new invariant.
try_to_inc_min_seq() still considers all zones, so min_seq[type] is
only bumped once the oldest gen is empty everywhere. At DEF_PRIORITY
the search bound stays min_seq[type], so the scan still starts from
the oldest gen regardless of whether it is populated.
Link: https://lore.kernel.org/20261111232603.9934-6-baohua@kernel.org
Signed-off-by: Kairui Song <kasong@tencent.com>
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Baoquan He <baoquan.he@linux.dev>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: <hui.zhu@linux.dev>
Cc: Kunwu Chan <kunwu.chan@gmail.com>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: wang lian <lianux.mm@gmail.com>
Cc: <liuxiang.277@bytedance.com>
Cc: <lyugaofei@xiaomi.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Qi Zheng <qi.zheng@linux.dev>
Cc: Ridong Chen <ridong.chen@linux.dev>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Kairui Song <ryncsn@gmail.com>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: wangzicheng <wangzicheng@honor.com>
Cc: Wei Xu <weixugc@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Bo Zhang <zhangbo56@xiaomi.com>
Cc: Hui Zhu <zhuhui@kylinos.cn>
Cc: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
---
mm/vmscan.c | 47 +++++++++++++++++++----------------------------
1 file changed, 19 insertions(+), 28 deletions(-)
--- a/mm/vmscan.c~mm-mglru-skip-zones-above-reclaim_idx-instead-of-rotating-them
+++ a/mm/vmscan.c
@@ -3992,7 +3992,7 @@ done:
/* Similar to lruvec_evictable_size, return the oldest populated seq of the type */
static unsigned long lruvec_populated_min_seq(struct lruvec *lruvec, int type,
- unsigned long max_seq)
+ int zone_limit, unsigned long max_seq)
{
int gen, zone;
unsigned long min_seq;
@@ -4003,7 +4003,7 @@ static unsigned long lruvec_populated_mi
min_seq = READ_ONCE(lrugen->min_seq[type]);
while (min_seq <= max_seq) {
gen = lru_gen_from_seq(min_seq);
- for (zone = 0; zone < MAX_NR_ZONES; zone++) {
+ for (zone = 0; zone < zone_limit; zone++) {
if (!list_empty(&lrugen->folios[gen][type][zone]))
return min_seq;
}
@@ -4026,7 +4026,7 @@ static void try_to_inc_min_seq(struct lr
/* Requires at least MIN_NR_GENS gens for basic functionality */
seq = lrugen->max_seq - MIN_NR_GENS;
for_each_evictable_type(type, swappiness) {
- min_seq[type] = lruvec_populated_min_seq(lruvec, type, seq + 1);
+ min_seq[type] = lruvec_populated_min_seq(lruvec, type, MAX_NR_ZONES, seq + 1);
if (min_seq[type] > lrugen->min_seq[type])
seq_inc_flag = true;
}
@@ -4691,6 +4691,7 @@ static bool sort_folio(struct lruvec *lr
struct lru_gen_folio *lrugen = &lruvec->lrugen;
VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio);
+ VM_WARN_ON_ONCE_FOLIO(zone > sc->reclaim_idx, folio);
/* unevictable: let it through and the generic path will cull it */
if (!folio_evictable(folio))
@@ -4707,10 +4708,8 @@ static bool sort_folio(struct lruvec *lr
* longer be promoted or protected, so just isolate them. The one
* exception is lru_gen_reparent_memcg(), which is handled above.
*/
- if (min_gen == max_gen) {
- VM_WARN_ON_ONCE(zone > sc->reclaim_idx);
+ if (min_gen == max_gen)
return false;
- }
/* protected */
if (tier > tier_idx || refs + workingset == BIT(LRU_REFS_WIDTH) + 1) {
@@ -4727,13 +4726,6 @@ static bool sort_folio(struct lruvec *lr
return true;
}
- /* ineligible */
- if (zone > sc->reclaim_idx) {
- gen = folio_inc_gen(lruvec, folio, min_gen);
- list_move_tail(&folio->lru, &lrugen->folios[gen][type][zone]);
- return true;
- }
-
return false;
}
@@ -4765,16 +4757,17 @@ static int scan_folios(unsigned long nr_
struct scan_control *sc, int type, int tier,
struct list_head *list, int *isolatedp)
{
- int i;
enum node_stat_item item;
+ int zone_idx;
int sorted = 0;
int scanned = 0;
int isolated = 0;
int skipped = 0;
int gen, max_gen;
- unsigned long min_seq;
+ unsigned long seq;
unsigned long remaining = nr_to_scan;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
DEFINE_MAX_SEQ(lruvec);
VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH);
@@ -4786,24 +4779,22 @@ static int scan_folios(unsigned long nr_
* when under pressure to respect swappiness more and reduce IO cost.
*/
if (sc->priority == DEF_PRIORITY)
- min_seq = lrugen->min_seq[type];
+ seq = lrugen->min_seq[type];
else
- min_seq = lruvec_populated_min_seq(lruvec, type, lrugen->max_seq);
+ seq = lrugen->max_seq;
- gen = lru_gen_from_seq(min_seq);
+ seq = lruvec_populated_min_seq(lruvec, type, sc->reclaim_idx + 1, seq);
+ gen = lru_gen_from_seq(seq);
max_gen = lru_gen_from_seq(max_seq);
- for (i = MAX_NR_ZONES; i > 0; i--) {
+ /* Scan eligible zones in descending order. */
+ for (zone_idx = sc->reclaim_idx; zone_idx >= 0; zone_idx--) {
LIST_HEAD(moved);
int skipped_zone = 0;
- int zone = (sc->reclaim_idx + i) % MAX_NR_ZONES;
- struct list_head *head = &lrugen->folios[gen][type][zone];
+ struct zone *zone = &pgdat->node_zones[zone_idx];
+ struct list_head *head = &lrugen->folios[gen][type][zone_idx];
- /*
- * If there is only one gen left, rotating the unreclaimable
- * zone become meaningless, just skip them.
- */
- if (gen == max_gen && zone > sc->reclaim_idx)
+ if (!managed_zone(zone))
continue;
while (!list_empty(head)) {
@@ -4813,7 +4804,7 @@ static int scan_folios(unsigned long nr_
VM_WARN_ON_ONCE_FOLIO(folio_test_unevictable(folio), folio);
VM_WARN_ON_ONCE_FOLIO(folio_test_active(folio), folio);
VM_WARN_ON_ONCE_FOLIO(folio_is_file_lru(folio) != type, folio);
- VM_WARN_ON_ONCE_FOLIO(folio_zonenum(folio) != zone, folio);
+ VM_WARN_ON_ONCE_FOLIO(folio_zonenum(folio) != zone_idx, folio);
scanned += delta;
if (sort_folio(lruvec, folio, sc, gen, max_gen, tier))
@@ -4832,7 +4823,7 @@ static int scan_folios(unsigned long nr_
if (skipped_zone) {
list_splice(&moved, head);
- __count_zid_vm_events(PGSCAN_SKIP, zone, skipped_zone);
+ __count_zid_vm_events(PGSCAN_SKIP, zone_idx, skipped_zone);
skipped += skipped_zone;
}
_
Patches currently in -mm which might be from kasong@tencent.com are
mm-mglru-return-for-unavailable-anon-reclaim-earlier.patch
mm-mglru-balance-anon-and-file-scan-budget-by-refault-rate.patch
mm-mglru-lift-youngest-gen-protection-for-higher-priority.patch
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-14 21:59 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14 21:59 [to-be-updated] mm-mglru-skip-zones-above-reclaim_idx-instead-of-rotating-them.patch removed from -mm tree Andrew Morton
-- strict thread matches above, loose matches on Subject: below --
2026-11-13 19:41 Andrew Morton
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.