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

Shakeel Butt <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.kernel.mm
Message-ID <[email protected]>
On Fri, Aug 07, 2026 at 04:00:30PM +0530, Ketan Kishore wrote:
> 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.

folio_putback_lru() requires lru_lock to not be held because it holds lru lock
internally.

folio_putback_lru
  folio_add_lru
    __folio_batch_add_and_move
      folio_batch_move_lru
        folio_lruvec_relock_irqsave
    

> 
> 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);

This will introduce a deadlock.
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.