mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Kiryl Shutsemau <kas@kernel.org>
To: "David Hildenbrand (Arm)" <david@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	 Muchun Song <muchun.song@linux.dev>,
	Matthew Wilcox <willy@infradead.org>,
	 Usama Arif <usamaarif642@gmail.com>,
	Frank van der Linden <fvdl@google.com>,
	 Oscar Salvador <osalvador@suse.de>,
	Mike Rapoport <rppt@kernel.org>,
	 Vlastimil Babka <vbabka@suse.cz>,
	Lorenzo Stoakes <lorenzo.stoakes@oracle.com>,
	 Zi Yan <ziy@nvidia.com>, Baoquan He <bhe@redhat.com>,
	Michal Hocko <mhocko@suse.com>,
	 Johannes Weiner <hannes@cmpxchg.org>,
	Jonathan Corbet <corbet@lwn.net>,
	 Huacai Chen <chenhuacai@kernel.org>,
	WANG Xuerui <kernel@xen0n.name>,
	 Palmer Dabbelt <palmer@dabbelt.com>,
	Paul Walmsley <paul.walmsley@sifive.com>,
	 Albert Ou <aou@eecs.berkeley.edu>,
	Alexandre Ghiti <alex@ghiti.fr>,
	kernel-team@meta.com,  linux-mm@kvack.org,
	linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
	 loongarch@lists.linux.dev, linux-riscv@lists.infradead.org
Subject: Re: [PATCHv7 13/18] mm: Drop fake head checks
Date: Mon, 26 Oct 2026 23:25:46 +0000	[thread overview]
Message-ID: <at_f43oDv73y-t7O@thinkstation> (raw)
In-Reply-To: <ebc7bde1-f52e-4da8-b16f-822d6c0a4d74@kernel.org>

On Mon, Oct 26, 2026 at 06:45:48PM +0100, David Hildenbrand (Arm) wrote:
> On 2/27/26 20:42, Kiryl Shutsemau (Meta) wrote:
> > From: Kiryl Shutsemau <kas@kernel.org>
> > 
> > With fake head pages eliminated in the previous commit, remove the
> > supporting infrastructure:
> > 
> >   - page_fixed_fake_head(): no longer needed to detect fake heads;
> >   - page_is_fake_head(): no longer needed;
> >   - page_count_writable(): no longer needed for RCU protection;
> >   - RCU read_lock in page_ref_add_unless(): no longer needed;
> 
> I have an AI report that removing RCU read-lock was wrong.

Ughh. Yes, it is a real bug.

My reasoning in the commit message was wrong. Nothing stops a
speculative get from writing to one after the remap. On x86 even a
failing cmpxchg faults there.

The fix I have in mind is to let the atomic fault.

If the update faults, the struct page is a read-only HVO tail with a
zero refcount, so the get just fails. An exception table entry on the
cmpxchg handles it. No RCU on either side, and the fast path stays the
same lock cmpxchg loop.

Only x86, riscv and loongarch have HVO. Below is x86 only, to show the
idea; riscv and loongarch need the same primitive in their LL/SC
fetch_add_unless().

Lightly tested on x86 by hitting a real HVO tail page.

Does it look sane?

I can prepare a proper patchset if looks reasonable.

diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h
index 75743f1dfd4e..5252b1382ed0 100644
--- a/arch/x86/include/asm/atomic.h
+++ b/arch/x86/include/asm/atomic.h
@@ -108,6 +108,25 @@ static __always_inline bool arch_atomic_try_cmpxchg(atomic_t *v, int *old, int n
 }
 #define arch_atomic_try_cmpxchg arch_atomic_try_cmpxchg
 
+/*
+ * arch_atomic_fetch_add_unless_nofault() is arch_atomic_fetch_add_unless(),
+ * except that a fault on the access jumps to @label instead of oopsing.  It
+ * works in any context, including with IRQs disabled.
+ */
+#define arch_atomic_fetch_add_unless_nofault(_v, _a, _u, label)	\
+({									\
+	atomic_t *__v = (_v);						\
+	int __a = (_a), __u = (_u);					\
+	int __c = arch_atomic_read(__v);				\
+									\
+	do {								\
+		if (unlikely(__c == __u))				\
+			break;						\
+	} while (!__try_cmpxchg_nofault(&__v->counter, &__c,		\
+					__c + __a, label));		\
+	__c;								\
+})
+
 static __always_inline int arch_atomic_xchg(atomic_t *v, int new)
 {
 	return arch_xchg(&v->counter, new);
diff --git a/arch/x86/include/asm/cmpxchg.h b/arch/x86/include/asm/cmpxchg.h
index a88b06f1c35e..685aab3edd3f 100644
--- a/arch/x86/include/asm/cmpxchg.h
+++ b/arch/x86/include/asm/cmpxchg.h
@@ -232,6 +232,63 @@ extern void __add_wrong_size(void)
 #define arch_try_cmpxchg_local(ptr, pold, new)				\
 	__try_cmpxchg_local((ptr), (pold), (new), sizeof(*(ptr)))
 
+/*
+ * __try_cmpxchg_nofault() is arch_try_cmpxchg() on a 4-byte kernel variable,
+ * except that a fault on the access jumps to @label instead of oopsing.
+ */
+#ifdef CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT
+#define __try_cmpxchg_nofault(_ptr, _pold, _new, label)		\
+({									\
+	bool success;							\
+	__typeof__(_ptr) _old = (__typeof__(_ptr))(_pold);		\
+	__typeof__(*(_ptr)) __old = *_old;				\
+	__typeof__(*(_ptr)) __new = (_new);				\
+									\
+	if (sizeof(*(_ptr)) != 4)					\
+		__cmpxchg_wrong_size();					\
+	asm_goto_output("\n"						\
+			"1: " LOCK_PREFIX "cmpxchgl %[new], %[ptr]\n"	\
+			_ASM_EXTABLE(1b, %l[label])			\
+			: "=@ccz" (success),				\
+			  [ptr] "+m" (*(_ptr)),				\
+			  [old] "+a" (__old)				\
+			: [new] "r" (__new)				\
+			: "memory"					\
+			: label);					\
+	if (unlikely(!success))						\
+		*_old = __old;						\
+	likely(success);						\
+})
+#else
+#define __try_cmpxchg_nofault(_ptr, _pold, _new, label)		\
+({									\
+	int __err = 0;							\
+	bool success;							\
+	__typeof__(_ptr) _old = (__typeof__(_ptr))(_pold);		\
+	__typeof__(*(_ptr)) __old = *_old;				\
+	__typeof__(*(_ptr)) __new = (_new);				\
+									\
+	if (sizeof(*(_ptr)) != 4)					\
+		__cmpxchg_wrong_size();					\
+	asm volatile("\n"						\
+		     "1: " LOCK_PREFIX "cmpxchgl %[new], %[ptr]\n"	\
+		     "2:\n"						\
+		     _ASM_EXTABLE_TYPE_REG(1b, 2b, EX_TYPE_EFAULT_REG,	\
+					   %[errout])			\
+		     : "=@ccz" (success),				\
+		       [errout] "+r" (__err),				\
+		       [ptr] "+m" (*(_ptr)),				\
+		       [old] "+a" (__old)				\
+		     : [new] "r" (__new)				\
+		     : "memory");					\
+	if (unlikely(__err))						\
+		goto label;						\
+	if (unlikely(!success))						\
+		*_old = __old;						\
+	likely(success);						\
+})
+#endif
+
 /*
  * xadd() adds "inc" to "*ptr" and atomically returns the previous
  * value of "*ptr".
diff --git a/include/linux/page_ref.h b/include/linux/page_ref.h
index 9f5c75d06f76..82a2fc445ce6 100644
--- a/include/linux/page_ref.h
+++ b/include/linux/page_ref.h
@@ -246,9 +246,31 @@ static inline int folio_ref_dec_return(struct folio *folio)
 	return page_ref_dec_return(&folio->page);
 }
 
+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
+/*
+ * HVO maps the tail struct pages of a hugetlb folio read-only.  A speculative
+ * caller can read a non-zero refcount, then the page gets freed, becomes a
+ * tail page of a hugetlb folio and is remapped before the update.  The
+ * atomic then faults.  A read-only struct page is a hugetlb tail page with a
+ * zero refcount, so the fault means no reference can be taken.
+ */
+static __always_inline bool __page_ref_add_unless_zero(atomic_t *v, int nr)
+{
+	instrument_atomic_read_write(v, sizeof(*v));
+	return arch_atomic_fetch_add_unless_nofault(v, nr, 0, fault) != 0;
+fault:
+	return false;
+}
+#else
+static __always_inline bool __page_ref_add_unless_zero(atomic_t *v, int nr)
+{
+	return atomic_add_unless(v, nr, 0);
+}
+#endif
+
 static inline bool page_ref_add_unless_zero(struct page *page, int nr)
 {
-	bool ret = atomic_add_unless(&page->_refcount, nr, 0);
+	bool ret = __page_ref_add_unless_zero(&page->_refcount, nr);
 
 	if (page_ref_tracepoint_active(page_ref_mod_unless))
 		__page_ref_mod_unless(page, nr, ret);
-- 
  Kiryl Shutsemau / Kirill A. Shutemov

  reply	other threads:[~2026-10-26 23:25 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-27 19:42 [PATCHv7 RESEND 00/17] mm: Eliminate fake head pages from vmemmap optimization Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 01/18] mm: Move MAX_FOLIO_ORDER definition to mmzone.h Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 02/18] mm: Change the interface of prep_compound_tail() Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 03/18] mm: Rename the 'compound_head' field in the 'struct page' to 'compound_info' Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 04/18] mm: Move set/clear_compound_head() next to compound_head() Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 05/18] riscv/mm: Align vmemmap to maximal folio size Kiryl Shutsemau (Meta)
2026-03-16 16:30   ` David Hildenbrand (Arm)
2026-02-27 19:42 ` [PATCHv7 06/18] LoongArch/mm: " Kiryl Shutsemau (Meta)
2026-03-16 16:31   ` David Hildenbrand (Arm)
2026-02-27 19:42 ` [PATCHv7 07/18] mm: Rework compound_head() for power-of-2 sizeof(struct page) Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 08/18] mm/sparse: Check memmap alignment for compound_info_has_mask() Kiryl Shutsemau (Meta)
2026-03-16 16:33   ` David Hildenbrand (Arm)
2026-02-27 19:42 ` [PATCHv7 09/18] mm/hugetlb: Defer vmemmap population for bootmem hugepages Kiryl Shutsemau (Meta)
2026-03-16 16:48   ` David Hildenbrand (Arm)
2026-03-17 11:28     ` Kiryl Shutsemau
2026-03-17 12:47       ` David Hildenbrand (Arm)
2026-02-27 19:42 ` [PATCHv7 10/18] mm/hugetlb: Refactor code around vmemmap_walk Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 11/18] x86/vdso: Undefine CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP for vdso32 Kiryl Shutsemau (Meta)
2026-03-16 16:49   ` David Hildenbrand (Arm)
2026-02-27 19:42 ` [PATCHv7 12/18] mm/hugetlb: Remove fake head pages Kiryl Shutsemau (Meta)
2026-03-05 10:39   ` Vlastimil Babka (SUSE)
2026-03-16 17:01   ` David Hildenbrand (Arm)
2026-03-17 11:47   ` [PATCHv7.1 " Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 13/18] mm: Drop fake head checks Kiryl Shutsemau (Meta)
2026-10-26 17:45   ` David Hildenbrand (Arm)
2026-10-26 23:25     ` Kiryl Shutsemau [this message]
2026-10-26 23:37       ` David Hildenbrand (Arm)
2026-10-27 11:00         ` Peter Zijlstra
2026-10-27 12:00           ` Kiryl Shutsemau
2026-10-27 12:05             ` David Hildenbrand (Arm)
2026-10-27 12:31               ` Kiryl Shutsemau
2026-10-27 12:47                 ` Peter Zijlstra
2026-10-27 12:49                   ` Peter Zijlstra
2026-10-27 12:13             ` Peter Zijlstra
2026-10-27 12:22               ` Kiryl Shutsemau
2026-10-27 12:29                 ` Peter Zijlstra
2026-02-27 19:42 ` [PATCHv7 14/18] hugetlb: Remove VMEMMAP_SYNCHRONIZE_RCU Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 15/18] mm/hugetlb: Remove hugetlb_optimize_vmemmap_key static key Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 16/18] mm: Remove the branch from compound_head() Kiryl Shutsemau (Meta)
2026-02-27 19:42 ` [PATCHv7 17/18] hugetlb: Update vmemmap_dedup.rst Kiryl Shutsemau (Meta)
2026-02-27 21:08   ` Randy Dunlap
2026-03-02  3:10   ` Muchun Song
2026-03-02 10:56   ` [PATCHv7.1 " Kiryl Shutsemau (Meta)
2026-04-30  3:25     ` patchwork-bot+linux-riscv
2026-02-27 19:42 ` [PATCHv7 18/18] mm/slab: Use compound_head() in page_slab() Kiryl Shutsemau (Meta)
2026-02-28 23:01 ` [PATCHv7 RESEND 00/17] mm: Eliminate fake head pages from vmemmap optimization Andrew Morton
2026-04-30  3:25 ` patchwork-bot+linux-riscv
  -- strict thread matches above, loose matches on Subject: below --
2026-02-02 15:56 [PATCHv6 " Kiryl Shutsemau
2026-02-27 19:30 ` [PATCHv7 13/18] mm: Drop fake head checks Kiryl Shutsemau (Meta)
2026-04-30  3:25   ` patchwork-bot+linux-riscv

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=at_f43oDv73y-t7O@thinkstation \
    --to=kas@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=alex@ghiti.fr \
    --cc=aou@eecs.berkeley.edu \
    --cc=bhe@redhat.com \
    --cc=chenhuacai@kernel.org \
    --cc=corbet@lwn.net \
    --cc=david@kernel.org \
    --cc=fvdl@google.com \
    --cc=hannes@cmpxchg.org \
    --cc=kernel-team@meta.com \
    --cc=kernel@xen0n.name \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=loongarch@lists.linux.dev \
    --cc=lorenzo.stoakes@oracle.com \
    --cc=mhocko@suse.com \
    --cc=muchun.song@linux.dev \
    --cc=osalvador@suse.de \
    --cc=palmer@dabbelt.com \
    --cc=paul.walmsley@sifive.com \
    --cc=rppt@kernel.org \
    --cc=usamaarif642@gmail.com \
    --cc=vbabka@suse.cz \
    --cc=willy@infradead.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®