* [PATCH 0/4] mm: Simplify device DAX vmemmap optimization plumbing
@ 2026-10-30 14:07 Muchun Song
2026-10-30 14:07 ` [PATCH 1/4] powerpc/mm: make vmemmap_populate_compound_pages() static Muchun Song
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Muchun Song @ 2026-10-30 14:07 UTC (permalink / raw)
To: David Hildenbrand, Madhavan Srinivasan, Mike Rapoport,
Andrew Morton
Cc: Lance Yang, Michael Ellerman, Nicholas Piggin, Christophe Leroy,
Ritesh Harjani, Shrikanth Hegde, Oscar Salvador, Qi Zheng,
Muchun Song, linux-mm, linuxppc-dev, linux-kernel,
linux-arm-kernel, linux-riscv, x86, loongarch, linux-s390,
Muchun Song
Hi,
This series is split out from the earlier, larger series "mm: Generalize
HVO for HugeTLB and device DAX" [1]. While the parent series generalizes
vmemmap optimization across HugeTLB and device DAX, this subset addresses
a single, self-contained step: simplifying the device DAX vmemmap
optimization plumbing.
After device DAX was converted to section-based vmemmap optimization,
whether a mapping can be optimized is carried in mem_section, and the
architecture-specific population path can handle any remaining fallback.
The code still retains the DAX-specific vmemmap_can_optimize() helper, a
PowerPC override, and @pgmap arguments threaded through lower-level
vmemmap population and memory-removal interfaces. These duplicate the
section-order policy and keep otherwise generic paths coupled to
dev_pagemap.
This series removes that obsolete plumbing by consolidating the
optimization policy around memory-section metadata and simplifying the
generic and architecture-specific population paths. This also eliminates
dependencies that are no longer needed once the optimization state is
carried by the memory section.
This leaves the section compound order as the single source of truth for
vmemmap optimization and decouples the generic sparse-vmemmap and
memory-hotplug paths from device DAX.
This is another smaller step toward the broader HVO generalization.
[1] https://lore.kernel.org/20260513130542.35604-1-songmuchun@bytedance.com/
Muchun Song (4):
powerpc/mm: make vmemmap_populate_compound_pages() static
powerpc/mm: drop powerpc vmemmap_can_optimize()
mm/sparse-vmemmap: drop vmemmap_can_optimize()
mm/sparse-vmemmap: drop @pgmap from vmemmap population APIs
arch/arm64/mm/mmu.c | 5 ++-
arch/loongarch/mm/init.c | 5 ++-
arch/powerpc/include/asm/book3s/64/radix.h | 12 -------
arch/powerpc/mm/book3s64/radix_pgtable.c | 23 ++++---------
arch/powerpc/mm/init_64.c | 1 +
arch/powerpc/mm/mem.c | 5 ++-
arch/riscv/mm/init.c | 5 ++-
arch/s390/mm/init.c | 5 ++-
arch/x86/mm/init_64.c | 5 ++-
include/linux/memory_hotplug.h | 8 ++---
include/linux/mm.h | 40 +---------------------
mm/memory_hotplug.c | 16 ++++-----
mm/memremap.c | 4 +--
mm/sparse-vmemmap.c | 34 ++++++++++--------
mm/sparse.c | 6 ++--
15 files changed, 54 insertions(+), 120 deletions(-)
base-commit: 5e6c45aaaf93343c7f8a1cde9797080238af5c1b
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH 1/4] powerpc/mm: make vmemmap_populate_compound_pages() static 2026-10-30 14:07 [PATCH 0/4] mm: Simplify device DAX vmemmap optimization plumbing Muchun Song @ 2026-10-30 14:07 ` Muchun Song 2026-10-31 0:54 ` sashiko-bot 2026-10-30 14:07 ` [PATCH 2/4] powerpc/mm: drop powerpc vmemmap_can_optimize() Muchun Song ` (2 subsequent siblings) 3 siblings, 1 reply; 10+ messages in thread From: Muchun Song @ 2026-10-30 14:07 UTC (permalink / raw) To: David Hildenbrand, Madhavan Srinivasan, Mike Rapoport, Andrew Morton Cc: Lance Yang, Michael Ellerman, Nicholas Piggin, Christophe Leroy, Ritesh Harjani, Shrikanth Hegde, Oscar Salvador, Qi Zheng, Muchun Song, linux-mm, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-riscv, x86, loongarch, linux-s390, Muchun Song vmemmap_populate_compound_pages() is no longer used outside radix_pgtable.c. Make it static and drop the unused dev_pagemap argument from its only remaining caller to simplify the code. Signed-off-by: Muchun Song <songmuchun@bytedance.com> Acked-by: Qi Zheng <qi.zheng@linux.dev> --- arch/powerpc/include/asm/book3s/64/radix.h | 6 ------ arch/powerpc/mm/book3s64/radix_pgtable.c | 13 +++++++------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/radix.h b/arch/powerpc/include/asm/book3s/64/radix.h index da954e779744..8452a2714cb1 100644 --- a/arch/powerpc/include/asm/book3s/64/radix.h +++ b/arch/powerpc/include/asm/book3s/64/radix.h @@ -356,11 +356,5 @@ int radix__remove_section_mapping(unsigned long start, unsigned long end); #define vmemmap_can_optimize vmemmap_can_optimize bool vmemmap_can_optimize(struct vmem_altmap *altmap, struct dev_pagemap *pgmap); #endif - -#define vmemmap_populate_compound_pages vmemmap_populate_compound_pages -int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, - unsigned long start, - unsigned long end, int node, - struct dev_pagemap *pgmap); #endif /* __ASSEMBLER__ */ #endif diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index ebac5f9058f6..2924604fbb38 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -1117,7 +1117,9 @@ static inline pte_t *vmemmap_pte_alloc(pmd_t *pmdp, int node, return pte_offset_kernel(pmdp, address); } - +static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, + unsigned long start, + unsigned long end, int node); int __meminit radix__vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) @@ -1132,7 +1134,7 @@ int __meminit radix__vmemmap_populate(unsigned long start, unsigned long end, in unsigned long pfn = page_to_pfn((struct page *)start); if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn))) - return vmemmap_populate_compound_pages(pfn, start, end, node, NULL); + return vmemmap_populate_compound_pages(pfn, start, end, node); /* * If altmap is present, Make sure we align the start vmemmap addr * to PAGE_SIZE so that we calculate the correct start_pfn in @@ -1228,10 +1230,9 @@ int __meminit radix__vmemmap_populate(unsigned long start, unsigned long end, in return 0; } -int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, - unsigned long start, - unsigned long end, int node, - struct dev_pagemap *pgmap) +static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, + unsigned long start, + unsigned long end, int node) { /* * we want to map things as base page size mapping so that -- 2.54.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] powerpc/mm: make vmemmap_populate_compound_pages() static 2026-10-30 14:07 ` [PATCH 1/4] powerpc/mm: make vmemmap_populate_compound_pages() static Muchun Song @ 2026-10-31 0:54 ` sashiko-bot 0 siblings, 0 replies; 10+ messages in thread From: sashiko-bot @ 2026-10-31 0:54 UTC (permalink / raw) To: Muchun Song Cc: linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Christian Borntraeger > vmemmap_populate_compound_pages() is no longer used outside > radix_pgtable.c. > > Make it static and drop the unused dev_pagemap argument from > its only remaining caller to simplify the code. > > Signed-off-by: Muchun Song <songmuchun@bytedance.com> Sashiko has reviewed this patch and found no issues. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261030140720.1256062-1-songmuchun@bytedance.com?part=1 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/4] powerpc/mm: drop powerpc vmemmap_can_optimize() 2026-10-30 14:07 [PATCH 0/4] mm: Simplify device DAX vmemmap optimization plumbing Muchun Song 2026-10-30 14:07 ` [PATCH 1/4] powerpc/mm: make vmemmap_populate_compound_pages() static Muchun Song @ 2026-10-30 14:07 ` Muchun Song 2026-10-31 0:54 ` sashiko-bot 2026-10-30 14:07 ` [PATCH 3/4] mm/sparse-vmemmap: drop vmemmap_can_optimize() Muchun Song 2026-10-30 14:07 ` [PATCH 4/4] mm/sparse-vmemmap: drop @pgmap from vmemmap population APIs Muchun Song 3 siblings, 1 reply; 10+ messages in thread From: Muchun Song @ 2026-10-30 14:07 UTC (permalink / raw) To: David Hildenbrand, Madhavan Srinivasan, Mike Rapoport, Andrew Morton Cc: Lance Yang, Michael Ellerman, Nicholas Piggin, Christophe Leroy, Ritesh Harjani, Shrikanth Hegde, Oscar Salvador, Qi Zheng, Muchun Song, linux-mm, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-riscv, x86, loongarch, linux-s390, Muchun Song PowerPC no longer needs an architecture-specific vmemmap_can_optimize() override for DAX vmemmap optimization. Whether the optimized mapping can be used is now decided in the architecture-specific vmemmap_populate() path. When PowerPC has to fall back, such as on Hash MMU, it can simply clear the section compound order there and disable the optimization for that section. Drop the radix-specific vmemmap_can_optimize() override and rely on the generic checks instead. Signed-off-by: Muchun Song <songmuchun@bytedance.com> --- arch/powerpc/include/asm/book3s/64/radix.h | 5 ----- arch/powerpc/mm/book3s64/radix_pgtable.c | 10 ---------- arch/powerpc/mm/init_64.c | 1 + 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/radix.h b/arch/powerpc/include/asm/book3s/64/radix.h index 8452a2714cb1..df67209b0c5b 100644 --- a/arch/powerpc/include/asm/book3s/64/radix.h +++ b/arch/powerpc/include/asm/book3s/64/radix.h @@ -351,10 +351,5 @@ int radix__create_section_mapping(unsigned long start, unsigned long end, int nid, pgprot_t prot); int radix__remove_section_mapping(unsigned long start, unsigned long end); #endif /* CONFIG_MEMORY_HOTPLUG */ - -#ifdef CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP -#define vmemmap_can_optimize vmemmap_can_optimize -bool vmemmap_can_optimize(struct vmem_altmap *altmap, struct dev_pagemap *pgmap); -#endif #endif /* __ASSEMBLER__ */ #endif diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index 2924604fbb38..63f369d296b4 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -978,16 +978,6 @@ int __meminit radix__vmemmap_create_mapping(unsigned long start, return 0; } -#ifdef CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP -bool vmemmap_can_optimize(struct vmem_altmap *altmap, struct dev_pagemap *pgmap) -{ - if (radix_enabled()) - return __vmemmap_can_optimize(altmap, pgmap); - - return false; -} -#endif - int __meminit vmemmap_check_pmd(pmd_t *pmdp, int node, unsigned long addr, unsigned long next) { diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index 0e777d7dc332..509cc4190db7 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -282,6 +282,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, return radix__vmemmap_populate(start, end, node, altmap); #endif + section_set_compound_order(__pfn_to_section(page_to_pfn((struct page *)start)), 0); return __vmemmap_populate(start, end, node, altmap); } -- 2.54.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/4] powerpc/mm: drop powerpc vmemmap_can_optimize() 2026-10-30 14:07 ` [PATCH 2/4] powerpc/mm: drop powerpc vmemmap_can_optimize() Muchun Song @ 2026-10-31 0:54 ` sashiko-bot 0 siblings, 0 replies; 10+ messages in thread From: sashiko-bot @ 2026-10-31 0:54 UTC (permalink / raw) To: Muchun Song Cc: linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Christian Borntraeger > PowerPC no longer needs an architecture-specific vmemmap_can_optimize() > override for DAX vmemmap optimization. > > Whether the optimized mapping can be used is now decided in the > architecture-specific vmemmap_populate() path. When PowerPC has to fall > back, such as on Hash MMU, it can simply clear the section compound order > there and disable the optimization for that section. > > Drop the radix-specific vmemmap_can_optimize() override and rely on the > generic checks instead. > > Signed-off-by: Muchun Song <songmuchun@bytedance.com> Sashiko has reviewed this patch and found no issues. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261030140720.1256062-1-songmuchun@bytedance.com?part=2 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 3/4] mm/sparse-vmemmap: drop vmemmap_can_optimize() 2026-10-30 14:07 [PATCH 0/4] mm: Simplify device DAX vmemmap optimization plumbing Muchun Song 2026-10-30 14:07 ` [PATCH 1/4] powerpc/mm: make vmemmap_populate_compound_pages() static Muchun Song 2026-10-30 14:07 ` [PATCH 2/4] powerpc/mm: drop powerpc vmemmap_can_optimize() Muchun Song @ 2026-10-30 14:07 ` Muchun Song 2026-10-31 0:54 ` sashiko-bot 2026-10-30 14:07 ` [PATCH 4/4] mm/sparse-vmemmap: drop @pgmap from vmemmap population APIs Muchun Song 3 siblings, 1 reply; 10+ messages in thread From: Muchun Song @ 2026-10-30 14:07 UTC (permalink / raw) To: David Hildenbrand, Madhavan Srinivasan, Mike Rapoport, Andrew Morton Cc: Lance Yang, Michael Ellerman, Nicholas Piggin, Christophe Leroy, Ritesh Harjani, Shrikanth Hegde, Oscar Salvador, Qi Zheng, Muchun Song, linux-mm, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-riscv, x86, loongarch, linux-s390, Muchun Song section_vmemmap_optimizable() can already decide whether a section uses vmemmap optimization based on the section compound order. The DAX-specific vmemmap_can_optimize() helper duplicates that policy, so remove it to simplify the code. Signed-off-by: Muchun Song <songmuchun@bytedance.com> --- include/linux/mm.h | 37 ------------------------------------- mm/sparse-vmemmap.c | 11 +++++++++-- 2 files changed, 9 insertions(+), 39 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index e875f73b0c6f..4fb48fbe712d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -38,7 +38,6 @@ #include <linux/bitops.h> #include <linux/iommu-debug-pagealloc.h> #include <linux/kcsan-checks.h> -#include <linux/vmemmap-optimization.h> struct mempolicy; struct anon_vma; @@ -5395,42 +5394,6 @@ static inline void vmem_altmap_free(struct vmem_altmap *altmap, } #endif -#ifdef CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP -static inline bool __vmemmap_can_optimize(struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) -{ - unsigned long nr_pages; - unsigned long nr_vmemmap_pages; - - if (!IS_ENABLED(CONFIG_VMEMMAP_OPTIMIZATION)) - return false; - - if (!pgmap || !is_power_of_2(sizeof(struct page))) - return false; - - nr_pages = pgmap_vmemmap_nr(pgmap); - nr_vmemmap_pages = ((nr_pages * sizeof(struct page)) >> PAGE_SHIFT); - /* - * For vmemmap optimization with DAX we need minimum 2 vmemmap - * pages. See layout diagram in Documentation/mm/vmemmap_dedup.rst - */ - return !altmap && (nr_vmemmap_pages > VMEMMAP_OPTIMIZATION_PAGES); -} -/* - * If we don't have an architecture override, use the generic rule - */ -#ifndef vmemmap_can_optimize -#define vmemmap_can_optimize __vmemmap_can_optimize -#endif - -#else -static inline bool vmemmap_can_optimize(struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) -{ - return false; -} -#endif - enum mf_flags { MF_COUNT_INCREASED = 1 << 0, MF_ACTION_REQUIRED = 1 << 1, diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 3e9d6617c4bb..43559593107c 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -466,12 +466,20 @@ struct page * __meminit __populate_section_memmap(unsigned long pfn, { unsigned long start = (unsigned long) pfn_to_page(pfn); unsigned long end = start + nr_pages * sizeof(struct page); + struct mem_section *ms = __pfn_to_section(pfn); int r; if (WARN_ON_ONCE(!IS_ALIGNED(pfn, PAGES_PER_SUBSECTION) || !IS_ALIGNED(nr_pages, PAGES_PER_SUBSECTION))) return NULL; + /* + * Vmemmap optimization is not supported when vmemmap pages are backed + * by an altmap. + */ + if (altmap && section_vmemmap_optimizable(ms)) + section_set_compound_order(ms, 0); + r = vmemmap_populate(start, end, nid, altmap); if (r < 0) return NULL; @@ -699,10 +707,9 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, struct mem_section *ms = __pfn_to_section(pfn); struct mem_section_usage *usage = NULL; struct page *memmap; - unsigned int order; + unsigned int order = pgmap ? pgmap->vmemmap_shift : 0; int rc; - order = vmemmap_can_optimize(altmap, pgmap) ? pgmap->vmemmap_shift : 0; if (nr_pages < PAGES_PER_SECTION && section_compound_order(ms)) return ERR_PTR(-EOPNOTSUPP); -- 2.54.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 3/4] mm/sparse-vmemmap: drop vmemmap_can_optimize() 2026-10-30 14:07 ` [PATCH 3/4] mm/sparse-vmemmap: drop vmemmap_can_optimize() Muchun Song @ 2026-10-31 0:54 ` sashiko-bot 2026-10-31 2:42 ` Muchun Song 0 siblings, 1 reply; 10+ messages in thread From: sashiko-bot @ 2026-10-31 0:54 UTC (permalink / raw) To: Muchun Song, David Hildenbrand, Madhavan Srinivasan, Mike Rapoport, Andrew Morton Cc: Lance Yang, Michael Ellerman, Nicholas Piggin, Christophe Leroy, Ritesh Harjani, Shrikanth Hegde, Oscar Salvador, Qi Zheng, Muchun Song, linux-mm, x86, linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Christian Borntraeger Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] mm/sparse-vmemmap: dropping vmemmap_can_optimize() bypasses DAX arch config - [Medium] mm/sparse-vmemmap: section_activate() stores non-optimizable compound order - [Low] mm/sparse-vmemmap: missing header include breaks powerpc init_64.c build -- commit 4171e4b9fc2abb84bf0e245a60563f33f94a263d Author: Muchun Song <muchun.song@linux.dev> mm/sparse-vmemmap: drop vmemmap_can_optimize() This commit removes the DAX-specific vmemmap_can_optimize() helper from include/linux/mm.h and relies on section_vmemmap_optimizable() to determine whether a section uses vmemmap optimization. > diff --git a/include/linux/mm.h b/include/linux/mm.h > index e875f73b0c6ff..4fb48fbe712d2 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -38,7 +38,6 @@ > #include <linux/bitops.h> > #include <linux/iommu-debug-pagealloc.h> > #include <linux/kcsan-checks.h> > -#include <linux/vmemmap-optimization.h> [Severity: Low] Does removing this include break the 64-bit PowerPC build when CONFIG_SPARSEMEM_VMEMMAP=y is enabled? In arch/powerpc/mm/init_64.c, vmemmap_populate() calls section_set_compound_order(): section_set_compound_order(__pfn_to_section(page_to_pfn((struct page *)start)), 0); Previously, arch/powerpc/mm/init_64.c got the declaration of section_set_compound_order() transitively via include/linux/mm.h. Without including linux/vmemmap-optimization.h in arch/powerpc/mm/init_64.c, compiling it will fail with an implicit function declaration error. > struct mempolicy; > struct anon_vma; > @@ -5395,42 +5394,6 @@ static inline void vmem_altmap_free(struct vmem_altmap *altmap, > } > #endif > > -#ifdef CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP > -static inline bool __vmemmap_can_optimize(struct vmem_altmap *altmap, > - struct dev_pagemap *pgmap) > -{ > - unsigned long nr_pages; > - unsigned long nr_vmemmap_pages; > - > - if (!IS_ENABLED(CONFIG_VMEMMAP_OPTIMIZATION)) > - return false; > - > - if (!pgmap || !is_power_of_2(sizeof(struct page))) > - return false; > - > - nr_pages = pgmap_vmemmap_nr(pgmap); > - nr_vmemmap_pages = ((nr_pages * sizeof(struct page)) >> PAGE_SHIFT); > - /* > - * For vmemmap optimization with DAX we need minimum 2 vmemmap > - * pages. See layout diagram in Documentation/mm/vmemmap_dedup.rst > - */ > - return !altmap && (nr_vmemmap_pages > VMEMMAP_OPTIMIZATION_PAGES); > -} > -/* > - * If we don't have an architecture override, use the generic rule > - */ > -#ifndef vmemmap_can_optimize > -#define vmemmap_can_optimize __vmemmap_can_optimize > -#endif > - > -#else > -static inline bool vmemmap_can_optimize(struct vmem_altmap *altmap, > - struct dev_pagemap *pgmap) > -{ > - return false; > -} > -#endif [Severity: High] With vmemmap_can_optimize() removed, nothing in C code checks CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP anymore. Could this unintendedly enable DAX vmemmap optimization on architectures that only select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP, such as 64-bit LoongArch? When CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP=y and CONFIG_ZONE_DEVICE=y are both enabled on 64-bit LoongArch, CONFIG_VMEMMAP_OPTIMIZATION is selected. In section_activate(), order is now set from pgmap->vmemmap_shift, and section_vmemmap_optimizable() -> vmemmap_optimizable_order() only checks IS_ENABLED(CONFIG_VMEMMAP_OPTIMIZATION). As a result, activating a DAX section with pgmap->vmemmap_shift >= VMEMMAP_OPTIMIZATION_MIN_ORDER on LoongArch will map read-only shared tail PTEs and grab references to vmemmap_shared_tail_page via vmemmap_alloc_pte(). However, LoongArch's vmemmap_free() in arch/loongarch/mm/init.c is an empty no-op stub: void vmemmap_free(unsigned long start, unsigned long end, struct vmem_altmap *altmap) { } On section_deactivate(), those shared tail page references are leaked and the read-only PTEs remain mapped, causing subsequent section activations to either trigger WARN_ON_ONCE(vmemmap_optimizable_pfn(pfn)) and fail with -ENOMEM in vmemmap_pte_populate(), or fault in kernel mode when writing to struct pages through the stale read-only PTEs. [ ... ] > diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c > index 3e9d6617c4bb2..43559593107c9 100644 > --- a/mm/sparse-vmemmap.c > +++ b/mm/sparse-vmemmap.c [ ... ] > @@ -699,10 +707,9 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, > struct mem_section *ms = __pfn_to_section(pfn); > struct mem_section_usage *usage = NULL; > struct page *memmap; > - unsigned int order; > + unsigned int order = pgmap ? pgmap->vmemmap_shift : 0; > int rc; > > - order = vmemmap_can_optimize(altmap, pgmap) ? pgmap->vmemmap_shift : 0; > if (nr_pages < PAGES_PER_SECTION && section_compound_order(ms)) > return ERR_PTR(-EOPNOTSUPP); [Severity: Medium] What happens in section_activate() when pgmap->vmemmap_shift is non-zero, but is not actually optimizable (for example, when 0 < pgmap->vmemmap_shift < VMEMMAP_OPTIMIZATION_MIN_ORDER, or when !is_power_of_2(sizeof(struct page)))? Previously, order was forced to 0 when vmemmap_can_optimize() returned false. Now, section_activate() passes the raw pgmap->vmemmap_shift to section_set_compound_order_range(pfn, nr_pages, order). Because __populate_section_memmap() only resets the order when both altmap and section_vmemmap_optimizable(ms) are true: if (altmap && section_vmemmap_optimizable(ms)) section_set_compound_order(ms, 0); ms->compound_page_order remains non-zero for unoptimizable non-zero orders. Since section_activate() checks section_compound_order(ms) rather than section_vmemmap_optimizable(ms), wouldn't a subsequent partial-section activation (nr_pages < PAGES_PER_SECTION) in that section be erroneously rejected with -EOPNOTSUPP even though vmemmap optimization was never used? -- Sashiko AI review · https://sashiko.dev/#/patchset/20261030140720.1256062-1-songmuchun@bytedance.com?part=3 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 3/4] mm/sparse-vmemmap: drop vmemmap_can_optimize() 2026-10-31 0:54 ` sashiko-bot @ 2026-10-31 2:42 ` Muchun Song 0 siblings, 0 replies; 10+ messages in thread From: Muchun Song @ 2026-10-31 2:42 UTC (permalink / raw) To: sashiko-reviews Cc: Muchun Song, David Hildenbrand, Madhavan Srinivasan, Mike Rapoport, Andrew Morton, Lance Yang, Michael Ellerman, Nicholas Piggin, Christophe Leroy, Ritesh Harjani, Shrikanth Hegde, Oscar Salvador, Qi Zheng, linux-mm, x86, linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Christian Borntraeger > On Oct 31, 2026, at 08:54, sashiko-bot@kernel.org wrote: > > Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: > - [High] mm/sparse-vmemmap: dropping vmemmap_can_optimize() bypasses DAX arch config > - [Medium] mm/sparse-vmemmap: section_activate() stores non-optimizable compound order > - [Low] mm/sparse-vmemmap: missing header include breaks powerpc init_64.c build > -- > > commit 4171e4b9fc2abb84bf0e245a60563f33f94a263d > Author: Muchun Song <muchun.song@linux.dev> > > mm/sparse-vmemmap: drop vmemmap_can_optimize() > > This commit removes the DAX-specific vmemmap_can_optimize() helper from > include/linux/mm.h and relies on section_vmemmap_optimizable() to determine > whether a section uses vmemmap optimization. > >> diff --git a/include/linux/mm.h b/include/linux/mm.h >> index e875f73b0c6ff..4fb48fbe712d2 100644 >> --- a/include/linux/mm.h >> +++ b/include/linux/mm.h >> @@ -38,7 +38,6 @@ >> #include <linux/bitops.h> >> #include <linux/iommu-debug-pagealloc.h> >> #include <linux/kcsan-checks.h> >> -#include <linux/vmemmap-optimization.h> > > [Severity: Low] > Does removing this include break the 64-bit PowerPC build when > CONFIG_SPARSEMEM_VMEMMAP=y is enabled? > > In arch/powerpc/mm/init_64.c, vmemmap_populate() calls > section_set_compound_order(): > > section_set_compound_order(__pfn_to_section(page_to_pfn((struct page *)start)), 0); > > Previously, arch/powerpc/mm/init_64.c got the declaration of > section_set_compound_order() transitively via include/linux/mm.h. Without > including linux/vmemmap-optimization.h in arch/powerpc/mm/init_64.c, > compiling it will fail with an implicit function declaration error. I should include this header in the patch 2 to fix this, because I don't like this implicit way of header file inclusion. > >> struct mempolicy; >> struct anon_vma; >> @@ -5395,42 +5394,6 @@ static inline void vmem_altmap_free(struct vmem_altmap *altmap, >> } >> #endif >> >> -#ifdef CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP >> -static inline bool __vmemmap_can_optimize(struct vmem_altmap *altmap, >> - struct dev_pagemap *pgmap) >> -{ >> - unsigned long nr_pages; >> - unsigned long nr_vmemmap_pages; >> - >> - if (!IS_ENABLED(CONFIG_VMEMMAP_OPTIMIZATION)) >> - return false; >> - >> - if (!pgmap || !is_power_of_2(sizeof(struct page))) >> - return false; >> - >> - nr_pages = pgmap_vmemmap_nr(pgmap); >> - nr_vmemmap_pages = ((nr_pages * sizeof(struct page)) >> PAGE_SHIFT); >> - /* >> - * For vmemmap optimization with DAX we need minimum 2 vmemmap >> - * pages. See layout diagram in Documentation/mm/vmemmap_dedup.rst >> - */ >> - return !altmap && (nr_vmemmap_pages > VMEMMAP_OPTIMIZATION_PAGES); >> -} >> -/* >> - * If we don't have an architecture override, use the generic rule >> - */ >> -#ifndef vmemmap_can_optimize >> -#define vmemmap_can_optimize __vmemmap_can_optimize >> -#endif >> - >> -#else >> -static inline bool vmemmap_can_optimize(struct vmem_altmap *altmap, >> - struct dev_pagemap *pgmap) >> -{ >> - return false; >> -} >> -#endif > > [Severity: High] > With vmemmap_can_optimize() removed, nothing in C code checks > CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP anymore. Could this unintendedly > enable DAX vmemmap optimization on architectures that only select > ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP, such as 64-bit LoongArch? You are right. This patch will enable vmemmap-optimization for device DAX on LoongArch. Because I plan to remove ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP completely in the future, so I'll add a new commit before this one to disable HVO for device DAX on LoongArch. It'll be easy to do this, just like patch 2 does for PowerPC. On LoongArch, HugeTLB HVO is supported without gigantic HugeTLB pages. So there is no users of section-based vmemmap optimization. So just disabling vmemmap optimization in architectural vmemmap_populate() is enough. > > When CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP=y and CONFIG_ZONE_DEVICE=y are > both enabled on 64-bit LoongArch, CONFIG_VMEMMAP_OPTIMIZATION is selected. > In section_activate(), order is now set from pgmap->vmemmap_shift, and > section_vmemmap_optimizable() -> vmemmap_optimizable_order() only checks > IS_ENABLED(CONFIG_VMEMMAP_OPTIMIZATION). > > As a result, activating a DAX section with pgmap->vmemmap_shift >= > VMEMMAP_OPTIMIZATION_MIN_ORDER on LoongArch will map read-only shared tail > PTEs and grab references to vmemmap_shared_tail_page via vmemmap_alloc_pte(). > However, LoongArch's vmemmap_free() in arch/loongarch/mm/init.c is an empty > no-op stub: > > void vmemmap_free(unsigned long start, unsigned long end, struct vmem_altmap *altmap) > { > } > > On section_deactivate(), those shared tail page references are leaked and > the read-only PTEs remain mapped, causing subsequent section activations to > either trigger WARN_ON_ONCE(vmemmap_optimizable_pfn(pfn)) and fail with > -ENOMEM in vmemmap_pte_populate(), or fault in kernel mode when writing to > struct pages through the stale read-only PTEs. > > [ ... ] > >> diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c >> index 3e9d6617c4bb2..43559593107c9 100644 >> --- a/mm/sparse-vmemmap.c >> +++ b/mm/sparse-vmemmap.c > > [ ... ] > >> @@ -699,10 +707,9 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, >> struct mem_section *ms = __pfn_to_section(pfn); >> struct mem_section_usage *usage = NULL; >> struct page *memmap; >> - unsigned int order; >> + unsigned int order = pgmap ? pgmap->vmemmap_shift : 0; >> int rc; >> >> - order = vmemmap_can_optimize(altmap, pgmap) ? pgmap->vmemmap_shift : 0; >> if (nr_pages < PAGES_PER_SECTION && section_compound_order(ms)) >> return ERR_PTR(-EOPNOTSUPP); > > [Severity: Medium] > What happens in section_activate() when pgmap->vmemmap_shift is non-zero, > but is not actually optimizable (for example, when 0 < pgmap->vmemmap_shift > < VMEMMAP_OPTIMIZATION_MIN_ORDER, or when !is_power_of_2(sizeof(struct page)))? Even though this issue is unreachable, I'm still going to fix it since it is easy. Something like: diff --git a/include/linux/vmemmap-optimization.h b/include/linux/vmemmap-optimization.h index fa9e9abd6656..104f7275a49f 100644 --- a/include/linux/vmemmap-optimization.h +++ b/include/linux/vmemmap-optimization.h @@ -42,7 +42,9 @@ static inline void section_set_compound_order(struct mem_section *section, { VM_WARN_ON(section_compound_order(section) && order && section_compound_order(section) != order); - section->compound_page_order = order; + + if (vmemmap_optimizable_order(order)) + section->compound_page_order = order; } Thanks. > > Previously, order was forced to 0 when vmemmap_can_optimize() returned > false. Now, section_activate() passes the raw pgmap->vmemmap_shift to > section_set_compound_order_range(pfn, nr_pages, order). > > Because __populate_section_memmap() only resets the order when both altmap > and section_vmemmap_optimizable(ms) are true: > > if (altmap && section_vmemmap_optimizable(ms)) > section_set_compound_order(ms, 0); > > ms->compound_page_order remains non-zero for unoptimizable non-zero orders. > Since section_activate() checks section_compound_order(ms) rather than > section_vmemmap_optimizable(ms), wouldn't a subsequent partial-section > activation (nr_pages < PAGES_PER_SECTION) in that section be erroneously > rejected with -EOPNOTSUPP even though vmemmap optimization was never used? > > -- > Sashiko AI review · https://sashiko.dev/#/patchset/20261030140720.1256062-1-songmuchun@bytedance.com?part=3 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/4] mm/sparse-vmemmap: drop @pgmap from vmemmap population APIs 2026-10-30 14:07 [PATCH 0/4] mm: Simplify device DAX vmemmap optimization plumbing Muchun Song ` (2 preceding siblings ...) 2026-10-30 14:07 ` [PATCH 3/4] mm/sparse-vmemmap: drop vmemmap_can_optimize() Muchun Song @ 2026-10-30 14:07 ` Muchun Song 2026-10-31 0:54 ` sashiko-bot 3 siblings, 1 reply; 10+ messages in thread From: Muchun Song @ 2026-10-30 14:07 UTC (permalink / raw) To: David Hildenbrand, Madhavan Srinivasan, Mike Rapoport, Andrew Morton Cc: Lance Yang, Michael Ellerman, Nicholas Piggin, Christophe Leroy, Ritesh Harjani, Shrikanth Hegde, Oscar Salvador, Qi Zheng, Muchun Song, linux-mm, linuxppc-dev, linux-kernel, linux-arm-kernel, linux-riscv, x86, loongarch, linux-s390, Muchun Song The vmemmap population and memory hotplug paths no longer need @pgmap to decide whether a mapping can be optimized. That state is now carried in mem_section, and the architecture-specific population code can make the remaining decisions internally. Drop the @pgmap parameter from the vmemmap population helpers and the related memory hotplug interfaces, and remove the remaining dev_pagemap-specific coupling from those call chains. Signed-off-by: Muchun Song <songmuchun@bytedance.com> --- arch/arm64/mm/mmu.c | 5 ++--- arch/loongarch/mm/init.c | 5 ++--- arch/powerpc/include/asm/book3s/64/radix.h | 1 - arch/powerpc/mm/mem.c | 5 ++--- arch/riscv/mm/init.c | 5 ++--- arch/s390/mm/init.c | 5 ++--- arch/x86/mm/init_64.c | 5 ++--- include/linux/memory_hotplug.h | 8 +++----- include/linux/mm.h | 3 +-- mm/memory_hotplug.c | 16 +++++++-------- mm/memremap.c | 4 ++-- mm/sparse-vmemmap.c | 23 ++++++++++------------ mm/sparse.c | 6 ++---- 13 files changed, 37 insertions(+), 54 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 19459a4a870a..a6b5774005b6 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -2038,13 +2038,12 @@ int arch_add_memory(int nid, u64 start, u64 size, return ret; } -void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) +void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap, pgmap); + __remove_pages(start_pfn, nr_pages, altmap); __remove_pgd_mapping(swapper_pg_dir, __phys_to_virt(start), size); } diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c index f801f7097f03..22d35e98d871 100644 --- a/arch/loongarch/mm/init.c +++ b/arch/loongarch/mm/init.c @@ -119,13 +119,12 @@ int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params) return ret; } -void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) +void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap, pgmap); + __remove_pages(start_pfn, nr_pages, altmap); } #endif diff --git a/arch/powerpc/include/asm/book3s/64/radix.h b/arch/powerpc/include/asm/book3s/64/radix.h index df67209b0c5b..0c9195dd50c9 100644 --- a/arch/powerpc/include/asm/book3s/64/radix.h +++ b/arch/powerpc/include/asm/book3s/64/radix.h @@ -316,7 +316,6 @@ static inline int radix__has_transparent_pud_hugepage(void) #endif struct vmem_altmap; -struct dev_pagemap; extern int __meminit radix__vmemmap_create_mapping(unsigned long start, unsigned long page_size, unsigned long phys); diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index b617b69452cd..2432ff888c03 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -158,13 +158,12 @@ int __ref arch_add_memory(int nid, u64 start, u64 size, return rc; } -void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) +void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap, pgmap); + __remove_pages(start_pfn, nr_pages, altmap); arch_remove_linear_mapping(start, size); } #endif diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 6f6b04795bb2..8bb48c270c2f 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1734,10 +1734,9 @@ int __ref arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *param return ret; } -void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) +void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) { - __remove_pages(start >> PAGE_SHIFT, size >> PAGE_SHIFT, altmap, pgmap); + __remove_pages(start >> PAGE_SHIFT, size >> PAGE_SHIFT, altmap); remove_linear_mapping(start, size); flush_tlb_all(); } diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index be7e009e7b59..91c0edccbb12 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -293,13 +293,12 @@ int arch_add_memory(int nid, u64 start, u64 size, return rc; } -void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) +void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap, pgmap); + __remove_pages(start_pfn, nr_pages, altmap); vmem_remove_mapping(start, size); } #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 8b6d86fb5a42..2a635b7f78ea 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1273,13 +1273,12 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end) remove_pagetable(start, end, true, NULL); } -void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) +void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - __remove_pages(start_pfn, nr_pages, altmap, pgmap); + __remove_pages(start_pfn, nr_pages, altmap); kernel_physical_mapping_remove(start, start + size); } #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index b39605d30896..ec71debc0673 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -136,10 +136,9 @@ static inline bool movable_node_is_enabled(void) return movable_node_enabled; } -extern void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap); +extern void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap); extern void __remove_pages(unsigned long start_pfn, unsigned long nr_pages, - struct vmem_altmap *altmap, struct dev_pagemap *pgmap); + struct vmem_altmap *altmap); /* reasonably generic interface to expand the physical pages */ extern int __add_pages(int nid, unsigned long start_pfn, unsigned long nr_pages, @@ -320,8 +319,7 @@ extern int sparse_add_section(int nid, unsigned long pfn, unsigned long nr_pages, struct vmem_altmap *altmap, struct dev_pagemap *pgmap); extern void sparse_remove_section(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap, - struct dev_pagemap *pgmap); + struct vmem_altmap *altmap); extern struct zone *zone_for_pfn_range(enum mmop online_type, int nid, struct memory_group *group, unsigned long start_pfn, unsigned long nr_pages); diff --git a/include/linux/mm.h b/include/linux/mm.h index 4fb48fbe712d..b4b63f89d188 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -5345,8 +5345,7 @@ static inline void print_vma_addr(char *prefix, unsigned long rip) #endif struct page * __populate_section_memmap(unsigned long pfn, - unsigned long nr_pages, int nid, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap); + unsigned long nr_pages, int nid, struct vmem_altmap *altmap); void *vmemmap_alloc_block(unsigned long size, int node); struct vmem_altmap; void *vmemmap_alloc_block_buf(unsigned long size, int node, diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index c9cc42e54f05..1e5fbc154123 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -415,8 +415,7 @@ int __add_pages(int nid, unsigned long pfn, unsigned long nr_pages, err = sparse_add_section(nid, pfn, cur_nr_pages, altmap, params->pgmap); if (err) { - __remove_pages(start_pfn, pfn - start_pfn, altmap, - params->pgmap); + __remove_pages(start_pfn, pfn - start_pfn, altmap); break; } cond_resched(); @@ -564,7 +563,6 @@ void remove_pfn_range_from_zone(struct zone *zone, * @pfn: starting pageframe (must be aligned to start of a section) * @nr_pages: number of pages to remove (must be multiple of section size) * @altmap: alternative device page map or %NULL if default memmap is used - * @pgmap: device page map or %NULL if not ZONE_DEVICE * * Generic helper function to remove section mappings and sysfs entries * for the section of the memory we are removing. Caller needs to make @@ -572,7 +570,7 @@ void remove_pfn_range_from_zone(struct zone *zone, * calling offline_pages(). */ void __remove_pages(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap, struct dev_pagemap *pgmap) + struct vmem_altmap *altmap) { const unsigned long end_pfn = pfn + nr_pages; unsigned long cur_nr_pages; @@ -587,7 +585,7 @@ void __remove_pages(unsigned long pfn, unsigned long nr_pages, /* Select all remaining pages up to the next section boundary */ cur_nr_pages = min(end_pfn - pfn, SECTION_ALIGN_UP(pfn + 1) - pfn); - sparse_remove_section(pfn, cur_nr_pages, altmap, pgmap); + sparse_remove_section(pfn, cur_nr_pages, altmap); } } @@ -1420,7 +1418,7 @@ static void remove_memory_blocks_and_altmaps(u64 start, u64 size) memory_block_put(mem); remove_memory_block_devices(cur_start, memblock_size); - arch_remove_memory(cur_start, memblock_size, altmap, NULL); + arch_remove_memory(cur_start, memblock_size, altmap); altmap_free(altmap); } } @@ -1460,7 +1458,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group, ret = create_memory_block_devices(cur_start, memblock_size, nid, params.altmap, group); if (ret) { - arch_remove_memory(cur_start, memblock_size, params.altmap, NULL); + arch_remove_memory(cur_start, memblock_size, params.altmap); altmap_free(params.altmap); goto out; } @@ -1547,7 +1545,7 @@ static int __add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags, /* create memory block devices after memory was added */ ret = create_memory_block_devices(start, size, nid, NULL, group); if (ret) { - arch_remove_memory(start, size, params.altmap, NULL); + arch_remove_memory(start, size, params.altmap); goto error; } } @@ -2308,7 +2306,7 @@ static int try_remove_memory(u64 start, u64 size) * No altmaps present, do the removal directly */ remove_memory_block_devices(start, size); - arch_remove_memory(start, size, NULL, NULL); + arch_remove_memory(start, size, NULL); } else { /* all memblocks in the range have altmaps */ remove_memory_blocks_and_altmaps(start, size); diff --git a/mm/memremap.c b/mm/memremap.c index accba23aef28..683a77dd679d 100644 --- a/mm/memremap.c +++ b/mm/memremap.c @@ -97,10 +97,10 @@ static void pageunmap_range(struct dev_pagemap *pgmap, int range_id) PHYS_PFN(range_len(range))); if (pgmap->type == MEMORY_DEVICE_PRIVATE) { __remove_pages(PHYS_PFN(range->start), - PHYS_PFN(range_len(range)), NULL, pgmap); + PHYS_PFN(range_len(range)), NULL); } else { arch_remove_memory(range->start, range_len(range), - pgmap_altmap(pgmap), pgmap); + pgmap_altmap(pgmap)); kasan_remove_zero_shadow(__va(range->start), range_len(range)); } mem_hotplug_done(); diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 43559593107c..df71125a6ee2 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -461,8 +461,7 @@ int __meminit vmemmap_populate_hugepages(unsigned long start, unsigned long end, } struct page * __meminit __populate_section_memmap(unsigned long pfn, - unsigned long nr_pages, int nid, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) + unsigned long nr_pages, int nid, struct vmem_altmap *altmap) { unsigned long start = (unsigned long) pfn_to_page(pfn); unsigned long end = start + nr_pages * sizeof(struct page); @@ -559,11 +558,9 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn) } static struct page * __meminit populate_section_memmap(unsigned long pfn, - unsigned long nr_pages, int nid, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) + unsigned long nr_pages, int nid, struct vmem_altmap *altmap) { - struct page *page = __populate_section_memmap(pfn, nr_pages, nid, altmap, - pgmap); + struct page *page = __populate_section_memmap(pfn, nr_pages, nid, altmap); memmap_pages_add(section_nr_vmemmap_pages(pfn, nr_pages)); @@ -571,7 +568,7 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn, } static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap, struct dev_pagemap *pgmap) + struct vmem_altmap *altmap) { unsigned long start = (unsigned long) pfn_to_page(pfn); unsigned long end = start + nr_pages * sizeof(struct page); @@ -652,7 +649,7 @@ static int fill_subsection_map(unsigned long pfn, unsigned long nr_pages) * usage map, but still need to free the vmemmap range. */ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap, struct dev_pagemap *pgmap) + struct vmem_altmap *altmap) { struct mem_section *ms = __pfn_to_section(pfn); bool section_is_early = early_section(ms); @@ -690,7 +687,7 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, * section_activate() and pfn_valid() . */ if (!section_is_early) - depopulate_section_memmap(pfn, nr_pages, altmap, pgmap); + depopulate_section_memmap(pfn, nr_pages, altmap); else if (memmap) free_map_bootmem(memmap); @@ -739,9 +736,9 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, return pfn_to_page(pfn); section_set_compound_order_range(pfn, nr_pages, order); - memmap = populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap); + memmap = populate_section_memmap(pfn, nr_pages, nid, altmap); if (!memmap) { - section_deactivate(pfn, nr_pages, altmap, pgmap); + section_deactivate(pfn, nr_pages, altmap); return ERR_PTR(-ENOMEM); } @@ -805,13 +802,13 @@ int __meminit sparse_add_section(int nid, unsigned long start_pfn, } void sparse_remove_section(unsigned long pfn, unsigned long nr_pages, - struct vmem_altmap *altmap, struct dev_pagemap *pgmap) + struct vmem_altmap *altmap) { struct mem_section *ms = __pfn_to_section(pfn); if (WARN_ON_ONCE(!valid_section(ms))) return; - section_deactivate(pfn, nr_pages, altmap, pgmap); + section_deactivate(pfn, nr_pages, altmap); } #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/mm/sparse.c b/mm/sparse.c index 6acb628c8a73..0b9a6c23a227 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -204,8 +204,7 @@ void __init sparse_sections_init(void) } #ifndef CONFIG_SPARSEMEM_VMEMMAP struct page __init *__populate_section_memmap(unsigned long pfn, - unsigned long nr_pages, int nid, struct vmem_altmap *altmap, - struct dev_pagemap *pgmap) + unsigned long nr_pages, int nid, struct vmem_altmap *altmap) { const unsigned long size = PAGE_ALIGN(sizeof(struct page) * PAGES_PER_SECTION); @@ -236,8 +235,7 @@ static void __init sparse_metadata_init_nid(int nid, if (section_nr >= end_section_nr) break; - mem_map = __populate_section_memmap(pfn, PAGES_PER_SECTION, nid, - NULL, NULL); + mem_map = __populate_section_memmap(pfn, PAGES_PER_SECTION, nid, NULL); if (!mem_map) panic("Failed to allocate memmap for section %lu\n", section_nr); -- 2.54.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 4/4] mm/sparse-vmemmap: drop @pgmap from vmemmap population APIs 2026-10-30 14:07 ` [PATCH 4/4] mm/sparse-vmemmap: drop @pgmap from vmemmap population APIs Muchun Song @ 2026-10-31 0:54 ` sashiko-bot 0 siblings, 0 replies; 10+ messages in thread From: sashiko-bot @ 2026-10-31 0:54 UTC (permalink / raw) To: Muchun Song, David Hildenbrand, Madhavan Srinivasan, Mike Rapoport, Andrew Morton Cc: Lance Yang, Michael Ellerman, Nicholas Piggin, Christophe Leroy, Ritesh Harjani, Shrikanth Hegde, Oscar Salvador, Qi Zheng, Muchun Song, linux-mm, x86, linux-s390, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Christian Borntraeger Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] mm/sparse-vmemmap: section_activate() ignores ARCH_WANT_OPTIMIZE_DAX_VMEMMAP - [Medium] mm/sparse-vmemmap: section_activate() stores non-optimizable compound order - [Low] powerpc/mm: missing vmemmap-optimization.h include in init_64.c -- commit 48cc74f276edacd74ae0108659b8fbbf37ab5193 Author: Muchun Song <muchun.song@linux.dev> mm/sparse-vmemmap: drop @pgmap from vmemmap population APIs This commit removes the @pgmap parameter from the vmemmap population helpers and related memory hotplug interfaces, relying on mem_section state and architecture-specific population code to determine whether vmemmap mappings can be optimized. > diff --git a/include/linux/mm.h b/include/linux/mm.h > index 4fb48fbe712d2..b4b63f89d1884 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -5345,8 +5345,7 @@ static inline void print_vma_addr(char *prefix, unsigned long rip) > #endif > > struct page * __populate_section_memmap(unsigned long pfn, > - unsigned long nr_pages, int nid, struct vmem_altmap *altmap, > - struct dev_pagemap *pgmap); > + unsigned long nr_pages, int nid, struct vmem_altmap *altmap); [Severity: Low] Does building arch/powerpc/mm/init_64.c with CONFIG_SPARSEMEM_VMEMMAP=y fail due to an implicit declaration of section_set_compound_order() after the earlier changes in this series? In commit 84948c20ec18 ("powerpc/mm: drop powerpc vmemmap_can_optimize()"), vmemmap_populate() in arch/powerpc/mm/init_64.c was updated to call section_set_compound_order(): arch/powerpc/mm/init_64.c:vmemmap_populate() { ... section_set_compound_order(__pfn_to_section(page_to_pfn((struct page *)start)), 0); return __vmemmap_populate(start, end, node, altmap); } Then commit 4171e4b9fc2a ("mm/sparse-vmemmap: drop vmemmap_can_optimize()") removed the linux/vmemmap-optimization.h include from include/linux/mm.h: include/linux/mm.h: #include <linux/bitops.h> #include <linux/iommu-debug-pagealloc.h> #include <linux/kcsan-checks.h> -#include <linux/vmemmap-optimization.h> Because neither arch/powerpc/mm/init_64.c nor any header it includes pulls in linux/vmemmap-optimization.h, should arch/powerpc/mm/init_64.c include linux/vmemmap-optimization.h directly? [ ... ] > diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c > index 43559593107c9..df71125a6ee23 100644 > --- a/mm/sparse-vmemmap.c > +++ b/mm/sparse-vmemmap.c [ ... ] > @@ -739,9 +736,9 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn, > return pfn_to_page(pfn); > > section_set_compound_order_range(pfn, nr_pages, order); [Severity: Medium] What happens in section_activate() when pgmap->vmemmap_shift is non-zero, but smaller than VMEMMAP_OPTIMIZATION_MIN_ORDER (or sizeof(struct page) is not a power of two)? Previously, section_activate() gated order on vmemmap_can_optimize(altmap, pgmap), leaving ms->compound_page_order as 0 whenever the order was not eligible for vmemmap optimization. With commit 4171e4b9fc2a ("mm/sparse-vmemmap: drop vmemmap_can_optimize()") and this commit, section_activate() sets: mm/sparse-vmemmap.c:section_activate() { ... unsigned int order = pgmap ? pgmap->vmemmap_shift : 0; int rc; if (nr_pages < PAGES_PER_SECTION && section_compound_order(ms)) return ERR_PTR(-EOPNOTSUPP); ... section_set_compound_order_range(pfn, nr_pages, order); memmap = populate_section_memmap(pfn, nr_pages, nid, altmap); } And __populate_section_memmap() only clears compound_page_order when both altmap and section_vmemmap_optimizable(ms) are true: mm/sparse-vmemmap.c:__populate_section_memmap() { ... if (altmap && section_vmemmap_optimizable(ms)) section_set_compound_order(ms, 0); } If 0 < pgmap->vmemmap_shift < VMEMMAP_OPTIMIZATION_MIN_ORDER (for instance, a 2MB PMD-aligned devdax range with vmemmap_shift = 5 on 64KB-page PowerPC where VMEMMAP_OPTIMIZATION_MIN_ORDER is 11), section_vmemmap_optimizable(ms) returns false. However, ms->compound_page_order remains set to that non-zero order even though the section's vmemmap is not optimized. If a subsection of that section is later deactivated (leaving the section non-empty) and re-added with nr_pages < PAGES_PER_SECTION, won't the section_compound_order(ms) check in section_activate() falsely reject the subsection activation with -EOPNOTSUPP? > - memmap = populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap); > + memmap = populate_section_memmap(pfn, nr_pages, nid, altmap); [Severity: High] Could dropping vmemmap_can_optimize() and @pgmap unintentionally enable DAX vmemmap optimization on architectures that select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP but not ARCH_WANT_OPTIMIZE_DAX_VMEMMAP, such as 64-bit LoongArch? Previously, __vmemmap_can_optimize() was guarded by CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP, so section_activate() kept order = 0 for dev_pagemap mappings when that option was disabled. After commit 4171e4b9fc2a ("mm/sparse-vmemmap: drop vmemmap_can_optimize()") and this commit, CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP is no longer checked. Instead, section_activate() unconditionally records pgmap->vmemmap_shift via section_set_compound_order_range(), and section_vmemmap_optimizable() -> vmemmap_optimizable_order() only checks IS_ENABLED(CONFIG_VMEMMAP_OPTIMIZATION). On 64-bit LoongArch, ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP is selected (enabling CONFIG_VMEMMAP_OPTIMIZATION when CONFIG_HUGETLB_PAGE=y), but ARCH_WANT_OPTIMIZE_DAX_VMEMMAP is not selected, and vmemmap_free() is an empty stub: arch/loongarch/mm/init.c: void vmemmap_free(unsigned long start, unsigned long end, struct vmem_altmap *altmap) { } When memremap_pages() maps a dev_pagemap with vmemmap_shift >= VMEMMAP_OPTIMIZATION_MIN_ORDER on LoongArch: memremap_pages() pagemap_range() arch_add_memory() / add_pages() __add_pages() sparse_add_section() section_activate() populate_section_memmap() __populate_section_memmap() vmemmap_populate() vmemmap_populate_hugepages() vmemmap_populate_basepages() vmemmap_pte_populate() vmemmap_alloc_pte() vmemmap_pte_populate() now installs read-only shared tail PTEs and takes page references on the shared tail page. When memunmap_pages() later runs, LoongArch's no-op vmemmap_free() leaves those PTEs mapped and leaks the vmemmap pages while section_deactivate() resets compound_page_order to 0, causing subsequent hot-add or memremap_pages() calls on that PFN range to trigger WARN_ON_ONCE(vmemmap_optimizable_pfn(pfn)) in vmemmap_pte_populate() or fault when writing to read-only vmemmap pages. Should section_activate() still gate pgmap->vmemmap_shift on IS_ENABLED(CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP) (as well as !altmap && vmemmap_optimizable_order(pgmap->vmemmap_shift))? -- Sashiko AI review · https://sashiko.dev/#/patchset/20261030140720.1256062-1-songmuchun@bytedance.com?part=4 ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-10-31 2:43 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-30 14:07 [PATCH 0/4] mm: Simplify device DAX vmemmap optimization plumbing Muchun Song 2026-10-30 14:07 ` [PATCH 1/4] powerpc/mm: make vmemmap_populate_compound_pages() static Muchun Song 2026-10-31 0:54 ` sashiko-bot 2026-10-30 14:07 ` [PATCH 2/4] powerpc/mm: drop powerpc vmemmap_can_optimize() Muchun Song 2026-10-31 0:54 ` sashiko-bot 2026-10-30 14:07 ` [PATCH 3/4] mm/sparse-vmemmap: drop vmemmap_can_optimize() Muchun Song 2026-10-31 0:54 ` sashiko-bot 2026-10-31 2:42 ` Muchun Song 2026-10-30 14:07 ` [PATCH 4/4] mm/sparse-vmemmap: drop @pgmap from vmemmap population APIs Muchun Song 2026-10-31 0:54 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox