From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
To: "David Hildenbrand (Arm)" <david@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>,
Zi Yan <ziy@nvidia.com>,
Baolin Wang <baolin.wang@linux.alibaba.com>,
"Liam R. Howlett" <liam@infradead.org>,
Nico Pache <npache@redhat.com>,
Ryan Roberts <ryan.roberts@arm.com>, Dev Jain <dev.jain@arm.com>,
Barry Song <baohua@kernel.org>,
Lance Yang <lance.yang@linux.dev>,
Usama Arif <usama.arif@linux.dev>,
Pankaj Raghav <p.raghav@samsung.com>,
Hannes Reinecke <hare@suse.de>, Hugh Dickins <hughd@google.com>,
Yang Shi <shy828301@gmail.com>, Kiryl Shutsemau <kas@kernel.org>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
Hengbin Zhang <uqbarz@gmail.com>,
stable@vger.kernel.org
Subject: Re: [PATCH mm-hotfixes 2/2] mm/huge_memory: fix huge_zero_pfn race
Date: Thu, 30 Jul 2026 10:55:33 +0100 [thread overview]
Message-ID: <amsch6HvEH5IJjfM@lucifer> (raw)
In-Reply-To: <25d560df-6d7e-462a-a987-60a8695793ba@kernel.org>
On Thu, Jul 30, 2026 at 11:22:09AM +0200, David Hildenbrand (Arm) wrote:
> [...]
>
> > So invariants are required - huge_zero_refcount MUST:
> >
> > * Only be set in the huge_zero_lock critical section to ensure
> > serialisation of huge_zero_pfn, huge_zero_folio and huge_zero_refcount
> > writes.
> >
> > * Be set non-zero only AFTER huge_zero_[pfn, folio] are set to valid values
> > so installation of the huge zero folio on read page fault ensures
> > concurrent is_huge_zero_*() calls correctly identify the huge zero folio.
> >
> > * Be set zero only BEFORE huge_zero_[pfn, folio] are set to NULL and ~0UL
> > respectively, and atomically.
> >
> > Establish these by:
> >
> > * Only updating huge_zero_refcount in the huge_zero_lock critical section
> > in get_huge_zero_folio() and shrink_huge_zero_folio_scan().
>
> That is imprecise. huge_zero_refcount is updated (incremented) outside of
> huge_zero_lock in get_huge_zero_folio().
You're right.
->
* Only setting huge_zero_refcount to zero or an absolute value in the
huge_zero_lock critical section in get_huge_zero_folio() and
shrink_huge_zero_folio_scan() and updating atomically elsewhere.
Seems better?
>
> >
> > * Using atomic_set_release(&huge_zero_refcount) in get_huge_zero_folio()
> > after huge_zero_[pfn, folio] are set. This is paired with
> > atomic_inc_not_zero() to ensure atomic_inc_not_zero() only observes a
> > non-zero value if huge_zero_[pfn, folio] are set.
>
> That makes sense, yes.
>
> >
> > * Using atomic_cmpxchg() in shrink_huge_zero_folio_scan() to ensure that it
> > is set zero only when equal to 1 and set atomically.
>
> What we already do, yes.
Yup, point is it's part of why the invariant holds.
>
> >
> > * atomic_cmpxchg() being fully ordered ensures this is done prior to
> > huge_zero_[folio, pfn] being set to NULL and ~0UL respectively.
>
> Which is always the case for atomic_cmpxchg() I think, yes.
Yup.
>
> [...]
>
> >
> > @@ -270,7 +272,8 @@ void mm_put_huge_zero_folio(struct mm_struct *mm)
> > static bool get_huge_zero_folio(void)
> > {
> > struct folio *zero_folio;
> > -retry:
> > +
> > + /* Paired with atomic_set_release(). */
> > if (likely(atomic_inc_not_zero(&huge_zero_refcount)))
> > return true;
> >
> > @@ -278,17 +281,21 @@ static bool get_huge_zero_folio(void)
> > if (unlikely(!zero_folio))
> > return false;
> >
> > - preempt_disable();
> > - if (cmpxchg(&huge_zero_folio, NULL, zero_folio)) {
> > - preempt_enable();
> > + /* Paired with critical section in shrink_huge_zero_folio_scan(). */
> > + spin_lock(&huge_zero_lock);
> > + if (huge_zero_folio) {
> > + /* Somebody else already installed it. */
> > + atomic_inc(&huge_zero_refcount);
>
> You replace the retry loop by an atomic_inc() here.
>
> That works now by moving the atomic_cmpxchg() in shrink_huge_zero_folio_scan()
> under the lock as well, so it cannot go away concurrently.
Yup. The retry logic in general would be silly now.
>
> Worth spelling that out in the patch description (unless I missed it).
OK.
>
> > + spin_unlock(&huge_zero_lock);
> > folio_put(zero_folio);
> > - goto retry;
> > + return true;
> > }
> > + WRITE_ONCE(huge_zero_folio, zero_folio);
> > WRITE_ONCE(huge_zero_pfn, folio_pfn(zero_folio));
> > + /* Paired with atomic_inc_not_zero(). +1 for shrinker pin. */
> > + atomic_set_release(&huge_zero_refcount, 2);
>
> Ah, that must now go into the lock as well, otherwise the concurrent
> atomic_inc() would be problematic as well.
Yup. It's all very delicate.
>
> > + spin_unlock(&huge_zero_lock);
> >
> > - /* We take additional reference here. It will be put back by shrinker */
> > - atomic_set(&huge_zero_refcount, 2);
> > - preempt_enable();
> > count_vm_event(THP_ZERO_PAGE_ALLOC);
> > return true;
> > }
> > @@ -312,15 +319,22 @@ static unsigned long shrink_huge_zero_folio_count(struct shrinker *shrink,
> > static unsigned long shrink_huge_zero_folio_scan(struct shrinker *shrink,
> > struct shrink_control *sc)
> > {
> > - if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) == 1) {
> > - struct folio *zero_folio = xchg(&huge_zero_folio, NULL);
> > - BUG_ON(zero_folio == NULL);
> > + struct folio *zero_folio;
> > +
> > + /* Paired with critical section in get_huge_zero_folio(). */
> > + scoped_guard(spinlock, &huge_zero_lock) {
> > + /* Paired with atomic_inc_not_zero() in get_huge_zero_folio(). */
> > + if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) != 1)
> > + return 0;
> > +
> > + zero_folio = huge_zero_folio;
> > + VM_WARN_ON_ONCE(!huge_zero_folio);
> > + WRITE_ONCE(huge_zero_folio, NULL);
> > WRITE_ONCE(huge_zero_pfn, HUGE_ZERO_UNSET_PFN);
> > - folio_put(zero_folio);
> > - return HPAGE_PMD_NR;
> > }
>
> One important point: The folio can't get freed and reallocated before we updated
> huge_zero_folio/huge_zero_pfn I think.
It's non-LRU + set not-rmappable so there's nothing that would do that AFAICT.
>
> The folio_put() should involve a full memory barrier (atomic RMW) and not get
> reordered.
Yeah will mention in commit msg.
>
> So we cannot misclassify the folio in new context as still being the zero folio.
>
> LGTM, thanks!
Thanks!
>
> --
> Cheers,
>
> David
CHeers, Lorenzo
next prev parent reply other threads:[~2026-07-30 9:55 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-28 12:05 [PATCH mm-hotfixes 0/2] mm/huge_memory: fix huge_zero_pfn race Lorenzo Stoakes (ARM)
2026-07-28 12:05 ` [PATCH mm-hotfixes 1/2] mm/huge_memory: separate out CONFIG_PERSISTENT_HUGE_ZERO_FOLIO logic Lorenzo Stoakes (ARM)
2026-07-28 15:08 ` Kiryl Shutsemau
2026-07-28 15:41 ` Lorenzo Stoakes (ARM)
2026-07-28 12:05 ` [PATCH mm-hotfixes 2/2] mm/huge_memory: fix huge_zero_pfn race Lorenzo Stoakes (ARM)
2026-07-30 9:22 ` David Hildenbrand (Arm)
2026-07-30 9:55 ` Lorenzo Stoakes (ARM) [this message]
2026-07-30 12:10 ` David Hildenbrand (Arm)
2026-07-30 13:09 ` Lorenzo Stoakes (ARM)
2026-07-28 19:02 ` [PATCH mm-hotfixes 0/2] " David Hildenbrand (Arm)
2026-07-29 8:15 ` Lorenzo Stoakes (ARM)
2026-07-30 0:55 ` Andrew Morton
2026-07-30 9:05 ` David Hildenbrand (Arm)
2026-07-30 9:15 ` Lorenzo Stoakes (ARM)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=amsch6HvEH5IJjfM@lucifer \
--to=ljs@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=david@kernel.org \
--cc=dev.jain@arm.com \
--cc=hare@suse.de \
--cc=hughd@google.com \
--cc=kas@kernel.org \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=npache@redhat.com \
--cc=p.raghav@samsung.com \
--cc=ryan.roberts@arm.com \
--cc=shy828301@gmail.com \
--cc=stable@vger.kernel.org \
--cc=uqbarz@gmail.com \
--cc=usama.arif@linux.dev \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.