Hi, On 2026-01-02 08:59:58 +0200, Mike Rapoport wrote: > From: "Mike Rapoport (Microsoft)" <[email protected]> > > To initialize node, zone and memory map data structures every architecture > calls free_area_init() during setup_arch() and passes it an array of zone > limits. > > Beside code duplication it creates "interesting" ordering cases between > allocation and initialization of hugetlb and the memory map. Some > architectures allocate hugetlb pages very early in setup_arch() in certain > cases, some only create hugetlb CMA areas in setup_arch() and sometimes > hugetlb allocations happen mm_core_init(). > > With arch_zone_limits_init() helper available now on all architectures it > is no longer necessary to call free_area_init() from architecture setup > code. Rather core MM initialization can call arch_zone_limits_init() in a > single place. > > This allows to unify ordering of hugetlb vs memory map allocation and > initialization. > > Remove the call to free_area_init() from architecture specific code and > place it in a new mm_core_init_early() function that is called immediately > after setup_arch(). > > After this refactoring it is possible to consolidate hugetlb allocations > and eliminate differences in ordering of hugetlb and memory map > initialization among different architectures. > > As the first step of this consolidation move hugetlb_bootmem_alloc() to > mm_core_early_init(). > > Signed-off-by: Mike Rapoport (Microsoft) <[email protected]>
This breaks boot on my Raspberry Pi 1. The reason seems to be the use of page_folio() when initializing the dynamically allocated zero page in arm, which doesn't work when free_area_init() hasn't been called yet. The following oopses are generated: Booting Linux on physical CPU 0x0 Linux version 6.19.0-rc3-03898-g7975b0084358 ([email protected]) (armv6j-unknown-linux-gnueabihf-gcc (Gentoo 15.2.1_p20251122 p3) 15.2.1 20251122, GNU ld (Gentoo 2.45.1 p1) 2.45.1) #451 Fri Jan 2 20:26:00 CET 2026 CPU: ARMv6-compatible processor [410fb767] revision 7 (ARMv7), cr=00c5387d CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache OF: fdt: Machine model: Raspberry Pi Model B Rev 2 earlycon: pl11 at MMIO32 0x20201000 (options '') printk: legacy bootconsole [pl11] enabled Memory policy: Data cache writeback Reserved memory: created CMA memory pool at 0x19400000, size 64 MiB OF: reserved mem: initialized node linux,cma, compatible id shared-dma-pool OF: reserved mem: 0x19400000..0x1d3fffff (65536 KiB) map reusable linux,cma 8<--- cut here --- Unable to handle kernel paging request at virtual address 003dfb44 when read [003dfb44] *pgd=00000000 Internal error: Oops: 5 [#1] ARM CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.19.0-rc3-03898-g7975b0084358 #451 NONE Hardware name: BCM2835 PC is at paging_init (include/linux/page-flags.h:284 (discriminator 2) arch/arm/mm/mmu.c:1790 (discriminator 2)) LR is at paging_init (arch/arm/mm/mmu.c:1789 (discriminator 1)) pc : lr : psr: 600000d3 sp : c0d01ef8 ip : defdb000 fp : 0000000b r10: 00200000 r9 : d9400000 r8 : ffe00000 r7 : c0d09050 r6 : c0d0902c r5 : c0d43d40 r4 : 0001efda r3 : c0dab20c r2 : 00000000 r1 : 003dfb40 r0 : 00000000 Flags: nZCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment none Control: 00c5387d Table: 00004008 DAC: 00000051 Register r0 information: NULL pointer Register r1 information: non-paged memory Register r2 information: NULL pointer Register r3 information: 8<--- cut here --- Unable to handle kernel paging request at virtual address 0001b564 when read [0001b564] *pgd=00000000 Internal error: Oops: 5 [#2] ARM CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.19.0-rc3-03898-g7975b0084358 #451 NONE Hardware name: BCM2835 PC is at kmem_dump_obj (mm/slab.h:142 (discriminator 2) mm/slab.h:178 (discriminator 2) mm/slab_common.c:609 (discriminator 2)) LR is at 0x1 pc : lr : psr: 200001d3 sp : c0d01cc8 ip : 00000000 fp : 0000000b r10: 00200000 r9 : c0dab1dc r8 : 00000000 r7 : 00000005 r6 : 00000dab r5 : 0001b560 r4 : c0dab20c r3 : c0dc2058 r2 : 1f000000 r1 : 00c00000 r0 : 00000001 Flags: nzCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment none Control: 00c5387d Table: 00004008 DAC: 00000051 Register r0 information: non-paged memory Register r1 information: non-paged memory Register r2 information: non-paged memory Register r3 information: 8<--- cut here --- and the second one repeats for some time afterwards. I experimented a little by allocating the zero page statically as many other arches do which fixes the issue as it does not need to be initialized at this point anymore, though I have no idea if that's appropriate. Regards, Klara Modin ... > diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c > index bdcc3639681f..a8f7b4084715 100644 > --- a/arch/arm/mm/init.c > +++ b/arch/arm/mm/init.c > @@ -118,15 +118,6 @@ void __init arch_zone_limits_init(unsigned long > *max_zone_pfn) > #endif > } > > -static void __init zone_sizes_init(unsigned long min, unsigned long max_low, > - unsigned long max_high) > -{ > - unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; > - > - arch_zone_limits_init(max_zone_pfn); > - free_area_init(max_zone_pfn); > -} > - > #ifdef CONFIG_HAVE_ARCH_PFN_VALID > int pfn_valid(unsigned long pfn) > { > @@ -222,13 +213,6 @@ void __init bootmem_init(void) > * done after the fixed reservations > */ > sparse_init(); > - > - /* > - * Now free the memory - free_area_init needs > - * the sparse mem_map arrays initialized by sparse_init() > - * for memmap_init_zone(), otherwise all PFNs are invalid. > - */ > - zone_sizes_init(min_low_pfn, max_low_pfn, max_pfn); > } > > /* ... > diff --git a/include/linux/mm.h b/include/linux/mm.h > index 628c0e0ac313..64d6f9c15ef1 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -45,6 +45,7 @@ struct pt_regs; > struct folio_batch; > > void arch_mm_preinit(void); > +void mm_core_init_early(void); > void mm_core_init(void); > void init_mm_internals(void); > > @@ -3536,7 +3537,7 @@ static inline unsigned long get_num_physpages(void) > } > > /* > - * Using memblock node mappings, an architecture may initialise its > + * FIXME: Using memblock node mappings, an architecture may initialise its > * zones, allocate the backing mem_map and account for memory holes in an > * architecture independent manner. > * > @@ -3551,7 +3552,6 @@ static inline unsigned long get_num_physpages(void) > * memblock_add_node(base, size, nid, MEMBLOCK_NONE) > * free_area_init(max_zone_pfns); > */ > -void free_area_init(unsigned long *max_zone_pfn); > void arch_zone_limits_init(unsigned long *max_zone_pfn); > unsigned long node_map_pfn_alignment(void); > extern unsigned long absent_pages_in_range(unsigned long start_pfn, > diff --git a/init/main.c b/init/main.c > index b84818ad9685..445b5643ecec 100644 > --- a/init/main.c > +++ b/init/main.c > @@ -1025,6 +1025,7 @@ void start_kernel(void) > page_address_init(); > pr_notice("%s", linux_banner); > setup_arch(&command_line); > + mm_core_init_early(); > /* Static keys and static calls are needed by LSMs */ > jump_label_init(); > static_call_init(); > diff --git a/mm/mm_init.c b/mm/mm_init.c > index fc2a6f1e518f..ffc4a0f1fee9 100644 > --- a/mm/mm_init.c > +++ b/mm/mm_init.c > @@ -1810,7 +1810,6 @@ static void __init set_high_memory(void) > > /** > * free_area_init - Initialise all pg_data_t and zone data > - * @max_zone_pfn: an array of max PFNs for each zone > * > * This will call free_area_init_node() for each active node in the system. > * Using the page ranges provided by memblock_set_node(), the size of each > @@ -1821,17 +1820,14 @@ static void __init set_high_memory(void) > * starts where the previous one ended. For example, ZONE_DMA32 starts > * at arch_max_dma_pfn. > */ > -void __init free_area_init(unsigned long *max_zone_pfn) > +static void __init free_area_init(void) > { > + unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; > unsigned long start_pfn, end_pfn; > int i, nid, zone; > bool descending; > > - /* Record where the zone boundaries are */ > - memset(arch_zone_lowest_possible_pfn, 0, > - sizeof(arch_zone_lowest_possible_pfn)); > - memset(arch_zone_highest_possible_pfn, 0, > - sizeof(arch_zone_highest_possible_pfn)); > + arch_zone_limits_init(max_zone_pfn); > > start_pfn = PHYS_PFN(memblock_start_of_DRAM()); > descending = arch_has_descending_max_zone_pfns(); > @@ -2681,13 +2677,19 @@ void __init __weak mem_init(void) > { > } > > +void __init mm_core_init_early(void) > +{ > + hugetlb_bootmem_alloc(); > + > + free_area_init(); > +} > + > /* > * Set up kernel memory allocators > */ > void __init mm_core_init(void) > { > arch_mm_preinit(); > - hugetlb_bootmem_alloc(); > > /* Initializations relying on SMP setup */ > BUILD_BUG_ON(MAX_ZONELISTS > 2); > -- > 2.51.0 >
