Re: [PATCH v2] mm/mglru: fix a no-op lru_gen_clear_refs() call in lru_lazyfree()
From: Hui Zhu
Date: Fri Nov 06 2026 - 03:45:01 EST
2026/11/6 05:13, Barry Song:
On Sat, Oct 31, 2026 at 3:27 PM Kairui Song <ryncsn@xxxxxxxxx> wrote:
[...]
lru_gen_clear_refs() is currently multifunctional. It clearsAnd it will be better if we can keep this operation off-list just likeOr prehaps we can have a folio_clear_lru_refs() do in a unified wayI recall that in v1 [1], Hui mentioned:mm/folio.c | 6 ++----Why can't we just call folio_set_lru_refs(.., 0), and should we clear
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/mm/folio.c b/mm/folio.c
index 35e242b48870..ea3adb6a89d7 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -597,12 +597,10 @@ static void lru_lazyfree(struct lruvec *lruvec, struct folio *folio)
folio_test_swapcache(folio) || folio_test_unevictable(folio))
return;
+ lru_gen_clear_refs(folio);
lruvec_del_folio(lruvec, folio);
folio_clear_active(folio);
- if (lru_gen_enabled())
- lru_gen_clear_refs(folio);
- else
- folio_clear_referenced(folio);
+ folio_clear_referenced(folio);
PG_workingset? Keeping it helps PSI on refualt / redirty tracking (not
sure if meaningful but CLRU does this), clearing it makes eviction
better, but the eviction issue is MGLRU-only, CLRU keeps it.
"Use folio_clear_referenced() rather than
folio_set_lru_refs(), which is not defined without
CONFIG_LRU_GEN."
for For both CLRU / MGLRU? (Just like folio_migrate_lru_refs).
right now :), some later works could benefit from it.
refs/workingset, but also returns true to skip
folio_batch_add_and_move() in both deactivate_file_folio()
and folio_deactivate().
This actually looks a bit weird. However, following the current
logic, I've drafted a version that we can use as a starting point
for discussion:
diff --git a/mm/folio.c b/mm/folio.c
index dc2c796a4ea7..bc17cd3b6b6c 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -444,12 +444,12 @@ static bool lru_gen_clear_refs(struct folio *folio)
int type = folio_is_file_lru(folio);
unsigned long seq;
- if (gen < 0)
- return true;
-
folio_set_lru_refs(folio, 0);
folio_clear_workingset(folio);
+ if (gen < 0)
+ return true;
+
rcu_read_lock();
seq = READ_ONCE(folio_lruvec(folio)->lrugen.min_seq[type]);
rcu_read_unlock();
@@ -465,6 +465,8 @@ static void lru_gen_inc_refs(struct folio *folio)
static bool lru_gen_clear_refs(struct folio *folio)
{
+ folio_clear_referenced(folio);
+ folio_clear_workingset(folio);
return false;
}
The another option is:
diff --git a/mm/folio.c b/mm/folio.c
index dc2c796a4ea7..8d1fd788188c 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -438,6 +438,12 @@ static void lru_gen_inc_refs(struct folio *folio)
} while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
}
+static inline void __lru_gen_clear_refs(struct folio *folio)
+{
+ folio_set_lru_refs(folio, 0);
+ folio_clear_workingset(folio);
+}
+
static bool lru_gen_clear_refs(struct folio *folio)
{
int gen = folio_lru_gen(folio);
@@ -447,8 +453,7 @@ static bool lru_gen_clear_refs(struct folio *folio)
if (gen < 0)
return true;
- folio_set_lru_refs(folio, 0);
- folio_clear_workingset(folio);
+ __lru_gen_clear_refs(folio);
rcu_read_lock();
seq = READ_ONCE(folio_lruvec(folio)->lrugen.min_seq[type]);
@@ -463,9 +468,10 @@ static void lru_gen_inc_refs(struct folio *folio)
{
}
-static bool lru_gen_clear_refs(struct folio *folio)
+static inline void __lru_gen_clear_refs(struct folio *folio)
{
- return false;
+ folio_clear_referenced(folio);
+ folio_clear_workingset(folio);
}
#endif /* CONFIG_LRU_GEN */
@@ -659,10 +665,7 @@ static void lru_lazyfree(struct lruvec *lruvec,
struct folio *folio)
lruvec_del_folio(lruvec, folio);
folio_clear_active(folio);
- if (lru_gen_enabled())
- lru_gen_clear_refs(folio);
- else
- folio_clear_referenced(folio);
+ __lru_gen_clear_refs(folio);
/*
* Lazyfree folios are clean anonymous folios. They have
* the swapbacked flag cleared, to distinguish them from normal
Hi Barry, Kairui,
I would personally prefer the latter. What do you think, Hui and Kairui?
I prefer option B too: it keeps the flag clearing independent of the
list state and leaves the two deactivate callers untouched.
But I'd suggest splitting this in two:
1. Take v2 as the fix now. If that is acceptable, I will respin it
with Cc: stable added.
2. Follow up with option B as a pure cleanup, no Fixes tag. The
lazyfree site then becomes the off-list __lru_gen_clear_refs()
call that Kairui wants for later work.
The reason is backporting. Every stable from 6.14 (cc8ec7be78ff) to
7.3 carries the regression, but B is written against
folio_set_lru_refs(), which only landed in this merge window
(64169ceea5b1e). None of the affected trees has that helper, so
backporting B would drag the refs-flags rework along with it. v2
applies to all of them with only the mm/swap.c -> mm/folio.c rename
to adjust.
B also starts clearing PG_workingset on the classic LRU. I agree
with Kairui that this is the right semantics for MADV_FREE, but it
is a change beyond this regression and deserves its own patch - it
fits naturally in the MGLRU-FG flag cleanup, as does the sort_folio()
lazyfree check.
Best,
Hui
Best,
Barry