All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB
@ 2026-07-20  9:31 Muchun Song
  2026-07-20  9:31 ` [PATCH v2 01/17] mm/sparse: relax struct mem_section size constraints Muchun Song
                   ` (16 more replies)
  0 siblings, 17 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

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: making the generic sparse-vmemmap code
section-based optimization aware and switching HugeTLB bootmem pages to
this path.

HugeTLB vmemmap optimization currently has its own early boot setup
path. It pre-populates optimized vmemmap mappings before the normal
sparse-vmemmap population code runs, and sparsemem carries
SPARSEMEM_VMEMMAP_PREINIT only to support that special case.

That makes the HugeTLB vmemmap optimization path harder to share with
other users of sparse-vmemmap optimization and leaves a fair amount of
HugeTLB-specific boot-time state in the generic memory initialization
flow.

This series introduces section-based vmemmap optimization support in
the sparse-vmemmap code and switches HugeTLB bootmem pages over to it.
Instead of having HugeTLB pre-populate optimized vmemmap mappings
itself, HugeTLB now records the compound page order in the corresponding
memory sections. The generic sparse-vmemmap population path can then
allocate or reuse shared tail vmemmap pages based on section metadata.

The patches are organized as follows:

  - patches 1-2 prepare sparsemem and vmemmap optimization metadata
  - patches 3-8 teach the common sparse-vmemmap paths to use that state
  - patches 9-10 switch HugeTLB bootmem optimization to the
    section-based path
  - patches 11-17 clean up sparsemem and HugeTLB bootmem code that is no
    longer needed after the conversion

This is intended to be the second smaller step toward the broader HVO
generalization. The device DAX conversion and the wider HVO
consolidation are left for follow-up series.

v2:
- Kept struct mem_section aligned after relaxing lookup constraints
  (suggested by David Laight).
- Folded section order tracking into the first user instead of keeping
  a standalone API-only patch (suggested by Mike Rapoport).
- Renamed the HVO order macros with a VMEMMAP_OPTIMIZATION prefix
  (suggested by Mike Rapoport).
- Moved pfn_to_zone() before the sparse-vmemmap optimization changes
  (suggested by Mike Rapoport).
- Kept vmemmap accounting and population logic in sparse-vmemmap.c
  (suggested by Mike Rapoport).
- Renamed the early sparsemem helper to sparse_sections_init()
  (suggested by Mike Rapoport).
- Fixed the !SPARSEMEM stub name so SPARSEMEM=n builds compile
  (reported by Sashiko).
- Guard section_order() with CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP so
  it returns 0 when HVO is disabled and lets the compiler optimize the
  code as much as possible (suggested by Mike Rapoport)
- Collected Acked-by tags from Mike Rapoport.

[1] https://lore.kernel.org/linux-mm/20260513130542.35604-1-songmuchun@bytedance.com/

Muchun Song (17):
  mm/sparse: relax struct mem_section size constraints
  mm/sparse-vmemmap: rename HVO order macros
  mm/mm_init: skip initializing shared vmemmap tail pages
  mm/sparse-vmemmap: initialize shared tail vmemmap pages on allocation
  mm/sparse-vmemmap: support section-based vmemmap accounting
  mm/mm_init: factor out pfn_to_zone()
  mm/sparse-vmemmap: move vmemmap_get_tail() before PTE population
  mm/sparse-vmemmap: support section-based vmemmap optimization
  mm/sparse: initialize memory sections earlier
  mm/hugetlb: switch HugeTLB to section-based vmemmap optimization
  mm/sparse-vmemmap: remove SPARSEMEM_VMEMMAP_PREINIT support
  mm/sparse: inline usemap allocation into sparse_init_nid()
  mm/sparse: remove section_map_size()
  mm/hugetlb: remove HUGE_BOOTMEM_HVO
  mm/hugetlb: remove HUGE_BOOTMEM_CMA
  mm/hugetlb: localize struct huge_bootmem_page
  mm/hugetlb: localize HUGE_BOOTMEM_ZONES_VALID

 arch/x86/Kconfig        |   1 -
 fs/Kconfig              |   1 -
 include/linux/hugetlb.h |   5 --
 include/linux/mm.h      |   4 -
 include/linux/mmzone.h  |  72 +++++++---------
 mm/Kconfig              |   5 --
 mm/hugetlb.c            |  64 ++++-----------
 mm/hugetlb_vmemmap.c    | 103 +----------------------
 mm/hugetlb_vmemmap.h    |  13 +--
 mm/internal.h           |   7 --
 mm/mm_init.c            |  62 ++++++++------
 mm/mm_init.h            |   1 +
 mm/sparse-vmemmap.c     | 176 +++++++++++++++++-----------------------
 mm/sparse.c             |  94 ++++-----------------
 mm/sparse.h             |  67 +++++++++++++++
 scripts/gdb/linux/mm.py |   6 +-
 16 files changed, 253 insertions(+), 428 deletions(-)


base-commit: 0718283ab28bc3907e10b61a6b4be6fefa1cbb2f
-- 
2.54.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v2 01/17] mm/sparse: relax struct mem_section size constraints
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 02/17] mm/sparse-vmemmap: rename HVO order macros Muchun Song
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

struct mem_section is currently forced to a power-of-2 size so the
section-to-root lookup can use a mask instead of a modulo.

That requirement makes future extensions harder than necessary: adding a
small field can require configuration-dependent padding or layout checks
just to preserve the lookup scheme.  Keep the lookup correct for any
struct mem_section size by using a plain modulo instead.

Do not leave the layout entirely unconstrained, though.  Keep struct
mem_section double-word aligned so modest size changes, such as adding
another word-sized field on 64-bit systems, still keep a compact and
efficient layout.  If future fields grow the structure beyond that sweet
spot, the lookup remains correct; only the exact layout efficiency
changes.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
v2:
- Align struct mem_section to power-of-2 where possible, but support
  non-power-of-2 lookup (suggested by David Laight)
---
 include/linux/mmzone.h  | 11 +++--------
 mm/sparse.c             |  2 --
 scripts/gdb/linux/mm.py |  6 ++----
 3 files changed, 5 insertions(+), 14 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 0507193b3ae3..04f3409edacb 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -2016,13 +2016,9 @@ struct mem_section {
 	 * section. (see page_ext.h about this.)
 	 */
 	struct page_ext *page_ext;
-	unsigned long pad;
 #endif
-	/*
-	 * WARNING: mem_section must be a power-of-2 in size for the
-	 * calculation and use of SECTION_ROOT_MASK to make sense.
-	 */
-};
+/* Sacrifice minor padding space for efficient lookup. */
+} __aligned(2 * sizeof(unsigned long));
 
 #ifdef CONFIG_SPARSEMEM_EXTREME
 #define SECTIONS_PER_ROOT       (PAGE_SIZE / sizeof (struct mem_section))
@@ -2032,7 +2028,6 @@ struct mem_section {
 
 #define SECTION_NR_TO_ROOT(sec)	((sec) / SECTIONS_PER_ROOT)
 #define NR_SECTION_ROOTS	DIV_ROUND_UP(NR_MEM_SECTIONS, SECTIONS_PER_ROOT)
-#define SECTION_ROOT_MASK	(SECTIONS_PER_ROOT - 1)
 
 #ifdef CONFIG_SPARSEMEM_EXTREME
 extern struct mem_section **mem_section;
@@ -2056,7 +2051,7 @@ static inline struct mem_section *__nr_to_section(unsigned long nr)
 	if (!mem_section || !mem_section[root])
 		return NULL;
 #endif
-	return &mem_section[root][nr & SECTION_ROOT_MASK];
+	return &mem_section[root][nr % SECTIONS_PER_ROOT];
 }
 extern size_t mem_section_usage_size(void);
 
diff --git a/mm/sparse.c b/mm/sparse.c
index 704a9dec2b9a..ca9875f568d3 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -332,8 +332,6 @@ void __init sparse_init(void)
 	unsigned long pnum_end, pnum_begin, map_count = 1;
 	int nid_begin;
 
-	/* see include/linux/mmzone.h 'struct mem_section' definition */
-	BUILD_BUG_ON(!is_power_of_2(sizeof(struct mem_section)));
 	memblocks_present();
 
 	if (compound_info_has_mask()) {
diff --git a/scripts/gdb/linux/mm.py b/scripts/gdb/linux/mm.py
index dffadccbb01d..da4e8e9655a6 100644
--- a/scripts/gdb/linux/mm.py
+++ b/scripts/gdb/linux/mm.py
@@ -70,7 +70,6 @@ class x86_page_ops():
             self.SECTIONS_PER_ROOT = 1
 
         self.NR_SECTION_ROOTS = DIV_ROUND_UP(self.NR_MEM_SECTIONS, self.SECTIONS_PER_ROOT)
-        self.SECTION_ROOT_MASK = self.SECTIONS_PER_ROOT - 1
 
         try:
             self.SECTION_HAS_MEM_MAP = 1 << int(gdb.parse_and_eval('SECTION_HAS_MEM_MAP_BIT'))
@@ -100,7 +99,7 @@ class x86_page_ops():
     def __nr_to_section(self, nr):
         root = self.SECTION_NR_TO_ROOT(nr)
         mem_section = gdb.parse_and_eval("mem_section")
-        return mem_section[root][nr & self.SECTION_ROOT_MASK]
+        return mem_section[root][nr % self.SECTIONS_PER_ROOT]
 
     def pfn_to_section_nr(self, pfn):
         return pfn >> self.PFN_SECTION_SHIFT
@@ -249,7 +248,6 @@ class aarch64_page_ops():
             self.SECTIONS_PER_ROOT = 1
 
         self.NR_SECTION_ROOTS = DIV_ROUND_UP(self.NR_MEM_SECTIONS, self.SECTIONS_PER_ROOT)
-        self.SECTION_ROOT_MASK = self.SECTIONS_PER_ROOT - 1
         self.SUBSECTION_SHIFT = 21
         self.SEBSECTION_SIZE = 1 << self.SUBSECTION_SHIFT
         self.PFN_SUBSECTION_SHIFT = self.SUBSECTION_SHIFT - self.PAGE_SHIFT
@@ -304,7 +302,7 @@ class aarch64_page_ops():
     def __nr_to_section(self, nr):
         root = self.SECTION_NR_TO_ROOT(nr)
         mem_section = gdb.parse_and_eval("mem_section")
-        return mem_section[root][nr & self.SECTION_ROOT_MASK]
+        return mem_section[root][nr % self.SECTIONS_PER_ROOT]
 
     def pfn_to_section_nr(self, pfn):
         return pfn >> self.PFN_SECTION_SHIFT
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 02/17] mm/sparse-vmemmap: rename HVO order macros
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
  2026-07-20  9:31 ` [PATCH v2 01/17] mm/sparse: relax struct mem_section size constraints Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-30 10:57   ` Mike Rapoport
  2026-07-20  9:31 ` [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages Muchun Song
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

The macros VMEMMAP_TAIL_MIN_ORDER and NR_VMEMMAP_TAILS describe the
order range where HVO can be applied, but their names tie that range to
the tail-page cache implementation.

Rename them with a VMEMMAP_OPTIMIZATION prefix and use the new names in
the HVO paths. This makes the code describe the optimization requirements
rather than the tail-page cache implementation detail.

No functional change intended.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
v2:
- Rename the macros with a VMEMMAP_OPTIMIZATION prefix (suggested by
  Mike Rapoport)
- Drop intermediate optimized-folio size macros (suggested by Mike
  Rapoport)
---
 include/linux/mmzone.h | 17 +++++++++--------
 mm/hugetlb.c           |  4 ++--
 mm/hugetlb_vmemmap.c   |  2 +-
 mm/sparse-vmemmap.c    |  4 ++--
 4 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 04f3409edacb..82b0155d886f 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -107,13 +107,14 @@
 	 is_power_of_2(sizeof(struct page)) ? \
 	 MAX_FOLIO_NR_PAGES * sizeof(struct page) : 0)
 
-/*
- * vmemmap optimization (like HVO) is only possible for page orders that fill
- * two or more pages with struct pages.
- */
-#define VMEMMAP_TAIL_MIN_ORDER (ilog2(2 * PAGE_SIZE / sizeof(struct page)))
-#define __NR_VMEMMAP_TAILS (MAX_FOLIO_ORDER - VMEMMAP_TAIL_MIN_ORDER + 1)
-#define NR_VMEMMAP_TAILS (__NR_VMEMMAP_TAILS > 0 ? __NR_VMEMMAP_TAILS : 0)
+/* The number of struct pages covered by the retained vmemmap pages with HVO enabled. */
+#define VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES	(PAGE_SIZE / sizeof(struct page))
+#define VMEMMAP_OPTIMIZATION_MIN_ORDER		(ilog2(VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES) + 1)
+
+#define __VMEMMAP_OPTIMIZATION_NR_ORDERS	\
+	(MAX_FOLIO_ORDER - VMEMMAP_OPTIMIZATION_MIN_ORDER + 1)
+#define VMEMMAP_OPTIMIZATION_NR_ORDERS		\
+	(__VMEMMAP_OPTIMIZATION_NR_ORDERS > 0 ? __VMEMMAP_OPTIMIZATION_NR_ORDERS : 0)
 
 enum migratetype {
 	MIGRATE_UNMOVABLE,
@@ -1147,7 +1148,7 @@ struct zone {
 	atomic_long_t		vm_stat[NR_VM_ZONE_STAT_ITEMS];
 	atomic_long_t		vm_numa_event[NR_VM_NUMA_EVENT_ITEMS];
 #ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
-	struct page *vmemmap_tails[NR_VMEMMAP_TAILS];
+	struct page *vmemmap_tails[VMEMMAP_OPTIMIZATION_NR_ORDERS];
 #endif
 } ____cacheline_internodealigned_in_smp;
 
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index db09f0ab6d97..b8e1569bda15 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3358,7 +3358,7 @@ void __init hugetlb_bootmem_struct_page_init(void)
 	struct zone *zone;
 
 	for_each_zone(zone) {
-		for (int i = 0; i < NR_VMEMMAP_TAILS; i++) {
+		for (int i = 0; i < VMEMMAP_OPTIMIZATION_NR_ORDERS; i++) {
 			struct page *tail, *p;
 			unsigned int order;
 
@@ -3366,7 +3366,7 @@ void __init hugetlb_bootmem_struct_page_init(void)
 			if (!tail)
 				continue;
 
-			order = i + VMEMMAP_TAIL_MIN_ORDER;
+			order = i + VMEMMAP_OPTIMIZATION_MIN_ORDER;
 			p = page_to_virt(tail);
 			/*
 			 * prep_and_add_bootmem_folios() can access pageblock
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 917db0984143..ae8fdaa42118 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -494,7 +494,7 @@ static bool vmemmap_should_optimize_folio(const struct hstate *h, struct folio *
 
 static struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)
 {
-	const unsigned int idx = order - VMEMMAP_TAIL_MIN_ORDER;
+	const unsigned int idx = order - VMEMMAP_OPTIMIZATION_MIN_ORDER;
 	struct page *tail, *p;
 	int node = zone_to_nid(zone);
 
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 5a2469fb1838..aa6a4a2fae98 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -329,12 +329,12 @@ static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone *
 	unsigned int idx;
 	int node = zone_to_nid(zone);
 
-	if (WARN_ON_ONCE(order < VMEMMAP_TAIL_MIN_ORDER))
+	if (WARN_ON_ONCE(order < VMEMMAP_OPTIMIZATION_MIN_ORDER))
 		return NULL;
 	if (WARN_ON_ONCE(order > MAX_FOLIO_ORDER))
 		return NULL;
 
-	idx = order - VMEMMAP_TAIL_MIN_ORDER;
+	idx = order - VMEMMAP_OPTIMIZATION_MIN_ORDER;
 	tail = zone->vmemmap_tails[idx];
 	if (tail)
 		return tail;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
  2026-07-20  9:31 ` [PATCH v2 01/17] mm/sparse: relax struct mem_section size constraints Muchun Song
  2026-07-20  9:31 ` [PATCH v2 02/17] mm/sparse-vmemmap: rename HVO order macros Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-26  4:30   ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 04/17] mm/sparse-vmemmap: initialize shared tail vmemmap pages on allocation Muchun Song
                   ` (13 subsequent siblings)
  16 siblings, 1 reply; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

memmap_init_range() initializes every struct page in the target range.
For compound pages with vmemmap optimization, the tail struct pages are
backed by a shared vmemmap page.

Initializing those tail struct pages would overwrite the shared
vmemmap page contents, requiring users such as HugeTLB to restore the
metadata afterwards.

Track the compound order for HVO-backed sections and use that metadata
to detect struct pages that fall into the shared tail vmemmap range.
Skip those shared tail pages in memmap_init_range(), then initialize
pageblock migratetypes for the processed range with a helper after the
per-page initialization loop.

The !SPARSEMEM __pfn_to_section() stub is needed only for the build:
memmap_init_range() references __pfn_to_section() after checking
pfn_vmemmap_optimizable(), and !SPARSEMEM builds still have to compile
that code even though pfn_vmemmap_optimizable() folds to false.

This is a preparatory change for consolidating handling across users of
vmemmap optimization, and it also avoids redundant initialization of
shared tail vmemmap pages during early boot.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
v2:
- Fold section order tracking into the first user instead of keeping a
  standalone API-only patch (suggested by Mike Rapoport)
- Rename page_vmemmap_optimizable() to pfn_vmemmap_optimizable() and
  pass a PFN directly (suggested by Mike Rapoport)
- Initialize pageblock migratetypes from a helper after the per-page
  loop (suggested by Mike Rapoport)
- Use a 1G PFN chunk for cond_resched() in the pageblock helper
  (suggested by Mike Rapoport)
- Guard section_order() with CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP so
  it returns 0 when HVO is disabled and lets the compiler optimize the
  code as much as possible (suggested by Mike Rapoport)
- Explain why the !SPARSEMEM __pfn_to_section() stub belongs here
  (suggested by Mike Rapoport)
---
 include/linux/mmzone.h | 14 ++++++++++++++
 mm/mm_init.c           | 33 +++++++++++++++++----------------
 mm/sparse.h            | 23 +++++++++++++++++++++++
 3 files changed, 54 insertions(+), 16 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 82b0155d886f..2a32101d55e6 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -2011,6 +2011,14 @@ struct mem_section {
 	unsigned long section_mem_map;
 
 	struct mem_section_usage *usage;
+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
+	/*
+	 * Normally, sections hold regular (order-0) pages. However, for
+	 * sections with HVO enabled, this tracks the compound page order
+	 * to enable deduplication of redundant vmemmap pages.
+	 */
+	unsigned int order;
+#endif
 #ifdef CONFIG_PAGE_EXTENSION
 	/*
 	 * If SPARSEMEM, pgdat doesn't have page_ext pointer. We use
@@ -2365,8 +2373,14 @@ static inline unsigned long next_present_section_nr(unsigned long section_nr)
 #endif
 
 #else
+struct mem_section;
+
 #define sparse_vmemmap_init_nid_early(_nid) do {} while (0)
 #define pfn_in_present_section pfn_valid
+static inline struct mem_section *__pfn_to_section(unsigned long pfn)
+{
+	return NULL;
+}
 #endif /* CONFIG_SPARSEMEM */
 
 /*
diff --git a/mm/mm_init.c b/mm/mm_init.c
index ddda9d6837f3..dcb757b36902 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -29,6 +29,7 @@
 #include <linux/cma.h>
 #include <linux/crash_dump.h>
 #include <linux/execmem.h>
+#include <linux/sizes.h>
 #include <linux/vmstat.h>
 #include <linux/kexec_handover.h>
 #include <linux/hugetlb.h>
@@ -677,19 +678,17 @@ static inline void fixup_hashdist(void)
 static inline void fixup_hashdist(void) {}
 #endif /* CONFIG_NUMA */
 
-#if defined(CONFIG_ZONE_DEVICE) || defined(CONFIG_DEFERRED_STRUCT_PAGE_INIT)
 static __meminit void pageblock_migratetype_init_range(unsigned long pfn,
-		unsigned long nr_pages, int migratetype, bool atomic)
+		unsigned long nr_pages, int migratetype, bool isolate, bool atomic)
 {
 	const unsigned long end = pfn + nr_pages;
 
 	for (pfn = pageblock_align(pfn); pfn < end; pfn += pageblock_nr_pages) {
-		init_pageblock_migratetype(pfn_to_page(pfn), migratetype, false);
-		if (!atomic && IS_ALIGNED(pfn, PAGES_PER_SECTION))
+		init_pageblock_migratetype(pfn_to_page(pfn), migratetype, isolate);
+		if (!atomic && IS_ALIGNED(pfn, PFN_DOWN(SZ_1G)))
 			cond_resched();
 	}
 }
-#endif
 
 #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
 static inline void pgdat_set_deferred_range(pg_data_t *pgdat)
@@ -884,6 +883,13 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
 			}
 		}
 
+		if (pfn_vmemmap_optimizable(pfn)) {
+			unsigned int order = section_order(__pfn_to_section(pfn));
+
+			pfn = min(ALIGN(pfn, 1UL << order), end_pfn);
+			continue;
+		}
+
 		page = pfn_to_page(pfn);
 		__init_single_page(page, pfn, zone, nid);
 		if (context == MEMINIT_HOTPLUG) {
@@ -895,18 +901,13 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
 				__SetPageOffline(page);
 		}
 
-		/*
-		 * Usually, we want to mark the pageblock MIGRATE_MOVABLE,
-		 * such that unmovable allocations won't be scattered all
-		 * over the place during system boot.
-		 */
-		if (pageblock_aligned(pfn)) {
-			init_pageblock_migratetype(page, migratetype,
-					isolate_pageblock);
+		if (pageblock_aligned(pfn))
 			cond_resched();
-		}
 		pfn++;
 	}
+
+	pageblock_migratetype_init_range(start_pfn, pfn - start_pfn, migratetype,
+					 isolate_pageblock, false);
 }
 
 static void __init memmap_init_zone_range(struct zone *zone,
@@ -1111,7 +1112,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
 				     compound_nr_pages(pfn, altmap, pgmap));
 	}
 
-	pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE, false);
+	pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE, false, false);
 
 	pr_debug("%s initialised %lu pages in %ums\n", __func__,
 		nr_pages, jiffies_to_msecs(jiffies - start));
@@ -1923,7 +1924,7 @@ static void __init deferred_free_pages(unsigned long pfn,
 	if (!nr_pages)
 		return;
 
-	pageblock_migratetype_init_range(pfn, nr_pages, mt, true);
+	pageblock_migratetype_init_range(pfn, nr_pages, mt, false, true);
 
 	page = pfn_to_page(pfn);
 
diff --git a/mm/sparse.h b/mm/sparse.h
index 95aa031213f2..030248030dc7 100644
--- a/mm/sparse.h
+++ b/mm/sparse.h
@@ -51,6 +51,29 @@ static inline void __section_mark_present(struct mem_section *ms,
 static inline void sparse_init(void) {}
 #endif /* CONFIG_SPARSEMEM */
 
+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
+static inline unsigned int section_order(const struct mem_section *section)
+{
+	return section->order;
+}
+#else
+static inline unsigned int section_order(const struct mem_section *section)
+{
+	return 0;
+}
+#endif
+
+static inline bool pfn_vmemmap_optimizable(unsigned long pfn)
+{
+	const unsigned int order = section_order(__pfn_to_section(pfn));
+	const unsigned long nr_pages = 1UL << order;
+
+	if (!is_power_of_2(sizeof(struct page)))
+		return false;
+
+	return (pfn & (nr_pages - 1)) >= VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES;
+}
+
 /*
  * mm/sparse-vmemmap.c
  */
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 04/17] mm/sparse-vmemmap: initialize shared tail vmemmap pages on allocation
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (2 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 05/17] mm/sparse-vmemmap: support section-based vmemmap accounting Muchun Song
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

The shared tail vmemmap page allocated in vmemmap_get_tail() used to be
left uninitialized, because memmap_init_range() would later overwrite
it.  That forced users such as HugeTLB to defer the initialization to
their own setup paths.

Now that memmap_init_range() skips shared tail vmemmap pages, initialize
them immediately in vmemmap_get_tail() with init_compound_tail()
instead.

This moves the initialization to the point where the shared tail page is
allocated and avoids relying on deferred handling in individual users.
The remaining deferred initialization in HugeTLB will be removed once it
switches to the section-based vmemmap optimization mechanism.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
 mm/sparse-vmemmap.c | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index aa6a4a2fae98..107215cf8488 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -338,19 +338,11 @@ static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone *
 	tail = zone->vmemmap_tails[idx];
 	if (tail)
 		return tail;
-
-	/*
-	 * Only allocate the page, but do not initialize it.
-	 *
-	 * Any initialization done here will be overwritten by memmap_init().
-	 *
-	 * hugetlb_bootmem_struct_page_init() will take care of initialization
-	 * after memmap_init().
-	 */
-
 	p = vmemmap_alloc_block_zero(PAGE_SIZE, node);
 	if (!p)
 		return NULL;
+	for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++)
+		init_compound_tail(p + i, NULL, order, zone);
 
 	tail = virt_to_page(p);
 	zone->vmemmap_tails[idx] = tail;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 05/17] mm/sparse-vmemmap: support section-based vmemmap accounting
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (3 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 04/17] mm/sparse-vmemmap: initialize shared tail vmemmap pages on allocation Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone() Muchun Song
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

section_nr_vmemmap_pages() can account ordinary sections and DAX sections,
but section-based vmemmap optimization keeps its compound order in struct
mem_section and retains a different number of vmemmap pages.

Teach section_nr_vmemmap_pages() to recognize section-based optimized
sections and calculate their vmemmap page count from the section order
and the HVO retained page count.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
Hi Mike,

I reintroduced VMEMMAP_OPTIMIZATION_PAGES (the intermediate macro), which we
discussed removing in v1, so the section-based HVO path doesn't have to
hard-code its retained vmemmap page count (i.e. 1) in section_nr_vmemmap_pages().
To better unify the codebase in the future, the new macro introduced in this patch
will also be applied to the HugeTLB code (e.g., to calculate
HUGETLB_VMEMMAP_RESERVE_SIZE from this new macro as well).

v2:
- Remove an unnecessary vmemmap_can_optimize() call to simplify the code
  (suggested by Mike Rapoport).
- Rewrite the commit message for better understanding.
---
 include/linux/mmzone.h |  6 ++++--
 mm/sparse-vmemmap.c    | 10 ++++++----
 mm/sparse.h            |  8 ++++++++
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 2a32101d55e6..025ea69f57d8 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -107,8 +107,10 @@
 	 is_power_of_2(sizeof(struct page)) ? \
 	 MAX_FOLIO_NR_PAGES * sizeof(struct page) : 0)
 
-/* The number of struct pages covered by the retained vmemmap pages with HVO enabled. */
-#define VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES	(PAGE_SIZE / sizeof(struct page))
+/* The number of retained vmemmap pages with HVO enabled. */
+#define VMEMMAP_OPTIMIZATION_PAGES		1
+#define VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES	\
+	(VMEMMAP_OPTIMIZATION_PAGES * PAGE_SIZE / sizeof(struct page))
 #define VMEMMAP_OPTIMIZATION_MIN_ORDER		(ilog2(VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES) + 1)
 
 #define __VMEMMAP_OPTIMIZATION_NR_ORDERS	\
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 107215cf8488..b7abc5494bb9 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -649,24 +649,26 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn)
 static int __meminit section_nr_vmemmap_pages(unsigned long pfn, unsigned long nr_pages,
 		struct vmem_altmap *altmap, struct dev_pagemap *pgmap)
 {
-	const unsigned int order = pgmap ? pgmap->vmemmap_shift : 0;
+	const struct mem_section *ms = __pfn_to_section(pfn);
+	const int order = pgmap ? pgmap->vmemmap_shift : section_order(ms);
+	const int vmemmap_pages = pgmap ? VMEMMAP_RESERVE_NR : VMEMMAP_OPTIMIZATION_PAGES;
 	const unsigned long pages_per_compound = 1UL << order;
 
 	VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, PAGES_PER_SUBSECTION));
 	VM_WARN_ON_ONCE(nr_pages > PAGES_PER_SECTION);
 
-	if (!vmemmap_can_optimize(altmap, pgmap))
+	if (!vmemmap_can_optimize(altmap, pgmap) && !section_vmemmap_optimizable(ms))
 		return DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE);
 
 	if (order < PFN_SECTION_SHIFT) {
 		VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, pages_per_compound));
-		return VMEMMAP_RESERVE_NR * nr_pages / pages_per_compound;
+		return vmemmap_pages * nr_pages / pages_per_compound;
 	}
 
 	VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, PAGES_PER_SECTION));
 
 	if (IS_ALIGNED(pfn, pages_per_compound))
-		return VMEMMAP_RESERVE_NR;
+		return vmemmap_pages;
 
 	return 0;
 }
diff --git a/mm/sparse.h b/mm/sparse.h
index 030248030dc7..1e9fcea10181 100644
--- a/mm/sparse.h
+++ b/mm/sparse.h
@@ -74,6 +74,14 @@ static inline bool pfn_vmemmap_optimizable(unsigned long pfn)
 	return (pfn & (nr_pages - 1)) >= VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES;
 }
 
+static inline bool section_vmemmap_optimizable(const struct mem_section *section)
+{
+	if (!is_power_of_2(sizeof(struct page)))
+		return false;
+
+	return section_order(section) >= VMEMMAP_OPTIMIZATION_MIN_ORDER;
+}
+
 /*
  * mm/sparse-vmemmap.c
  */
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone()
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (4 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 05/17] mm/sparse-vmemmap: support section-based vmemmap accounting Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20 11:04   ` Muchun Song
                     ` (2 more replies)
  2026-07-20  9:31 ` [PATCH v2 07/17] mm/sparse-vmemmap: move vmemmap_get_tail() before PTE population Muchun Song
                   ` (10 subsequent siblings)
  16 siblings, 3 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

pfn_to_zone() in hugetlb_vmemmap.c duplicates the zone lookup logic in
__init_deferred_page().

Move it to mm_init.c, declare it in mm/internal.h, and reuse it from
__init_deferred_page() and HugeTLB early vmemmap initialization instead
of open-coding the zone walk there.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
v2:
- Move this preparatory patch before the sparse-vmemmap optimization
  changes (suggested by Mike Rapoport)
---
 mm/hugetlb_vmemmap.c | 17 ++---------------
 mm/mm_init.c         | 28 ++++++++++++++++++----------
 mm/mm_init.h         |  1 +
 3 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index ae8fdaa42118..c48fcea076a5 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -19,6 +19,7 @@
 #include <asm/tlbflush.h>
 #include "hugetlb_vmemmap.h"
 #include "internal.h"
+#include "mm_init.h"
 
 /**
  * struct vmemmap_remap_walk - walk vmemmap page table
@@ -744,20 +745,6 @@ static bool vmemmap_should_optimize_bootmem_page(struct huge_bootmem_page *m)
 	return true;
 }
 
-static struct zone *pfn_to_zone(unsigned nid, unsigned long pfn)
-{
-	struct zone *zone;
-	enum zone_type zone_type;
-
-	for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) {
-		zone = &NODE_DATA(nid)->node_zones[zone_type];
-		if (zone_spans_pfn(zone, pfn))
-			return zone;
-	}
-
-	return NULL;
-}
-
 /*
  * Initialize memmap section for a gigantic page, HVO-style.
  */
@@ -787,7 +774,7 @@ void __init hugetlb_vmemmap_init_early(int nid)
 		map = pfn_to_page(pfn);
 		start = (unsigned long)map;
 		end = start + hugetlb_vmemmap_size(m->hstate);
-		zone = pfn_to_zone(nid, pfn);
+		zone = pfn_to_zone(pfn, nid);
 
 		if (vmemmap_populate_hvo(start, end, huge_page_order(m->hstate),
 					 zone, HUGETLB_VMEMMAP_RESERVE_SIZE))
diff --git a/mm/mm_init.c b/mm/mm_init.c
index dcb757b36902..191d4ebe9314 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -690,6 +690,20 @@ static __meminit void pageblock_migratetype_init_range(unsigned long pfn,
 	}
 }
 
+struct zone __meminit *pfn_to_zone(unsigned long pfn, int nid)
+{
+	pg_data_t *pgdat = NODE_DATA(nid);
+
+	for (enum zone_type zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) {
+		struct zone *zone = &pgdat->node_zones[zone_type];
+
+		if (zone_spans_pfn(zone, pfn))
+			return zone;
+	}
+
+	return NULL;
+}
+
 #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
 static inline void pgdat_set_deferred_range(pg_data_t *pgdat)
 {
@@ -748,20 +762,14 @@ defer_init(int nid, unsigned long pfn, unsigned long end_pfn)
 
 static void __meminit __init_deferred_page(unsigned long pfn, int nid)
 {
-	pg_data_t *pgdat = NODE_DATA(nid);
-	int zid;
+	struct zone *zone;
 
 	if (early_page_initialised(pfn, nid))
 		return;
 
-	for (zid = 0; zid < MAX_NR_ZONES; zid++) {
-		struct zone *zone = &pgdat->node_zones[zid];
-
-		if (zone_spans_pfn(zone, pfn))
-			break;
-	}
-	__init_single_page(pfn_to_page(pfn), pfn, zid, nid);
-
+	zone = pfn_to_zone(pfn, nid);
+	__init_single_page(pfn_to_page(pfn), pfn,
+			   zone ? zone_idx(zone) : MAX_NR_ZONES, nid);
 	if (pageblock_aligned(pfn)) {
 		enum migratetype mt =
 			kho_scratch_migratetype(pfn, MIGRATE_MOVABLE);
diff --git a/mm/mm_init.h b/mm/mm_init.h
index 39f75df9be1c..c9fc35e7e9f1 100644
--- a/mm/mm_init.h
+++ b/mm/mm_init.h
@@ -39,6 +39,7 @@ void memmap_init_range(unsigned long size, int nid, unsigned long zone,
 		enum meminit_context context,
 		struct vmem_altmap *altmap, int migratetype,
 		bool isolate_pageblock);
+struct zone *pfn_to_zone(unsigned long pfn, int nid);
 
 #if defined CONFIG_COMPACTION || defined CONFIG_CMA
 /* Free whole pageblock and set its migration type to MIGRATE_CMA. */
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 07/17] mm/sparse-vmemmap: move vmemmap_get_tail() before PTE population
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (5 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone() Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 08/17] mm/sparse-vmemmap: support section-based vmemmap optimization Muchun Song
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

A follow-up change will call vmemmap_get_tail() from
vmemmap_pte_populate(). Move it before the PTE population helpers to
avoid adding a forward declaration.

Move vmemmap_alloc_block_zero() with it because vmemmap_get_tail()
depends on that helper. No functional change is intended.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
 mm/sparse-vmemmap.c | 78 +++++++++++++++++++++++----------------------
 1 file changed, 40 insertions(+), 38 deletions(-)

diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index b7abc5494bb9..b770fe2428fd 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -148,6 +148,46 @@ void __meminit vmemmap_verify(pte_t *pte, int node,
 			start, end - 1);
 }
 
+static void * __meminit vmemmap_alloc_block_zero(unsigned long size, int node)
+{
+	void *p = vmemmap_alloc_block(size, node);
+
+	if (!p)
+		return NULL;
+	memset(p, 0, size);
+
+	return p;
+}
+
+#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
+static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)
+{
+	struct page *p, *tail;
+	unsigned int idx;
+	int node = zone_to_nid(zone);
+
+	if (WARN_ON_ONCE(order < VMEMMAP_OPTIMIZATION_MIN_ORDER))
+		return NULL;
+	if (WARN_ON_ONCE(order > MAX_FOLIO_ORDER))
+		return NULL;
+
+	idx = order - VMEMMAP_OPTIMIZATION_MIN_ORDER;
+	tail = zone->vmemmap_tails[idx];
+	if (tail)
+		return tail;
+	p = vmemmap_alloc_block_zero(PAGE_SIZE, node);
+	if (!p)
+		return NULL;
+	for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++)
+		init_compound_tail(p + i, NULL, order, zone);
+
+	tail = virt_to_page(p);
+	zone->vmemmap_tails[idx] = tail;
+
+	return tail;
+}
+#endif
+
 static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node,
 				       struct vmem_altmap *altmap,
 				       unsigned long ptpfn, unsigned long flags)
@@ -181,17 +221,6 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
 	return pte;
 }
 
-static void * __meminit vmemmap_alloc_block_zero(unsigned long size, int node)
-{
-	void *p = vmemmap_alloc_block(size, node);
-
-	if (!p)
-		return NULL;
-	memset(p, 0, size);
-
-	return p;
-}
-
 static pmd_t * __meminit vmemmap_pmd_populate(pud_t *pud, unsigned long addr, int node)
 {
 	pmd_t *pmd = pmd_offset(pud, addr);
@@ -323,33 +352,6 @@ void vmemmap_wrprotect_hvo(unsigned long addr, unsigned long end,
 }
 
 #ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
-static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)
-{
-	struct page *p, *tail;
-	unsigned int idx;
-	int node = zone_to_nid(zone);
-
-	if (WARN_ON_ONCE(order < VMEMMAP_OPTIMIZATION_MIN_ORDER))
-		return NULL;
-	if (WARN_ON_ONCE(order > MAX_FOLIO_ORDER))
-		return NULL;
-
-	idx = order - VMEMMAP_OPTIMIZATION_MIN_ORDER;
-	tail = zone->vmemmap_tails[idx];
-	if (tail)
-		return tail;
-	p = vmemmap_alloc_block_zero(PAGE_SIZE, node);
-	if (!p)
-		return NULL;
-	for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++)
-		init_compound_tail(p + i, NULL, order, zone);
-
-	tail = virt_to_page(p);
-	zone->vmemmap_tails[idx] = tail;
-
-	return tail;
-}
-
 int __meminit vmemmap_populate_hvo(unsigned long addr, unsigned long end,
 				       unsigned int order, struct zone *zone,
 				       unsigned long headsize)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 08/17] mm/sparse-vmemmap: support section-based vmemmap optimization
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (6 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 07/17] mm/sparse-vmemmap: move vmemmap_get_tail() before PTE population Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20 11:10   ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 09/17] mm/sparse: initialize memory sections earlier Muchun Song
                   ` (8 subsequent siblings)
  16 siblings, 1 reply; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

Teach sparse-vmemmap population code to use the compound page order
when deciding whether a vmemmap page can be optimized.

With this information, the common sparse-vmemmap population path can
allocate or reuse shared tail vmemmap pages directly instead of relying
on HugeTLB-specific handling.

This centralizes vmemmap optimization logic in the sparse-vmemmap code,
based on section metadata, and prepares for sharing the same mechanism
across different users of vmemmap optimization, including HugeTLB and
DAX.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
v2:
- Keep vmemmap accounting and population logic in sparse-vmemmap.c
  (suggested by Mike Rapoport)
- Move vmemmap_get_tail() before its first use instead of adding only a
  forward declaration in the previous patch (suggested by Mike Rapoport)
- Simplify the PMD path handling for HVO-covered sections
---
 mm/sparse-vmemmap.c | 36 ++++++++++++++++++++++++++++++------
 mm/sparse.c         |  4 ++--
 mm/sparse.h         |  7 +++++++
 3 files changed, 39 insertions(+), 8 deletions(-)

diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index b770fe2428fd..e818b4109777 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -186,6 +186,11 @@ static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone *
 
 	return tail;
 }
+#else
+static inline struct page *vmemmap_get_tail(unsigned int order, struct zone *zone)
+{
+	return NULL;
+}
 #endif
 
 static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node,
@@ -193,12 +198,24 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
 				       unsigned long ptpfn, unsigned long flags)
 {
 	pte_t *pte = pte_offset_kernel(pmd, addr);
+	unsigned long pfn = page_to_pfn((struct page *)addr);
+
 	if (pte_none(ptep_get(pte))) {
 		pte_t entry;
-		void *p;
+
+		if (pfn_vmemmap_optimizable(pfn) && ptpfn == (unsigned long)-1) {
+			unsigned int order = section_order(__pfn_to_section(pfn));
+			struct zone *zone = pfn_to_zone(pfn, node);
+			struct page *page = vmemmap_get_tail(order, zone);
+
+			if (!page)
+				return NULL;
+			ptpfn = page_to_pfn(page);
+		}
 
 		if (ptpfn == (unsigned long)-1) {
-			p = vmemmap_alloc_block_buf(PAGE_SIZE, node, altmap);
+			void *p = vmemmap_alloc_block_buf(PAGE_SIZE, node, altmap);
+
 			if (!p)
 				return NULL;
 			ptpfn = PHYS_PFN(__pa(p));
@@ -217,7 +234,8 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
 		}
 		entry = pfn_pte(ptpfn, PAGE_KERNEL);
 		set_pte_at(&init_mm, addr, pte, entry);
-	}
+	} else if (WARN_ON_ONCE(pfn_vmemmap_optimizable(pfn)))
+		return NULL;
 	return pte;
 }
 
@@ -406,6 +424,9 @@ int __meminit vmemmap_populate_hugepages(unsigned long start, unsigned long end,
 	pmd_t *pmd;
 
 	for (addr = start; addr < end; addr = next) {
+		unsigned long pfn = page_to_pfn((struct page *)addr);
+		const struct mem_section *ms = __pfn_to_section(pfn);
+
 		next = pmd_addr_end(addr, end);
 
 		pgd = vmemmap_pgd_populate(addr, node);
@@ -421,7 +442,7 @@ int __meminit vmemmap_populate_hugepages(unsigned long start, unsigned long end,
 			return -ENOMEM;
 
 		pmd = pmd_offset(pud, addr);
-		if (pmd_none(pmdp_get(pmd))) {
+		if (pmd_none(pmdp_get(pmd)) && !section_vmemmap_optimizable(ms)) {
 			void *p;
 
 			p = vmemmap_alloc_block_buf(PMD_SIZE, node, altmap);
@@ -439,8 +460,11 @@ int __meminit vmemmap_populate_hugepages(unsigned long start, unsigned long end,
 				 */
 				return -ENOMEM;
 			}
-		} else if (vmemmap_check_pmd(pmd, node, addr, next))
+		} else if (vmemmap_check_pmd(pmd, node, addr, next)) {
+			if (WARN_ON_ONCE(section_vmemmap_optimizable(ms)))
+				return -ENOTSUPP;
 			continue;
+		}
 		if (vmemmap_populate_basepages(addr, next, node, altmap))
 			return -ENOMEM;
 	}
@@ -648,7 +672,7 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn)
 	}
 }
 
-static int __meminit section_nr_vmemmap_pages(unsigned long pfn, unsigned long nr_pages,
+int __meminit section_nr_vmemmap_pages(unsigned long pfn, unsigned long nr_pages,
 		struct vmem_altmap *altmap, struct dev_pagemap *pgmap)
 {
 	const struct mem_section *ms = __pfn_to_section(pfn);
diff --git a/mm/sparse.c b/mm/sparse.c
index ca9875f568d3..24555a32a5d9 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -315,8 +315,8 @@ static void __init sparse_init_nid(int nid, unsigned long pnum_begin,
 							nid, NULL, NULL);
 			if (!map)
 				panic("Failed to allocate memmap for section %lu\n", pnum);
-			memmap_boot_pages_add(DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page),
-							   PAGE_SIZE));
+			memmap_boot_pages_add(section_nr_vmemmap_pages(pfn, PAGES_PER_SECTION,
+								       NULL, NULL));
 			sparse_init_early_section(nid, map, pnum, 0);
 		}
 	}
diff --git a/mm/sparse.h b/mm/sparse.h
index 1e9fcea10181..4377b097dc8d 100644
--- a/mm/sparse.h
+++ b/mm/sparse.h
@@ -87,8 +87,15 @@ static inline bool section_vmemmap_optimizable(const struct mem_section *section
  */
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
 void sparse_init_subsection_map(void);
+int __meminit section_nr_vmemmap_pages(unsigned long pfn, unsigned long nr_pages,
+		struct vmem_altmap *altmap, struct dev_pagemap *pgmap);
 #else
 static inline void sparse_init_subsection_map(void) {}
+static inline int section_nr_vmemmap_pages(unsigned long pfn, unsigned long nr_pages,
+		struct vmem_altmap *altmap, struct dev_pagemap *pgmap)
+{
+	return DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE);
+}
 #endif /* CONFIG_SPARSEMEM_VMEMMAP */
 
 #endif /* __MM_SPARSE_H */
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 09/17] mm/sparse: initialize memory sections earlier
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (7 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 08/17] mm/sparse-vmemmap: support section-based vmemmap optimization Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 10/17] mm/hugetlb: switch HugeTLB to section-based vmemmap optimization Muchun Song
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

Upcoming HugeTLB bootmem changes need sparsemem section metadata before
the HugeTLB bootmem allocation path runs. The memory sections are
initialized from sparse_init(), which is called too late for that setup.

Move the code that initializes sparsemem section metadata for memblock
ranges into mm_core_init_early(), before free_area_init() and the HugeTLB
bootmem setup. Rename the helper to sparse_sections_init() so the new
caller describes the sparsemem-specific initialization step.

This is a preparatory change.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
v2:
- Rename the helper to sparse_sections_init() to describe the section
  metadata initialization (suggested by Mike Rapoport)
- Fix the !SPARSEMEM stub name so SPARSEMEM=n builds compile
  (reported by Sashiko)
---
 mm/mm_init.c |  1 +
 mm/sparse.c  | 10 ++--------
 mm/sparse.h  |  2 ++
 3 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/mm/mm_init.c b/mm/mm_init.c
index 191d4ebe9314..b3ebecf077a0 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -2636,6 +2636,7 @@ void __init __weak mem_init(void)
 
 void __init mm_core_init_early(void)
 {
+	sparse_sections_init();
 	free_area_init();
 
 	hugetlb_cma_reserve();
diff --git a/mm/sparse.c b/mm/sparse.c
index 24555a32a5d9..d4641aa70497 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -191,12 +191,8 @@ static void __init memory_present(int nid, unsigned long start, unsigned long en
 	}
 }
 
-/*
- * Mark all memblocks as present using memory_present().
- * This is a convenience function that is useful to mark all of the systems
- * memory as present during initialization.
- */
-static void __init memblocks_present(void)
+/* Initialize memory section metadata for all system memory. */
+void __init sparse_sections_init(void)
 {
 	unsigned long start, end;
 	int i, nid;
@@ -332,8 +328,6 @@ void __init sparse_init(void)
 	unsigned long pnum_end, pnum_begin, map_count = 1;
 	int nid_begin;
 
-	memblocks_present();
-
 	if (compound_info_has_mask()) {
 		VM_WARN_ON_ONCE(!IS_ALIGNED((unsigned long) pfn_to_page(0),
 				    MAX_FOLIO_VMEMMAP_ALIGN));
diff --git a/mm/sparse.h b/mm/sparse.h
index 4377b097dc8d..b2dc7362af4f 100644
--- a/mm/sparse.h
+++ b/mm/sparse.h
@@ -14,6 +14,7 @@
  * mm/sparse.c
  */
 #ifdef CONFIG_SPARSEMEM
+void sparse_sections_init(void);
 void sparse_init(void);
 int sparse_index_init(unsigned long section_nr, int nid);
 
@@ -48,6 +49,7 @@ static inline void __section_mark_present(struct mem_section *ms,
 	ms->section_mem_map |= SECTION_MARKED_PRESENT;
 }
 #else
+static inline void sparse_sections_init(void) {}
 static inline void sparse_init(void) {}
 #endif /* CONFIG_SPARSEMEM */
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 10/17] mm/hugetlb: switch HugeTLB to section-based vmemmap optimization
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (8 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 09/17] mm/sparse: initialize memory sections earlier Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 11/17] mm/sparse-vmemmap: remove SPARSEMEM_VMEMMAP_PREINIT support Muchun Song
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

HugeTLB bootmem vmemmap optimization still carries its own early setup
path, including pre-populating optimized mappings before the generic
sparse-vmemmap code runs.

Now that the section-based vmemmap optimization can derive HugeTLB
vmemmap deduplication from section metadata, HugeTLB only needs to mark
the bootmem huge page range with the appropriate order. The generic
sparse-vmemmap population path can then allocate and map the shared tail
vmemmap pages without any HugeTLB-specific early population code.

Do that by setting the section order when a bootmem huge page is
allocated and dropping the dedicated pre-HVO helpers and related
special-casing.

This removes duplicate early setup logic and switches HugeTLB to the
section-based vmemmap optimization path.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
v2:
- Collect Acked-by from Mike Rapoport
---
 include/linux/hugetlb.h |  1 -
 include/linux/mm.h      |  3 --
 mm/hugetlb.c            | 30 ++------------
 mm/hugetlb_vmemmap.c    | 90 +++--------------------------------------
 mm/hugetlb_vmemmap.h    | 14 +++----
 mm/sparse-vmemmap.c     | 31 --------------
 mm/sparse.h             | 27 +++++++++++++
 7 files changed, 42 insertions(+), 154 deletions(-)

diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 16c4c4caa126..fe28f98e1b22 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -171,7 +171,6 @@ struct address_space *hugetlb_folio_mapping_lock_write(struct folio *folio);
 
 extern int movable_gigantic_pages __read_mostly;
 extern int sysctl_hugetlb_shm_group __read_mostly;
-extern struct list_head huge_boot_pages[MAX_NUMNODES];
 
 void hugetlb_bootmem_struct_page_init(void);
 void hugetlb_bootmem_alloc(void);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 550fb92957d1..1462669418b1 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -5041,9 +5041,6 @@ int vmemmap_populate_hugepages(unsigned long start, unsigned long end,
 			       int node, struct vmem_altmap *altmap);
 int vmemmap_populate(unsigned long start, unsigned long end, int node,
 		struct vmem_altmap *altmap);
-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);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index b8e1569bda15..efe0d639933e 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -52,6 +52,7 @@
 #include "hugetlb_cma.h"
 #include "hugetlb_internal.h"
 #include "mm_init.h"
+#include "sparse.h"
 #include <linux/page-isolation.h>
 
 int hugetlb_max_hstate __read_mostly;
@@ -59,7 +60,7 @@ unsigned int default_hstate_idx;
 struct hstate hstates[HUGE_MAX_HSTATE];
 
 __initdata nodemask_t hugetlb_bootmem_nodes;
-__initdata struct list_head huge_boot_pages[MAX_NUMNODES];
+static struct list_head huge_boot_pages[MAX_NUMNODES] __initdata;
 
 /*
  * Due to ordering constraints across the init code for various
@@ -3151,6 +3152,7 @@ static bool __init alloc_bootmem_huge_page(struct hstate *h, int nid)
 	} else {
 		list_add_tail(&m->list, &huge_boot_pages[nid]);
 		m->flags |= HUGE_BOOTMEM_ZONES_VALID;
+		hugetlb_vmemmap_optimize_bootmem_page(m);
 		/*
 		 * Only initialize the head struct page in memmap_init_reserved_pages,
 		 * rest of the struct pages will be initialized by the HugeTLB
@@ -3311,6 +3313,7 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
 			 * this folio.
 			 */
 			folio_set_hugetlb_vmemmap_optimized(folio);
+		section_set_order_range(folio_pfn(folio), folio_nr_pages(folio), 0);
 
 		if (hugetlb_bootmem_page_earlycma(m))
 			folio_set_hugetlb_cma(folio);
@@ -3354,31 +3357,6 @@ void __init hugetlb_bootmem_struct_page_init(void)
 		.max_threads	= num_node_state(N_MEMORY),
 		.numa_aware	= true,
 	};
-#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
-	struct zone *zone;
-
-	for_each_zone(zone) {
-		for (int i = 0; i < VMEMMAP_OPTIMIZATION_NR_ORDERS; i++) {
-			struct page *tail, *p;
-			unsigned int order;
-
-			tail = zone->vmemmap_tails[i];
-			if (!tail)
-				continue;
-
-			order = i + VMEMMAP_OPTIMIZATION_MIN_ORDER;
-			p = page_to_virt(tail);
-			/*
-			 * prep_and_add_bootmem_folios() can access pageblock
-			 * flags on bootmem HugeTLB pages, so initialize the
-			 * shared tail struct pages here before bootmem folios
-			 * start using them.
-			 */
-			for (int j = 0; j < PAGE_SIZE / sizeof(struct page); j++)
-				init_compound_tail(p + j, NULL, order, zone);
-		}
-	}
-#endif
 
 	padata_do_multithreaded(&job);
 }
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index c48fcea076a5..e37ac4745add 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -18,8 +18,7 @@
 
 #include <asm/tlbflush.h>
 #include "hugetlb_vmemmap.h"
-#include "internal.h"
-#include "mm_init.h"
+#include "sparse.h"
 
 /**
  * struct vmemmap_remap_walk - walk vmemmap page table
@@ -706,95 +705,18 @@ void hugetlb_vmemmap_optimize_bootmem_folios(struct hstate *h, struct list_head
 	__hugetlb_vmemmap_optimize_folios(h, folio_list, true);
 }
 
-#ifdef CONFIG_SPARSEMEM_VMEMMAP_PREINIT
-
-/* Return true of a bootmem allocated HugeTLB page should be pre-HVO-ed */
-static bool vmemmap_should_optimize_bootmem_page(struct huge_bootmem_page *m)
+void __init hugetlb_vmemmap_optimize_bootmem_page(struct huge_bootmem_page *m)
 {
-	unsigned long section_size, psize, pmd_vmemmap_size;
-	phys_addr_t paddr;
-
-	if (!READ_ONCE(vmemmap_optimize_enabled))
-		return false;
-
-	if (!hugetlb_vmemmap_optimizable(m->hstate))
-		return false;
-
-	psize = huge_page_size(m->hstate);
-	paddr = virt_to_phys(m);
-
-	/*
-	 * Pre-HVO only works if the bootmem huge page
-	 * is aligned to the section size.
-	 */
-	section_size = (1UL << PA_SECTION_SHIFT);
-	if (!IS_ALIGNED(paddr, section_size) ||
-	    !IS_ALIGNED(psize, section_size))
-		return false;
-
-	/*
-	 * The pre-HVO code does not deal with splitting PMDS,
-	 * so the bootmem page must be aligned to the number
-	 * of base pages that can be mapped with one vmemmap PMD.
-	 */
-	pmd_vmemmap_size = (PMD_SIZE / (sizeof(struct page))) << PAGE_SHIFT;
-	if (!IS_ALIGNED(paddr, pmd_vmemmap_size) ||
-	    !IS_ALIGNED(psize, pmd_vmemmap_size))
-		return false;
-
-	return true;
-}
-
-/*
- * Initialize memmap section for a gigantic page, HVO-style.
- */
-void __init hugetlb_vmemmap_init_early(int nid)
-{
-	unsigned long psize, paddr, section_size;
-	unsigned long ns, i, pnum, pfn, nr_pages;
-	unsigned long start, end;
-	struct huge_bootmem_page *m = NULL;
-	void *map;
+	struct hstate *h = m->hstate;
+	unsigned long pfn = PHYS_PFN(__pa(m));
 
 	if (!READ_ONCE(vmemmap_optimize_enabled))
 		return;
 
-	section_size = (1UL << PA_SECTION_SHIFT);
-
-	list_for_each_entry(m, &huge_boot_pages[nid], list) {
-		struct zone *zone;
-
-		if (!vmemmap_should_optimize_bootmem_page(m))
-			continue;
-
-		nr_pages = pages_per_huge_page(m->hstate);
-		psize = nr_pages << PAGE_SHIFT;
-		paddr = virt_to_phys(m);
-		pfn = PHYS_PFN(paddr);
-		map = pfn_to_page(pfn);
-		start = (unsigned long)map;
-		end = start + hugetlb_vmemmap_size(m->hstate);
-		zone = pfn_to_zone(pfn, nid);
-
-		if (vmemmap_populate_hvo(start, end, huge_page_order(m->hstate),
-					 zone, HUGETLB_VMEMMAP_RESERVE_SIZE))
-			panic("Failed to allocate memmap for HugeTLB page\n");
-		memmap_boot_pages_add(DIV_ROUND_UP(HUGETLB_VMEMMAP_RESERVE_SIZE, PAGE_SIZE));
-
-		pnum = pfn_to_section_nr(pfn);
-		ns = psize / section_size;
-
-		for (i = 0; i < ns; i++) {
-			sparse_init_early_section(nid, map, pnum,
-					SECTION_IS_VMEMMAP_PREINIT);
-			map += section_map_size();
-			pnum++;
-		}
-
+	section_set_order_range(pfn, pages_per_huge_page(h), huge_page_order(h));
+	if (section_vmemmap_optimizable(__pfn_to_section(pfn)))
 		m->flags |= HUGE_BOOTMEM_HVO;
-	}
 }
-#endif
 
 static const struct ctl_table hugetlb_vmemmap_sysctls[] = {
 	{
diff --git a/mm/hugetlb_vmemmap.h b/mm/hugetlb_vmemmap.h
index 7ac49c52457d..20eb03df542a 100644
--- a/mm/hugetlb_vmemmap.h
+++ b/mm/hugetlb_vmemmap.h
@@ -9,8 +9,7 @@
 #ifndef _LINUX_HUGETLB_VMEMMAP_H
 #define _LINUX_HUGETLB_VMEMMAP_H
 #include <linux/hugetlb.h>
-#include <linux/io.h>
-#include <linux/memblock.h>
+#include "internal.h"
 
 /*
  * Reserve one vmemmap page, all vmemmap addresses are mapped to it. See
@@ -27,10 +26,7 @@ long hugetlb_vmemmap_restore_folios(const struct hstate *h,
 void hugetlb_vmemmap_optimize_folio(const struct hstate *h, struct folio *folio);
 void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_list);
 void hugetlb_vmemmap_optimize_bootmem_folios(struct hstate *h, struct list_head *folio_list);
-#ifdef CONFIG_SPARSEMEM_VMEMMAP_PREINIT
-void hugetlb_vmemmap_init_early(int nid);
-#endif
-
+void hugetlb_vmemmap_optimize_bootmem_page(struct huge_bootmem_page *m);
 
 static inline unsigned int hugetlb_vmemmap_size(const struct hstate *h)
 {
@@ -76,13 +72,13 @@ static inline void hugetlb_vmemmap_optimize_bootmem_folios(struct hstate *h,
 {
 }
 
-static inline void hugetlb_vmemmap_init_early(int nid)
+static inline unsigned int hugetlb_vmemmap_optimizable_size(const struct hstate *h)
 {
+	return 0;
 }
 
-static inline unsigned int hugetlb_vmemmap_optimizable_size(const struct hstate *h)
+static inline void hugetlb_vmemmap_optimize_bootmem_page(struct huge_bootmem_page *m)
 {
-	return 0;
 }
 #endif /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */
 
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index e818b4109777..531927bc9380 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -32,8 +32,6 @@
 #include <asm/dma.h>
 #include <asm/tlbflush.h>
 
-#include "hugetlb_vmemmap.h"
-
 /*
  * Flags for vmemmap_populate_range and friends.
  */
@@ -369,34 +367,6 @@ void vmemmap_wrprotect_hvo(unsigned long addr, unsigned long end,
 	}
 }
 
-#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
-int __meminit vmemmap_populate_hvo(unsigned long addr, unsigned long end,
-				       unsigned int order, struct zone *zone,
-				       unsigned long headsize)
-{
-	unsigned long maddr;
-	struct page *tail;
-	pte_t *pte;
-	int node = zone_to_nid(zone);
-
-	tail = vmemmap_get_tail(order, zone);
-	if (!tail)
-		return -ENOMEM;
-
-	for (maddr = addr; maddr < addr + headsize; maddr += PAGE_SIZE) {
-		pte = vmemmap_populate_address(maddr, node, NULL, -1, 0);
-		if (!pte)
-			return -ENOMEM;
-	}
-
-	/*
-	 * Reuse the last page struct page mapped above for the rest.
-	 */
-	return vmemmap_populate_range(maddr, end, node, NULL,
-				      page_to_pfn(tail), 0);
-}
-#endif
-
 void __weak __meminit vmemmap_set_pmd(pmd_t *pmd, void *p, int node,
 				      unsigned long addr, unsigned long next)
 {
@@ -599,7 +569,6 @@ struct page * __meminit __populate_section_memmap(unsigned long pfn,
  */
 void __init sparse_vmemmap_init_nid_early(int nid)
 {
-	hugetlb_vmemmap_init_early(nid);
 }
 #endif
 
diff --git a/mm/sparse.h b/mm/sparse.h
index b2dc7362af4f..b904cf3f0a2f 100644
--- a/mm/sparse.h
+++ b/mm/sparse.h
@@ -58,11 +58,38 @@ static inline unsigned int section_order(const struct mem_section *section)
 {
 	return section->order;
 }
+
+static inline void section_set_order(struct mem_section *section, unsigned int order)
+{
+	VM_WARN_ON(section_order(section) && order && section_order(section) != order);
+	section->order = order;
+}
+
+static inline void section_set_order_range(unsigned long pfn, unsigned long nr_pages,
+					   unsigned int order)
+{
+	unsigned long section_nr = pfn_to_section_nr(pfn);
+
+	if (!IS_ALIGNED(pfn | nr_pages, PAGES_PER_SECTION))
+		return;
+
+	for (unsigned long i = 0; i < nr_pages / PAGES_PER_SECTION; i++)
+		section_set_order(__nr_to_section(section_nr + i), order);
+}
 #else
 static inline unsigned int section_order(const struct mem_section *section)
 {
 	return 0;
 }
+
+static inline void section_set_order(struct mem_section *section, unsigned int order)
+{
+}
+
+static inline void section_set_order_range(unsigned long pfn, unsigned long nr_pages,
+					   unsigned int order)
+{
+}
 #endif
 
 static inline bool pfn_vmemmap_optimizable(unsigned long pfn)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 11/17] mm/sparse-vmemmap: remove SPARSEMEM_VMEMMAP_PREINIT support
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (9 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 10/17] mm/hugetlb: switch HugeTLB to section-based vmemmap optimization Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 12/17] mm/sparse: inline usemap allocation into sparse_init_nid() Muchun Song
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

SPARSEMEM_VMEMMAP_PREINIT existed only to support HugeTLB's early
vmemmap optimization setup.

Now that HugeTLB bootmem vmemmap optimization uses the common
section-based sparse-vmemmap path, the sparse initialization code no
longer needs a separate pre-initialization mechanism for vmemmap
population.

Remove the related Kconfig symbols, section flag, and empty early hook,
so present sections always go through the normal sparse setup path.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
v2:
- Collect Acked-by from Mike Rapoport
---
 arch/x86/Kconfig       |  1 -
 fs/Kconfig             |  1 -
 include/linux/mmzone.h | 25 -------------------------
 mm/Kconfig             |  5 -----
 mm/sparse-vmemmap.c    | 13 -------------
 mm/sparse.c            | 23 ++++++++---------------
 6 files changed, 8 insertions(+), 60 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 583b4cf4922a..7be1fab6f365 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -149,7 +149,6 @@ config X86
 	select ARCH_WANT_LD_ORPHAN_WARN
 	select ARCH_WANT_OPTIMIZE_DAX_VMEMMAP	if X86_64
 	select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP	if X86_64
-	select ARCH_WANT_HUGETLB_VMEMMAP_PREINIT if X86_64
 	select ARCH_WANTS_THP_SWAP		if X86_64
 	select ARCH_HAS_PARANOID_L1D_FLUSH
 	select ARCH_WANT_IRQS_OFF_ACTIVATE_MM
diff --git a/fs/Kconfig b/fs/Kconfig
index 64ff193fb42c..5af471024abc 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -278,7 +278,6 @@ config HUGETLB_PAGE_OPTIMIZE_VMEMMAP
 	def_bool HUGETLB_PAGE
 	depends on ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP
 	depends on SPARSEMEM_VMEMMAP
-	select SPARSEMEM_VMEMMAP_PREINIT if ARCH_WANT_HUGETLB_VMEMMAP_PREINIT
 
 config HUGETLB_PMD_PAGE_TABLE_SHARING
 	def_bool HUGETLB_PAGE
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 025ea69f57d8..4a4310552976 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -2085,9 +2085,6 @@ enum {
 	SECTION_IS_EARLY_BIT,
 #ifdef CONFIG_ZONE_DEVICE
 	SECTION_TAINT_ZONE_DEVICE_BIT,
-#endif
-#ifdef CONFIG_SPARSEMEM_VMEMMAP_PREINIT
-	SECTION_IS_VMEMMAP_PREINIT_BIT,
 #endif
 	SECTION_MAP_LAST_BIT,
 };
@@ -2099,9 +2096,6 @@ enum {
 #ifdef CONFIG_ZONE_DEVICE
 #define SECTION_TAINT_ZONE_DEVICE	BIT(SECTION_TAINT_ZONE_DEVICE_BIT)
 #endif
-#ifdef CONFIG_SPARSEMEM_VMEMMAP_PREINIT
-#define SECTION_IS_VMEMMAP_PREINIT	BIT(SECTION_IS_VMEMMAP_PREINIT_BIT)
-#endif
 #define SECTION_MAP_MASK		(~(BIT(SECTION_MAP_LAST_BIT) - 1))
 #define SECTION_NID_SHIFT		SECTION_MAP_LAST_BIT
 
@@ -2156,24 +2150,6 @@ static inline int online_device_section(const struct mem_section *section)
 }
 #endif
 
-#ifdef CONFIG_SPARSEMEM_VMEMMAP_PREINIT
-static inline int preinited_vmemmap_section(const struct mem_section *section)
-{
-	return (section &&
-		(section->section_mem_map & SECTION_IS_VMEMMAP_PREINIT));
-}
-
-void sparse_vmemmap_init_nid_early(int nid);
-#else
-static inline int preinited_vmemmap_section(const struct mem_section *section)
-{
-	return 0;
-}
-static inline void sparse_vmemmap_init_nid_early(int nid)
-{
-}
-#endif
-
 static inline int online_section_nr(unsigned long nr)
 {
 	return online_section(__nr_to_section(nr));
@@ -2377,7 +2353,6 @@ static inline unsigned long next_present_section_nr(unsigned long section_nr)
 #else
 struct mem_section;
 
-#define sparse_vmemmap_init_nid_early(_nid) do {} while (0)
 #define pfn_in_present_section pfn_valid
 static inline struct mem_section *__pfn_to_section(unsigned long pfn)
 {
diff --git a/mm/Kconfig b/mm/Kconfig
index 3cec14092b28..2885fba13ebe 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -459,8 +459,6 @@ config SPARSEMEM_VMEMMAP
 	  pfn_to_page and page_to_pfn operations.  This is the most
 	  efficient option when sufficient kernel resources are available.
 
-config SPARSEMEM_VMEMMAP_PREINIT
-	bool
 #
 # Select this config option from the architecture Kconfig, if it is preferred
 # to enable the feature of HugeTLB/dev_dax vmemmap optimization.
@@ -471,9 +469,6 @@ config ARCH_WANT_OPTIMIZE_DAX_VMEMMAP
 config ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP
 	bool
 
-config ARCH_WANT_HUGETLB_VMEMMAP_PREINIT
-	bool
-
 config HAVE_MEMBLOCK_PHYS_MAP
 	bool
 
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 531927bc9380..3cfcd490dcba 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -559,19 +559,6 @@ struct page * __meminit __populate_section_memmap(unsigned long pfn,
 	return pfn_to_page(pfn);
 }
 
-#ifdef CONFIG_SPARSEMEM_VMEMMAP_PREINIT
-/*
- * This is called just before initializing sections for a NUMA node.
- * Any special initialization that needs to be done before the
- * generic initialization can be done from here. Sections that
- * are initialized in hooks called from here will be skipped by
- * the generic initialization.
- */
-void __init sparse_vmemmap_init_nid_early(int nid)
-{
-}
-#endif
-
 static void subsection_mask_set(unsigned long *map, unsigned long pfn,
 		unsigned long nr_pages)
 {
diff --git a/mm/sparse.c b/mm/sparse.c
index d4641aa70497..f0d9b7263bb2 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -294,27 +294,20 @@ static void __init sparse_init_nid(int nid, unsigned long pnum_begin,
 	if (sparse_usage_init(nid, map_count))
 		panic("Failed to allocate usemap for node %d\n", nid);
 
-	sparse_vmemmap_init_nid_early(nid);
-
 	for_each_present_section_nr(pnum_begin, pnum) {
-		struct mem_section *ms;
 		unsigned long pfn = section_nr_to_pfn(pnum);
+		struct page *map;
 
 		if (pnum >= pnum_end)
 			break;
 
-		ms = __nr_to_section(pnum);
-		if (!preinited_vmemmap_section(ms)) {
-			struct page *map;
-
-			map = __populate_section_memmap(pfn, PAGES_PER_SECTION,
-							nid, NULL, NULL);
-			if (!map)
-				panic("Failed to allocate memmap for section %lu\n", pnum);
-			memmap_boot_pages_add(section_nr_vmemmap_pages(pfn, PAGES_PER_SECTION,
-								       NULL, NULL));
-			sparse_init_early_section(nid, map, pnum, 0);
-		}
+		map = __populate_section_memmap(pfn, PAGES_PER_SECTION,
+						nid, NULL, NULL);
+		if (!map)
+			panic("Failed to allocate memmap for section %lu\n", pnum);
+		memmap_boot_pages_add(section_nr_vmemmap_pages(pfn, PAGES_PER_SECTION,
+							       NULL, NULL));
+		sparse_init_early_section(nid, map, pnum, 0);
 	}
 	sparse_usage_fini();
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 12/17] mm/sparse: inline usemap allocation into sparse_init_nid()
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (10 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 11/17] mm/sparse-vmemmap: remove SPARSEMEM_VMEMMAP_PREINIT support Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 13/17] mm/sparse: remove section_map_size() Muchun Song
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

After removing SPARSEMEM_VMEMMAP_PREINIT, sparse_init_nid() no longer
needs the transient sparse_usagebuf state and its helper wrappers.

Allocate the usemap buffer directly in sparse_init_nid(), pass it to
sparse_init_one_section(), and drop sparse_usage_init(),
sparse_usage_fini(), and sparse_init_early_section().

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
v2:
- Collect Acked-by from Mike Rapoport
---
 include/linux/mmzone.h |  3 ---
 mm/sparse.c            | 46 +++++++-----------------------------------
 2 files changed, 7 insertions(+), 42 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 4a4310552976..2e9e5369f599 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -2213,9 +2213,6 @@ static inline bool pfn_section_first_valid(struct mem_section *ms, unsigned long
 }
 #endif
 
-void sparse_init_early_section(int nid, struct page *map, unsigned long pnum,
-			       unsigned long flags);
-
 #ifndef CONFIG_HAVE_ARCH_PFN_VALID
 /**
  * pfn_valid - check if there is a valid memory map entry for a PFN
diff --git a/mm/sparse.c b/mm/sparse.c
index f0d9b7263bb2..743112ac84b1 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -245,42 +245,6 @@ void __weak __meminit vmemmap_populate_print_last(void)
 {
 }
 
-static void *sparse_usagebuf __meminitdata;
-static void *sparse_usagebuf_end __meminitdata;
-
-/*
- * Helper function that is used for generic section initialization, and
- * can also be used by any hooks added above.
- */
-void __init sparse_init_early_section(int nid, struct page *map,
-				      unsigned long pnum, unsigned long flags)
-{
-	BUG_ON(!sparse_usagebuf || sparse_usagebuf >= sparse_usagebuf_end);
-	sparse_init_one_section(__nr_to_section(pnum), pnum, map,
-			sparse_usagebuf, SECTION_IS_EARLY | flags);
-	sparse_usagebuf = (void *)sparse_usagebuf + mem_section_usage_size();
-}
-
-static int __init sparse_usage_init(int nid, unsigned long map_count)
-{
-	unsigned long size;
-
-	size = mem_section_usage_size() * map_count;
-	sparse_usagebuf = memblock_alloc_node(size, SMP_CACHE_BYTES, nid);
-	if (!sparse_usagebuf) {
-		sparse_usagebuf_end = NULL;
-		return -ENOMEM;
-	}
-
-	sparse_usagebuf_end = sparse_usagebuf + size;
-	return 0;
-}
-
-static void __init sparse_usage_fini(void)
-{
-	sparse_usagebuf = sparse_usagebuf_end = NULL;
-}
-
 /*
  * Initialize sparse on a specific node. The node spans [pnum_begin, pnum_end)
  * And number of present sections in this node is map_count.
@@ -290,8 +254,11 @@ static void __init sparse_init_nid(int nid, unsigned long pnum_begin,
 				   unsigned long map_count)
 {
 	unsigned long pnum;
+	struct mem_section_usage *usage;
 
-	if (sparse_usage_init(nid, map_count))
+	usage = memblock_alloc_node(map_count * mem_section_usage_size(),
+				    SMP_CACHE_BYTES, nid);
+	if (!usage)
 		panic("Failed to allocate usemap for node %d\n", nid);
 
 	for_each_present_section_nr(pnum_begin, pnum) {
@@ -307,9 +274,10 @@ static void __init sparse_init_nid(int nid, unsigned long pnum_begin,
 			panic("Failed to allocate memmap for section %lu\n", pnum);
 		memmap_boot_pages_add(section_nr_vmemmap_pages(pfn, PAGES_PER_SECTION,
 							       NULL, NULL));
-		sparse_init_early_section(nid, map, pnum, 0);
+		sparse_init_one_section(__nr_to_section(pnum), pnum, map, usage,
+					SECTION_IS_EARLY);
+		usage = (void *)usage + mem_section_usage_size();
 	}
-	sparse_usage_fini();
 }
 
 /*
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 13/17] mm/sparse: remove section_map_size()
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (11 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 12/17] mm/sparse: inline usemap allocation into sparse_init_nid() Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 14/17] mm/hugetlb: remove HUGE_BOOTMEM_HVO Muchun Song
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

section_map_size() no longer provides any shared logic.

After the sparse-vmemmap changes, its only remaining user is the
!CONFIG_SPARSEMEM_VMEMMAP path in __populate_section_memmap(), which can
compute the size inline with PAGE_ALIGN(sizeof(struct page) *
PAGES_PER_SECTION).

Remove section_map_size() and inline the remaining calculation.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
v2:
- Collect Acked-by from Mike Rapoport
---
 include/linux/mm.h |  1 -
 mm/sparse.c        | 15 ++-------------
 2 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 1462669418b1..624c52da88c4 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -5022,7 +5022,6 @@ static inline void print_vma_addr(char *prefix, unsigned long rip)
 }
 #endif
 
-unsigned long section_map_size(void);
 struct page * __populate_section_memmap(unsigned long pfn,
 		unsigned long nr_pages, int nid, struct vmem_altmap *altmap,
 		struct dev_pagemap *pgmap);
diff --git a/mm/sparse.c b/mm/sparse.c
index 743112ac84b1..785c0be80aed 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -219,23 +219,12 @@ size_t mem_section_usage_size(void)
 	return sizeof(struct mem_section_usage) + usemap_size();
 }
 
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-unsigned long __init section_map_size(void)
-{
-	return ALIGN(sizeof(struct page) * PAGES_PER_SECTION, PMD_SIZE);
-}
-
-#else
-unsigned long __init section_map_size(void)
-{
-	return PAGE_ALIGN(sizeof(struct page) * PAGES_PER_SECTION);
-}
-
+#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 size = section_map_size();
+	unsigned long size = PAGE_ALIGN(sizeof(struct page) * PAGES_PER_SECTION);
 
 	return memmap_alloc(size, size, __pa(MAX_DMA_ADDRESS), nid, false);
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 14/17] mm/hugetlb: remove HUGE_BOOTMEM_HVO
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (12 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 13/17] mm/sparse: remove section_map_size() Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 15/17] mm/hugetlb: remove HUGE_BOOTMEM_CMA Muchun Song
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

The HUGE_BOOTMEM_HVO flag tracked whether a bootmem huge page had
already gone through the old early vmemmap optimization path.

Now that HugeTLB uses section-based vmemmap optimization, that state is
already reflected in the section order.

Remove HUGE_BOOTMEM_HVO and its helper, and use the section state
directly when deciding whether to mark a folio as vmemmap-optimized.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
v2:
- Collect Acked-by from Mike Rapoport
---
 include/linux/hugetlb.h |  5 ++---
 mm/hugetlb.c            | 12 +-----------
 mm/hugetlb_vmemmap.c    |  2 --
 3 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index fe28f98e1b22..3559041a5a57 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -675,9 +675,8 @@ struct hstate {
 	char name[HSTATE_NAME_LEN];
 };
 
-#define HUGE_BOOTMEM_HVO		0x0001
-#define HUGE_BOOTMEM_ZONES_VALID	0x0002
-#define HUGE_BOOTMEM_CMA		0x0004
+#define HUGE_BOOTMEM_ZONES_VALID	BIT(0)
+#define HUGE_BOOTMEM_CMA		BIT(1)
 
 int isolate_or_dissolve_huge_folio(struct folio *folio, struct list_head *list);
 int replace_free_hugepage_folios(unsigned long start_pfn, unsigned long end_pfn);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index efe0d639933e..c03c5c805cd7 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3210,11 +3210,6 @@ static void __init hugetlb_folio_init_vmemmap(struct folio *folio,
 	prep_compound_head(&folio->page, huge_page_order(h));
 }
 
-static bool __init hugetlb_bootmem_page_prehvo(struct huge_bootmem_page *m)
-{
-	return m->flags & HUGE_BOOTMEM_HVO;
-}
-
 static bool __init hugetlb_bootmem_page_earlycma(struct huge_bootmem_page *m)
 {
 	return m->flags & HUGE_BOOTMEM_CMA;
@@ -3306,12 +3301,7 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
 					   HUGETLB_VMEMMAP_RESERVE_PAGES);
 		init_new_hugetlb_folio(folio);
 
-		if (hugetlb_bootmem_page_prehvo(m))
-			/*
-			 * If pre-HVO was done, just set the
-			 * flag, the HVO code will then skip
-			 * this folio.
-			 */
+		if (section_vmemmap_optimizable(__pfn_to_section(folio_pfn(folio))))
 			folio_set_hugetlb_vmemmap_optimized(folio);
 		section_set_order_range(folio_pfn(folio), folio_nr_pages(folio), 0);
 
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index e37ac4745add..a25adc474351 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -714,8 +714,6 @@ void __init hugetlb_vmemmap_optimize_bootmem_page(struct huge_bootmem_page *m)
 		return;
 
 	section_set_order_range(pfn, pages_per_huge_page(h), huge_page_order(h));
-	if (section_vmemmap_optimizable(__pfn_to_section(pfn)))
-		m->flags |= HUGE_BOOTMEM_HVO;
 }
 
 static const struct ctl_table hugetlb_vmemmap_sysctls[] = {
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 15/17] mm/hugetlb: remove HUGE_BOOTMEM_CMA
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (13 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 14/17] mm/hugetlb: remove HUGE_BOOTMEM_HVO Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 16/17] mm/hugetlb: localize struct huge_bootmem_page Muchun Song
  2026-07-20  9:31 ` [PATCH v2 17/17] mm/hugetlb: localize HUGE_BOOTMEM_ZONES_VALID Muchun Song
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

Track early CMA hugetlb pages from the hstate instead of storing a
redundant bootmem flag. This removes the unused helper and keeps the
bootmem metadata minimal.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
v2:
- Collect Acked-by from Mike Rapoport
---
 include/linux/hugetlb.h |  1 -
 mm/hugetlb.c            | 14 ++++----------
 2 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 3559041a5a57..255a258f11d1 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -676,7 +676,6 @@ struct hstate {
 };
 
 #define HUGE_BOOTMEM_ZONES_VALID	BIT(0)
-#define HUGE_BOOTMEM_CMA		BIT(1)
 
 int isolate_or_dissolve_huge_folio(struct folio *folio, struct list_head *list);
 int replace_free_hugepage_folios(unsigned long start_pfn, unsigned long end_pfn);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index c03c5c805cd7..d0c726845fb9 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3134,7 +3134,7 @@ static bool __init alloc_bootmem_huge_page(struct hstate *h, int nid)
 	 */
 	INIT_LIST_HEAD(&m->list);
 	m->hstate = h;
-	m->flags = hugetlb_early_cma(h) ? HUGE_BOOTMEM_CMA : 0;
+	m->flags = 0;
 
 	/* CMA pages: zone-crossing is validated in hugetlb_cma_reserve(). */
 	if (!hugetlb_early_cma(h) &&
@@ -3210,11 +3210,6 @@ static void __init hugetlb_folio_init_vmemmap(struct folio *folio,
 	prep_compound_head(&folio->page, huge_page_order(h));
 }
 
-static bool __init hugetlb_bootmem_page_earlycma(struct huge_bootmem_page *m)
-{
-	return m->flags & HUGE_BOOTMEM_CMA;
-}
-
 /*
  * memblock-allocated pageblocks might not have the migrate type set
  * if marked with the 'noinit' flag. Set it to the default (MIGRATE_MOVABLE)
@@ -3305,9 +3300,6 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
 			folio_set_hugetlb_vmemmap_optimized(folio);
 		section_set_order_range(folio_pfn(folio), folio_nr_pages(folio), 0);
 
-		if (hugetlb_bootmem_page_earlycma(m))
-			folio_set_hugetlb_cma(folio);
-
 		list_add(&folio->lru, &folio_list);
 
 		/*
@@ -3318,7 +3310,9 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
 		 * For CMA pages, this is done in init_cma_pageblock
 		 * (via hugetlb_bootmem_init_migratetype), so skip it here.
 		 */
-		if (!folio_test_hugetlb_cma(folio))
+		if (hugetlb_early_cma(h))
+			folio_set_hugetlb_cma(folio);
+		else
 			adjust_managed_page_count(page, pages_per_huge_page(h));
 		cond_resched();
 	}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 16/17] mm/hugetlb: localize struct huge_bootmem_page
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (14 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 15/17] mm/hugetlb: remove HUGE_BOOTMEM_CMA Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  2026-07-20  9:31 ` [PATCH v2 17/17] mm/hugetlb: localize HUGE_BOOTMEM_ZONES_VALID Muchun Song
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

struct huge_bootmem_page is only used by hugetlb boot-time allocation
code, but its definition currently lives in mm/internal.h because
hugetlb_vmemmap_optimize_bootmem_page() takes it as an argument. This
exposes a hugetlb-specific internal type more broadly than needed.

Change hugetlb_vmemmap_optimize_bootmem_page() to take the information it
actually needs. With that interface, mm/hugetlb_vmemmap.h no longer needs
to include mm/internal.h, and struct huge_bootmem_page can move into
mm/hugetlb.c.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
v2:
- Pass PFN and order to hugetlb_vmemmap_optimize_bootmem_page() instead
  of struct hstate and bootmem metadata to simplify the code further
- Collect Acked-by from Mike Rapoport
---
 mm/hugetlb.c         | 8 +++++++-
 mm/hugetlb_vmemmap.c | 8 +++-----
 mm/hugetlb_vmemmap.h | 5 ++---
 mm/internal.h        | 7 -------
 4 files changed, 12 insertions(+), 16 deletions(-)

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index d0c726845fb9..364fdf254e78 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -55,6 +55,12 @@
 #include "sparse.h"
 #include <linux/page-isolation.h>
 
+struct huge_bootmem_page {
+	struct list_head list;
+	struct hstate *hstate;
+	unsigned long flags;
+};
+
 int hugetlb_max_hstate __read_mostly;
 unsigned int default_hstate_idx;
 struct hstate hstates[HUGE_MAX_HSTATE];
@@ -3152,7 +3158,7 @@ static bool __init alloc_bootmem_huge_page(struct hstate *h, int nid)
 	} else {
 		list_add_tail(&m->list, &huge_boot_pages[nid]);
 		m->flags |= HUGE_BOOTMEM_ZONES_VALID;
-		hugetlb_vmemmap_optimize_bootmem_page(m);
+		hugetlb_vmemmap_optimize_bootmem_page(pfn, huge_page_order(h));
 		/*
 		 * Only initialize the head struct page in memmap_init_reserved_pages,
 		 * rest of the struct pages will be initialized by the HugeTLB
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index a25adc474351..eb339c4a71f4 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -19,6 +19,7 @@
 #include <asm/tlbflush.h>
 #include "hugetlb_vmemmap.h"
 #include "sparse.h"
+#include "internal.h"
 
 /**
  * struct vmemmap_remap_walk - walk vmemmap page table
@@ -705,15 +706,12 @@ void hugetlb_vmemmap_optimize_bootmem_folios(struct hstate *h, struct list_head
 	__hugetlb_vmemmap_optimize_folios(h, folio_list, true);
 }
 
-void __init hugetlb_vmemmap_optimize_bootmem_page(struct huge_bootmem_page *m)
+void __init hugetlb_vmemmap_optimize_bootmem_page(unsigned long pfn, unsigned int order)
 {
-	struct hstate *h = m->hstate;
-	unsigned long pfn = PHYS_PFN(__pa(m));
-
 	if (!READ_ONCE(vmemmap_optimize_enabled))
 		return;
 
-	section_set_order_range(pfn, pages_per_huge_page(h), huge_page_order(h));
+	section_set_order_range(pfn, 1UL << order, order);
 }
 
 static const struct ctl_table hugetlb_vmemmap_sysctls[] = {
diff --git a/mm/hugetlb_vmemmap.h b/mm/hugetlb_vmemmap.h
index 20eb03df542a..464192e32dec 100644
--- a/mm/hugetlb_vmemmap.h
+++ b/mm/hugetlb_vmemmap.h
@@ -9,7 +9,6 @@
 #ifndef _LINUX_HUGETLB_VMEMMAP_H
 #define _LINUX_HUGETLB_VMEMMAP_H
 #include <linux/hugetlb.h>
-#include "internal.h"
 
 /*
  * Reserve one vmemmap page, all vmemmap addresses are mapped to it. See
@@ -26,7 +25,7 @@ long hugetlb_vmemmap_restore_folios(const struct hstate *h,
 void hugetlb_vmemmap_optimize_folio(const struct hstate *h, struct folio *folio);
 void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_list);
 void hugetlb_vmemmap_optimize_bootmem_folios(struct hstate *h, struct list_head *folio_list);
-void hugetlb_vmemmap_optimize_bootmem_page(struct huge_bootmem_page *m);
+void hugetlb_vmemmap_optimize_bootmem_page(unsigned long pfn, unsigned int order);
 
 static inline unsigned int hugetlb_vmemmap_size(const struct hstate *h)
 {
@@ -77,7 +76,7 @@ static inline unsigned int hugetlb_vmemmap_optimizable_size(const struct hstate
 	return 0;
 }
 
-static inline void hugetlb_vmemmap_optimize_bootmem_page(struct huge_bootmem_page *m)
+static inline void hugetlb_vmemmap_optimize_bootmem_page(unsigned long pfn, unsigned int order)
 {
 }
 #endif /* CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP */
diff --git a/mm/internal.h b/mm/internal.h
index f26423de4ca2..9d3196f9d19e 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -23,13 +23,6 @@
 #include "vma.h"
 
 struct folio_batch;
-struct hstate;
-
-struct huge_bootmem_page {
-	struct list_head list;
-	struct hstate *hstate;
-	unsigned long flags;
-};
 
 /* mm/workingset.c */
 bool workingset_test_recent(void *shadow, bool file, bool *workingset,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v2 17/17] mm/hugetlb: localize HUGE_BOOTMEM_ZONES_VALID
  2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
                   ` (15 preceding siblings ...)
  2026-07-20  9:31 ` [PATCH v2 16/17] mm/hugetlb: localize struct huge_bootmem_page Muchun Song
@ 2026-07-20  9:31 ` Muchun Song
  16 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20  9:31 UTC (permalink / raw)
  To: Andrew Morton, Oscar Salvador, David Hildenbrand
  Cc: Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel, Muchun Song, Muchun Song

HUGE_BOOTMEM_ZONES_VALID is only used by the huge_bootmem_page flag
handling in mm/hugetlb.c. Keep the definition next to that private data
structure instead of exposing it through the public hugetlb header.

No functional change is intended.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
v2:
- Collect Acked-by from Mike Rapoport
---
 include/linux/hugetlb.h | 2 --
 mm/hugetlb.c            | 2 ++
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 255a258f11d1..900c95e346b2 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -675,8 +675,6 @@ struct hstate {
 	char name[HSTATE_NAME_LEN];
 };
 
-#define HUGE_BOOTMEM_ZONES_VALID	BIT(0)
-
 int isolate_or_dissolve_huge_folio(struct folio *folio, struct list_head *list);
 int replace_free_hugepage_folios(unsigned long start_pfn, unsigned long end_pfn);
 void wait_for_freed_hugetlb_folios(void);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 364fdf254e78..47e3147ddc12 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -55,6 +55,8 @@
 #include "sparse.h"
 #include <linux/page-isolation.h>
 
+#define HUGE_BOOTMEM_ZONES_VALID	BIT(0)
+
 struct huge_bootmem_page {
 	struct list_head list;
 	struct hstate *hstate;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone()
  2026-07-20  9:31 ` [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone() Muchun Song
@ 2026-07-20 11:04   ` Muchun Song
  2026-07-20 11:05   ` Muchun Song
  2026-07-30 10:57   ` Mike Rapoport
  2 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20 11:04 UTC (permalink / raw)
  To: Muchun Song
  Cc: Andrew Morton, Oscar Salvador, David Hildenbrand, Mike Rapoport,
	Vlastimil Babka, Lorenzo Stoakes, Michal Hocko, David Laight,
	linux-mm, linux-kernel



> On Jul 20, 2026, at 17:31, Muchun Song <songmuchun@bytedance.com> wrote:
> 
> pfn_to_zone() in hugetlb_vmemmap.c duplicates the zone lookup logic in
> __init_deferred_page().
> 
> Move it to mm_init.c, declare it in mm/internal.h, and reuse it from
> __init_deferred_page() and HugeTLB early vmemmap initialization instead
> of open-coding the zone walk there.

Sashiko said "the declaration is actually in mm/mm_init.h". That is indeed
an issue. When I was handling the rebase, I forgot to update the commit
message here due to code conflicts. I will fix this in the next version.

Thanks.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone()
  2026-07-20  9:31 ` [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone() Muchun Song
  2026-07-20 11:04   ` Muchun Song
@ 2026-07-20 11:05   ` Muchun Song
  2026-07-30 10:57   ` Mike Rapoport
  2 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20 11:05 UTC (permalink / raw)
  To: Muchun Song
  Cc: Andrew Morton, Oscar Salvador, David Hildenbrand, Mike Rapoport,
	Vlastimil Babka, Lorenzo Stoakes, Michal Hocko, David Laight,
	linux-mm, linux-kernel



> On Jul 20, 2026, at 17:31, Muchun Song <songmuchun@bytedance.com> wrote:
> 
> pfn_to_zone() in hugetlb_vmemmap.c duplicates the zone lookup logic in
> __init_deferred_page().
> 
> Move it to mm_init.c, declare it in mm/internal.h, and reuse it from
> __init_deferred_page() and HugeTLB early vmemmap initialization instead
> of open-coding the zone walk there.

Sashiko said "The commit message states that pfn_to_zone() is declared
in mm/internal.h, but the patch actually adds the declaration to mm/mm_init.h
instead". That is indeed an issue. When I was handling the rebase, I forgot
to update the commit message here due to code conflicts. I will fix this in
the next version.

Thanks.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 08/17] mm/sparse-vmemmap: support section-based vmemmap optimization
  2026-07-20  9:31 ` [PATCH v2 08/17] mm/sparse-vmemmap: support section-based vmemmap optimization Muchun Song
@ 2026-07-20 11:10   ` Muchun Song
  0 siblings, 0 replies; 27+ messages in thread
From: Muchun Song @ 2026-07-20 11:10 UTC (permalink / raw)
  To: Muchun Song
  Cc: Andrew Morton, Oscar Salvador, David Hildenbrand, Mike Rapoport,
	Vlastimil Babka, Lorenzo Stoakes, Michal Hocko, David Laight,
	linux-mm, linux-kernel



> On Jul 20, 2026, at 17:31, Muchun Song <songmuchun@bytedance.com> wrote:
> 
> Teach sparse-vmemmap population code to use the compound page order
> when deciding whether a vmemmap page can be optimized.
> 
> With this information, the common sparse-vmemmap population path can
> allocate or reuse shared tail vmemmap pages directly instead of relying
> on HugeTLB-specific handling.
> 
> This centralizes vmemmap optimization logic in the sparse-vmemmap code,
> based on section metadata, and prepares for sharing the same mechanism
> across different users of vmemmap optimization, including HugeTLB and
> DAX.
> 
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>

Sashiko reported four issues [1] regarding memory hotplug, but the entire
series doesn't actually support applying HVO via memory hotplug. Therefore,
all four of these issues are false positives.

[1] https://sashiko.dev/#/patchset/20260720093127.540540-1-songmuchun%40bytedance.com?part=8

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages
  2026-07-20  9:31 ` [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages Muchun Song
@ 2026-07-26  4:30   ` Muchun Song
  2026-07-30 10:48     ` Mike Rapoport
  0 siblings, 1 reply; 27+ messages in thread
From: Muchun Song @ 2026-07-26  4:30 UTC (permalink / raw)
  To: Muchun Song
  Cc: Andrew Morton, Oscar Salvador, David Hildenbrand, Mike Rapoport,
	Vlastimil Babka, Lorenzo Stoakes, Michal Hocko, David Laight,
	linux-mm, linux-kernel



> On Jul 20, 2026, at 17:31, Muchun Song <songmuchun@bytedance.com> wrote:
> 
> memmap_init_range() initializes every struct page in the target range.
> For compound pages with vmemmap optimization, the tail struct pages are
> backed by a shared vmemmap page.
> 
> Initializing those tail struct pages would overwrite the shared
> vmemmap page contents, requiring users such as HugeTLB to restore the
> metadata afterwards.
> 
> Track the compound order for HVO-backed sections and use that metadata
> to detect struct pages that fall into the shared tail vmemmap range.
> Skip those shared tail pages in memmap_init_range(), then initialize
> pageblock migratetypes for the processed range with a helper after the
> per-page initialization loop.
> 
> The !SPARSEMEM __pfn_to_section() stub is needed only for the build:
> memmap_init_range() references __pfn_to_section() after checking
> pfn_vmemmap_optimizable(), and !SPARSEMEM builds still have to compile
> that code even though pfn_vmemmap_optimizable() folds to false.
> 
> This is a preparatory change for consolidating handling across users of
> vmemmap optimization, and it also avoids redundant initialization of
> shared tail vmemmap pages during early boot.
> 
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> ---
> v2:
> - Fold section order tracking into the first user instead of keeping a
>  standalone API-only patch (suggested by Mike Rapoport)
> - Rename page_vmemmap_optimizable() to pfn_vmemmap_optimizable() and
>  pass a PFN directly (suggested by Mike Rapoport)
> - Initialize pageblock migratetypes from a helper after the per-page
>  loop (suggested by Mike Rapoport)
> - Use a 1G PFN chunk for cond_resched() in the pageblock helper
>  (suggested by Mike Rapoport)
> - Guard section_order() with CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP so
>  it returns 0 when HVO is disabled and lets the compiler optimize the
>  code as much as possible (suggested by Mike Rapoport)
> - Explain why the !SPARSEMEM __pfn_to_section() stub belongs here
>  (suggested by Mike Rapoport)
> ---
> include/linux/mmzone.h | 14 ++++++++++++++
> mm/mm_init.c           | 33 +++++++++++++++++----------------
> mm/sparse.h            | 23 +++++++++++++++++++++++
> 3 files changed, 54 insertions(+), 16 deletions(-)
> 
> diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
> index 82b0155d886f..2a32101d55e6 100644
> --- a/include/linux/mmzone.h
> +++ b/include/linux/mmzone.h
> @@ -2011,6 +2011,14 @@ struct mem_section {
> unsigned long section_mem_map;
> 
> struct mem_section_usage *usage;
> +#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
> + 	/*
> +	 * Normally, sections hold regular (order-0) pages. However, for
> +	 * sections with HVO enabled, this tracks the compound page order
> +	 * to enable deduplication of redundant vmemmap pages.
> +	 */
> + 	unsigned int order;
> +#endif
> #ifdef CONFIG_PAGE_EXTENSION
> 	/*
> 	 * If SPARSEMEM, pgdat doesn't have page_ext pointer. We use
> @@ -2365,8 +2373,14 @@ static inline unsigned long next_present_section_nr(unsigned long section_nr)
> #endif
> 
> #else
> +struct mem_section;
> +
> #define sparse_vmemmap_init_nid_early(_nid) do {} while (0)
> #define pfn_in_present_section pfn_valid
> +static inline struct mem_section *__pfn_to_section(unsigned long pfn)
> +{
> + 	return NULL;
> +}

I'd like to propose an alternative implementation that doesn't require
exposing the mem_section. The idea is to add a new helper function,
pfn_to_section_order(), so that for non-sparse-memory configurations,
the mem_section concept stays hidden internally. I'd really appreciate
any thoughts or concerns — if everyone is comfortable with it, I can go
ahead and implement this in the next version.

Muchun,
Thanks.



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages
  2026-07-26  4:30   ` Muchun Song
@ 2026-07-30 10:48     ` Mike Rapoport
  2026-07-30 13:09       ` Muchun Song
  0 siblings, 1 reply; 27+ messages in thread
From: Mike Rapoport @ 2026-07-30 10:48 UTC (permalink / raw)
  To: Muchun Song
  Cc: Muchun Song, Andrew Morton, Oscar Salvador, David Hildenbrand,
	Mike Rapoport, Vlastimil Babka, Lorenzo Stoakes, Michal Hocko,
	David Laight, linux-mm, linux-kernel

Hi Muchun,

On 2026-07-26 12:30:17+08:00, Muchun Song wrote:
> > On Jul 20, 2026, at 17:31, Muchun Song <songmuchun@bytedance.com> wrote:
> > 
> > memmap_init_range() initializes every struct page in the target range.
> > For compound pages with vmemmap optimization, the tail struct pages are
> > backed by a shared vmemmap page.
> > 
> > Initializing those tail struct pages would overwrite the shared
> > vmemmap page contents, requiring users such as HugeTLB to restore the
> > metadata afterwards.
> > 
> > Track the compound order for HVO-backed sections and use that metadata
> > to detect struct pages that fall into the shared tail vmemmap range.
> > Skip those shared tail pages in memmap_init_range(), then initialize
> > pageblock migratetypes for the processed range with a helper after the
> > per-page initialization loop.
> > 
> > The !SPARSEMEM __pfn_to_section() stub is needed only for the build:
> > memmap_init_range() references __pfn_to_section() after checking
> > pfn_vmemmap_optimizable(), and !SPARSEMEM builds still have to compile
> > that code even though pfn_vmemmap_optimizable() folds to false.
> > 
> > This is a preparatory change for consolidating handling across users of
> > vmemmap optimization, and it also avoids redundant initialization of
> > shared tail vmemmap pages during early boot.
> > 
> > Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> > ---
> > v2:
> > - Fold section order tracking into the first user instead of keeping a
> >  standalone API-only patch (suggested by Mike Rapoport)
> > - Rename page_vmemmap_optimizable() to pfn_vmemmap_optimizable() and
> >  pass a PFN directly (suggested by Mike Rapoport)
> > - Initialize pageblock migratetypes from a helper after the per-page
> >  loop (suggested by Mike Rapoport)
> > - Use a 1G PFN chunk for cond_resched() in the pageblock helper
> >  (suggested by Mike Rapoport)
> > - Guard section_order() with CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP so
> >  it returns 0 when HVO is disabled and lets the compiler optimize the
> >  code as much as possible (suggested by Mike Rapoport)
> > - Explain why the !SPARSEMEM __pfn_to_section() stub belongs here
> >  (suggested by Mike Rapoport)
> > ---
> > include/linux/mmzone.h | 14 ++++++++++++++
> > mm/mm_init.c           | 33 +++++++++++++++++----------------
> > mm/sparse.h            | 23 +++++++++++++++++++++++
> > 3 files changed, 54 insertions(+), 16 deletions(-)
> > 
> > diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
> > index 82b0155d886f..2a32101d55e6 100644
> > --- a/include/linux/mmzone.h
> > +++ b/include/linux/mmzone.h
> > @@ -2011,6 +2011,14 @@ struct mem_section {
> > unsigned long section_mem_map;
> > 
> > struct mem_section_usage *usage;
> > +#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
> > + 	/*
> > +	 * Normally, sections hold regular (order-0) pages. However, for
> > +	 * sections with HVO enabled, this tracks the compound page order
> > +	 * to enable deduplication of redundant vmemmap pages.
> > +	 */
> > + 	unsigned int order;
> > +#endif
> > #ifdef CONFIG_PAGE_EXTENSION
> > 	/*
> > 	 * If SPARSEMEM, pgdat doesn't have page_ext pointer. We use
> > @@ -2365,8 +2373,14 @@ static inline unsigned long next_present_section_nr(unsigned long section_nr)
> > #endif
> > 
> > #else
> > +struct mem_section;
> > +
> > #define sparse_vmemmap_init_nid_early(_nid) do {} while (0)
> > #define pfn_in_present_section pfn_valid
> > +static inline struct mem_section *__pfn_to_section(unsigned long pfn)
> > +{
> > + 	return NULL;
> > +}
> 
> I'd like to propose an alternative implementation that doesn't require
> exposing the mem_section. The idea is to add a new helper function,
> pfn_to_section_order(), so that for non-sparse-memory configurations,
> the mem_section concept stays hidden internally. I'd really appreciate
> any thoughts or concerns — if everyone is comfortable with it, I can go
> ahead and implement this in the next version.

A helper that keeps mem_section hidden from !SPARSMEM makes perfect
sense to me.

I'd even take it one step further and make it return how many pfns
should be skipped in pfn_vmemmap_optimizable case.

> Muchun,
> Thanks.




^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 02/17] mm/sparse-vmemmap: rename HVO order macros
  2026-07-20  9:31 ` [PATCH v2 02/17] mm/sparse-vmemmap: rename HVO order macros Muchun Song
@ 2026-07-30 10:57   ` Mike Rapoport
  0 siblings, 0 replies; 27+ messages in thread
From: Mike Rapoport @ 2026-07-30 10:57 UTC (permalink / raw)
  To: Muchun Song
  Cc: Andrew Morton, Oscar Salvador, David Hildenbrand, Mike Rapoport,
	Vlastimil Babka, Lorenzo Stoakes, Michal Hocko, David Laight,
	linux-mm, linux-kernel, Muchun Song

On Mon, 20 Jul 2026 17:31:12 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> The macros VMEMMAP_TAIL_MIN_ORDER and NR_VMEMMAP_TAILS describe the
> order range where HVO can be applied, but their names tie that range to
> the tail-page cache implementation.
> 
> Rename them with a VMEMMAP_OPTIMIZATION prefix and use the new names in
> the HVO paths. This makes the code describe the optimization requirements
> rather than the tail-page cache implementation detail.
> 
> [...]

Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

-- 
Sincerely yours,
Mike.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone()
  2026-07-20  9:31 ` [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone() Muchun Song
  2026-07-20 11:04   ` Muchun Song
  2026-07-20 11:05   ` Muchun Song
@ 2026-07-30 10:57   ` Mike Rapoport
  2 siblings, 0 replies; 27+ messages in thread
From: Mike Rapoport @ 2026-07-30 10:57 UTC (permalink / raw)
  To: Muchun Song
  Cc: Andrew Morton, Oscar Salvador, David Hildenbrand, Mike Rapoport,
	Vlastimil Babka, Lorenzo Stoakes, Michal Hocko, David Laight,
	linux-mm, linux-kernel, Muchun Song

> pfn_to_zone() in hugetlb_vmemmap.c duplicates the zone lookup logic in
> __init_deferred_page().
> 
> Move it to mm_init.c, declare it in mm/internal.h, and reuse it from
> __init_deferred_page() and HugeTLB early vmemmap initialization instead
> of open-coding the zone walk there.
> 
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>

With the commit message updated as you mentioned in your replies

Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

-- 
Sincerely yours,
Mike.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages
  2026-07-30 10:48     ` Mike Rapoport
@ 2026-07-30 13:09       ` Muchun Song
  2026-07-30 14:32         ` Mike Rapoport
  0 siblings, 1 reply; 27+ messages in thread
From: Muchun Song @ 2026-07-30 13:09 UTC (permalink / raw)
  To: Mike Rapoport
  Cc: Muchun Song, Andrew Morton, Oscar Salvador, David Hildenbrand,
	Vlastimil Babka, Lorenzo Stoakes, Michal Hocko, David Laight,
	linux-mm, linux-kernel



> On Jul 30, 2026, at 18:48, Mike Rapoport <rppt@kernel.org> wrote:
> 
> Hi Muchun,

Hi,

> 
> On 2026-07-26 12:30:17+08:00, Muchun Song wrote:
>>> On Jul 20, 2026, at 17:31, Muchun Song <songmuchun@bytedance.com> wrote:
>>> 
>>> memmap_init_range() initializes every struct page in the target range.
>>> For compound pages with vmemmap optimization, the tail struct pages are
>>> backed by a shared vmemmap page.
>>> 
>>> Initializing those tail struct pages would overwrite the shared
>>> vmemmap page contents, requiring users such as HugeTLB to restore the
>>> metadata afterwards.
>>> 
>>> Track the compound order for HVO-backed sections and use that metadata
>>> to detect struct pages that fall into the shared tail vmemmap range.
>>> Skip those shared tail pages in memmap_init_range(), then initialize
>>> pageblock migratetypes for the processed range with a helper after the
>>> per-page initialization loop.
>>> 
>>> The !SPARSEMEM __pfn_to_section() stub is needed only for the build:
>>> memmap_init_range() references __pfn_to_section() after checking
>>> pfn_vmemmap_optimizable(), and !SPARSEMEM builds still have to compile
>>> that code even though pfn_vmemmap_optimizable() folds to false.
>>> 
>>> This is a preparatory change for consolidating handling across users of
>>> vmemmap optimization, and it also avoids redundant initialization of
>>> shared tail vmemmap pages during early boot.
>>> 
>>> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
>>> ---
>>> v2:
>>> - Fold section order tracking into the first user instead of keeping a
>>> standalone API-only patch (suggested by Mike Rapoport)
>>> - Rename page_vmemmap_optimizable() to pfn_vmemmap_optimizable() and
>>> pass a PFN directly (suggested by Mike Rapoport)
>>> - Initialize pageblock migratetypes from a helper after the per-page
>>> loop (suggested by Mike Rapoport)
>>> - Use a 1G PFN chunk for cond_resched() in the pageblock helper
>>> (suggested by Mike Rapoport)
>>> - Guard section_order() with CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP so
>>> it returns 0 when HVO is disabled and lets the compiler optimize the
>>> code as much as possible (suggested by Mike Rapoport)
>>> - Explain why the !SPARSEMEM __pfn_to_section() stub belongs here
>>> (suggested by Mike Rapoport)
>>> ---
>>> include/linux/mmzone.h | 14 ++++++++++++++
>>> mm/mm_init.c           | 33 +++++++++++++++++----------------
>>> mm/sparse.h            | 23 +++++++++++++++++++++++
>>> 3 files changed, 54 insertions(+), 16 deletions(-)
>>> 
>>> diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
>>> index 82b0155d886f..2a32101d55e6 100644
>>> --- a/include/linux/mmzone.h
>>> +++ b/include/linux/mmzone.h
>>> @@ -2011,6 +2011,14 @@ struct mem_section {
>>> unsigned long section_mem_map;
>>> 
>>> struct mem_section_usage *usage;
>>> +#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
>>> +  	/*
>>> + 	 * Normally, sections hold regular (order-0) pages. However, for
>>> + 	 * sections with HVO enabled, this tracks the compound page order
>>> + 	 * to enable deduplication of redundant vmemmap pages.
>>> + 	 */
>>> +  	unsigned int order;
>>> +#endif
>>> #ifdef CONFIG_PAGE_EXTENSION
>>> /*
>>>  * If SPARSEMEM, pgdat doesn't have page_ext pointer. We use
>>> @@ -2365,8 +2373,14 @@ static inline unsigned long next_present_section_nr(unsigned long section_nr)
>>> #endif
>>> 
>>> #else
>>> +struct mem_section;
>>> +
>>> #define sparse_vmemmap_init_nid_early(_nid) do {} while (0)
>>> #define pfn_in_present_section pfn_valid
>>> +static inline struct mem_section *__pfn_to_section(unsigned long pfn)
>>> +{
>>> +  	return NULL;
>>> +}
>> 
>> I'd like to propose an alternative implementation that doesn't require
>> exposing the mem_section. The idea is to add a new helper function,
>> pfn_to_section_order(), so that for non-sparse-memory configurations,
>> the mem_section concept stays hidden internally. I'd really appreciate
>> any thoughts or concerns — if everyone is comfortable with it, I can go
>> ahead and implement this in the next version.
> 
> A helper that keeps mem_section hidden from !SPARSMEM makes perfect
> sense to me.
> 
> I'd even take it one step further and make it return how many pfns
> should be skipped in pfn_vmemmap_optimizable case.

To make sure we're on the same page, let me walk you through the specific
changes I have in mind. My initial plan is to introduce pfn_to_section_order,
and the expected diff changes are as follow to keep mem_sectionhidden from
!SPARSEMEM.

diff --git a/mm/mm_init.c b/mm/mm_init.c
index dcb757b36902..0b0c2996d080 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -884,7 +884,7 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
                }

                if (pfn_vmemmap_optimizable(pfn)) {
-                       unsigned int order = section_order(__pfn_to_section(pfn));
+                       unsigned int order = pfn_to_section_order(pfn);

                        pfn = min(ALIGN(pfn, 1UL << order), end_pfn);
                        continue;
diff --git a/mm/sparse.h b/mm/sparse.h
index 030248030dc7..c5fbcdde3cee 100644
--- a/mm/sparse.h
+++ b/mm/sparse.h
@@ -47,6 +47,11 @@ static inline void __section_mark_present(struct mem_section *ms,

        ms->section_mem_map |= SECTION_MARKED_PRESENT;
 }
+
+static inline unsigned int pfn_to_section_order(unsigned long pfn)
+{
+       return section_order(__pfn_to_section(pfn));
+}
 #else
 static inline void sparse_init(void) {}
 #endif /* CONFIG_SPARSEMEM */

Since we also use __pfn_to_section in the patch 14 in this series for
!SPARSEMEM, we need to make corresponding adjustments—specifically, by using
pfn_to_section_order to determine whether the vmemmap of a given section is
optimizable. This new helper will be called from several places, so I'm afraid
its introduction is unavoidable.

That said, I've also considered an alternative: introducing another helper that
returns the exact number of PFNs to skip, and using it solely within
memmap_init_range(). However, that approach doesn't seem to offer much in terms
of code simplification. If I'm missing something or if my reasoning doesn't align
with your expectations, I would really appreciate your guidance. Thank you for
your patience!

Thanks,
Muchun





^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages
  2026-07-30 13:09       ` Muchun Song
@ 2026-07-30 14:32         ` Mike Rapoport
  0 siblings, 0 replies; 27+ messages in thread
From: Mike Rapoport @ 2026-07-30 14:32 UTC (permalink / raw)
  To: Muchun Song
  Cc: Muchun Song, Andrew Morton, Oscar Salvador, David Hildenbrand,
	Vlastimil Babka, Lorenzo Stoakes, Michal Hocko, David Laight,
	linux-mm, linux-kernel

On Thu, Jul 30, 2026 at 09:09:00PM +0800, Muchun Song wrote:
> > On 2026-07-26 12:30:17+08:00, Muchun Song wrote:
> >>> 
> >>> #else
> >>> +struct mem_section;
> >>> +
> >>> #define sparse_vmemmap_init_nid_early(_nid) do {} while (0)
> >>> #define pfn_in_present_section pfn_valid
> >>> +static inline struct mem_section *__pfn_to_section(unsigned long pfn)
> >>> +{
> >>> +  	return NULL;
> >>> +}
> >> 
> >> I'd like to propose an alternative implementation that doesn't require
> >> exposing the mem_section. The idea is to add a new helper function,
> >> pfn_to_section_order(), so that for non-sparse-memory configurations,
> >> the mem_section concept stays hidden internally. I'd really appreciate
> >> any thoughts or concerns — if everyone is comfortable with it, I can go
> >> ahead and implement this in the next version.
> > 
> > A helper that keeps mem_section hidden from !SPARSMEM makes perfect
> > sense to me.
> > 
> > I'd even take it one step further and make it return how many pfns
> > should be skipped in pfn_vmemmap_optimizable case.
> 
> To make sure we're on the same page, let me walk you through the specific
> changes I have in mind. My initial plan is to introduce pfn_to_section_order,
> and the expected diff changes are as follow to keep mem_sectionhidden from
> !SPARSEMEM.
> 
> diff --git a/mm/mm_init.c b/mm/mm_init.c
> index dcb757b36902..0b0c2996d080 100644
> --- a/mm/mm_init.c
> +++ b/mm/mm_init.c
> @@ -884,7 +884,7 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
>                 }
> 
>                 if (pfn_vmemmap_optimizable(pfn)) {
> -                       unsigned int order = section_order(__pfn_to_section(pfn));
> +                       unsigned int order = pfn_to_section_order(pfn);
> 
>                         pfn = min(ALIGN(pfn, 1UL << order), end_pfn);
>                         continue;
> diff --git a/mm/sparse.h b/mm/sparse.h
> index 030248030dc7..c5fbcdde3cee 100644
> --- a/mm/sparse.h
> +++ b/mm/sparse.h
> @@ -47,6 +47,11 @@ static inline void __section_mark_present(struct mem_section *ms,
> 
>         ms->section_mem_map |= SECTION_MARKED_PRESENT;
>  }
> +
> +static inline unsigned int pfn_to_section_order(unsigned long pfn)
> +{
> +       return section_order(__pfn_to_section(pfn));
> +}
>  #else
>  static inline void sparse_init(void) {}
>  #endif /* CONFIG_SPARSEMEM */
> 
> Since we also use __pfn_to_section in the patch 14 in this series for

I still didn't get to patch 14 :)

> !SPARSEMEM, we need to make corresponding adjustments—specifically, by using
> pfn_to_section_order to determine whether the vmemmap of a given section is
> optimizable. This new helper will be called from several places, so I'm afraid
> its introduction is unavoidable.

Do you mean that section_vmemmap_optimizable() will receive pfn as a
parameter and use pfn_to_section_order() internally?

If that's the case and pfn_to_section_order() will be used in several
places I think it's better than to have a dedicated helper just for
memmap_init_range().
 
> That said, I've also considered an alternative: introducing another helper that
> returns the exact number of PFNs to skip, and using it solely within
> memmap_init_range(). However, that approach doesn't seem to offer much in terms
> of code simplification. If I'm missing something or if my reasoning doesn't align
> with your expectations, I would really appreciate your guidance. Thank you for
> your patience!
> 
> Thanks,
> Muchun
> 
> 
> 

-- 
Sincerely yours,
Mike.


^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2026-07-30 14:32 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20  9:31 [PATCH v2 00/17] mm: Introduce section-based vmemmap optimization for HugeTLB Muchun Song
2026-07-20  9:31 ` [PATCH v2 01/17] mm/sparse: relax struct mem_section size constraints Muchun Song
2026-07-20  9:31 ` [PATCH v2 02/17] mm/sparse-vmemmap: rename HVO order macros Muchun Song
2026-07-30 10:57   ` Mike Rapoport
2026-07-20  9:31 ` [PATCH v2 03/17] mm/mm_init: skip initializing shared vmemmap tail pages Muchun Song
2026-07-26  4:30   ` Muchun Song
2026-07-30 10:48     ` Mike Rapoport
2026-07-30 13:09       ` Muchun Song
2026-07-30 14:32         ` Mike Rapoport
2026-07-20  9:31 ` [PATCH v2 04/17] mm/sparse-vmemmap: initialize shared tail vmemmap pages on allocation Muchun Song
2026-07-20  9:31 ` [PATCH v2 05/17] mm/sparse-vmemmap: support section-based vmemmap accounting Muchun Song
2026-07-20  9:31 ` [PATCH v2 06/17] mm/mm_init: factor out pfn_to_zone() Muchun Song
2026-07-20 11:04   ` Muchun Song
2026-07-20 11:05   ` Muchun Song
2026-07-30 10:57   ` Mike Rapoport
2026-07-20  9:31 ` [PATCH v2 07/17] mm/sparse-vmemmap: move vmemmap_get_tail() before PTE population Muchun Song
2026-07-20  9:31 ` [PATCH v2 08/17] mm/sparse-vmemmap: support section-based vmemmap optimization Muchun Song
2026-07-20 11:10   ` Muchun Song
2026-07-20  9:31 ` [PATCH v2 09/17] mm/sparse: initialize memory sections earlier Muchun Song
2026-07-20  9:31 ` [PATCH v2 10/17] mm/hugetlb: switch HugeTLB to section-based vmemmap optimization Muchun Song
2026-07-20  9:31 ` [PATCH v2 11/17] mm/sparse-vmemmap: remove SPARSEMEM_VMEMMAP_PREINIT support Muchun Song
2026-07-20  9:31 ` [PATCH v2 12/17] mm/sparse: inline usemap allocation into sparse_init_nid() Muchun Song
2026-07-20  9:31 ` [PATCH v2 13/17] mm/sparse: remove section_map_size() Muchun Song
2026-07-20  9:31 ` [PATCH v2 14/17] mm/hugetlb: remove HUGE_BOOTMEM_HVO Muchun Song
2026-07-20  9:31 ` [PATCH v2 15/17] mm/hugetlb: remove HUGE_BOOTMEM_CMA Muchun Song
2026-07-20  9:31 ` [PATCH v2 16/17] mm/hugetlb: localize struct huge_bootmem_page Muchun Song
2026-07-20  9:31 ` [PATCH v2 17/17] mm/hugetlb: localize HUGE_BOOTMEM_ZONES_VALID Muchun Song

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.