RE: [PATCH v4 1/2] mm: move overlap memory map init check to memmap_init()
From: Liu, Yuan1
Date: Wed Apr 22 2026 - 05:30:32 EST
> -----Original Message-----
> From: Wei Yang <richard.weiyang@xxxxxxxxx>
> Sent: Wednesday, April 22, 2026 11:27 AM
> To: Wei Yang <richard.weiyang@xxxxxxxxx>
> Cc: Liu, Yuan1 <yuan1.liu@xxxxxxxxx>; David Hildenbrand
> <david@xxxxxxxxxx>; Oscar Salvador <osalvador@xxxxxxx>; Mike Rapoport
> <rppt@xxxxxxxxxx>; linux-mm@xxxxxxxxx; Hu, Yong <yong.hu@xxxxxxxxx>; Zou,
> Nanhai <nanhai.zou@xxxxxxxxx>; Tim Chen <tim.c.chen@xxxxxxxxxxxxxxx>;
> Zhuo, Qiuxu <qiuxu.zhuo@xxxxxxxxx>; Chen, Yu C <yu.c.chen@xxxxxxxxx>;
> Deng, Pan <pan.deng@xxxxxxxxx>; Li, Tianyou <tianyou.li@xxxxxxxxx>; Chen
> Zhang <zhangchen.kidd@xxxxxx>; linux-kernel@xxxxxxxxxxxxxxx
> Subject: Re: [PATCH v4 1/2] mm: move overlap memory map init check to
> memmap_init()
>
> On Wed, Apr 22, 2026 at 01:11:26AM +0000, Wei Yang wrote:
> >On Tue, Apr 21, 2026 at 08:55:07AM -0400, Yuan Liu wrote:
> >>Move the overlap memmap init check from memmap_init_range() into
> >>memmap_init().
> >>
> >>When mirrored kernelcore is enabled, avoid memory map initialization
> >>for overlap regions. There are two cases that may overlap: a mirror
> >>memory region assigned to movable zone, or a non-mirror memory region
> >>assigned to a non-movable zone but falling within the movable zone
> >>range.
> >>
> >>Signed-off-by: Yuan Liu <yuan1.liu@xxxxxxxxx>
> >>---
> >> mm/mm_init.c | 37 +++++++++++++------------------------
> >> 1 file changed, 13 insertions(+), 24 deletions(-)
> >>
> >>diff --git a/mm/mm_init.c b/mm/mm_init.c
> >>index df34797691bd..2b5233060504 100644
> >>--- a/mm/mm_init.c
> >>+++ b/mm/mm_init.c
> >>@@ -797,28 +797,6 @@ void __meminit reserve_bootmem_region(phys_addr_t
> start,
> >> }
> >> }
> >>
> >>-/* 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;
> >>-
> >>- 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
> >>@@ -905,8 +883,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;
> >>@@ -971,6 +947,7 @@ static void __init memmap_init(void)
> >>
> >> for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, &nid)
> {
> >> struct pglist_data *node = NODE_DATA(nid);
> >>+ struct memblock_region *r = &memblock.memory.regions[i];
> >>
> >> for (j = 0; j < MAX_NR_ZONES; j++) {
> >> struct zone *zone = node->node_zones + j;
> >>@@ -978,6 +955,18 @@ static void __init memmap_init(void)
> >> if (!populated_zone(zone))
> >> continue;
> >>
> >>+ if (mirrored_kernelcore) {
> >>+ const bool is_mirror = memblock_is_mirror(r);
> >>+ const bool is_movable_zone = (j == ZONE_MOVABLE);
> >>+
> >>+ if (is_mirror && is_movable_zone)
> >>+ continue;
> >>+
> >>+ if (!is_mirror && !is_movable_zone &&
> >>+ start_pfn >= zone_movable_pfn[nid])
> >>+ continue;
> >
> >IIUC, when mirrored_kernelcore is set but !memblock_has_mirror() or
> >is_kdump_kernel(), zone_movable_pfn[nid] is kept to be 0.
> >
> >This means it will skip all memory regions.
> >
>
> Did some tests. When mirrored_kernelcore && !memblock_has_mirror(), which
> means there is no is_mirror memblock. This will leave
> zone_movable_pfn[nid] 0.
>
> So for all memory regions, the above logic will skip them.
>
> Adjust the code as below, my local test could pass and kernel bootup as
> expected.
>
> From 6351ac79a17edbfd830510fba2959ddc47b17258 Mon Sep 17 00:00:00 2001
> From: Wei Yang <richard.weiyang@xxxxxxxxx>
> Date: Wed, 22 Apr 2026 09:13:24 +0800
> Subject: [PATCH] skip overlap region higher level
>
> ---
> mm/mm_init.c | 29 ++++++++++++++++++++++-------
> 1 file changed, 22 insertions(+), 7 deletions(-)
>
> diff --git a/mm/mm_init.c b/mm/mm_init.c
> index 79f93f2a90cf..7a85ba58e87f 100644
> --- a/mm/mm_init.c
> +++ b/mm/mm_init.c
> @@ -916,8 +916,8 @@ 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 (overlap_memmap_init(zone, &pfn))
> + // continue;
> if (defer_init(nid, pfn, zone_end_pfn)) {
> deferred_struct_pages = true;
> break;
> @@ -974,6 +974,17 @@ static void __init memmap_init_zone_range(struct zone
> *zone,
> *hole_pfn = end_pfn;
> }
>
> +static bool __init region_overlapped(struct memblock_region *rgn,
> unsigned long zone_type)
> +{
> + if (zone_type == ZONE_MOVABLE && memblock_is_mirror(rgn))
> + return true;
> +
> + if (zone_type == ZONE_NORMAL && !memblock_is_mirror(rgn))
> + return true;
> +
> + return false;
> +}
> +
> static void __init memmap_init(void)
> {
> unsigned long start_pfn, end_pfn;
> @@ -985,10 +996,15 @@ static void __init memmap_init(void)
>
> for (j = 0; j < MAX_NR_ZONES; j++) {
> struct zone *zone = node->node_zones + j;
> + struct memblock_region *r = &memblock.memory.regions[i];
>
> if (!populated_zone(zone))
> continue;
>
> + if (mirrored_kernelcore && zone_movable_pfn[nid] &&
> + region_overlapped(r, j))
> + continue;
> +
> memmap_init_zone_range(zone, start_pfn, end_pfn,
> &hole_pfn);
> zone_id = j;
> @@ -1257,13 +1273,12 @@ static unsigned long __init
> zone_absent_pages_in_node(int nid,
> 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 (start_pfn == end_pfn)
> + continue;
>
> - if (zone_type == ZONE_NORMAL &&
> - !memblock_is_mirror(r))
> + if (region_overlapped(r, zone_type))
> nr_absent += end_pfn - start_pfn;
> +
> }
> }
Hi Wei Yang
I ran some tests based on this patch and didn't observe any issues.
Thanks for the patch.
> Want to confirm, the logic in zone_absent_pages_in_node() only handle
> ZONE_NORMAL and ZONE_MOVABLE. So the assumption is ZONE_MOVABLE only could
> overlap with ZONE_NORMAL?
I think memory below 4GB does not include mirrored memory and 4GB is also
the upper boundary of DMA32, ZONE_MOVABLE may only overlap with ZONE_NORMAL
This is just my understanding, so it would be good to get a clearer confirmation
from David and Mike.
void __init arch_zone_limits_init(unsigned long *max_zone_pfns) {
...
/* 4GB broken PCI/AGP hardware bus master zone */
#define MAX_DMA32_PFN (1UL << (32 - PAGE_SHIFT))
...
}
static void __init find_zone_movable_pfns_for_nodes(void) {
...
if (usable_startpfn < PHYS_PFN(SZ_4G)) {
mem_below_4gb_not_mirrored = true;
continue;
}
...
}
> When kernelcore=[nn]M is used, the "highest" populated zone is picked up
> to be
> ZONE_MOVABLE, as indicated by find_usable_zone_for_movable(). So looks it
> is
> possible to choose ZONE_DMA32 as ZONE_MOVABLE.
>
> For kernelcore=mirror, we want to eliminate the complexity?
>
> --
> Wei Yang
> Help you, Help me