[PATCH 11/49] mm: defer sparse_init() until after zone initialization

Muchun Song
2026-04-05T12:54:39Z

According to the comment of free_area_init(), its main goal is to
initialise all pg_data_t and zone data. However, sparse_init() and
memmap_init() are aimed at allocating vmemmap pages and initializing
struct page respectively, which differs from the goal of free_area_init().
Therefore, it is reasonable to move them out of free_area_init().

Call sparse_init() after free_area_init() to guarantee that zone data
structures are available when sparse_init() executes. This change is a
prerequisite for integrating vmemmap initialization steps and allows
sparse_init() to safely access zone information if needed (e.g. HVO case).

Also, move hugetlb reservation functions (hugetlb_cma_reserve() and
hugetlb_bootmem_alloc()) to be after free_area_init(). This allows
hugetlb reservation to access zone information to ensure that contiguous
pages are not allocated across zone boundaries, which simplifies the
hugetlb code. So this is a preparation for subsequent changes.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/mm_init.c | 15 ++++++++-------
mm/sparse.c | 3 ---
2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/mm/mm_init.c b/mm/mm_init.c
index 5ca4503e7622..72604d02a853 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1807,7 +1807,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();
@@ -1896,11 +1895,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();
@@ -2669,10 +2664,16 @@ void __init __weak mem_init(void)

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

free_area_init();
+ /* Zone data structures are available from here. */
+ hugetlb_cma_reserve();
+ hugetlb_bootmem_alloc();
+ sparse_init();
+ for_each_node_state(nid, N_MEMORY)
+ sparse_vmemmap_init_nid_late(nid);
+ memmap_init();
}

/*
diff --git a/mm/sparse.c b/mm/sparse.c
index c7f91dc2e5b5..5fe0a7e66775 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -406,9 +406,6 @@ void __init sparse_init(void)
pnum_begin = first_present_section_nr();
nid_begin = sparse_early_nid(__nr_to_section(pnum_begin));

- /* Setup pageblock_order for HUGETLB_PAGE_SIZE_VARIABLE */
- set_pageblock_order();
-
for_each_present_section_nr(pnum_begin + 1, pnum_end) {
int nid = sparse_early_nid(__nr_to_section(pnum_end));

--
2.20.1