mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Muchun Song <muchun.song@linux.dev>
To: Kiryl Shutsemau <kas@kernel.org>
Cc: "David Hildenbrand (Arm)" <david@kernel.org>,
	Peter Zijlstra <peterz@infradead.org>,
	Andrew Morton <akpm@linux-foundation.org>,
	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,
	Dave Hansen <dave.hansen@linux.intel.com>,
	Andy Lutomirski <luto@kernel.org>
Subject: Re: [PATCHv7 13/18] mm: Drop fake head checks
Date: Wed, 28 Oct 2026 10:38:22 +0800	[thread overview]
Message-ID: <7cb3dd91-f518-49b6-b311-9ec807b3c63b@linux.dev> (raw)
In-Reply-To: <auDkbDlgSZxqlk2G@thinkstation>



On 2026/10/28 01:51, Kiryl Shutsemau wrote:
> On Tue, Oct 27, 2026 at 10:03:54PM +0800, Muchun Song wrote:
>> Therefore, I am kind of leaning towards removing the RO protection
>> and restoring RW permissions to solve the race.
> Okay, what about this:
>
>  From e23ab850837b53cc79d57d58f1cfb4266e745c3d Mon Sep 17 00:00:00 2001
> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
> Date: Tue, 27 Oct 2026 17:23:47 +0000
> Subject: [PATCH] mm/hugetlb_vmemmap: map the shared tail page writable
>
> page_ref_add_unless_zero() reads the refcount and then updates it with
> a cmpxchg.  A lockless get, such as a page cache lookup or a PFN
> scanner, can read a non-zero refcount from a page that is then freed
> and reused as a hugetlb tail page.  If HVO remaps its struct page to
> the shared tail page in between, the cmpxchg writes to read-only
> vmemmap:
>
>      CPU0 (lockless get)               CPU1
>      c = atomic_read(&page->_refcount)
>                                        page freed
>                                        page allocated as a hugetlb tail
>                                        HVO remaps the struct page RO
>      try_cmpxchg(&page->_refcount, &c, c + 1)
>        -> write fault on read-only vmemmap
>
> The refcount is zero by then and the compare fails, but LOCK CMPXCHG on
> x86 writes anyway.  The kernel oopses when hugetlb pages are allocated

Is this an X86-specific issue? If not, I suggest a more generic
description.

> at runtime with HVO enabled while a lockless get races with it.
>
> Commit bd225530a4c7 ("mm/hugetlb_vmemmap: fix race with speculative PFN
> walkers") closed the window with RCU on both sides.  The RCU was removed
> together with fake heads, on the wrong belief that it was only needed
> for them.
>
> Map the shared tail page writable instead of restoring the RCU.  Every
> struct page on it is a tail with a zero refcount, so a late cmpxchg
> fails and leaves the page unchanged.  RCU would put rcu_read_lock()
> back around every get and synchronize_rcu() back into hugetlb
> allocation and freeing.
>
> The cost is the protection against stray writes to tail struct pages.
> The shared page is shared by all hugetlb folios of the same order in
> the zone, so such a write now corrupts all of them instead of oopsing.
>
> Bootmem folios optimized in early boot get their tails mapped writable
> and write protected later.  Drop the write protect step.

For bootmem folios, I don't think the RO mapping should be removed,
because we are in the initialization stage of struct page.
No one should touch those pages at this point; otherwise, they could
encounter uninitialized struct page as well. This assumes no race
exists during early boot.

Since such a problem does not exist during the boot stage, why should
we give up the protection that RO provides?

Thoughts?

Thanks,
Muchun

>
> Fixes: 32c440d67e6c ("mm: drop fake head checks")
> Fixes: 01b1d0ffb6f7 ("hugetlb: remove VMEMMAP_SYNCHRONIZE_RCU")
> Reported-by: David Hildenbrand (Arm) <david@kernel.org>
> Closes: https://lore.kernel.org/r/ebc7bde1-f52e-4da8-b16f-822d6c0a4d74@kernel.org
> Cc: stable@vger.kernel.org
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> ---
>   Documentation/mm/vmemmap_dedup.rst | 13 +++++++++----
>   include/linux/mm.h                 |  2 --
>   mm/hugetlb.c                       |  4 ----
>   mm/hugetlb_vmemmap.c               | 21 +++++++--------------
>   mm/sparse-vmemmap.c                | 23 -----------------------
>   5 files changed, 16 insertions(+), 47 deletions(-)
>
> diff --git a/Documentation/mm/vmemmap_dedup.rst b/Documentation/mm/vmemmap_dedup.rst
> index 9fa8642ded48..6cd85aca4b96 100644
> --- a/Documentation/mm/vmemmap_dedup.rst
> +++ b/Documentation/mm/vmemmap_dedup.rst
> @@ -131,8 +131,15 @@ pages of ``struct page`` (page 1 to page 7) are tail pages.
>   The optimization is only applied when the size of the struct page is a power
>   of 2. In this case, all tail pages of the same order are identical. See
>   compound_head(). This allows us to remap the tail pages of the vmemmap to a
> -shared, read-only page. The head page is also remapped to a new page. This
> -allows the original vmemmap pages to be freed.
> +shared page. The head page is also remapped to a new page. This allows the
> +original vmemmap pages to be freed.
> +
> +The shared page is mapped writable. A lockless get of a page reference can
> +read a non-zero refcount before the remap and attempt its atomic update
> +after it. The refcount of a tail page is zero, so the update fails without
> +changing the page. Nothing else may write to the shared page: it is shared
> +by all HugeTLB pages of the same size in the zone, and a stray write would
> +corrupt all of them.
>   
>   Here is how things look after remapping::
>   
> @@ -196,8 +203,6 @@ happens at a later stage when we populate the sections. HugeTLB reuses the
>   the head vmemmap page representing, whereas device-dax reuses the tail
>   vmemmap page. This results in only half of the savings compared to HugeTLB.
>   
> -Deduplicated tail pages are not mapped read-only.
> -
>   Here's how things look like on device-dax after the sections are populated::
>   
>    +-----------+ ---virt_to_page---> +-----------+   mapping to   +-----------+
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index dd09c438fa23..bfaf4ce6d831 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -5162,8 +5162,6 @@ int vmemmap_populate(unsigned long start, unsigned long end, int node,
>   int vmemmap_populate_hvo(unsigned long start, unsigned long end,
>   			 unsigned int order, struct zone *zone,
>   			 unsigned long headsize);
> -void vmemmap_wrprotect_hvo(unsigned long start, unsigned long end, int node,
> -			  unsigned long headsize);
>   void vmemmap_populate_print_last(void);
>   #ifdef CONFIG_MEMORY_HOTPLUG
>   void vmemmap_free(unsigned long start, unsigned long end,
> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> index cea25773a6c9..7596704d94b5 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -3220,10 +3220,6 @@ static bool __init hugetlb_bootmem_page_earlycma(struct huge_bootmem_page *m)
>    * if marked with the 'noinit' flag. Set it to the default (MIGRATE_MOVABLE)
>    * here, or MIGRATE_CMA if this was a page allocated through an early CMA
>    * reservation.
> - *
> - * In case of vmemmap optimized folios, the tail vmemmap pages are mapped
> - * read-only, but that's ok - for sparse vmemmap this does not write to
> - * the page structure.
>    */
>   static void __init hugetlb_bootmem_init_migratetype(struct folio *folio,
>   							  struct hstate *h)
> diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
> index 917db0984143..77f9d035d04c 100644
> --- a/mm/hugetlb_vmemmap.c
> +++ b/mm/hugetlb_vmemmap.c
> @@ -222,10 +222,12 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
>   		VM_WARN_ON_ONCE(!PageTail((const struct page *)addr));
>   
>   		/*
> -		 * Remap the tail pages as read-only to catch illegal write
> -		 * operation to the tail pages.
> +		 * Map the shared tail page writable. A lockless get that read
> +		 * a non-zero refcount before the remap can still do its
> +		 * cmpxchg after it. The refcount is zero, so the cmpxchg
> +		 * fails and leaves the page unchanged.
>   		 */
> -		entry = mk_pte(walk->vmemmap_tail, PAGE_KERNEL_RO);
> +		entry = mk_pte(walk->vmemmap_tail, PAGE_KERNEL);
>   	}
>   
>   	list_add(&page->lru, walk->vmemmap_pages);
> @@ -629,19 +631,10 @@ static void __hugetlb_vmemmap_optimize_folios(struct hstate *h,
>   	nr_to_optimize = 0;
>   	list_for_each_entry(folio, folio_list, lru) {
>   		int ret;
> -		unsigned long spfn, epfn;
>   
> -		if (boot && folio_test_hugetlb_vmemmap_optimized(folio)) {
> -			/*
> -			 * Already optimized by pre-HVO, just map the
> -			 * mirrored tail page structs RO.
> -			 */
> -			spfn = (unsigned long)&folio->page;
> -			epfn = spfn + hugetlb_vmemmap_size(h);
> -			vmemmap_wrprotect_hvo(spfn, epfn, folio_nid(folio),
> -					HUGETLB_VMEMMAP_RESERVE_SIZE);
> +		/* Already optimized by pre-HVO */
> +		if (boot && folio_test_hugetlb_vmemmap_optimized(folio))
>   			continue;
> -		}
>   
>   		nr_to_optimize++;
>   
> diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
> index 5a2469fb1838..3f25d4a0bf47 100644
> --- a/mm/sparse-vmemmap.c
> +++ b/mm/sparse-vmemmap.c
> @@ -299,29 +299,6 @@ int __meminit vmemmap_populate_basepages(unsigned long start, unsigned long end,
>   	return vmemmap_populate_range(start, end, node, altmap, -1, 0);
>   }
>   
> -/*
> - * Write protect the mirrored tail page structs for HVO. This will be
> - * called from the hugetlb code when gathering and initializing the
> - * memblock allocated gigantic pages. The write protect can't be
> - * done earlier, since it can't be guaranteed that the reserved
> - * page structures will not be written to during initialization,
> - * even if CONFIG_DEFERRED_STRUCT_PAGE_INIT is enabled.
> - *
> - * The PTEs are known to exist, and nothing else should be touching
> - * these pages. The caller is responsible for any TLB flushing.
> - */
> -void vmemmap_wrprotect_hvo(unsigned long addr, unsigned long end,
> -				    int node, unsigned long headsize)
> -{
> -	unsigned long maddr;
> -	pte_t *pte;
> -
> -	for (maddr = addr + headsize; maddr < end; maddr += PAGE_SIZE) {
> -		pte = virt_to_kpte(maddr);
> -		ptep_set_wrprotect(&init_mm, maddr, pte);
> -	}
> -}
> -
>   #ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
>   static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)
>   {


  parent reply	other threads:[~2026-10-28  2:38 UTC|newest]

Thread overview: 61+ 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
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:58                   ` David Hildenbrand (Arm)
2026-10-27 13:13                 ` Muchun Song
2026-10-27 13:35                   ` David Hildenbrand (Arm)
2026-10-27 14:03                     ` Muchun Song
2026-10-27 17:51                       ` Kiryl Shutsemau
2026-10-27 20:16                         ` David Hildenbrand (Arm)
2026-10-28  2:38                         ` Muchun Song [this message]
2026-10-28  8:06                           ` Peter Zijlstra
2026-10-28 12:58                             ` Kiryl Shutsemau
2026-10-28 13:34                               ` Peter Zijlstra
2026-10-28 14:16                                 ` Kiryl Shutsemau
2026-10-28 13:04                           ` Kiryl Shutsemau
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=7cb3dd91-f518-49b6-b311-9ec807b3c63b@linux.dev \
    --to=muchun.song@linux.dev \
    --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=dave.hansen@linux.intel.com \
    --cc=david@kernel.org \
    --cc=fvdl@google.com \
    --cc=hannes@cmpxchg.org \
    --cc=kas@kernel.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=luto@kernel.org \
    --cc=mhocko@suse.com \
    --cc=osalvador@suse.de \
    --cc=palmer@dabbelt.com \
    --cc=paul.walmsley@sifive.com \
    --cc=peterz@infradead.org \
    --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®