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

From: Kiryl Shutsemau

Date: Sun Aug 30 2026 - 20:33:48 EST


On Thu, Aug 27, 2026 at 09:57:20PM -0400, Zi Yan wrote:
> On Thu Aug 27, 2026 at 12:38 PM EDT, Usama Arif wrote:
> > On Wed, 26 Aug 2026 17:20:57 +0100 Kiryl Shutsemau <kirill@xxxxxxxxxxxxx> 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:
> >>
> >> 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/
> >> 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;
> >
> > I think we might have a problem here for ZONE_DEVICE folios?
>
> For coherent ZONE_DEVICE folios, yes. IIRC, private ZONE_DEVICE folios
> are not added to deferred split queue.
> >
> > This assumes the final put always dequeues the folio, but ZONE_DEVICE folios
> > bypass the generic folio_unqueue_deferred_split() path.
> > With memcg disabled, this can leave a recycled folio linked on the
> > deferred-split list?
> >
> > Should we dequeue folios in free_zone_device_folio()?
>
> I think so, before mem_cgroup_uncharge().
>
> But it is a pre-existing issue. We need a separate patch unqueuing
> folios in free_zone_device_folio() to fix commit a30b48bf1b24
> ("mm/migrate_device: implement THP migration of zone device pages").

Agreed, and it is inert today: nothing allocates a large coherent folio.

amdkfd is the only driver with a coherent pgmap and it hands out order-0
pages, and test_hmm builds its coherent chunk the same way. With test_hmm
taught to hand out PMD sized folios, memcg on gives the WARN_ON_ONCE() in
uncharge_folio() and cgroup_disable=memory gives list_del corruption once
the driver reuses the page.

I am not sure what the right solution is: keep such pages off the queue
or dequeue them on free? Or both?

Balbir, I don't know much about zone device. Do you want to take it on?

--
Kiryl Shutsemau / Kirill A. Shutemov