Re: [PATCH 1/5] mm/huge_memory: do not touch frozen folios in deferred_split_isolate()

From: David Hildenbrand (Arm)

Date: Thu Aug 27 2026 - 11:24:06 EST


On 8/26/26 18:20, Kiryl Shutsemau wrote:
> From: "Kiryl Shutsemau (Meta)" <kas@xxxxxxxxxx>
>
> deferred_split_isolate() probes each queued folio with folio_try_get().
> folio_try_get() failure is treated as a lost race with folio_put(): clear
> PG_partially_mapped, correct MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, take
> the folio off the queue.
>
> The folio_put() race is the most common case for !folio_try_get(), but
> it is not the only option. Another scenario is folio_ref_freeze().
>
> A zero refcount in such cases does not mean the folio is going away. It
> means "don't touch me" and current deferred_split_isolate() doesn't
> respect it. It can lead to unqueueing folios from the deferred list for
> no reason:

Yes.

>
> CPU 0 CPU 1
> --------------------------- ------------------------------
> freeze a mapped folio deferred_split_scan()
> folio_ref_freeze() folio_try_get() fails
> folio_clear_partially_mapped()
> NR_ANON_PARTIALLY_MAPPED--
> folio off the queue
> give up, put it back
> folio_ref_unfreeze()
>
> The folio is still partially mapped, but it is no longer a split candidate.
> Nothing queues it again until part of it is unmapped once more.
>
> Skip the folio instead: whoever freezes the folio, owns it and owner is
> responsible for its fate. It also covers the folio_put() case:
> __folio_put() unqueues the folio via folio_unqueue_deferred_split().
>
> Reported-by: Lance Yang <lance.yang@xxxxxxxxx>
> Link: https://lore.kernel.org/all/20260824131224.73344-1-lance.yang@xxxxxxxxx/

Fixes?

> Assisted-by: Claude-Code:claude-opus-5
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
> ---
> mm/huge_memory.c | 19 ++++---------------
> 1 file changed, 4 insertions(+), 15 deletions(-)
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index ced400f72d43..6281ed993243 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -4590,22 +4590,11 @@ static enum lru_status deferred_split_isolate(struct list_head *item,
> struct folio *folio = container_of(item, struct folio, _deferred_list);
> struct list_head *freeable = cb_arg;
>
> - if (folio_try_get(folio)) {
> - list_lru_isolate_move(lru, item, freeable);
> - return LRU_REMOVED;
> - }
> + /* Lost race to folio_put() or the folio is under folio_ref_freeze() */
> + if (!folio_try_get(folio))
> + return LRU_SKIP;
>
> - /*
> - * We lost race with folio_put(). Read folio state before the
> - * isolate: folio_unqueue_deferred_split() checks list_empty()
> - * locklessly, so once removed the folio can be freed any time.
> - */
> - if (folio_test_partially_mapped(folio)) {
> - folio_clear_partially_mapped(folio);
> - mod_mthp_stat(folio_order(folio),
> - MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
> - }
> - list_lru_isolate(lru, item);
> + list_lru_isolate_move(lru, item, freeable);
> return LRU_REMOVED;
> }
>

Who will clean up the stats that we used to clean up? That should be mentioned
in the commit log, otherwise it looks like some piece of the puzzle is missing.

--
Cheers,

David