* [RFC PATCH v3 0/6] mm: mglru: fix swappiness behavior
@ 2026-07-31 8:38 Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 1/6] mm: mglru: prevent min_seq[type] from pointing to an empty generation Barry Song (Xiaomi)
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Barry Song (Xiaomi) @ 2026-07-31 8:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: axelrasmussen, david, hannes, kasong, linux-kernel, ljs,
lyugaofei, mhocko, qi.zheng, shakeel.butt, stevensd, weixugc,
yuanchu, chenridong, zhangbo56, wangzicheng, lianux.mm,
Barry Song (Xiaomi)
RFC v3:
- Added Ridong's patch of improving isolate_folios() readability;
- for lru_gen_imbalanced(), apply some suggestions from Zicheng
and Kairui, considering lruvec size and use macro;
- drop "mm: mglru: only fall back when reclaim is running at high
priority"
- mitigate aging for balanced swappiess value(near middle 100)
as swappiness bias is less a concern for them; but slightly
increased aging overhead might not be good.
* RFC v2 link:
https://lore.kernel.org/linux-mm/20260726122123.7614-1-baohua@kernel.org/
RFC v2:
- Quickly address a few issues raised in the Sashiko comments so
reviewers can ignore v1 and review a cleaner version instead.
https://sashiko.dev/#/patchset/20260726012946.18684-1-baohua@kernel.org
Thanks, Sashiko!
The active/inactive LRU respects swappiness well. Anonymous page
scanning and reclamation increase roughly linearly with
swappiness, while file page scanning and reclamation decrease
accordingly.
For example, when swappiness reaches 200, both pgsteal_file and
pgscan_file drop to zero while building the kernel in a 1 GB
memcg. In contrast, MGLRU shows almost no change across different
swappiness values.
pgsteal_file
Swappiness LRU MGLRU
--------------------------------
1 10567455 763612
36 990706 480205
71 688170 415848
106 446294 386164
141 286307 359196
176 201733 351686
200 0 330093
pgsteal_anon
Swappiness LRU MGLRU
--------------------------------
1 4410548 2726362
36 2465268 2762859
71 2677908 2885124
106 2737227 2841796
141 2984276 3035015
176 3381338 2938302
200 13116359 3113499
pgscan_file
Swappiness LRU MGLRU
--------------------------------
1 17997223 923094
36 1325674 539571
71 852345 464222
106 538207 464477
141 357253 412277
176 217536 399446
200 0 375902
pgscan_anon
Swappiness LRU MGLRU
--------------------------------
1 31639423 5987136
36 23441521 5753224
71 26067110 6101780
106 25619448 5782919
141 26842088 6234264
176 29200021 5980292
200 62193924 6413125
This patchset respects the type selected by positive_ctrl_err(),
which uses swappiness as its gain. It does so by running aging when
the preferred type has few or no reclaimable folios, allowing more
folios of that type to become reclaimable.
For balanced swappiness values near the middle (for example,
60~140), the swappiness bias is less of a concern. We still avoid
aggressive aging and allow fallback to reduce any potential aging
overhead.
In other words, this patch strengthens reclaim type preference
for extreme swappiness values while preserving the existing
MGLRU behavior for balanced swappiness values.
With this patchset, swappiness starts to behave similarly to the
active/inactive LRU.
pgsteal_file
Swappiness LRU MGLRU MGLRU+Patch
-------------------------------------------------
1 10567455 763612 1938714
36 990706 480205 427934
71 688170 415848 373450
106 446294 386164 368160
141 286307 359196 334467
176 201733 351686 293716
200 0 330093 25
pgsteal_anon
Swappiness LRU MGLRU MGLRU+Patch
-------------------------------------------------
1 4410548 2726362 3284272
36 2465268 2762859 3315112
71 2677908 2885124 2942357
106 2737227 2841796 2941288
141 2984276 3035015 3345933
176 3381338 2938302 3409531
200 13116359 3113499 14414151
pgscan_file
Swappiness LRU MGLRU MGLRU+Patch
-------------------------------------------------
1 17997223 923094 2612649
36 1325674 539571 514506
71 852345 464222 423155
106 538207 464477 436573
141 357253 412277 389644
176 217536 399446 333973
200 0 375902 25
pgscan_anon
Swappiness LRU MGLRU MGLRU+Patch
-------------------------------------------------
1 31639423 5987136 6191482
36 23441521 5753224 6097925
71 26067110 6101780 6157755
106 25619448 5782919 6203221
141 26842088 6234264 6599502
176 29200021 5980292 6783074
200 62193924 6413125 22814377
Barry Song (Xiaomi) (4):
mm: mglru: prevent min_seq[type] from pointing to an empty generation
mm: mglru: let scan_folios() scan both reclaimable generations
mm: mglru: improve scan_folios() exhaustion detection
mm: mglru: run aging if the preferred type has no folios in
reclaimable gens
Ridong Chen (1):
mm/mglru: improve readability of isolate_folios()
lyugaofei (1):
mm: mglru: run aging when pages are severely imbalanced across gens
include/linux/mmzone.h | 6 +-
mm/vmscan.c | 145 +++++++++++++++++++++++++++++++----------
2 files changed, 112 insertions(+), 39 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH v3 1/6] mm: mglru: prevent min_seq[type] from pointing to an empty generation
2026-07-31 8:38 [RFC PATCH v3 0/6] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
@ 2026-07-31 8:38 ` Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 2/6] mm: mglru: let scan_folios() scan both reclaimable generations Barry Song (Xiaomi)
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Barry Song (Xiaomi) @ 2026-07-31 8:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: axelrasmussen, david, hannes, kasong, linux-kernel, ljs,
lyugaofei, mhocko, qi.zheng, shakeel.butt, stevensd, weixugc,
yuanchu, chenridong, zhangbo56, wangzicheng, lianux.mm,
Barry Song (Xiaomi)
In try_to_inc_min_seq(), min_seq[LRU_GEN_ANON] and
min_seq[LRU_GEN_FILE] can be adjusted to point to empty generations
to keep their gap within one. As a result, scan_folios() may
repeatedly scan empty generations because it assumes the min_seq
generation still contains folios. Likewise,
should_run_aging() has no way to tell that the min_seq generation
has already been drained.
This is quite confusing. I observed scan_folios() returning 0 even
though the following check in scan_folios() doesn't take effect:
if (get_nr_gens(lruvec, type) == MIN_NR_GENS)
return 0;
There is no need to adjust min_seq[], since the reclaim logic already
triggers aging when the number of generations reaches
MIN_NR_GENS, and reclaim never reduces it below MIN_NR_GENS.
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
---
include/linux/mmzone.h | 6 ++----
mm/vmscan.c | 10 ----------
2 files changed, 2 insertions(+), 14 deletions(-)
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index a26c8b855222..233d2006a541 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -552,10 +552,8 @@ enum {
* The youngest generation number is stored in max_seq for both anon and file
* types as they are aged on an equal footing. The oldest generation numbers are
* stored in min_seq[] separately for anon and file types so that they can be
- * incremented independently. Ideally min_seq[] are kept in sync when both anon
- * and file types are evictable. However, to adapt to situations like extreme
- * swappiness, they are allowed to be out of sync by at most
- * MAX_NR_GENS-MIN_NR_GENS-1.
+ * incremented independently. For both file and anonymous memory, the minimum
+ * generation must be at least MIN_NR_GENS.
*
* The number of pages in each generation is eventually consistent and therefore
* can be transiently negative when reset_batch_size() is pending.
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 566c4e837c7d..ce027c271e9b 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -3972,16 +3972,6 @@ static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness)
if (!seq_inc_flag)
return;
- /* 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;
- }
-
for_each_evictable_type(type, swappiness) {
if (min_seq[type] <= lrugen->min_seq[type])
continue;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH v3 2/6] mm: mglru: let scan_folios() scan both reclaimable generations
2026-07-31 8:38 [RFC PATCH v3 0/6] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 1/6] mm: mglru: prevent min_seq[type] from pointing to an empty generation Barry Song (Xiaomi)
@ 2026-07-31 8:38 ` Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 3/6] mm/mglru: improve readability of isolate_folios() Barry Song (Xiaomi)
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Barry Song (Xiaomi) @ 2026-07-31 8:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: axelrasmussen, david, hannes, kasong, linux-kernel, ljs,
lyugaofei, mhocko, qi.zheng, shakeel.butt, stevensd, weixugc,
yuanchu, chenridong, zhangbo56, wangzicheng, lianux.mm,
Barry Song (Xiaomi)
When we have four generations, and scan_folios() exhausts the oldest
one while the second-oldest still contains reclaimable folios, the
current implementation doesn't move on to scan the second-oldest
generation. Instead, it returns, leaving that generation with no
chance to be scanned at the current sc->priority.
This doesn't seem right. Rather than breaking out and starting a
larger next iteration, let's let scan_folios() continue scanning the
second-oldest generation directly.
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
---
mm/vmscan.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index ce027c271e9b..31947fa60f18 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4718,6 +4718,7 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
int skipped = 0;
unsigned long remaining = nr_to_scan;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
+ unsigned long min_seq = lrugen->min_seq[type];
VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH);
VM_WARN_ON_ONCE(!list_empty(list));
@@ -4725,8 +4726,8 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
if (get_nr_gens(lruvec, type) == MIN_NR_GENS)
return 0;
- gen = lru_gen_from_seq(lrugen->min_seq[type]);
-
+next_gen:
+ gen = lru_gen_from_seq(min_seq);
for (i = MAX_NR_ZONES; i > 0; i--) {
LIST_HEAD(moved);
int skipped_zone = 0;
@@ -4768,6 +4769,14 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
break;
}
+ /*
+ * This generation is exhausted across all zones, but the scan
+ * target has not been reached yet. Continue with the next
+ * reclaimable generation.
+ */
+ if (i == 0 && ++min_seq + MIN_NR_GENS <= lrugen->max_seq)
+ goto next_gen;
+
item = PGSCAN_KSWAPD + reclaimer_offset(sc);
mod_lruvec_state(lruvec, item, isolated);
mod_lruvec_state(lruvec, PGREFILL, sorted);
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH v3 3/6] mm/mglru: improve readability of isolate_folios()
2026-07-31 8:38 [RFC PATCH v3 0/6] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 1/6] mm: mglru: prevent min_seq[type] from pointing to an empty generation Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 2/6] mm: mglru: let scan_folios() scan both reclaimable generations Barry Song (Xiaomi)
@ 2026-07-31 8:38 ` Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 4/6] mm: mglru: improve scan_folios() exhaustion detection Barry Song (Xiaomi)
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Barry Song (Xiaomi) @ 2026-07-31 8:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: axelrasmussen, david, hannes, kasong, linux-kernel, ljs,
lyugaofei, mhocko, qi.zheng, shakeel.butt, stevensd, weixugc,
yuanchu, chenridong, zhangbo56, wangzicheng, lianux.mm,
Barry Song
From: Ridong Chen <chenridong@xiaomi.com>
The for_each_evictable_type() loop in isolate_folios()
is misleading: it does not actually iterate over each
evictable type. Instead, get_type_to_scan() selects the
type to scan, while the iterator `i` merely bounds the
number of attempts.
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
Co-developed-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
---
mm/vmscan.c | 47 +++++++++++++++++++++++++++--------------------
1 file changed, 27 insertions(+), 20 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 31947fa60f18..0038f33aa318 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4828,35 +4828,42 @@ static int get_type_to_scan(struct lruvec *lruvec, int swappiness)
return positive_ctrl_err(&sp, &pv);
}
+static inline bool is_single_type_reclaim(int swappiness)
+{
+ return swappiness == MIN_SWAPPINESS ||
+ swappiness == SWAPPINESS_ANON_ONLY;
+}
+
static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
struct scan_control *sc, int swappiness,
struct list_head *list, int *isolated,
int *isolate_type, int *isolate_scanned)
{
- int i;
- int total_scanned = 0;
+ bool single_type = is_single_type_reclaim(swappiness);
int type = get_type_to_scan(lruvec, swappiness);
+ int total_scanned = 0, scanned, tier;
+ bool tried = false;
- for_each_evictable_type(i, swappiness) {
- int scanned;
- int tier = get_tier_idx(lruvec, type);
+retry:
+ tier = get_tier_idx(lruvec, type);
+ scanned = scan_folios(nr_to_scan, lruvec, sc,
+ type, tier, list, isolated);
- scanned = scan_folios(nr_to_scan, lruvec, sc,
- type, tier, list, isolated);
+ total_scanned += scanned;
+ if (*isolated) {
+ *isolate_type = type;
+ *isolate_scanned = scanned;
+ return total_scanned;
+ }
- total_scanned += scanned;
- if (*isolated) {
- *isolate_type = type;
- *isolate_scanned = scanned;
- break;
- }
- /*
- * If scanned > 0 and isolated == 0, avoid falling back to the
- * other type, as this type remains sufficient. Falling back
- * too readily can disrupt the positive_ctrl_err() bias.
- */
- if (!scanned)
- type = !type;
+ /*
+ * We are running out of the current reclaim type. Fall back to
+ * the other type if allowed.
+ */
+ if (!tried && !scanned && !single_type) {
+ type = !type;
+ tried = true;
+ goto retry;
}
return total_scanned;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH v3 4/6] mm: mglru: improve scan_folios() exhaustion detection
2026-07-31 8:38 [RFC PATCH v3 0/6] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
` (2 preceding siblings ...)
2026-07-31 8:38 ` [RFC PATCH v3 3/6] mm/mglru: improve readability of isolate_folios() Barry Song (Xiaomi)
@ 2026-07-31 8:38 ` Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 5/6] mm: mglru: run aging when pages are severely imbalanced across gens Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 6/6] mm: mglru: run aging if the preferred type has no folios in reclaimable gens Barry Song (Xiaomi)
5 siblings, 0 replies; 7+ messages in thread
From: Barry Song (Xiaomi) @ 2026-07-31 8:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: axelrasmussen, david, hannes, kasong, linux-kernel, ljs,
lyugaofei, mhocko, qi.zheng, shakeel.butt, stevensd, weixugc,
yuanchu, chenridong, zhangbo56, wangzicheng, lianux.mm,
Barry Song (Xiaomi)
Commit 16b475d2ac3c ("mm/mglru: avoid reclaim type fall back when
isolation makes no progress") uses scanned == 0 to determine
whether scan_folios() has exhausted a reclaim type. However,
this is not always sufficient. It is possible for scanned > 0,
while the oldest reclaimable generation is exhausted after the
first scan_folios() call.
Add an exhausted output argument to scan_folios() so it can
explicitly report whether the reclaimable lists for the current
type have been exhausted.
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
---
mm/vmscan.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 0038f33aa318..2e7fef6975b6 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4707,7 +4707,8 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca
static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
struct scan_control *sc, int type, int tier,
- struct list_head *list, int *isolatedp)
+ struct list_head *list, int *isolatedp,
+ bool *exhausted)
{
int i;
int gen;
@@ -4723,8 +4724,10 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
VM_WARN_ON_ONCE(nr_to_scan > MAX_LRU_BATCH);
VM_WARN_ON_ONCE(!list_empty(list));
- if (get_nr_gens(lruvec, type) == MIN_NR_GENS)
+ if (get_nr_gens(lruvec, type) == MIN_NR_GENS) {
+ *exhausted = true;
return 0;
+ }
next_gen:
gen = lru_gen_from_seq(min_seq);
@@ -4785,6 +4788,11 @@ static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
scanned, skipped, isolated,
type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
+ /*
+ * This scan exhausted the reclaimable lists before reaching the
+ * scan target or accumulating enough isolated folios.
+ */
+ *exhausted = remaining > 0 && isolated < MIN_LRU_BATCH;
*isolatedp = isolated;
return scanned;
}
@@ -4842,12 +4850,12 @@ static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
bool single_type = is_single_type_reclaim(swappiness);
int type = get_type_to_scan(lruvec, swappiness);
int total_scanned = 0, scanned, tier;
- bool tried = false;
+ bool tried = false, exhausted;
retry:
tier = get_tier_idx(lruvec, type);
scanned = scan_folios(nr_to_scan, lruvec, sc,
- type, tier, list, isolated);
+ type, tier, list, isolated, &exhausted);
total_scanned += scanned;
if (*isolated) {
@@ -4860,7 +4868,7 @@ static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
* We are running out of the current reclaim type. Fall back to
* the other type if allowed.
*/
- if (!tried && !scanned && !single_type) {
+ if (!tried && exhausted && !single_type) {
type = !type;
tried = true;
goto retry;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH v3 5/6] mm: mglru: run aging when pages are severely imbalanced across gens
2026-07-31 8:38 [RFC PATCH v3 0/6] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
` (3 preceding siblings ...)
2026-07-31 8:38 ` [RFC PATCH v3 4/6] mm: mglru: improve scan_folios() exhaustion detection Barry Song (Xiaomi)
@ 2026-07-31 8:38 ` Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 6/6] mm: mglru: run aging if the preferred type has no folios in reclaimable gens Barry Song (Xiaomi)
5 siblings, 0 replies; 7+ messages in thread
From: Barry Song (Xiaomi) @ 2026-07-31 8:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: axelrasmussen, david, hannes, kasong, linux-kernel, ljs,
lyugaofei, mhocko, qi.zheng, shakeel.butt, stevensd, weixugc,
yuanchu, chenridong, zhangbo56, wangzicheng, lianux.mm,
Barry Song
From: lyugaofei <lyugaofei@xiaomi.com>
This partially restores the reclaim behavior introduced in Yu
Zhao's initial MGLRU commit, ac35a4902370 ("mm: multi-gen LRU:
minimal implementation"):
/*
* It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
* of the total number of pages for each generation. A reasonable range
* for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
* aging cares about the upper bound of hot pages, while the eviction
* cares about the lower bound of cold pages.
*/
if (young * MIN_NR_GENS > total)
return true;
if (old * (MIN_NR_GENS + 2) < total)
return true;
But with a stricter condition: the younger generations must
contain at least old_ratio times as many folios as the older
generations. The old_ratio is derived from the lruvec size, so
larger lruvecs use a higher old_ratio. This allows aging to
keep folios of the preferred type distributed across the
reclaimable generations.
Also, imbalanced aging is only applied to non-balanced swappiness
values (for example, < 60 or > 140). For balanced swappiness values
near the middle, the swappiness bias is less of a concern, while the
slightly increased aging overhead might be unacceptable.
Signed-off-by: lyugaofei <lyugaofei@xiaomi.com>
Co-developed-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
---
mm/vmscan.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 2e7fef6975b6..98fd2fac90ac 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4973,9 +4973,62 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
return scanned;
}
+static inline bool swappiness_is_balanced(int swappiness)
+{
+ int range = MAX_SWAPPINESS - MIN_SWAPPINESS;
+ int middle = MIN_SWAPPINESS + range / 2;
+
+ return swappiness >= middle - range / 5 &&
+ swappiness <= middle + range / 5;
+}
+
+static bool lru_gen_imbalanced(struct lruvec *lruvec, int type,
+ unsigned long max_seq, unsigned long min_seq,
+ struct scan_control *sc, int swappiness)
+{
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
+ unsigned long young = 0, old = 0, seq;
+ unsigned long old_ratio, gb;
+
+ /* Skip swappiness bias for single-type reclaim or balanced swappiness */
+ if (is_single_type_reclaim(swappiness) || swappiness_is_balanced(swappiness))
+ return false;
+
+ /* More than two generations remain to reclaim */
+ if (min_seq + MIN_NR_GENS < max_seq)
+ return false;
+
+ /*
+ * Run aging if the only remaining reclaimable generation
+ * has few folios.
+ */
+ for (seq = min_seq; seq <= max_seq; seq++) {
+ int gen = lru_gen_from_seq(seq);
+ unsigned long size = 0;
+ int zone;
+
+ for (zone = 0; zone < MAX_NR_ZONES; zone++)
+ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
+
+ if (seq + MIN_NR_GENS > max_seq)
+ young += size;
+ else
+ old += size;
+ }
+ /*
+ * Copied from inactive_is_low(), but uses a higher old_ratio to
+ * make aging less aggressive.
+ */
+ gb = (young + old) >> (30 - PAGE_SHIFT);
+ old_ratio = gb ? int_sqrt(10 * gb) : 1;
+ old_ratio *= MAX_NR_GENS;
+ return young > old * old_ratio;
+}
+
static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
struct scan_control *sc, int swappiness)
{
+ int type = get_type_to_scan(lruvec, swappiness);
DEFINE_MIN_SEQ(lruvec);
/* have to run aging, since eviction is not possible anymore */
@@ -4987,7 +5040,11 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
return false;
/* better to run aging even though eviction is still possible */
- return evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS == max_seq;
+ if (evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS == max_seq)
+ return true;
+
+ /* Run aging if the preferred type is severely imbalanced across gens */
+ return lru_gen_imbalanced(lruvec, type, max_seq, min_seq[type], sc, swappiness);
}
static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH v3 6/6] mm: mglru: run aging if the preferred type has no folios in reclaimable gens
2026-07-31 8:38 [RFC PATCH v3 0/6] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
` (4 preceding siblings ...)
2026-07-31 8:38 ` [RFC PATCH v3 5/6] mm: mglru: run aging when pages are severely imbalanced across gens Barry Song (Xiaomi)
@ 2026-07-31 8:38 ` Barry Song (Xiaomi)
5 siblings, 0 replies; 7+ messages in thread
From: Barry Song (Xiaomi) @ 2026-07-31 8:38 UTC (permalink / raw)
To: akpm, linux-mm
Cc: axelrasmussen, david, hannes, kasong, linux-kernel, ljs,
lyugaofei, mhocko, qi.zheng, shakeel.butt, stevensd, weixugc,
yuanchu, chenridong, zhangbo56, wangzicheng, lianux.mm,
Barry Song (Xiaomi)
Respect the type selected by positive_ctrl_err(). If there are no
reclaimable generations left for that type, run aging.
For balanced swappiness values near the middle, still allow gentle
reclaim since the swappiness bias is less of a concern, while the
slightly increased aging overhead might become a problem.
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
---
mm/vmscan.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 98fd2fac90ac..902d85afc5f6 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -5035,6 +5035,10 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
if (evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS > max_seq)
return true;
+ /* run aging if the preferred type is exhausted for extreme swappiness */
+ if (min_seq[type] + MIN_NR_GENS > max_seq && !swappiness_is_balanced(swappiness))
+ return true;
+
/* try to avoid aging, do gentle reclaim at the default priority */
if (sc->priority == DEF_PRIORITY)
return false;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-31 8:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31 8:38 [RFC PATCH v3 0/6] mm: mglru: fix swappiness behavior Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 1/6] mm: mglru: prevent min_seq[type] from pointing to an empty generation Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 2/6] mm: mglru: let scan_folios() scan both reclaimable generations Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 3/6] mm/mglru: improve readability of isolate_folios() Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 4/6] mm: mglru: improve scan_folios() exhaustion detection Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 5/6] mm: mglru: run aging when pages are severely imbalanced across gens Barry Song (Xiaomi)
2026-07-31 8:38 ` [RFC PATCH v3 6/6] mm: mglru: run aging if the preferred type has no folios in reclaimable gens Barry Song (Xiaomi)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox