[PATCH v3 08/19] mm/mm_init: Defer sparse_init() until after zone initialization

From: Muchun Song

Date: Tue Jun 02 2026 - 06:20:34 EST


free_area_init() is responsible for initializing pgdat and zone state.
Calling sparse_init() from there mixes in later vmemmap and struct page
setup, which makes the initialization flow less clear.

Defer sparse_init(), sparse_vmemmap_init_nid_late(), and memmap_init()
until after free_area_init() completes, when zone initialization is fully
done. This keeps free_area_init() focused on zone setup and ensures that
sparse_init() runs with the relevant zone state already available.

This is also a prerequisite for later hugetlb vmemmap changes that need
zone information during early sparse vmemmap setup.

Signed-off-by: Muchun Song <songmuchun@xxxxxxxxxxxxx>
Reviewed-by: Mike Rapoport (Microsoft) <rppt@xxxxxxxxxx>
Reviewed-by: Oscar Salvador (SUSE) <osalvador@xxxxxxxxxx>
---
v2->v3:
- collect Reviewed-by from Oscar Salvador
---
mm/mm_init.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/mm/mm_init.c b/mm/mm_init.c
index 3a57bf5a9b46..f349a6f34139 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1829,7 +1829,6 @@ static void __init free_area_init(void)
bool descending;

arch_zone_limits_init(max_zone_pfn);
- sparse_init();

start_pfn = PHYS_PFN(memblock_start_of_DRAM());
descending = arch_has_descending_max_zone_pfns();
@@ -1918,11 +1917,7 @@ static void __init free_area_init(void)
}
}

- for_each_node_state(nid, N_MEMORY)
- sparse_vmemmap_init_nid_late(nid);
-
calc_nr_kernel_pages();
- memmap_init();

/* disable hash distribution for systems with a single node */
fixup_hashdist();
@@ -2694,10 +2689,17 @@ void __init __weak mem_init(void)

void __init mm_core_init_early(void)
{
+ int nid;
+
hugetlb_cma_reserve();
hugetlb_bootmem_alloc();

free_area_init();
+
+ sparse_init();
+ for_each_node_state(nid, N_MEMORY)
+ sparse_vmemmap_init_nid_late(nid);
+ memmap_init();
}

/*
--
2.54.0