Re: [PATCH 10/46] mm/huge_memory: fix list corruption when deferred_split_scan() races with deferred_split_folio()
From: David Hildenbrand (Arm)
Date: Tue Oct 27 2026 - 11:28:13 EST
On 10/27/26 15:10, Johannes Weiner wrote:
> On Tue, Oct 27, 2026 at 12:17:30AM +0100, David Hildenbrand (Arm) wrote:
>> Since the deferred split queue became a list_lru, the isolate callback
>> moves the folios it took a reference on to an on-stack list, linked
>> through folio->_deferred_list, and deferred_split_scan() takes them off
>> that list with list_del_init() without holding the list_lru lock.
>>
>> deferred_split_folio() does not know about the on-stack list. It only
>> holds the list_lru lock when it looks at the same list_head, and it can
>> run for such a folio: a THP is queued when it is mapped, as a candidate
>> for the underused check, and is queued "again" when a part of it gets
>> unmapped:
>>
>> CPU0 deferred_split_scan() CPU1 deferred_split_folio()
>> list_del_init(&folio->_deferred_list)
>> next->prev = prev
>> prev->next = next
>> entry->next = entry
>> list_lru_lock_irqsave()
>> folio_set_partially_mapped()
>> __list_lru_add()
>> list_empty(item) -> true
>> list_add_tail(item, &l->list)
>> item->next = &l->list
>> item->prev = tail
>> entry->prev = entry
>>
>> The folio now sits on the list_lru with ->prev pointing to itself. The
>> next removal leaves the neighbours pointing to it, and they still do so
>> after the folio has been freed. With CONFIG_DEBUG_LIST:
>>
>> list_del corruption. prev->next should be ffd4000000270090, but was
>> ffd4000000278090. (prev=ffd4000000270090)
>>
>> Fix it by grabbing the lock around list_del_init() so
>> deferred_split_folio() cannot race with it.
>>
>> Fixes: fafaeceb89a5 ("mm: switch deferred split shrinker to list_lru")
>> Cc: stable@xxxxxxxxxxxxxxx
>> Reported-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
>> Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
>
> Reviewed-by: Johannes Weiner <hannes@xxxxxxxxxxx>
While I have your attention: This variant works, a different proposal used
list_del_init_careful()
instead. However, that is documented to require list_empty_careful().
Do you think it's worth looking into?
https://lore.kernel.org/all/20261026-thpsplit-careful-v1-1-d357d70fd6f4@xxxxxxxxxxxx/
--
Cheers,
David