[PATCH RFC v2 09/15] mm/madvise: adapt for LRU refs based operations in MGLRU
From: Kairui Song via B4 Relay
Date: Fri Sep 11 2026 - 08:05:36 EST
From: Kairui Song <kasong@xxxxxxxxxxx>
For the active/inactive LRU, madvise clears PG_referenced so that one more
access is not enough to reactivate a folio, and keeps PG_workingset on a
folio demoted out of the active list so its refault is still accounted as
a workingset refault (PSI).
MGLRU keeps that history in the folio's refs count instead, which now
lives in PG_referenced, PG_workingset and LRU_REFS_MASK, so writing to
those bits directly corrupts the count. The two hints want different
things:
- MADV_COLD resets the count in folio_deactivate(), which also moves the
folio to the oldest generation.
- MADV_PAGEOUT isolates the folio and hands it to reclaim_pages(), which
ignores the count, so keep it and let the eviction shadow record the
folio's real hotness.
PG_young is idle-page-tracking state, not a reclaim input, so it is still
cleared for both LRUs.
Signed-off-by: Kairui Song <kasong@xxxxxxxxxxx>
---
mm/madvise.c | 48 ++++++++++++++++++++++++++++++++++--------------
1 file changed, 34 insertions(+), 14 deletions(-)
diff --git a/mm/madvise.c b/mm/madvise.c
index f75a9d139980..cdd1bebf2485 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -361,6 +361,38 @@ static inline int madvise_folio_pte_batch(unsigned long addr, unsigned long end,
FPB_MERGE_YOUNG_DIRTY);
}
+/*
+ * Drop the recency information madvise() is expected to drop before
+ * deactivating or reclaiming a folio.
+ */
+static void madvise_cold_or_pageout_prep_folio(struct folio *folio)
+{
+ /*
+ * VM couldn't reclaim the folio unless we clear PG_young.
+ * As a side effect, it makes confuse idle-page tracking
+ * because they will miss recent referenced history.
+ */
+ folio_test_clear_young(folio);
+
+ /*
+ * MGLRU keeps this history in the refs count. MADV_COLD resets
+ * it in folio_deactivate(), MADV_PAGEOUT hands the folio to
+ * reclaim_pages(), which ignores the count, so keep it, and let
+ * the eviction shadow record the folio's real hotness.
+ */
+ if (lru_gen_enabled())
+ return;
+
+ /*
+ * For the active/inactive LRU, a folio demoted out of the active
+ * list should have PG_workingset so its refault is still accounted
+ * as a workingset refault.
+ */
+ folio_clear_referenced(folio);
+ if (folio_test_active(folio))
+ folio_set_workingset(folio);
+}
+
static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
unsigned long addr, unsigned long end,
struct mm_walk *walk)
@@ -438,10 +470,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
}
- folio_clear_referenced(folio);
- folio_test_clear_young(folio);
- if (folio_test_active(folio))
- folio_set_workingset(folio);
+ madvise_cold_or_pageout_prep_folio(folio);
if (pageout) {
if (folio_isolate_lru(folio)) {
if (folio_test_unevictable(folio))
@@ -547,16 +576,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
tlb_remove_tlb_entries(tlb, pte, nr, addr);
}
- /*
- * We are deactivating a folio for accelerating reclaiming.
- * VM couldn't reclaim the folio unless we clear PG_young.
- * As a side effect, it makes confuse idle-page tracking
- * because they will miss recent referenced history.
- */
- folio_clear_referenced(folio);
- folio_test_clear_young(folio);
- if (folio_test_active(folio))
- folio_set_workingset(folio);
+ madvise_cold_or_pageout_prep_folio(folio);
if (pageout) {
if (folio_isolate_lru(folio)) {
if (folio_test_unevictable(folio))
--
2.55.0