[PATCH RFC] mm/vmscan:Fix the hot/cold inversion when swappiness =
From: w00021541
Date: Tue Apr 07 2026 - 04:17:53 EST
In some cases, when swappiness is set to 0 or 201, the oldest generation pa=
ges will be changed to the newest generation incorrectly.
Consider the following aging scenario:
MAX_NR_GENS=3D4, MIN_NR_GENS=3D2, swappiness=3D201, 3 anon gens, 4 file gen=
s.
1. When swappiness =3D 201, should_run_aging will only check anon type.
should_run_aging return true.
2. In inc_max_seq, if the anon and file type have MAX_NR_GENS, inc_min_seq =
will move the oldest generation pages to the second oldest to prepare for i=
ncreasing max_seq.
Here, the file type will enter inc_min_seq.
3. In inc_min_seq, first goto is true, the pages migration was skipped, res=
ulting in the inversion of cold/hot pages.
In fact, when MAX_NR_GENS=3D4 and MIN_NR_GENS=3D2, the for loop after the g=
oto is unreachable.
Consider the code in inc_max_seq:
if (get_nr_gens(lruvec, type) ! =3D MAX_NR_GENS)
continue;
This means that only get_nr_gens=3D=3D4 can enter the inc_min_seq.
Discuss the swappiness in three different scenarios:
1<=3Dswappiness<=3D200:
If should_run_aging returns true, both anon and file types must satisfy get=
_nr_gens<=3D3, indicating that no type satisfies get_nr_gens=3D=3DMAX_NR_GE=
NS.
Therefore, both cannot enter inc_min_seq.
swappiness=3D201:
If should_run_aging returns true, the anon type must satisfy get_nr_gens<=
=3D3. Only file type can satisfy get_nr_gens=3D=3DMAX_NR_GENS.
After entering inc_min_seq, type && (swappiness =3D=3D SWAPPINESS_ANON_ONLY=
) is true, the for loop will be skipped.
swappiness=3D0:
Same as swappiness=3D201
so the two goto statements should be removed. This ensures that when swappi=
ness=3D0 or 201, the oldest generation pages are correctly promoted to the =
second oldest generation.
(When 1<=3D swappiness<=3D200, only both anon and file types get_nr_gens<=
=3D3 will age, preventing the inversion of hot/cold pages).
Signed-off-by: w00021541 <wangzhen5@xxxxxxxxxxx>
---
mm/vmscan.c | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 0fc9373e8251..54c835b07d3e 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -3843,7 +3843,7 @@ static void clear_mm_walk(void)
kfree(walk);
}
=20
-static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness)
+static bool inc_min_seq(struct lruvec *lruvec, int type)
{
int zone;
int remaining =3D MAX_LRU_BATCH;
@@ -3851,14 +3851,6 @@ static bool inc_min_seq(struct lruvec *lruvec, int t=
ype, int swappiness)
int hist =3D lru_hist_from_seq(lrugen->min_seq[type]);
int new_gen, old_gen =3D lru_gen_from_seq(lrugen->min_seq[type]);
=20
- /* For file type, skip the check if swappiness is anon only */
- if (type && (swappiness =3D=3D SWAPPINESS_ANON_ONLY))
- goto done;
-
- /* For anon type, skip the check if swappiness is zero (file only) */
- if (!type && !swappiness)
- goto done;
-
/* prevent cold/hot inversion if the type is evictable */
for (zone =3D 0; zone < MAX_NR_ZONES; zone++) {
struct list_head *head =3D &lrugen->folios[old_gen][type][zone];
@@ -3889,7 +3881,7 @@ static bool inc_min_seq(struct lruvec *lruvec, int ty=
pe, int swappiness)
return false;
}
}
-done:
+
reset_ctrl_pos(lruvec, type, true);
WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1);
=20
@@ -3975,7 +3967,7 @@ static bool inc_max_seq(struct lruvec *lruvec, unsign=
ed long seq, int swappiness
if (get_nr_gens(lruvec, type) !=3D MAX_NR_GENS)
continue;
=20
- if (inc_min_seq(lruvec, type, swappiness))
+ if (inc_min_seq(lruvec, type))
continue;
=20
spin_unlock_irq(&lruvec->lru_lock);
--
2.17.1