[PATCH] mm/mglru: fix lockless folio_putback_lru() race in evict_folios()

Ketan Kishore <[email protected]>
Newsgroups org.kvack.linux-mm,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <[email protected]>
Signed-off-by: Ketan Kishore <[email protected]>
---
 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 <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.