[PATCH] mm/mglru: fix lockless folio_putback_lru() race in evict_folios()
From: Ketan Kishore
Date: Fri Aug 07 2026 - 06:41:20 EST
folio_putback_lru() requires lru_lock to not be held by the caller,
but it internally calls folio_add_lru() -> lruvec_add_folio() ->
lru_gen_add_folio() -> list_add(), which modifies lrugen->folios[]
without acquiring lruvec->lru_lock.
In evict_folios(), lru_lock is dropped before shrink_folio_list() and
the subsequent list_for_each_entry_safe_reverse() loop. When an
unevictable folio is encountered in this lockless section,
folio_putback_lru() is called to return it to lrugen->folios[]. This
races with any concurrent CPU that holds lru_lock and operates on the
same list (e.g. via list_del or list_move), corrupting prev->next.
The corruption is detected later when move_folios_to_lru() re-acquires
lru_lock and calls list_del() on a folio whose list linkage was
corrupted, triggering BUG at lib/list_debug.c:64:
list_del corruption. prev->next should be fffffffeead4fbc8,
but was ffffeafeead44188. (prev=fffffffee4fc4c08)
kernel BUG at lib/list_debug.c:64!
Call trace:
__list_del_entry_valid_or_report+0x100/0x14c
evict_folios+0x145c/0x16dc
try_to_shrink_lruvec+0x228/0x35c
shrink_one+0x94/0x158
shrink_many+0x1c8/0x1f4
lru_gen_shrink_node+0x94/0x110
shrink_node+0x468/0x8b4
balance_pgdat+0x4f0/0x9a0
kswapd+0x268/0x470
The race window is amplified when unevictable memory is high, causing
folio_putback_lru() to be called many times in the lockless section.
move_folios_to_lru() already handles this correctly: it drops and
re-acquires lru_lock around folio_putback_lru() for unevictable folios.
Apply the same pattern to evict_folios().
Fixes: 359a5e1416ca ("mm: multi-gen LRU: retry folios written back while isolated")
Signed-off-by: Prakash Gupta <prakash.gupta@xxxxxxxxxxxxxxxx>
Signed-off-by: Ketan Kishore <ketan.kishore@xxxxxxxxxxxxxxxx>
---
mm/vmscan.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 26436059ea39..7c4adba13e4f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4923,7 +4923,9 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
if (!folio_evictable(folio)) {
list_del(&folio->lru);
+ spin_lock_irq(&lruvec->lru_lock);
folio_putback_lru(folio);
+ spin_unlock_irq(&lruvec->lru_lock);
continue;
}
---
base-commit: ea2bff00da89d7767d677bb68470130ba96f4928
change-id: 20260807-evict_folios_race-f3b119e0cf97
Best regards,
--
Ketan Kishore <ketan.kishore@xxxxxxxxxxxxxxxx>