* [PATCH 01/11] mm/mm_init: verify zones for template-initialized pages
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
@ 2026-11-09 9:57 ` Muchun Song
2026-11-09 9:57 ` [PATCH 02/11] mm/mm_init: factor out common struct page initialization Muchun Song
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
Template-based ZONE_DEVICE initialization bypasses
__init_single_page(), which means copied struct pages do not run the
vmemmap zone consistency check.
Verify each destination after copying the template so the fast path gets
the same validation as regular page initialization.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/mm_init.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/mm/mm_init.c b/mm/mm_init.c
index a9d848c92678..0129152b74d8 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1082,6 +1082,7 @@ static void zone_device_page_init_from_template(struct page *page,
set_page_address(template, __va(pfn << PAGE_SHIFT));
#endif
memcpy_nontemporal(page, template, sizeof(*page));
+ vmemmap_optimization_verify_zone(page);
}
/*
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 02/11] mm/mm_init: factor out common struct page initialization
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
2026-11-09 9:57 ` [PATCH 01/11] mm/mm_init: verify zones for template-initialized pages Muchun Song
@ 2026-11-09 9:57 ` Muchun Song
2026-11-09 9:57 ` [PATCH 03/11] mm/mm_init: simplify zone device " Muchun Song
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
Several struct page fields have the same initial values for every page in
a zone. __init_single_page() initializes this state as part of its
per-page setup, while the shared vmemmap tail path open-codes only a
subset.
Factor the PFN-independent state into memmap_init_page_common() and use
it for both regular and shared tail struct pages. Keep section-, PFN-
and instance-specific initialization in __init_single_page().
This also initializes the last cpupid and KASAN tag for shared tail
struct pages consistently with regular struct pages.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
include/linux/mm.h | 8 --------
mm/mm_init.c | 23 ++++++++++++++++++-----
mm/mm_init.h | 2 ++
mm/sparse-vmemmap.c | 7 +------
4 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 77b5d450fe45..2e182498898e 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3022,14 +3022,6 @@ static inline void set_page_node(struct page *page, unsigned long node)
page->flags.f |= (node & NODES_MASK) << NODES_PGSHIFT;
}
-static inline void set_page_links(struct page *page, enum zone_type zone,
- unsigned long node, unsigned long pfn)
-{
- set_page_zone(page, zone);
- set_page_node(page, node);
- set_page_section_from_pfn(page, pfn);
-}
-
/**
* folio_nr_pages - The number of pages in the folio.
* @folio: The folio.
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 0129152b74d8..9677698f30d3 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -602,15 +602,28 @@ static inline void vmemmap_optimization_verify_zone(struct page *page)
page_zone_id(page));
}
-void __meminit __init_single_page(struct page *page, unsigned long pfn,
- unsigned long zone, int nid)
+/* Initialize PFN-independent fields common to pages in the same zone. */
+void __meminit memmap_init_page_common(struct page *page,
+ enum zone_type zone, int nid)
{
mm_zero_struct_page(page);
- set_page_links(page, zone, nid, pfn);
- init_page_count(page);
- atomic_set(&page->_mapcount, -1);
+ set_page_zone(page, zone);
+ set_page_node(page, nid);
page_cpupid_reset_last(page);
page_kasan_tag_reset(page);
+ atomic_set(&page->_mapcount, -1);
+#ifdef CONFIG_ZONE_DEVICE
+ if (zone == ZONE_DEVICE)
+ __SetPageReserved(page);
+#endif
+}
+
+void __meminit __init_single_page(struct page *page, unsigned long pfn,
+ unsigned long zone, int nid)
+{
+ memmap_init_page_common(page, zone, nid);
+ set_page_section_from_pfn(page, pfn);
+ init_page_count(page);
INIT_LIST_HEAD(&page->lru);
#ifdef WANT_PAGE_VIRTUAL
diff --git a/mm/mm_init.h b/mm/mm_init.h
index 0034e37dbabd..0df0644c703c 100644
--- a/mm/mm_init.h
+++ b/mm/mm_init.h
@@ -109,6 +109,8 @@ extern bool mirrored_kernelcore;
bool memblock_has_mirror(void);
void memblock_free_all(void);
+void __meminit memmap_init_page_common(struct page *page, enum zone_type zone,
+ int nid);
void __meminit __init_single_page(struct page *page, unsigned long pfn,
unsigned long zone, int nid);
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 9b80dd7326f7..fd869617423a 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -212,12 +212,7 @@ struct page __ref *vmemmap_shared_tail_page(unsigned int order, struct zone *zon
for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++) {
page = (struct page *)addr + i;
- mm_zero_struct_page(page);
- atomic_set(&page->_mapcount, -1);
- set_page_node(page, zone_to_nid(zone));
- set_page_zone(page, zone_idx(zone));
- if (zone_is_zone_device(zone))
- __SetPageReserved(page);
+ memmap_init_page_common(page, zone_idx(zone), zone_to_nid(zone));
prep_compound_tail(page, NULL, order);
}
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 03/11] mm/mm_init: simplify zone device page initialization
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
2026-11-09 9:57 ` [PATCH 01/11] mm/mm_init: verify zones for template-initialized pages Muchun Song
2026-11-09 9:57 ` [PATCH 02/11] mm/mm_init: factor out common struct page initialization Muchun Song
@ 2026-11-09 9:57 ` Muchun Song
2026-11-09 9:57 ` [PATCH 04/11] mm/mm_init: factor out frozen compound " Muchun Song
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
memmap_init_zone_device() currently seeds its reusable head and tail
templates by fully initializing the first real struct page and copying
it back into a stack object. This keeps the first page outside the
common loops and leaves __init_zone_device_page() only for template
construction.
Build both templates directly from the PFN-independent state. Add the
head-specific pgmap and refcount state to the head template and the
compound metadata to the tail template, then initialize every page
through zone_device_page_init_from_template().
This removes the special handling for the first head and tail pages
without changing their final state.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/mm_init.c | 91 ++++++++--------------------------------------------
1 file changed, 14 insertions(+), 77 deletions(-)
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 9677698f30d3..6cf74f6840de 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1044,48 +1044,6 @@ static void __init memmap_init(void)
}
#ifdef CONFIG_ZONE_DEVICE
-static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
- unsigned long zone_idx, int nid,
- struct dev_pagemap *pgmap)
-{
-
- __init_single_page(page, pfn, zone_idx, nid);
-
- /*
- * Mark page reserved as it will need to wait for onlining
- * phase for it to be fully associated with a zone.
- *
- * We can use the non-atomic __set_bit operation for setting
- * the flag as we are still initializing the pages.
- */
- __SetPageReserved(page);
-
- /*
- * ZONE_DEVICE pages union ->lru with a ->pgmap back pointer
- * and zone_device_data. It is a bug if a ZONE_DEVICE page is
- * ever freed or placed on a driver-private list.
- */
- page_folio(page)->pgmap = pgmap;
- page->zone_device_data = NULL;
-
- /*
- * MEMORY_DEVICE_GENERIC pages regain a refcount of 1 in the free
- * path. The remaining ZONE_DEVICE types start from 0 here and raise
- * the count again when the allocator or driver hands the page out.
- */
- switch (pgmap->type) {
- case MEMORY_DEVICE_FS_DAX:
- case MEMORY_DEVICE_PRIVATE:
- case MEMORY_DEVICE_COHERENT:
- case MEMORY_DEVICE_PCI_P2PDMA:
- set_page_count(page, 0);
- break;
-
- case MEMORY_DEVICE_GENERIC:
- break;
- }
-}
-
static void zone_device_page_init_from_template(struct page *page,
unsigned long pfn, struct page *template)
{
@@ -1126,30 +1084,13 @@ static void __ref memmap_init_compound(struct page *head,
unsigned long pfn, end_pfn = head_pfn + nr_pages;
unsigned int order = pgmap->vmemmap_shift;
struct page template;
- struct page *page;
- /*
- * We have to initialize the pages, including setting up page links.
- * prep_compound_page() does not take care of that, so instead we
- * open-code prep_compound_page() so we can take care of initializing
- * the pages in the same go.
- */
- __SetPageHead(head);
+ memmap_init_page_common(&template, zone_idx, nid);
+ prep_compound_tail(&template, head, order);
- /*
- * All tails of the same compound page share the state established by
- * prep_compound_tail(). Reuse one tail template for the whole range and
- * refresh only the PFN-dependent fields in that template before each copy.
- */
- pfn = head_pfn + 1;
- page = pfn_to_page(pfn);
- __init_zone_device_page(page, pfn, zone_idx, nid, pgmap);
- prep_compound_tail(page, head, order);
- set_page_count(page, 0);
- memcpy(&template, page, sizeof(*page));
-
- /* Initialize the remaining tail pages from template. */
- for (pfn = head_pfn + 2; pfn < end_pfn; pfn++)
+ __SetPageHead(head);
+ /* Initialize the tail pages from template. */
+ for (pfn = head_pfn + 1; pfn < end_pfn; pfn++)
zone_device_page_init_from_template(pfn_to_page(pfn), pfn,
&template);
prep_compound_head(head, order);
@@ -1188,22 +1129,18 @@ void __ref memmap_init_zone_device(struct zone *zone,
VM_WARN_ON(!IS_ALIGNED(start_pfn | nr_pages, pfns_per_compound));
+ memmap_init_page_common(&template, zone_idx, nid);
+ page_folio(&template)->pgmap = pgmap;
/*
- * Seed the reusable head-page template from the first real struct
- * page. The normal page-init and refcount helpers must operate on
- * a real memmap entry rather than a stack object.
+ * MEMORY_DEVICE_GENERIC pages regain a refcount of one in the free
+ * path. The remaining ZONE_DEVICE types start from zero here and raise
+ * the count again when the allocator or driver hands the page out.
*/
- pfn = start_pfn;
- page = pfn_to_page(pfn);
- __init_zone_device_page(page, pfn, zone_idx, nid, pgmap);
- memcpy(&template, page, sizeof(*page));
- if (pfns_per_compound != 1)
- memmap_init_compound(page, pfn, zone_idx, nid, pgmap,
- compound_nr_pages(pfn, pgmap));
- pfn += pfns_per_compound;
+ if (pgmap->type == MEMORY_DEVICE_GENERIC)
+ atomic_set(&template._refcount, 1);
- /* Initialize the remaining head pages from template. */
- for (; pfn < end_pfn; pfn += pfns_per_compound) {
+ /* Initialize the head pages from template. */
+ for (pfn = start_pfn; pfn < end_pfn; pfn += pfns_per_compound) {
page = pfn_to_page(pfn);
zone_device_page_init_from_template(page, pfn, &template);
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 04/11] mm/mm_init: factor out frozen compound page initialization
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
` (2 preceding siblings ...)
2026-11-09 9:57 ` [PATCH 03/11] mm/mm_init: simplify zone device " Muchun Song
@ 2026-11-09 9:57 ` Muchun Song
2026-11-09 9:58 ` [PATCH 05/11] mm/hugetlb: drop boot-time HVO handling for gigantic folios Muchun Song
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
ZONE_DEVICE initializes compound struct pages while their refcounts remain
frozen. The setup is open-coded in memmap_init_compound(), including the
per-page initialization and compound tail preparation.
Factor the common setup into memmap_init_compound_page_frozen(). Keep the
pgmap association and MEMORY_DEVICE_GENERIC refcount handling in the
ZONE_DEVICE-specific path.
This provides an MM-internal helper for other early memmap users to
share.
No functional change is intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/mm_init.c | 137 +++++++++++++++++++++++++--------------------------
mm/mm_init.h | 2 +
2 files changed, 70 insertions(+), 69 deletions(-)
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 6cf74f6840de..458496271a44 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -719,6 +719,40 @@ static __meminit void pageblock_migratetype_init_range(unsigned long pfn,
}
}
+static inline void memmap_init_page_from_template(struct page *page,
+ unsigned long pfn, const struct zone *zone, struct page *template)
+{
+ set_page_section_from_pfn(template, pfn);
+#ifdef WANT_PAGE_VIRTUAL
+ if (!is_highmem_idx(zone_idx(zone)))
+ set_page_address(template, __va(pfn << PAGE_SHIFT));
+#endif
+ memcpy_nontemporal(page, template, sizeof(*page));
+ vmemmap_optimization_verify_zone(page);
+}
+
+void __meminit memmap_init_compound_page_frozen(struct page *head,
+ unsigned long pfn, const struct zone *zone, unsigned int order)
+{
+ unsigned long nr_pages = 1UL << order;
+ struct page template;
+
+ memmap_init_page_common(&template, zone_idx(zone), zone_to_nid(zone));
+ prep_compound_tail(&template, head, order);
+
+ /* Initialize the head page. */
+ __init_single_page(head, pfn, zone_idx(zone), zone_to_nid(zone));
+ set_page_count(head, 0);
+ __SetPageHead(head);
+
+ if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
+ nr_pages = VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES;
+ /* Initialize the tail pages from PFN-independent template page. */
+ for (unsigned long i = 1; i < nr_pages; i++)
+ memmap_init_page_from_template(head + i, pfn + i, zone, &template);
+ prep_compound_head(head, order);
+}
+
struct zone __meminit *pfn_to_zone(unsigned long pfn, int nid)
{
pg_data_t *pgdat = NODE_DATA(nid);
@@ -1044,56 +1078,38 @@ static void __init memmap_init(void)
}
#ifdef CONFIG_ZONE_DEVICE
-static void zone_device_page_init_from_template(struct page *page,
- unsigned long pfn, struct page *template)
-{
- set_page_section_from_pfn(template, pfn);
-#ifdef WANT_PAGE_VIRTUAL
- if (!is_highmem_idx(ZONE_DEVICE))
- set_page_address(template, __va(pfn << PAGE_SHIFT));
-#endif
- memcpy_nontemporal(page, template, sizeof(*page));
- vmemmap_optimization_verify_zone(page);
-}
-
-/*
- * With compound page geometry and when struct pages are stored in ram most
- * tail pages are reused. Consequently, the amount of unique struct pages to
- * initialize is a lot smaller that the total amount of struct pages being
- * mapped. This is a paired / mild layering violation with explicit knowledge
- * of how the sparse_vmemmap internals handle compound pages in the lack
- * of an altmap.
- */
-static inline unsigned long compound_nr_pages(unsigned long pfn,
- struct dev_pagemap *pgmap)
+static void memmap_init_zone_device_head_page(struct page *head, struct dev_pagemap *pgmap)
{
- const struct mem_section *ms = __pfn_to_section(pfn);
-
- if (!section_vmemmap_optimizable(ms))
- return pgmap_vmemmap_nr(pgmap);
-
- return VMEMMAP_OPTIMIZATION_PAGES * (PAGE_SIZE / sizeof(struct page));
+ /*
+ * MEMORY_DEVICE_GENERIC pages start with a refcount of one; other
+ * ZONE_DEVICE pages raise it when handed out. Use atomic_set() because
+ * @head may be a stack template, while init_page_count() expects a
+ * memmap-backed page.
+ */
+ if (pgmap->type == MEMORY_DEVICE_GENERIC)
+ atomic_set(&head->_refcount, 1);
+ /*
+ * ZONE_DEVICE pages union ->lru with a ->pgmap back pointer
+ * and zone_device_data. It is a bug if a ZONE_DEVICE page is
+ * ever freed or placed on a driver-private list.
+ */
+ ((struct folio *)head)->pgmap = pgmap;
+ head->zone_device_data = NULL;
}
-static void __ref memmap_init_compound(struct page *head,
- unsigned long head_pfn,
- unsigned long zone_idx, int nid,
- struct dev_pagemap *pgmap,
- unsigned long nr_pages)
+static void memmap_init_zone_device_basepages(unsigned long start, unsigned long end,
+ const struct zone *zone, struct dev_pagemap *pgmap)
{
- unsigned long pfn, end_pfn = head_pfn + nr_pages;
- unsigned int order = pgmap->vmemmap_shift;
struct page template;
- memmap_init_page_common(&template, zone_idx, nid);
- prep_compound_tail(&template, head, order);
+ memmap_init_page_common(&template, zone_idx(zone), zone_to_nid(zone));
+ memmap_init_zone_device_head_page(&template, pgmap);
- __SetPageHead(head);
- /* Initialize the tail pages from template. */
- for (pfn = head_pfn + 1; pfn < end_pfn; pfn++)
- zone_device_page_init_from_template(pfn_to_page(pfn), pfn,
- &template);
- prep_compound_head(head, order);
+ for (unsigned long pfn = start; pfn < end; pfn++) {
+ memmap_init_page_from_template(pfn_to_page(pfn), pfn, zone, &template);
+ if (IS_ALIGNED(pfn, PAGES_PER_SECTION))
+ cond_resched();
+ }
}
void __ref memmap_init_zone_device(struct zone *zone,
@@ -1102,16 +1118,11 @@ void __ref memmap_init_zone_device(struct zone *zone,
struct dev_pagemap *pgmap)
{
unsigned long pfn, end_pfn = start_pfn + nr_pages;
- struct pglist_data *pgdat = zone->zone_pgdat;
struct vmem_altmap *altmap = pgmap_altmap(pgmap);
unsigned int pfns_per_compound = pgmap_vmemmap_nr(pgmap);
- unsigned long zone_idx = zone_idx(zone);
unsigned long start = jiffies;
- int nid = pgdat->node_id;
- struct page template;
- struct page *page;
- if (WARN_ON_ONCE(!pgmap || zone_idx != ZONE_DEVICE))
+ if (WARN_ON_ONCE(!pgmap || !zone_is_zone_device(zone)))
return;
/*
@@ -1129,32 +1140,20 @@ void __ref memmap_init_zone_device(struct zone *zone,
VM_WARN_ON(!IS_ALIGNED(start_pfn | nr_pages, pfns_per_compound));
- memmap_init_page_common(&template, zone_idx, nid);
- page_folio(&template)->pgmap = pgmap;
- /*
- * MEMORY_DEVICE_GENERIC pages regain a refcount of one in the free
- * path. The remaining ZONE_DEVICE types start from zero here and raise
- * the count again when the allocator or driver hands the page out.
- */
- if (pgmap->type == MEMORY_DEVICE_GENERIC)
- atomic_set(&template._refcount, 1);
+ if (pfns_per_compound == 1) {
+ memmap_init_zone_device_basepages(start_pfn, end_pfn, zone, pgmap);
+ goto out;
+ }
- /* Initialize the head pages from template. */
for (pfn = start_pfn; pfn < end_pfn; pfn += pfns_per_compound) {
- page = pfn_to_page(pfn);
-
- zone_device_page_init_from_template(page, pfn, &template);
+ struct page *head = pfn_to_page(pfn);
+ memmap_init_compound_page_frozen(head, pfn, zone, pgmap->vmemmap_shift);
+ memmap_init_zone_device_head_page(head, pgmap);
if (IS_ALIGNED(pfn, PAGES_PER_SECTION))
cond_resched();
-
- if (pfns_per_compound == 1)
- continue;
-
- memmap_init_compound(page, pfn, zone_idx, nid, pgmap,
- compound_nr_pages(pfn, pgmap));
}
-
+out:
pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE,
/* isolate */ false, /* atomic */ false);
diff --git a/mm/mm_init.h b/mm/mm_init.h
index 0df0644c703c..1560ac9e9286 100644
--- a/mm/mm_init.h
+++ b/mm/mm_init.h
@@ -113,5 +113,7 @@ void __meminit memmap_init_page_common(struct page *page, enum zone_type zone,
int nid);
void __meminit __init_single_page(struct page *page, unsigned long pfn,
unsigned long zone, int nid);
+void __meminit memmap_init_compound_page_frozen(struct page *head, unsigned long pfn,
+ const struct zone *zone, unsigned int order);
#endif /* __MM_MM_INIT_H */
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 05/11] mm/hugetlb: drop boot-time HVO handling for gigantic folios
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
` (3 preceding siblings ...)
2026-11-09 9:57 ` [PATCH 04/11] mm/mm_init: factor out frozen compound " Muchun Song
@ 2026-11-09 9:58 ` Muchun Song
2026-11-09 9:58 ` [PATCH 06/11] mm/hugetlb: initialize all struct pages for bootmem hugepages Muchun Song
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:58 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
HugeTLB HVO is currently supported on x86-64, riscv64, and LoongArch.
On x86-64 and riscv64, gigantic HugeTLB pages are larger than the
section size, so the existing section-based vmemmap optimization
infrastructure is already sufficient to cover the whole folio. On
LoongArch, HugeTLB HVO is supported without gigantic HugeTLB pages.
Therefore, boot-time HugeTLB HVO folios can rely on the section-based
vmemmap optimization infrastructure directly, without the extra bulk
optimization and fallback handling.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/hugetlb.c | 25 +++++++------------------
mm/hugetlb_vmemmap.c | 17 +----------------
2 files changed, 8 insertions(+), 34 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index da1c17f14a62..ff6f837b6df0 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3305,21 +3305,7 @@ static void __init prep_and_add_bootmem_folios(struct hstate *h,
unsigned long flags;
struct folio *folio, *tmp_f;
- /* Send list for bulk vmemmap optimization processing */
- hugetlb_vmemmap_optimize_folios(h, folio_list);
-
list_for_each_entry_safe(folio, tmp_f, folio_list, lru) {
- if (!folio_test_hugetlb_vmemmap_optimized(folio)) {
- /*
- * If HVO fails, initialize all tail struct pages
- * We do not worry about potential long lock hold
- * time as this is early in boot and there should
- * be no contention.
- */
- hugetlb_folio_init_tail_vmemmap(folio, h,
- VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES,
- pages_per_huge_page(h));
- }
hugetlb_bootmem_init_migratetype(folio, h);
/* Subdivide locks to achieve better parallel performance */
spin_lock_irqsave(&hugetlb_lock, flags);
@@ -3343,6 +3329,8 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
struct page *page = virt_to_page(m);
struct folio *folio = (void *)page;
const unsigned long pfn = folio_pfn(folio);
+ const unsigned long nr_pages = pages_per_huge_page(m->hstate);
+ unsigned long nr_struct_pages = nr_pages;
h = m->hstate;
/*
@@ -3356,13 +3344,14 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
VM_BUG_ON(!hstate_is_gigantic(h));
WARN_ON(folio_ref_count(folio) != 1);
- hugetlb_folio_init_vmemmap(folio, h,
- VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES);
+ if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
+ nr_struct_pages = VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES;
+ hugetlb_folio_init_vmemmap(folio, h, nr_struct_pages);
init_new_hugetlb_folio(folio);
if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
folio_set_hugetlb_vmemmap_optimized(folio);
- section_set_compound_order_range(pfn, folio_nr_pages(folio), 0);
+ section_set_compound_order_range(pfn, nr_pages, 0);
list_add(&folio->lru, &folio_list);
@@ -3377,7 +3366,7 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
if (hugetlb_early_cma(h))
folio_set_hugetlb_cma(folio);
else
- adjust_managed_page_count(page, pages_per_huge_page(h));
+ adjust_managed_page_count(page, nr_pages);
cond_resched();
}
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index ba8e11e399b2..d73ceeef392e 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -552,7 +552,6 @@ static int hugetlb_vmemmap_pteify_folio(const struct hstate *h, struct folio *fo
void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_list)
{
struct folio *folio;
- unsigned long nr_to_optimize = 0;
LIST_HEAD(vmemmap_pages);
unsigned long flags = VMEMMAP_DEFER_TLB_FLUSH;
@@ -560,18 +559,7 @@ void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_l
return;
list_for_each_entry(folio, folio_list, lru) {
- int ret;
-
- /*
- * Bootmem gigantic folios may already be marked optimized when
- * their vmemmap layout was prepared earlier, so skip them here.
- */
- if (folio_test_hugetlb_vmemmap_optimized(folio))
- continue;
-
- nr_to_optimize++;
-
- ret = hugetlb_vmemmap_pteify_folio(h, folio, flags);
+ int ret = hugetlb_vmemmap_pteify_folio(h, folio, flags);
/*
* Splitting the PMD requires allocating a page, thus let's fail
@@ -583,9 +571,6 @@ void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_l
break;
}
- if (!nr_to_optimize)
- return;
-
flush_tlb_all();
list_for_each_entry(folio, folio_list, lru) {
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 06/11] mm/hugetlb: initialize all struct pages for bootmem hugepages
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
` (4 preceding siblings ...)
2026-11-09 9:58 ` [PATCH 05/11] mm/hugetlb: drop boot-time HVO handling for gigantic folios Muchun Song
@ 2026-11-09 9:58 ` Muchun Song
2026-11-09 9:58 ` [PATCH 07/11] mm/hugetlb: use common compound page initialization helper Muchun Song
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:58 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
Boot-time gigantic hugepages currently leave the head struct page to
the generic memmap initialization path while the HugeTLB code
initializes the remaining struct pages itself.
Mark the full hugepage noinit and initialize the head struct page in
hugetlb_folio_init_vmemmap() as well, so the whole compound-page setup
is handled in one place.
This can also reduce memblock metadata overhead when many boot-time
HugeTLB pages are reserved, because physically contiguous hugepages
can be covered by fewer noinit regions.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/hugetlb.c | 27 ++++++++++-----------------
1 file changed, 10 insertions(+), 17 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index ff6f837b6df0..d2aaad235458 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3216,15 +3216,7 @@ static bool __init alloc_bootmem_huge_page(struct hstate *h, int nid)
list_add_tail(&m->list, &huge_boot_pages[nid]);
m->flags |= HUGE_BOOTMEM_ZONES_VALID;
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
- * subsystem itself.
- * The head struct page is used to get folio information by the HugeTLB
- * subsystem like zone id and node id.
- */
- memblock_reserved_mark_noinit(__pa((void *)m + PAGE_SIZE),
- huge_page_size(h) - PAGE_SIZE);
+ memblock_reserved_mark_noinit(__pa(m), huge_page_size(h));
}
return true;
@@ -3255,22 +3247,23 @@ static void __init hugetlb_folio_init_tail_vmemmap(struct folio *folio,
}
static void __init hugetlb_folio_init_vmemmap(struct folio *folio,
+ unsigned long pfn,
struct hstate *h,
+ enum zone_type zone, int nid,
unsigned long nr_pages)
{
- int ret;
+ struct page *head = &folio->page;
/*
* This is an open-coded prep_compound_page() whereby we avoid
* walking pages twice by initializing/preparing+freezing them in the
* same go.
*/
- __folio_clear_reserved(folio);
+ __init_single_page(head, pfn, zone, nid);
__folio_set_head(folio);
- ret = folio_ref_freeze(folio, 1);
- VM_BUG_ON(!ret);
+ set_page_count(head, 0);
hugetlb_folio_init_tail_vmemmap(folio, h, 1, nr_pages);
- prep_compound_head(&folio->page, huge_page_order(h));
+ prep_compound_head(head, huge_page_order(h));
}
/*
@@ -3328,8 +3321,9 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
list_for_each_entry_safe(m, tm, &huge_boot_pages[nid], list) {
struct page *page = virt_to_page(m);
struct folio *folio = (void *)page;
- const unsigned long pfn = folio_pfn(folio);
+ const unsigned long pfn = PHYS_PFN(__pa(m));
const unsigned long nr_pages = pages_per_huge_page(m->hstate);
+ const enum zone_type zone = zone_idx(pfn_to_zone(pfn, nid));
unsigned long nr_struct_pages = nr_pages;
h = m->hstate;
@@ -3342,11 +3336,10 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
prev_h = h;
VM_BUG_ON(!hstate_is_gigantic(h));
- WARN_ON(folio_ref_count(folio) != 1);
if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
nr_struct_pages = VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES;
- hugetlb_folio_init_vmemmap(folio, h, nr_struct_pages);
+ hugetlb_folio_init_vmemmap(folio, pfn, h, zone, nid, nr_struct_pages);
init_new_hugetlb_folio(folio);
if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 07/11] mm/hugetlb: use common compound page initialization helper
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
` (5 preceding siblings ...)
2026-11-09 9:58 ` [PATCH 06/11] mm/hugetlb: initialize all struct pages for bootmem hugepages Muchun Song
@ 2026-11-09 9:58 ` Muchun Song
2026-11-09 9:58 ` [PATCH 08/11] mm/mm_init: make __init_single_page() static Muchun Song
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:58 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
Boot-time gigantic hugetlb folios open-code the frozen compound
struct page initialization across two helpers.
Use memmap_init_compound_page_frozen() and remove the open-coded
helpers. This keeps the initialization rules shared with ZONE_DEVICE
instead of maintaining two implementations.
No functional change is intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/hugetlb.c | 52 ++--------------------------------------------------
1 file changed, 2 insertions(+), 50 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index d2aaad235458..290e7859b2d7 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3222,50 +3222,6 @@ static bool __init alloc_bootmem_huge_page(struct hstate *h, int nid)
return true;
}
-/* Initialize [start_page:end_page_number] tail struct pages of a hugepage */
-static void __init hugetlb_folio_init_tail_vmemmap(struct folio *folio,
- struct hstate *h,
- unsigned long start_page_number,
- unsigned long end_page_number)
-{
- enum zone_type zone = folio_zonenum(folio);
- int nid = folio_nid(folio);
- struct page *page = folio_page(folio, start_page_number);
- unsigned long head_pfn = folio_pfn(folio);
- unsigned long pfn, end_pfn = head_pfn + end_page_number;
- unsigned int order = huge_page_order(h);
-
- /*
- * As we marked all tail pages with memblock_reserved_mark_noinit(),
- * we must initialize them ourselves here.
- */
- for (pfn = head_pfn + start_page_number; pfn < end_pfn; page++, pfn++) {
- __init_single_page(page, pfn, zone, nid);
- prep_compound_tail(page, &folio->page, order);
- set_page_count(page, 0);
- }
-}
-
-static void __init hugetlb_folio_init_vmemmap(struct folio *folio,
- unsigned long pfn,
- struct hstate *h,
- enum zone_type zone, int nid,
- unsigned long nr_pages)
-{
- struct page *head = &folio->page;
-
- /*
- * This is an open-coded prep_compound_page() whereby we avoid
- * walking pages twice by initializing/preparing+freezing them in the
- * same go.
- */
- __init_single_page(head, pfn, zone, nid);
- __folio_set_head(folio);
- set_page_count(head, 0);
- hugetlb_folio_init_tail_vmemmap(folio, h, 1, nr_pages);
- prep_compound_head(head, huge_page_order(h));
-}
-
/*
* memblock-allocated pageblocks might not have the migrate type set
* if marked with the 'noinit' flag. Set it to the default (MIGRATE_MOVABLE)
@@ -3323,8 +3279,7 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
struct folio *folio = (void *)page;
const unsigned long pfn = PHYS_PFN(__pa(m));
const unsigned long nr_pages = pages_per_huge_page(m->hstate);
- const enum zone_type zone = zone_idx(pfn_to_zone(pfn, nid));
- unsigned long nr_struct_pages = nr_pages;
+ const struct zone *zone = pfn_to_zone(pfn, nid);
h = m->hstate;
/*
@@ -3336,10 +3291,7 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
prev_h = h;
VM_BUG_ON(!hstate_is_gigantic(h));
-
- if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
- nr_struct_pages = VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES;
- hugetlb_folio_init_vmemmap(folio, pfn, h, zone, nid, nr_struct_pages);
+ memmap_init_compound_page_frozen(page, pfn, zone, huge_page_order(h));
init_new_hugetlb_folio(folio);
if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 08/11] mm/mm_init: make __init_single_page() static
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
` (6 preceding siblings ...)
2026-11-09 9:58 ` [PATCH 07/11] mm/hugetlb: use common compound page initialization helper Muchun Song
@ 2026-11-09 9:58 ` Muchun Song
2026-11-09 9:58 ` [PATCH 09/11] mm/cma: centralize CMA pageblock initialization Muchun Song
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:58 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
__init_single_page() is only used within mm/mm_init.c, so make it
static.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/mm_init.c | 2 +-
mm/mm_init.h | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 458496271a44..b719e6f82d66 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -618,7 +618,7 @@ void __meminit memmap_init_page_common(struct page *page,
#endif
}
-void __meminit __init_single_page(struct page *page, unsigned long pfn,
+static void __meminit __init_single_page(struct page *page, unsigned long pfn,
unsigned long zone, int nid)
{
memmap_init_page_common(page, zone, nid);
diff --git a/mm/mm_init.h b/mm/mm_init.h
index 1560ac9e9286..1a1cde5126c3 100644
--- a/mm/mm_init.h
+++ b/mm/mm_init.h
@@ -111,8 +111,6 @@ void memblock_free_all(void);
void __meminit memmap_init_page_common(struct page *page, enum zone_type zone,
int nid);
-void __meminit __init_single_page(struct page *page, unsigned long pfn,
- unsigned long zone, int nid);
void __meminit memmap_init_compound_page_frozen(struct page *head, unsigned long pfn,
const struct zone *zone, unsigned int order);
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 09/11] mm/cma: centralize CMA pageblock initialization
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
` (7 preceding siblings ...)
2026-11-09 9:58 ` [PATCH 08/11] mm/mm_init: make __init_single_page() static Muchun Song
@ 2026-11-09 9:58 ` Muchun Song
2026-11-09 9:58 ` [PATCH 10/11] mm/cma: localize init_cma_pageblock() to cma.c Muchun Song
2026-11-09 9:58 ` [PATCH 11/11] mm/mm_init: move pageblock migratetype init to memmap helpers Muchun Song
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:58 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
Early reservations made with cma_reserve_early() currently require callers
to set the pageblock migratetype and update CMA statistics. These
operations maintain CMA state and should be handled by the CMA core.
Move initialization of early-reserved pageblocks into cma_activate_area(),
alongside the remaining pageblocks. If activation fails, restore the
managed-page accounting for early-reserved pages because they remain owned
by the caller and are not released by the CMA cleanup path.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/cma.c | 16 ++++++++++------
mm/hugetlb.c | 8 +++-----
2 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/mm/cma.c b/mm/cma.c
index 9ec1d1fd5dff..46e72734ba72 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -163,6 +163,10 @@ static void __init cma_activate_area(struct cma *cma)
count = early_pfn[r] - cmr->base_pfn;
bitmap_count = cma_bitmap_pages_to_bits(cma, count);
bitmap_set(cmr->bitmap, 0, bitmap_count);
+
+ for (pfn = cmr->base_pfn; pfn < early_pfn[r];
+ pfn += pageblock_nr_pages)
+ init_cma_pageblock(pfn_to_page(pfn));
}
WARN_ON_ONCE(!pfn_valid(cmr->base_pfn));
@@ -191,12 +195,13 @@ static void __init cma_activate_area(struct cma *cma)
/* Expose all pages to the buddy, they are useless for CMA. */
if (!test_bit(CMA_RESERVE_PAGES_ON_ERROR, &cma->flags)) {
for (r = 0; r < cma->nranges; r++) {
- unsigned long start_pfn;
-
cmr = &cma->ranges[r];
- start_pfn = r <= allocrange ? early_pfn[r] : cmr->early_pfn;
+ end_pfn = r <= allocrange ? early_pfn[r] : cmr->early_pfn;
+ for (pfn = cmr->base_pfn; pfn < end_pfn; pfn++)
+ adjust_managed_page_count(pfn_to_page(pfn), 1);
+
end_pfn = cmr->base_pfn + cmr->count;
- for (pfn = start_pfn; pfn < end_pfn; pfn++)
+ for (; pfn < end_pfn; pfn++)
free_reserved_page(pfn_to_page(pfn));
}
}
@@ -1110,8 +1115,7 @@ bool cma_intersects(struct cma *cma, unsigned long start, unsigned long end)
*
* The caller is responsible for initializing the page structures
* in the area properly, since this just points to memblock-allocated
- * memory. The caller should subsequently use init_cma_pageblock to
- * set the migrate type and CMA stats the pageblocks that were reserved.
+ * memory.
*
* If the CMA area fails to activate later, memory obtained through
* this interface is not handed to the page allocator, this is
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 290e7859b2d7..ee47a3b09df3 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3240,9 +3240,7 @@ static void __init hugetlb_bootmem_init_migratetype(struct folio *folio,
WARN_ON_ONCE(!pageblock_aligned(folio_pfn(folio)));
for (i = 0; i < nr_pages; i += pageblock_nr_pages) {
- if (folio_test_hugetlb_cma(folio))
- init_cma_pageblock(folio_page(folio, i));
- else
+ if (!folio_test_hugetlb_cma(folio))
init_pageblock_migratetype(folio_page(folio, i),
MIGRATE_MOVABLE, false);
}
@@ -3305,8 +3303,8 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
* in order to fix confusing memory reports from free(1) and
* other side-effects, like CommitLimit going negative.
*
- * For CMA pages, this is done in init_cma_pageblock
- * (via hugetlb_bootmem_init_migratetype), so skip it here.
+ * For CMA pages, this is done in cma_activate_area(), so skip
+ * it here.
*/
if (hugetlb_early_cma(h))
folio_set_hugetlb_cma(folio);
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 10/11] mm/cma: localize init_cma_pageblock() to cma.c
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
` (8 preceding siblings ...)
2026-11-09 9:58 ` [PATCH 09/11] mm/cma: centralize CMA pageblock initialization Muchun Song
@ 2026-11-09 9:58 ` Muchun Song
2026-11-09 9:58 ` [PATCH 11/11] mm/mm_init: move pageblock migratetype init to memmap helpers Muchun Song
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:58 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
Move init_cma_pageblock() from mm_init.c into cma.c so it lives
alongside the CMA code that uses it.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/cma.c | 8 ++++++++
mm/mm_init.c | 9 ---------
mm/mm_init.h | 8 --------
3 files changed, 8 insertions(+), 17 deletions(-)
diff --git a/mm/cma.c b/mm/cma.c
index 46e72734ba72..7ca0e100504d 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -30,6 +30,7 @@
#include <linux/io.h>
#include <linux/kmemleak.h>
#include <trace/events/cma.h>
+#include <linux/page-isolation.h>
#include "internal.h"
#include "cma.h"
@@ -138,6 +139,13 @@ bool cma_validate_zones(struct cma *cma)
return true;
}
+static void __init init_cma_pageblock(struct page *page)
+{
+ init_pageblock_migratetype(page, MIGRATE_CMA, false);
+ adjust_managed_page_count(page, pageblock_nr_pages);
+ page_zone(page)->cma_pages += pageblock_nr_pages;
+}
+
static void __init cma_activate_area(struct cma *cma)
{
unsigned long pfn, end_pfn, early_pfn[CMA_MAX_RANGES];
diff --git a/mm/mm_init.c b/mm/mm_init.c
index b719e6f82d66..61a720bb44dc 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -2205,15 +2205,6 @@ void __init init_cma_reserved_pageblock(struct page *page)
adjust_managed_page_count(page, pageblock_nr_pages);
page_zone(page)->cma_pages += pageblock_nr_pages;
}
-/*
- * Similar to above, but only set the migrate type and stats.
- */
-void __init init_cma_pageblock(struct page *page)
-{
- init_pageblock_migratetype(page, MIGRATE_CMA, false);
- adjust_managed_page_count(page, pageblock_nr_pages);
- page_zone(page)->cma_pages += pageblock_nr_pages;
-}
#endif
/*
diff --git a/mm/mm_init.h b/mm/mm_init.h
index 1a1cde5126c3..54d6043bed9a 100644
--- a/mm/mm_init.h
+++ b/mm/mm_init.h
@@ -40,14 +40,6 @@ struct zone *pfn_to_zone(unsigned long pfn, int nid);
void init_cma_reserved_pageblock(struct page *page);
#endif
-#ifdef CONFIG_CMA
-void init_cma_pageblock(struct page *page);
-#else
-static inline void init_cma_pageblock(struct page *page)
-{
-}
-#endif
-
/* Memory initialisation debug and verification */
#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
DECLARE_STATIC_KEY_TRUE(deferred_pages);
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 11/11] mm/mm_init: move pageblock migratetype init to memmap helpers
2026-11-09 9:57 [PATCH 00/11] mm: Unify memmap initialization for compound pages Muchun Song
` (9 preceding siblings ...)
2026-11-09 9:58 ` [PATCH 10/11] mm/cma: localize init_cma_pageblock() to cma.c Muchun Song
@ 2026-11-09 9:58 ` Muchun Song
10 siblings, 0 replies; 12+ messages in thread
From: Muchun Song @ 2026-11-09 9:58 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Oscar Salvador, Mike Rapoport
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Muchun Song, James Houghton,
Lance Yang, Qi Zheng, linux-mm, linux-kernel, Muchun Song
ZONE_DEVICE memmap initialization already initializes pageblock
migratetype after all struct pages have been initialized, while hugetlb
bootmem folios have their own duplicate bootmem-specific initialization.
Move the MIGRATE_MOVABLE pageblock initialization into the frozen memmap
initialization helpers instead. This makes memmap_init_page_frozen()
initialize the pageblock for single pages and
memmap_init_compound_page_frozen() initialize the whole range for compound
pages, so the ZONE_DEVICE and hugetlb bootmem paths can share the same
initialization.
The old hugetlb_bootmem_init_migratetype() skipped CMA folios via
folio_test_hugetlb_cma(), but the shared helper always initializes
pageblocks as MIGRATE_MOVABLE. This is safe because cma_activate_area()
later overrides the migratetype for CMA pageblocks.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/hugetlb.c | 25 -------------------------
mm/mm_init.c | 11 ++++++-----
2 files changed, 6 insertions(+), 30 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index ee47a3b09df3..5dfe9091b5ca 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3222,30 +3222,6 @@ static bool __init alloc_bootmem_huge_page(struct hstate *h, int nid)
return true;
}
-/*
- * memblock-allocated pageblocks might not have the migrate type set
- * if marked with the 'noinit' flag. Set it to the default (MIGRATE_MOVABLE)
- * here, or MIGRATE_CMA if this was a page allocated through an early CMA
- * reservation.
- *
- * In case of vmemmap optimized folios, the tail vmemmap pages are mapped
- * read-only, but that's ok - for sparse vmemmap this does not write to
- * the page structure.
- */
-static void __init hugetlb_bootmem_init_migratetype(struct folio *folio,
- struct hstate *h)
-{
- unsigned long nr_pages = pages_per_huge_page(h), i;
-
- WARN_ON_ONCE(!pageblock_aligned(folio_pfn(folio)));
-
- for (i = 0; i < nr_pages; i += pageblock_nr_pages) {
- if (!folio_test_hugetlb_cma(folio))
- init_pageblock_migratetype(folio_page(folio, i),
- MIGRATE_MOVABLE, false);
- }
-}
-
static void __init prep_and_add_bootmem_folios(struct hstate *h,
struct list_head *folio_list)
{
@@ -3253,7 +3229,6 @@ static void __init prep_and_add_bootmem_folios(struct hstate *h,
struct folio *folio, *tmp_f;
list_for_each_entry_safe(folio, tmp_f, folio_list, lru) {
- hugetlb_bootmem_init_migratetype(folio, h);
/* Subdivide locks to achieve better parallel performance */
spin_lock_irqsave(&hugetlb_lock, flags);
account_new_hugetlb_folio(h, folio);
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 61a720bb44dc..e05db8abde2f 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -751,6 +751,9 @@ void __meminit memmap_init_compound_page_frozen(struct page *head,
for (unsigned long i = 1; i < nr_pages; i++)
memmap_init_page_from_template(head + i, pfn + i, zone, &template);
prep_compound_head(head, order);
+
+ pageblock_migratetype_init_range(pfn, 1UL << order, MIGRATE_MOVABLE,
+ /* isolate */ false, /* atomic */ false);
}
struct zone __meminit *pfn_to_zone(unsigned long pfn, int nid)
@@ -1110,6 +1113,9 @@ static void memmap_init_zone_device_basepages(unsigned long start, unsigned long
if (IS_ALIGNED(pfn, PAGES_PER_SECTION))
cond_resched();
}
+
+ pageblock_migratetype_init_range(start, end - start, MIGRATE_MOVABLE,
+ /* isolate */ false, /* atomic */ false);
}
void __ref memmap_init_zone_device(struct zone *zone,
@@ -1150,13 +1156,8 @@ void __ref memmap_init_zone_device(struct zone *zone,
memmap_init_compound_page_frozen(head, pfn, zone, pgmap->vmemmap_shift);
memmap_init_zone_device_head_page(head, pgmap);
- if (IS_ALIGNED(pfn, PAGES_PER_SECTION))
- cond_resched();
}
out:
- pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE,
- /* isolate */ false, /* atomic */ false);
-
pr_debug("%s initialised %lu pages in %ums\n", __func__,
nr_pages, jiffies_to_msecs(jiffies - start));
}
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread