From a5d67c54d414ee707333ce179d4492e99122e5b6 Mon Sep 17 00:00:00 2001 From: Zhen Ni Date: Fri, 12 Jun 2026 11:11:05 +0800 Subject: [PATCH 01/25] mm/memblock: Remove redundant pageblock_align() in free_unused_memmap() The assignment `prev_end = pageblock_align(end)` is redundant because `prev_end` was already aligned to pageblock oundaries inside the loop. Since pageblock_align() is a pure function, calling it again with the same input produces the same result. This line was added in commit f921f53e089a ("memblock: align freed memory map on pageblock boundaries with SPARSEMEM"). Remove it to simplify the code. Signed-off-by: Zhen Ni Link: https://patch.msgid.link/20260612031105.3350181-1-zhen.ni@easystack.cn Signed-off-by: Mike Rapoport (Microsoft) --- mm/memblock.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mm/memblock.c b/mm/memblock.c index 6349c48154f4..43e036f3e11a 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -2224,10 +2224,8 @@ static void __init free_unused_memmap(void) } #ifdef CONFIG_SPARSEMEM - if (!IS_ALIGNED(prev_end, PAGES_PER_SECTION)) { - prev_end = pageblock_align(end); + if (!IS_ALIGNED(prev_end, PAGES_PER_SECTION)) free_memmap(prev_end, ALIGN(prev_end, PAGES_PER_SECTION)); - } #endif } From 7783dcd79ae9c4aa48bc47bd4275772445dc4b2a Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Mon, 22 Jun 2026 02:24:03 +0000 Subject: [PATCH 02/25] mm/mm_init: fix incorrect node_spanned_pages Current node_spanned_pages is got as a summation of all zone's spanned page in calculate_node_totalpages(). Generally this is good, but if we use kernelcore=mirror, it is would be wrong. Without kernelcore=mirror: The test machine has below memory layout: memory[0x0] [0x0000000000001000-0x000000000009efff], 0x000000000009e000 bytes on node 0 flags: 0x0 memory[0x1] [0x0000000000100000-0x00000000bffdefff], 0x00000000bfedf000 bytes on node 0 flags: 0x0 memory[0x2] [0x0000000100000000-0x00000001bfffffff], 0x00000000c0000000 bytes on node 0 flags: 0x0 And the Zone range is: DMA [mem 0x0000000000001000-0x0000000000ffffff] DMA32 [mem 0x0000000001000000-0x00000000ffffffff] Normal [mem 0x0000000100000000-0x00000001bfffffff] Then we see, with spanned_pages printed: On node 0 spanned_pages: 1835007 totalpages: 1572733 With kernelcore=mirror: The test machine has below memory layout: memory[0x0] [0x0000000000001000-0x000000000009efff], 0x000000000009e000 bytes on node 0 flags: 0x2 memory[0x1] [0x0000000000100000-0x00000000bffdefff], 0x00000000bfedf000 bytes on node 0 flags: 0x2 memory[0x2] [0x0000000100000000-0x000000013fffffff], 0x0000000040000000 bytes on node 0 flags: 0x2 memory[0x3] [0x0000000140000000-0x00000001bfffffff], 0x0000000080000000 bytes on node 0 flags: 0x0 And the Zone range is: DMA [mem 0x0000000000001000-0x0000000000ffffff] DMA32 [mem 0x0000000001000000-0x00000000ffffffff] Normal [mem 0x0000000100000000-0x00000001bfffffff] Device empty Movable zone start for each node Node 0: 0x0000000140000000 Then we see, with spanned_pages printed: On node 0 spanned_pages: 2359295 totalpages: 1572733 The total range of memory on node 0 doesn't change, but the spanned_pages becomes much larger. The reason is when kernelcore=mirror is specified, the range of Zone Normal and Zone Movable would overlap. So the overlapped range would be calculated twice. A wrong node_spanned_pages would effect defer_init(), since each zone_end_pfn is less than pgdat_end_pfn(). As we already passed in node_start_pfn and node_end_pfn, fix this by get it from (node_start_pfn - node_end_pfn) directly. Fixes: 342332e6a925 ("mm/page_alloc.c: introduce kernelcore=mirror option") Signed-off-by: Wei Yang Cc: Yuan Liu Link: https://patch.msgid.link/20260622022403.16375-1-richard.weiyang@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/mm_init.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 0f64909e8d20..0d2eb82fa068 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1338,7 +1338,7 @@ static void __init calculate_node_totalpages(struct pglist_data *pgdat, unsigned long node_start_pfn, unsigned long node_end_pfn) { - unsigned long realtotalpages = 0, totalpages = 0; + unsigned long realtotalpages = 0; enum zone_type i; for (i = 0; i < MAX_NR_ZONES; i++) { @@ -1368,11 +1368,10 @@ static void __init calculate_node_totalpages(struct pglist_data *pgdat, zone->present_early_pages = real_size; #endif - totalpages += spanned; realtotalpages += real_size; } - pgdat->node_spanned_pages = totalpages; + pgdat->node_spanned_pages = node_end_pfn - node_start_pfn; pgdat->node_present_pages = realtotalpages; pr_debug("On node %d totalpages: %lu\n", pgdat->node_id, realtotalpages); } From f6e6c57f2e100813be6d5882060fef81cb6f32de Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 1 Jul 2026 00:04:06 +0900 Subject: [PATCH 03/25] arm64: mm: remove unreachable invalid range check in kasan_init_shadow() kasan_init_shadow() maps each memblock region with for_each_mem_range() and breaks the loop when start >= end. for_each_mem_range() never returns an invalid range, so start < end always. Therefore the start >= end check is unreachable, so remove it. No functional change. Signed-off-by: Sang-Heon Jeon Reviewed-by: Andrey Ryabinin Link: https://patch.msgid.link/20260630150413.1718632-2-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/arm64/mm/kasan_init.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm64/mm/kasan_init.c b/arch/arm64/mm/kasan_init.c index 3fcad956fdf7..45fbdce684c8 100644 --- a/arch/arm64/mm/kasan_init.c +++ b/arch/arm64/mm/kasan_init.c @@ -353,9 +353,6 @@ static void __init kasan_init_shadow(void) void *start = (void *)__phys_to_virt(pa_start); void *end = (void *)__phys_to_virt(pa_end); - if (start >= end) - break; - kasan_map_populate((unsigned long)kasan_mem_to_shadow(start), (unsigned long)kasan_mem_to_shadow(end), early_pfn_to_nid(virt_to_pfn(start))); From 68d3b7d58237b8794d9343210d4c90b8381cfa96 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 1 Jul 2026 00:04:07 +0900 Subject: [PATCH 04/25] LoongArch: remove unreachable invalid range check in kasan_init() kasan_init() populates the linear mapping shadow with for_each_mem_range() and breaks the loop when start >= end. for_each_mem_range() never returns an invalid range, so start < end always. Therefore the start >= end check is unreachable, so remove it. No functional change. Signed-off-by: Sang-Heon Jeon Reviewed-by: Andrey Ryabinin Link: https://patch.msgid.link/20260630150413.1718632-3-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/loongarch/mm/kasan_init.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/loongarch/mm/kasan_init.c b/arch/loongarch/mm/kasan_init.c index 0fc02ca06457..92ca0ba86406 100644 --- a/arch/loongarch/mm/kasan_init.c +++ b/arch/loongarch/mm/kasan_init.c @@ -305,9 +305,6 @@ void __init kasan_init(void) void *start = (void *)phys_to_virt(pa_start); void *end = (void *)phys_to_virt(pa_end); - if (start >= end) - break; - kasan_map_populate((unsigned long)mem_to_shadow(start), (unsigned long)mem_to_shadow(end), NUMA_NO_NODE); } From 99e60ebe108817fd48791f2cb81f1e672ad8285b Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 1 Jul 2026 00:04:08 +0900 Subject: [PATCH 05/25] riscv: remove unreachable invalid range check in create_linear_mapping_page_table() create_linear_mapping_page_table() iterates memblock regions with for_each_mem_range() and breaks the loop when start >= end. for_each_mem_range() never returns an invalid range, so start < end always. Therefore the start >= end check is unreachable, so remove it. No functional change. Signed-off-by: Sang-Heon Jeon Reviewed-by: Charlie Jenkins Tested-by: Charlie Jenkins Link: https://patch.msgid.link/20260630150413.1718632-4-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/riscv/mm/init.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 5b1b3c88b4d1..eb93c2ac05a6 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1229,8 +1229,6 @@ static void __init create_linear_mapping_page_table(void) /* Map all memory banks in the linear mapping */ for_each_mem_range(i, &start, &end) { - if (start >= end) - break; if (start <= __pa(PAGE_OFFSET) && __pa(PAGE_OFFSET) < end) start = __pa(PAGE_OFFSET); From 8008f6995d0c0cf22e7b4d60e95fe14085c10f35 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 1 Jul 2026 00:04:09 +0900 Subject: [PATCH 06/25] riscv: remove unreachable invalid range check in kasan_init() kasan_init() populates the linear mapping shadow with for_each_mem_range() and breaks the loop when start >= end. for_each_mem_range() never returns an invalid range, so start < end always. Therefore the start >= end check is unreachable, so remove it. No functional change. Signed-off-by: Sang-Heon Jeon Reviewed-by: Charlie Jenkins Tested-by: Charlie Jenkins Reviewed-by: Andrey Ryabinin Link: https://patch.msgid.link/20260630150413.1718632-5-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/riscv/mm/kasan_init.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/riscv/mm/kasan_init.c b/arch/riscv/mm/kasan_init.c index c4a2a9e5586e..1f3aa9611187 100644 --- a/arch/riscv/mm/kasan_init.c +++ b/arch/riscv/mm/kasan_init.c @@ -512,9 +512,6 @@ void __init kasan_init(void) void *start = (void *)__va(p_start); void *end = (void *)__va(p_end); - if (start >= end) - break; - kasan_populate(kasan_mem_to_shadow(start), kasan_mem_to_shadow(end)); } From 3db1a9ccf9290ea3fe01aadf3bd743ef7032b24a Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 1 Jul 2026 00:04:10 +0900 Subject: [PATCH 07/25] ARM: remove unreachable invalid range check in kasan_init() kasan_init() maps each memblock region with for_each_mem_range(), which guarantees pa_start < pa_end. Then it skips any region with pa_start >= arm_lowmem_limit, so pa_start < arm_lowmem_limit is guaranteed as well. When pa_end <= arm_lowmem_limit, pa_start < pa_end means start < end, so the start >= end check is unreachable. When pa_end > arm_lowmem_limit, end is clamped to __va(arm_lowmem_limit), and pa_start < arm_lowmem_limit means start < end, so the check is unreachable as well. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260630150413.1718632-6-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/arm/mm/kasan_init.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm/mm/kasan_init.c b/arch/arm/mm/kasan_init.c index c6625e808bf8..1f7c74c5df9e 100644 --- a/arch/arm/mm/kasan_init.c +++ b/arch/arm/mm/kasan_init.c @@ -262,12 +262,6 @@ void __init kasan_init(void) &pa_start, &pa_end, &arm_lowmem_limit); end = __va(arm_lowmem_limit); } - if (start >= end) { - pr_info("Skipping invalid memory block %pa-%pa (virtual %p-%p)\n", - &pa_start, &pa_end, start, end); - continue; - } - create_mapping(start, end); } From 21e95a4b6f34770571eeeaa6adb535efba1b7281 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 1 Jul 2026 00:04:11 +0900 Subject: [PATCH 08/25] powerpc64/kasan: Remove unreachable invalid range check in kasan_init_phys_region() kasan_init() maps each memblock region with for_each_mem_range() and passes it to kasan_init_phys_region(), which does nothing when start >= end. for_each_mem_range() never returns an invalid range, so start < end always. Therefore the start >= end check is unreachable, so remove it. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260630150413.1718632-7-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/powerpc/mm/kasan/init_book3e_64.c | 3 --- arch/powerpc/mm/kasan/init_book3s_64.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/arch/powerpc/mm/kasan/init_book3e_64.c b/arch/powerpc/mm/kasan/init_book3e_64.c index 0d3a73d6d4b0..0ed372fb8b09 100644 --- a/arch/powerpc/mm/kasan/init_book3e_64.c +++ b/arch/powerpc/mm/kasan/init_book3e_64.c @@ -68,9 +68,6 @@ static void __init kasan_init_phys_region(void *start, void *end) unsigned long k_start, k_end, k_cur; void *va; - if (start >= end) - return; - k_start = ALIGN_DOWN((unsigned long)kasan_mem_to_shadow(start), PAGE_SIZE); k_end = ALIGN((unsigned long)kasan_mem_to_shadow(end), PAGE_SIZE); diff --git a/arch/powerpc/mm/kasan/init_book3s_64.c b/arch/powerpc/mm/kasan/init_book3s_64.c index dcafa641804c..ccfbfb894637 100644 --- a/arch/powerpc/mm/kasan/init_book3s_64.c +++ b/arch/powerpc/mm/kasan/init_book3s_64.c @@ -24,9 +24,6 @@ static void __init kasan_init_phys_region(void *start, void *end) unsigned long k_start, k_end, k_cur; void *va; - if (start >= end) - return; - k_start = ALIGN_DOWN((unsigned long)kasan_mem_to_shadow(start), PAGE_SIZE); k_end = ALIGN((unsigned long)kasan_mem_to_shadow(end), PAGE_SIZE); From c6af91e48594191f62ad3c1fcc8269b1eb539ef8 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 1 Jul 2026 00:04:12 +0900 Subject: [PATCH 09/25] mm: remove unnecessary empty range check in early_calculate_totalpages() early_calculate_totalpages() iterates the memory ranges with for_each_mem_pfn_range() and calls node_set_state(nid, N_MEMORY) only when end_pfn - start_pfn is non-zero. for_each_mem_pfn_range() never returns an empty range, so start_pfn < end_pfn always. Therefore the check is unnecessary, so remove it. No functional change. Signed-off-by: Sang-Heon Jeon Reviewed-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260630150413.1718632-8-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/mm_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 0f64909e8d20..17498a13eb99 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -332,8 +332,7 @@ static unsigned long __init early_calculate_totalpages(void) unsigned long pages = end_pfn - start_pfn; totalpages += pages; - if (pages) - node_set_state(nid, N_MEMORY); + node_set_state(nid, N_MEMORY); } return totalpages; } From 2db44dc8bdd3cfc933a3193724f4d38b03e35b80 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Wed, 1 Jul 2026 00:04:13 +0900 Subject: [PATCH 10/25] mm/hugetlb: remove unnecessary empty range check in hugetlb_bootmem_set_nodes() hugetlb_bootmem_set_nodes() iterates the memory ranges with for_each_mem_pfn_range() and calls node_set(nid, hugetlb_bootmem_nodes) only when end_pfn > start_pfn. for_each_mem_pfn_range() never returns an empty range, so start_pfn < end_pfn always. Therefore the check is unnecessary, so remove it. start_pfn and end_pfn are no longer used, so remove the local variables. No functional change. Signed-off-by: Sang-Heon Jeon Reviewed-by: David Hildenbrand (Arm) Link: https://patch.msgid.link/20260630150413.1718632-9-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/hugetlb.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 571212b80835..4f7020962486 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -4444,15 +4444,12 @@ hugetlb_early_param("default_hugepagesz", default_hugepagesz_setup); void __init hugetlb_bootmem_set_nodes(void) { int i, nid; - unsigned long start_pfn, end_pfn; if (!nodes_empty(hugetlb_bootmem_nodes)) return; - for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, &nid) { - if (end_pfn > start_pfn) - node_set(nid, hugetlb_bootmem_nodes); - } + for_each_mem_pfn_range(i, MAX_NUMNODES, NULL, NULL, &nid) + node_set(nid, hugetlb_bootmem_nodes); } void __init hugetlb_bootmem_alloc(void) From eb0e5b61369f7614574fe3262c1cc3c88441e04d Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Tue, 30 Jun 2026 10:22:11 +0300 Subject: [PATCH 11/25] mm/mm_init: don't overlap NORMAL and MOVABLE zones with kernelcore=mirror When kernelcore or movablecore kernel parameters define size of the NORMAL and MOVABLE zones as percents of the total memory or by absolute value, ZONE_NORMAL is clamped at the beginning of ZONE_MOVABLE. However, when kernelcore=mirror the ZONE_NORMAL span is not changed but rather pages from ZONE_MOVABLE counted as absent in ZONE_NORMAL. Make the behaviour of kernelcore= parameter uniform and treat mirror just as another way to size the zones. Co-developed-by: Wei Yang Signed-off-by: Wei Yang Link: https://patch.msgid.link/20260630072212.624305-2-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: David Hildenbrand (Arm) --- mm/mm_init.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 0f64909e8d20..57923dd33d06 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1174,9 +1174,8 @@ static void __init adjust_zone_range_for_zone_movable(int nid, arch_zone_highest_possible_pfn[movable_zone]); /* Adjust for ZONE_MOVABLE starting within this range */ - } else if (!mirrored_kernelcore && - *zone_start_pfn < zone_movable_pfn[nid] && - *zone_end_pfn > zone_movable_pfn[nid]) { + } else if (*zone_start_pfn < zone_movable_pfn[nid] && + *zone_end_pfn > zone_movable_pfn[nid]) { *zone_end_pfn = zone_movable_pfn[nid]; /* Check if this whole range is within ZONE_MOVABLE */ @@ -1224,40 +1223,11 @@ static unsigned long __init zone_absent_pages_in_node(int nid, unsigned long zone_start_pfn, unsigned long zone_end_pfn) { - unsigned long nr_absent; - /* zone is empty, we don't have any absent pages */ if (zone_start_pfn == zone_end_pfn) return 0; - nr_absent = __absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn); - - /* - * ZONE_MOVABLE handling. - * Treat pages to be ZONE_MOVABLE in ZONE_NORMAL as absent pages - * and vice versa. - */ - if (mirrored_kernelcore && zone_movable_pfn[nid]) { - unsigned long start_pfn, end_pfn; - struct memblock_region *r; - - for_each_mem_region(r) { - start_pfn = clamp(memblock_region_memory_base_pfn(r), - zone_start_pfn, zone_end_pfn); - end_pfn = clamp(memblock_region_memory_end_pfn(r), - zone_start_pfn, zone_end_pfn); - - if (zone_type == ZONE_MOVABLE && - memblock_is_mirror(r)) - nr_absent += end_pfn - start_pfn; - - if (zone_type == ZONE_NORMAL && - !memblock_is_mirror(r)) - nr_absent += end_pfn - start_pfn; - } - } - - return nr_absent; + return __absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn); } /* From f042b69ebb9f049403a73df504567477dd0c5f65 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Tue, 30 Jun 2026 10:22:12 +0300 Subject: [PATCH 12/25] mm/mm_init: drop overlap_memmap_init() When ZONE_NORMAL and ZONE_MOVABLE could overlap because kernelcore=mirror didn't reduce the span of ZONE_NORMAL, initialization of the memory map had to skip overlapping pages during initialization of ZONE_MOVABLE to avoid double initialization of the same struct pages. Since kernelcore=mirror works now the same way as other variants of kernelcore=/movablecore=, and adjusts the span of ZONE_NORMAL, there can't be an overlap between ZONE_NORMAL and ZONE_MOVABLE. Remove overlap_memmap_init(). Co-developed-by: Wei Yang Signed-off-by: Wei Yang Reviewed-by: David Hildenbrand (Arm) Link: https://patch.msgid.link/20260630072212.624305-3-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: David Hildenbrand (Arm) --- mm/mm_init.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 57923dd33d06..838b5a0ad98d 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -799,28 +799,6 @@ void __meminit init_deferred_page(unsigned long pfn, int nid) __init_deferred_page(pfn, nid); } -/* If zone is ZONE_MOVABLE but memory is mirrored, it is an overlapped init */ -static bool __meminit -overlap_memmap_init(unsigned long zone, unsigned long *pfn) -{ - static struct memblock_region *r __meminitdata; - - if (mirrored_kernelcore && zone == ZONE_MOVABLE) { - if (!r || *pfn >= memblock_region_memory_end_pfn(r)) { - for_each_mem_region(r) { - if (*pfn < memblock_region_memory_end_pfn(r)) - break; - } - } - if (*pfn >= memblock_region_memory_base_pfn(r) && - memblock_is_mirror(r)) { - *pfn = memblock_region_memory_end_pfn(r); - return true; - } - } - return false; -} - /* * Only struct pages that correspond to ranges defined by memblock.memory * are zeroed and initialized by going through __init_single_page() during @@ -907,8 +885,6 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone * function. They do not exist on hotplugged memory. */ if (context == MEMINIT_EARLY) { - if (overlap_memmap_init(zone, &pfn)) - continue; if (defer_init(nid, pfn, zone_end_pfn)) { deferred_struct_pages = true; break; From 2ebce860bdd7ae5e13002811bc9bbbf33fcfc221 Mon Sep 17 00:00:00 2001 From: Gregory Price Date: Wed, 1 Jul 2026 18:16:13 -0400 Subject: [PATCH 13/25] mm/mm_init: handle alloc_percpu failure in free_area_init_core_hotplug We miss a failed allocation check for pgdat->per_cpu_nodestats, which results in a NULL deref when we offset into the per-cpu area. Propagate -ENOMEM up the stack and leave per_cpu_nodestats pointing at boot_nodestats so a later online can retry the allocation. hotadd_init_pgdat() returns NULL on failure, which __try_online_node() already maps to -ENOMEM. On failure nothing needs to be unwound: - the node is never marked online - per_cpu_nodestats is left pointing at boot_nodestats - __add_memory_resource() cleans up pending memblock resources - later online attempts retry the per_cpu_nodestats allocation Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260627202243.758289-1-gourry%40gourry.net Fixes: 75ef71840539 ("mm, vmstat: add infrastructure for per-node vmstats") Signed-off-by: Gregory Price Acked-by: David Hildenbrand (Arm) Link: https://patch.msgid.link/20260701221613.2818148-1-gourry@gourry.net Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/memory_hotplug.h | 2 +- mm/memory_hotplug.c | 3 ++- mm/mm_init.c | 14 +++++++++++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 7c9d66729c60..06c58cb05779 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -289,7 +289,7 @@ static inline void __remove_memory(u64 start, u64 size) {} /* Default online_type (MMOP_*) when new memory blocks are added. */ extern enum mmop mhp_get_default_online_type(void); extern void mhp_set_default_online_type(enum mmop online_type); -extern void __ref free_area_init_core_hotplug(struct pglist_data *pgdat); +int __ref free_area_init_core_hotplug(struct pglist_data *pgdat); extern int __add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags); extern int add_memory(int nid, u64 start, u64 size, mhp_t mhp_flags); extern int add_memory_resource(int nid, struct resource *resource, diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 7ac19fab2263..8b137328dcf0 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1263,7 +1263,8 @@ static pg_data_t *hotadd_init_pgdat(int nid) pgdat = NODE_DATA(nid); /* init node's zones as empty zones, we don't have any present pages.*/ - free_area_init_core_hotplug(pgdat); + if (free_area_init_core_hotplug(pgdat)) + return NULL; /* * The node we allocated has no zone fallback lists. For avoiding diff --git a/mm/mm_init.c b/mm/mm_init.c index 0d2eb82fa068..1ba1181d8ef9 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1535,7 +1535,7 @@ void __init set_pageblock_order(void) * NOTE: this function is only called during memory hotplug */ #ifdef CONFIG_MEMORY_HOTPLUG -void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) +int __ref free_area_init_core_hotplug(struct pglist_data *pgdat) { int nid = pgdat->node_id; enum zone_type z; @@ -1543,8 +1543,14 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) pgdat_init_internals(pgdat); - if (pgdat->per_cpu_nodestats == &boot_nodestats) - pgdat->per_cpu_nodestats = alloc_percpu(struct per_cpu_nodestat); + if (pgdat->per_cpu_nodestats == &boot_nodestats) { + struct per_cpu_nodestat __percpu *p; + + p = alloc_percpu(struct per_cpu_nodestat); + if (!p) + return -ENOMEM; + pgdat->per_cpu_nodestats = p; + } /* * Reset the nr_zones, order and highest_zoneidx before reuse. @@ -1575,6 +1581,8 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) zone->present_pages = 0; zone_init_internals(zone, z, nid, 0); } + + return 0; } #endif From abdbd8329281f40afd381346410d6d43604af82c Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:21 +0900 Subject: [PATCH 14/25] mm: numa_memblks: set numa_nodes_parsed in numa_add_memblk() Every existing numa_add_memblk() caller separately marks the new node in numa_nodes_parsed with node_set(). Set the node in numa_add_memblk() itself on a successful add, so this no longer depends on each caller. numa_add_memblk_to() now returns -EINVAL for an out-of-range node id, so a zero return implies @nid was valid. No caller passes an invalid one, so existing callers are unaffected. The per-caller node_set() calls are removed in later patches. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260703041329.2797584-2-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/numa_memblks.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/mm/numa_memblks.c b/mm/numa_memblks.c index 3c3c4eac3514..844c71d6d4c0 100644 --- a/mm/numa_memblks.c +++ b/mm/numa_memblks.c @@ -135,13 +135,20 @@ EXPORT_SYMBOL(__node_distance); static int __init numa_add_memblk_to(int nid, u64 start, u64 end, struct numa_meminfo *mi) { + /* whine about and ignore invalid nid */ + if (nid < 0 || nid >= MAX_NUMNODES) { + pr_warn("Warning: invalid memblk node id %d [mem %#010Lx-%#010Lx]\n", + nid, start, end - 1); + return -EINVAL; + } + /* ignore zero length blks */ if (start == end) return 0; - /* whine about and ignore invalid blks */ - if (start > end || nid < 0 || nid >= MAX_NUMNODES) { - pr_warn("Warning: invalid memblk node %d [mem %#010Lx-%#010Lx]\n", + /* whine about and ignore invalid ranges */ + if (start > end) { + pr_warn("Warning: invalid memblk range for node %d [mem %#010Lx-%#010Lx]\n", nid, start, end - 1); return 0; } @@ -193,13 +200,20 @@ static void __init numa_move_tail_memblk(struct numa_meminfo *dst, int idx, * @end: End address of the new memblk * * Add a new memblk to the default numa_meminfo. + * On success @nid is also set in numa_nodes_parsed. * * RETURNS: * 0 on success, -errno on failure. */ int __init numa_add_memblk(int nid, u64 start, u64 end) { - return numa_add_memblk_to(nid, start, end, &numa_meminfo); + int ret; + + ret = numa_add_memblk_to(nid, start, end, &numa_meminfo); + if (!ret) + node_set(nid, numa_nodes_parsed); + + return ret; } /** From 7cbdade40fb8f440c13ccd7a02d104bf32285187 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:22 +0900 Subject: [PATCH 15/25] ACPI: NUMA: remove redundant numa_nodes_parsed node_set() numa_add_memblk() now sets the node in numa_nodes_parsed itself, so the caller's own node_set() is redundant. Remove it. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260703041329.2797584-3-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- drivers/acpi/numa/srat.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c index 62d4a8df0b8c..5c407dc6401e 100644 --- a/drivers/acpi/numa/srat.c +++ b/drivers/acpi/numa/srat.c @@ -399,8 +399,6 @@ acpi_parse_memory_affinity(union acpi_subtable_headers *header, goto out_err_bad_srat; } - node_set(node, numa_nodes_parsed); - pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n", node, pxm, (unsigned long long) start, (unsigned long long) end - 1, From 3b1e5d902dfa832e4b175cb1f5a000236d45ceb5 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:23 +0900 Subject: [PATCH 16/25] of/numa: remove redundant numa_nodes_parsed node_set() numa_add_memblk() now sets the node in numa_nodes_parsed itself, so the caller's own node_set() is redundant. Remove it. No functional change. Signed-off-by: Sang-Heon Jeon Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260703041329.2797584-4-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- drivers/of/of_numa.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/of/of_numa.c b/drivers/of/of_numa.c index cd2dc8e825c9..230d5f628c1b 100644 --- a/drivers/of/of_numa.c +++ b/drivers/of/of_numa.c @@ -59,11 +59,8 @@ static int __init of_numa_parse_memory_nodes(void) r = -EINVAL; } - for (i = 0; !r && !of_address_to_resource(np, i, &rsrc); i++) { + for (i = 0; !r && !of_address_to_resource(np, i, &rsrc); i++) r = numa_add_memblk(nid, rsrc.start, rsrc.end + 1); - if (!r) - node_set(nid, numa_nodes_parsed); - } if (!i || r) { of_node_put(np); From 63fa742bae02f0d2ffe95ff540a51837815abc5c Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:24 +0900 Subject: [PATCH 17/25] x86/numa: remove redundant numa_nodes_parsed node_set() numa_add_memblk() now sets the node in numa_nodes_parsed itself, so the caller's own node_set() is redundant. Remove it. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260703041329.2797584-5-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/x86/mm/amdtopology.c | 1 - arch/x86/mm/numa.c | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/x86/mm/amdtopology.c b/arch/x86/mm/amdtopology.c index f980b0eb0105..1cb581bbf2b6 100644 --- a/arch/x86/mm/amdtopology.c +++ b/arch/x86/mm/amdtopology.c @@ -150,7 +150,6 @@ int __init amd_numa_init(void) prevbase = base; numa_add_memblk(nodeid, base, limit); - node_set(nodeid, numa_nodes_parsed); } if (nodes_empty(numa_nodes_parsed)) diff --git a/arch/x86/mm/numa.c b/arch/x86/mm/numa.c index 99d0a9332c14..ced66e68a68e 100644 --- a/arch/x86/mm/numa.c +++ b/arch/x86/mm/numa.c @@ -216,7 +216,6 @@ static int __init dummy_numa_init(void) printk(KERN_INFO "Faking a node at [mem %#018Lx-%#018Lx]\n", 0LLU, PFN_PHYS(max_pfn) - 1); - node_set(0, numa_nodes_parsed); node_set(0, numa_phys_nodes_parsed); numa_add_memblk(0, 0, PFN_PHYS(max_pfn)); From 8b9cecbdc78c5a6cfaaf3b00ce7ebe05cf5417e7 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:25 +0900 Subject: [PATCH 18/25] arch_numa: remove redundant numa_nodes_parsed node_set() numa_add_memblk() now sets the node in numa_nodes_parsed itself, so the caller's own node_set() is redundant. Remove it. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260703041329.2797584-6-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- drivers/base/arch_numa.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/base/arch_numa.c b/drivers/base/arch_numa.c index c99f2ab105e5..19bc806313c4 100644 --- a/drivers/base/arch_numa.c +++ b/drivers/base/arch_numa.c @@ -283,7 +283,6 @@ static int __init dummy_numa_init(void) pr_err("NUMA init failed\n"); return ret; } - node_set(0, numa_nodes_parsed); numa_off = true; return 0; From 3aeac07c5b1c3399487f4b38182f8cfbc3dbbd53 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:26 +0900 Subject: [PATCH 19/25] LoongArch: remove redundant numa_nodes_parsed node_set() numa_add_memblk() now sets the node in numa_nodes_parsed itself, so the caller's own node_set() is redundant. Remove it. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260703041329.2797584-7-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- arch/loongarch/kernel/numa.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/loongarch/kernel/numa.c b/arch/loongarch/kernel/numa.c index 8b89898e20df..c96c53623715 100644 --- a/arch/loongarch/kernel/numa.c +++ b/arch/loongarch/kernel/numa.c @@ -216,7 +216,6 @@ static int __init fake_numa_init(void) phys_addr_t start = memblock_start_of_DRAM(); phys_addr_t end = memblock_end_of_DRAM() - 1; - node_set(0, numa_nodes_parsed); pr_info("Faking a node at [mem %pap-%pap]\n", &start, &end); return numa_add_memblk(0, start, end + 1); From a9bafc1832d2db97813069823821ed333b8ecda6 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:27 +0900 Subject: [PATCH 20/25] mm: numa_memblks: remove redundant numa_nodemask_from_meminfo() numa_add_memblk() now sets each added node in numa_nodes_parsed, so numa_nodes_parsed already contains every node that owns memory. The nodes numa_nodemask_from_meminfo() adds from numa_meminfo are already set, so the calls in numa_alloc_distance() and numa_register_meminfo() are redundant. So remove both call sites and the unused function itself. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260703041329.2797584-8-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/numa_memblks.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/mm/numa_memblks.c b/mm/numa_memblks.c index 844c71d6d4c0..67dc9f4edd7b 100644 --- a/mm/numa_memblks.c +++ b/mm/numa_memblks.c @@ -17,20 +17,6 @@ nodemask_t numa_nodes_parsed __initdata; static struct numa_meminfo numa_meminfo __initdata_or_meminfo; static struct numa_meminfo numa_reserved_meminfo __initdata_or_meminfo; -/* - * Set nodes, which have memory in @mi, in *@nodemask. - */ -static void __init numa_nodemask_from_meminfo(nodemask_t *nodemask, - const struct numa_meminfo *mi) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mi->blk); i++) - if (mi->blk[i].start != mi->blk[i].end && - mi->blk[i].nid != NUMA_NO_NODE) - node_set(mi->blk[i].nid, *nodemask); -} - /** * numa_reset_distance - Reset NUMA distance table * @@ -56,7 +42,6 @@ static int __init numa_alloc_distance(void) /* size the new table and allocate it */ nodes_parsed = numa_nodes_parsed; - numa_nodemask_from_meminfo(&nodes_parsed, &numa_meminfo); for_each_node_mask(i, nodes_parsed) cnt = i; @@ -415,7 +400,6 @@ static int __init numa_register_meminfo(struct numa_meminfo *mi) /* Account for nodes with cpus and no memory */ node_possible_map = numa_nodes_parsed; - numa_nodemask_from_meminfo(&node_possible_map, mi); if (WARN_ON(nodes_empty(node_possible_map))) return -EINVAL; From f5a77a50a14dffb659ee8f550c824f8280e37fce Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:28 +0900 Subject: [PATCH 21/25] arch_numa: remove redundant node_possible_map assignment numa_register_meminfo() sets node_possible_map to numa_nodes_parsed. The later assignment in numa_register_nodes() is therefore redundant, so remove it. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260703041329.2797584-9-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- drivers/base/arch_numa.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/base/arch_numa.c b/drivers/base/arch_numa.c index 19bc806313c4..d4e426aa51c5 100644 --- a/drivers/base/arch_numa.c +++ b/drivers/base/arch_numa.c @@ -221,9 +221,6 @@ static int __init numa_register_nodes(void) node_set_online(nid); } - /* Setup online nodes to actual nodes*/ - node_possible_map = numa_nodes_parsed; - return 0; } From e55424c84afd48aa2f0f761ae0c006128ef541cf Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 3 Jul 2026 13:13:29 +0900 Subject: [PATCH 22/25] mm: numa_memblks: use numa_add_reserved_memblk() in numa_cleanup_meminfo() numa_cleanup_meminfo() calls the internal numa_add_memblk_to() to add a block to numa_reserved_meminfo, even though numa_add_reserved_memblk() wraps exactly that. Use the wrapper instead, so numa_add_memblk_to() is reached only through its two wrappers. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260703041329.2797584-10-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/numa_memblks.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mm/numa_memblks.c b/mm/numa_memblks.c index 67dc9f4edd7b..be6e7346f6c7 100644 --- a/mm/numa_memblks.c +++ b/mm/numa_memblks.c @@ -255,8 +255,7 @@ int __init numa_cleanup_meminfo(struct numa_meminfo *mi) /* preserve info for non-RAM areas above 'max_pfn': */ if (bi->end > high) { - numa_add_memblk_to(bi->nid, high, bi->end, - &numa_reserved_meminfo); + numa_add_reserved_memblk(bi->nid, high, bi->end); bi->end = high; } From d7bfd98ba01d2af48cc238d5525201c83fdef251 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Tue, 21 Jul 2026 00:09:13 +0900 Subject: [PATCH 23/25] mm/mm_init: remove redundant memset in free_area_init() zone_movable_pfn is zero-initialized and only set by find_zone_movable_pfns_for_nodes(), which runs once during boot right after the memset, so the memset has no effect. Remove redundant memset. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260720150915.756749-1-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/mm_init.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 0f64909e8d20..10eda88bf476 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1836,7 +1836,6 @@ static void __init free_area_init(void) } /* Find the PFNs that ZONE_MOVABLE begins at in each node */ - memset(zone_movable_pfn, 0, sizeof(zone_movable_pfn)); find_zone_movable_pfns_for_nodes(); /* Print out the zone ranges */ From 78fac571b06490654af45c902c7ab5f651fe7067 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Thu, 30 Jul 2026 00:51:15 +0900 Subject: [PATCH 24/25] mm/mm_init: remove unnecessary initialization of pgdat->per_cpu_nodestats free_area_init_node() sets pgdat->per_cpu_nodestats to NULL and later calls free_area_init_core(), which unconditionally overwrites it with &boot_nodestats. Nothing reads the field in between, so the store has no effect. Remove unnecessary initialization. No functional change. Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260729155143.177790-1-ekffu200098@gmail.com Signed-off-by: Mike Rapoport (Microsoft) --- mm/mm_init.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 10eda88bf476..acba6b30b535 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1713,7 +1713,6 @@ static void __init free_area_init_node(int nid) pgdat->node_id = nid; pgdat->node_start_pfn = start_pfn; - pgdat->per_cpu_nodestats = NULL; if (start_pfn != end_pfn) { pr_info("Initmem setup node %d [mem %#018Lx-%#018Lx]\n", nid, From 97090500d776c3f6d08e857e3a0a7cf092999094 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 7 Aug 2026 03:12:43 +0000 Subject: [PATCH 25/25] mm/mm_init: deferred_grow_zone(): fix out-of-range first_deferred_pfn With CONFIG_DEFERRED_STRUCT_PAGE_INIT enabled, deferred_grow_zone() initializes struct pages early in boot to satisfy an allocation. With a large CMA reservation in place, the ranges deferred_init_memmap() finds may not add up to the allocation it was asked for, and the function ends up initializing the memory map of the entire zone and still falls short. That is fine in itself: the function accounts for it and leaves the caller to decide whether it now has enough memory. However, the update of pgdat->first_deferred_pfn that tracks where uninitialized memory map starts could overflow. If the node's RAM end is not aligned on PAGES_PER_SECTION boundaries and some deferred struct pages were initialized, pgdat->first_deferred_pfn would point past the end of the node's memory. deferred_init_memmap() later picks up from pgdat->first_deferred_pfn and hits a BUG_ON(), because it expects a pfn within its node. For example, when running a kernel with CONFIG_DEFERRED_STRUCT_PAGE_INIT=y and CONFIG_CMA=y using the following qemu command line qemu-system-x86_64 -enable-kvm -m 8032M -kernel bzImage \ -append "nokaslr cma=4768M@0x100000000" the kernel panics: kernel BUG at mm/mm_init.c:2131! CPU: 3 UID: 0 PID: 36 Comm: pgdatinit0 Not tainted 7.2.0-rc6 #1 RIP: 0010:deferred_init_memmap+0x1b8/0x1c0 RAX: 0000000000236000 R13: 0000000000238000 Call Trace: kthread+0xdf/0x120 ret_from_fork+0x187/0x250 Make sure that the update of pgdta->first_deferred_pfn does not overflow when the entire zone's (and therefore node's) memory map is initialized. Fixes: 3acb913c9d5b ("mm/mm_init: use deferred_init_memmap_chunk() in deferred_grow_zone()") Cc: stable@vger.kernel.org Assisted-by: Kiro:claude-opus-5 Signed-off-by: Alexander Graf Link: https://patch.msgid.link/20260807031243.87904-1-graf@amazon.com [rppt: massaged the changelog] Signed-off-by: Mike Rapoport (Microsoft) --- mm/mm_init.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 1ba1181d8ef9..37a425a14595 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -2214,10 +2214,13 @@ bool __init deferred_grow_zone(struct zone *zone, unsigned int order) } /* - * There were no pages to initialize and free which means the zone's - * memory map is completely initialized. + * The loop only tests spfn before entering an iteration, so on exit it + * may point up to a section past the end of the zone. When it does, + * the rest of the zone has already been handed to + * deferred_init_memmap_chunk() and nothing is left to initialize. */ - pgdat->first_deferred_pfn = nr_pages ? spfn : ULONG_MAX; + pgdat->first_deferred_pfn = + spfn < zone_end_pfn(zone) ? spfn : ULONG_MAX; pgdat_resize_unlock(pgdat, &flags);