* [PATCH v3 1/5] kho: reject zero bootmem scale
2026-11-12 10:38 [PATCH v3 0/5] kho: fix bootmem sizing Mike Rapoport (Microsoft)
@ 2026-11-12 10:38 ` Mike Rapoport (Microsoft)
2026-11-12 10:38 ` [PATCH v3 2/5] kho: walk bootmem areas in address order Mike Rapoport (Microsoft)
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-11-12 10:38 UTC (permalink / raw)
To: Jonathan Corbet, Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Chen Pei, George Guo, Kexin Liu,
Randy Dunlap, Shuah Khan, Sourabh Jain, kexec, linux-doc,
linux-kernel, linux-mm
kho_bootmem=0% sets bootmem_scale to 0, and then the bootmem areas are
sized using the explicit sizes that default to 0. KHO remains enabled,
but there is no usable bootmem to load the next kernel.
Reject zero scale the same way zero explicit sizes are rejected.
Fixes: 3dc92c311498 ("kexec: add Kexec HandOver (KHO) generation helpers")
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
kernel/liveupdate/kexec_handover.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 6f1f55fe31819..b30ba686cb301 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -703,16 +703,22 @@ static int __init kho_parse_bootmem_size(char *p)
if (p[len - 1] == '%') {
/* unsigned int max is 4,294,967,295, 10 chars */
char s_scale[11] = {};
+ unsigned int scale;
int ret = 0;
if (len > ARRAY_SIZE(s_scale))
return -EINVAL;
memcpy(s_scale, p, len - 1);
- ret = kstrtouint(s_scale, 10, &bootmem_scale);
- if (!ret)
- pr_notice("bootmem scale is %d%%\n", bootmem_scale);
- return ret;
+ ret = kstrtouint(s_scale, 10, &scale);
+ if (ret)
+ return ret;
+ if (!scale)
+ return -EINVAL;
+
+ bootmem_scale = scale;
+ pr_notice("bootmem scale is %d%%\n", bootmem_scale);
+ return 0;
}
/* parse ll[KMG],mm[KMG],nn[KMG] */
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v3 2/5] kho: walk bootmem areas in address order
2026-11-12 10:38 [PATCH v3 0/5] kho: fix bootmem sizing Mike Rapoport (Microsoft)
2026-11-12 10:38 ` [PATCH v3 1/5] kho: reject zero bootmem scale Mike Rapoport (Microsoft)
@ 2026-11-12 10:38 ` Mike Rapoport (Microsoft)
2026-11-12 10:38 ` [PATCH v3 3/5] kho: skip zero-sized lowmem and global bootmem areas Mike Rapoport (Microsoft)
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-11-12 10:38 UTC (permalink / raw)
To: Jonathan Corbet, Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Chen Pei, George Guo, Kexin Liu,
Randy Dunlap, Shuah Khan, Sourabh Jain, kexec, linux-doc,
linux-kernel, linux-mm, Jose A. Perez de Azpillaga,
Mukesh Pilaniya
kexec_add_buffer() users may depend on the order in which memory holes are
searched. For instance, arm64 places the kernel bottom-up and then
requires the DTB to be above the kernel.
kho_walk_bootmem() walks the bootmem areas in the order they were
reserved, so if the kernel lands in a small area at the top of the memory
there is no room left for the DTB and kexec fails. For example, on an
arm64 VM with 2G of RAM and kho_bootmem=8M,64M,64M, the lowmem area is
reserved first at the top of the memory, the kernel image takes most of
it and kexec fails with
kexec_file(Image): Could not find any suitable kernel location!
Sort the bootmem areas by address and walk them in ascending order, or in
descending order for top-down searches, like kexec_walk_resources() does.
Fixes: 3bdecc3c93f9 ("kexec: add KHO support to kexec file loads")
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
Reviewed-by: Mukesh Pilaniya <mpilaniy@redhat.com>
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
kernel/liveupdate/kexec_handover.c | 57 ++++++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 11 deletions(-)
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index b30ba686cb301..df4a972a860da 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -26,6 +26,7 @@
#include <linux/list.h>
#include <linux/memblock.h>
#include <linux/page-isolation.h>
+#include <linux/sort.h>
#include <linux/unaligned.h>
#include <linux/vmalloc.h>
@@ -826,6 +827,14 @@ bool kho_bootmem_overlap(phys_addr_t phys, size_t size)
return false;
}
+static int __init kho_bootmem_cmp(const void *_a, const void *_b)
+{
+ const struct kho_bootmem *a = _a;
+ const struct kho_bootmem *b = _b;
+
+ return cmp_int(a->addr, b->addr);
+}
+
/**
* kho_reserve_bootmem - Reserve a contiguous chunk of memory for kexec
*
@@ -910,6 +919,9 @@ static void __init kho_reserve_bootmem(void)
*/
kho_bootmem_cnt = i;
+ sort(kho_bootmem, kho_bootmem_cnt, sizeof(*kho_bootmem),
+ kho_bootmem_cmp, NULL);
+
return;
err_free_bootmem_areas:
@@ -2087,20 +2099,40 @@ int kho_fill_kimage(struct kimage *image)
return 0;
}
-static int kho_walk_bootmem(struct kexec_buf *kbuf,
- int (*func)(struct resource *, void *))
+static int kho_bootmem_walk_one(struct kexec_buf *kbuf,
+ const struct kho_bootmem *area,
+ int (*func)(struct resource *, void *))
+{
+ struct resource res = {
+ .start = area->addr,
+ .end = area->addr + area->size - 1,
+ };
+
+ /* Try to fit the kbuf into our KHO bootmem region */
+ return func(&res, kbuf);
+}
+
+static int kho_bootmem_walk_top_down(struct kexec_buf *kbuf,
+ int (*func)(struct resource *, void *))
{
int ret = 0;
- int i;
- for (i = 0; i < kho_bootmem_cnt; i++) {
- struct resource res = {
- .start = kho_bootmem[i].addr,
- .end = kho_bootmem[i].addr + kho_bootmem[i].size - 1,
- };
+ for (int i = kho_bootmem_cnt - 1; i >= 0; i--) {
+ ret = kho_bootmem_walk_one(kbuf, &kho_bootmem[i], func);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
- /* Try to fit the kimage into our KHO bootmem region */
- ret = func(&res, kbuf);
+static int kho_bootmem_walk_bottom_up(struct kexec_buf *kbuf,
+ int (*func)(struct resource *, void *))
+{
+ int ret = 0;
+
+ for (int i = 0; i < kho_bootmem_cnt; i++) {
+ ret = kho_bootmem_walk_one(kbuf, &kho_bootmem[i], func);
if (ret)
break;
}
@@ -2116,7 +2148,10 @@ int kho_locate_mem_hole(struct kexec_buf *kbuf,
if (!kho_enable || kbuf->image->type == KEXEC_TYPE_CRASH)
return 1;
- ret = kho_walk_bootmem(kbuf, func);
+ if (kbuf->top_down)
+ ret = kho_bootmem_walk_top_down(kbuf, func);
+ else
+ ret = kho_bootmem_walk_bottom_up(kbuf, func);
return ret == 1 ? 0 : -EADDRNOTAVAIL;
}
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v3 3/5] kho: skip zero-sized lowmem and global bootmem areas
2026-11-12 10:38 [PATCH v3 0/5] kho: fix bootmem sizing Mike Rapoport (Microsoft)
2026-11-12 10:38 ` [PATCH v3 1/5] kho: reject zero bootmem scale Mike Rapoport (Microsoft)
2026-11-12 10:38 ` [PATCH v3 2/5] kho: walk bootmem areas in address order Mike Rapoport (Microsoft)
@ 2026-11-12 10:38 ` Mike Rapoport (Microsoft)
2026-11-12 10:38 ` [PATCH v3 4/5] kho: fix bootmem sizing Mike Rapoport (Microsoft)
2026-11-12 10:38 ` [PATCH v3 5/5] kho: docs: update description of bootmem regions Mike Rapoport (Microsoft)
4 siblings, 0 replies; 6+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-11-12 10:38 UTC (permalink / raw)
To: Jonathan Corbet, Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Chen Pei, George Guo, Kexin Liu,
Randy Dunlap, Shuah Khan, Sourabh Jain, kexec, linux-doc,
linux-kernel, linux-mm, Jose A. Perez de Azpillaga,
Mukesh Pilaniya
The lowmem and the global bootmem areas can have zero size, for example
when none or all of the memory is below ARCH_LOW_ADDRESS_LIMIT, or when
either of those areas is explicitly set to zero with kho_bootmem=.
For a zero-sized allocation memblock returns an address without reserving
anything, and that empty area is recorded in kho_bootmem. If there is no
memory below ARCH_LOW_ADDRESS_LIMIT, allocation of the empty lowmem area
fails and KHO gets disabled.
Extract a helper that checks for zero size and allocates memory from
memblock, and use it to skip empty areas the same way as empty per-node
areas are skipped.
Fixes: 3dc92c311498 ("kexec: add Kexec HandOver (KHO) generation helpers")
Reported-by: Chen Pei <cp0613@linux.alibaba.com>
Closes: https://lore.kernel.org/all/20261026131851.9942-1-cp0613@linux.alibaba.com
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
Reviewed-by: Mukesh Pilaniya <mpilaniy@redhat.com>
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
kernel/liveupdate/kexec_handover.c | 66 +++++++++++++++++---------------------
1 file changed, 30 insertions(+), 36 deletions(-)
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index df4a972a860da..bbc9e644addc9 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -835,6 +835,26 @@ static int __init kho_bootmem_cmp(const void *_a, const void *_b)
return cmp_int(a->addr, b->addr);
}
+static int __init kho_reserve_bootmem_area(phys_addr_t size, phys_addr_t end,
+ int nid)
+{
+ phys_addr_t addr;
+
+ if (!size)
+ return 0;
+
+ addr = memblock_alloc_range_nid(size, BOOTMEM_ALIGNMENT_BYTES, 0, end,
+ nid, numa_valid_node(nid));
+ if (!addr)
+ return -ENOMEM;
+
+ kho_bootmem[kho_bootmem_cnt].addr = addr;
+ kho_bootmem[kho_bootmem_cnt].size = size;
+ kho_bootmem_cnt++;
+
+ return 0;
+}
+
/**
* kho_reserve_bootmem - Reserve a contiguous chunk of memory for kexec
*
@@ -846,8 +866,8 @@ static int __init kho_bootmem_cmp(const void *_a, const void *_b)
*/
static void __init kho_reserve_bootmem(void)
{
- int nid, i = 0, max_nr_bootmem;
- phys_addr_t addr, size;
+ int nid, max_nr_bootmem;
+ phys_addr_t size;
if (!kho_enable)
return;
@@ -867,66 +887,40 @@ static void __init kho_reserve_bootmem(void)
* reserve bootmem area in low memory for lowmem allocations in the
* next kernel
*/
- size = bootmem_size_lowmem;
- addr = memblock_phys_alloc_range(size, BOOTMEM_ALIGNMENT_BYTES, 0,
- ARCH_LOW_ADDRESS_LIMIT);
- if (!addr) {
+ if (kho_reserve_bootmem_area(bootmem_size_lowmem, ARCH_LOW_ADDRESS_LIMIT,
+ NUMA_NO_NODE)) {
pr_err("Failed to reserve lowmem bootmem\n");
goto err_free_bootmem_desc;
}
- kho_bootmem[i].addr = addr;
- kho_bootmem[i].size = size;
- i++;
-
/* reserve large contiguous area for allocations without nid */
- size = bootmem_size_global;
- addr = memblock_phys_alloc(size, BOOTMEM_ALIGNMENT_BYTES);
- if (!addr) {
+ if (kho_reserve_bootmem_area(bootmem_size_global,
+ MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE)) {
pr_err("Failed to reserve global bootmem\n");
goto err_free_bootmem_areas;
}
- kho_bootmem[i].addr = addr;
- kho_bootmem[i].size = size;
- i++;
-
/*
* Loop over nodes that have both memory and are online. Skip
* memoryless nodes, as we can not allocate bootmem areas there.
*/
for_each_node_state(nid, N_MEMORY) {
- size = bootmem_size_node(nid);
- if (!size)
- continue;
-
- addr = memblock_alloc_range_nid(size, BOOTMEM_ALIGNMENT_BYTES,
- 0, MEMBLOCK_ALLOC_ACCESSIBLE,
- nid, true);
- if (!addr) {
+ if (kho_reserve_bootmem_area(bootmem_size_node(nid),
+ MEMBLOCK_ALLOC_ACCESSIBLE, nid)) {
pr_err("Failed to reserve nid %d bootmem\n", nid);
goto err_free_bootmem_areas;
}
-
- kho_bootmem[i].addr = addr;
- kho_bootmem[i].size = size;
- i++;
}
- /*
- * Nodes that needed no bootmem were skipped, so fewer areas than
- * initially estimated may have been reserved.
- */
- kho_bootmem_cnt = i;
-
sort(kho_bootmem, kho_bootmem_cnt, sizeof(*kho_bootmem),
kho_bootmem_cmp, NULL);
return;
err_free_bootmem_areas:
- for (i--; i >= 0; i--)
+ for (int i = 0; i < kho_bootmem_cnt; i++)
memblock_phys_free(kho_bootmem[i].addr, kho_bootmem[i].size);
+ kho_bootmem_cnt = 0;
err_free_bootmem_desc:
memblock_free(kho_bootmem, max_nr_bootmem * sizeof(*kho_bootmem));
err_disable_kho:
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v3 4/5] kho: fix bootmem sizing
2026-11-12 10:38 [PATCH v3 0/5] kho: fix bootmem sizing Mike Rapoport (Microsoft)
` (2 preceding siblings ...)
2026-11-12 10:38 ` [PATCH v3 3/5] kho: skip zero-sized lowmem and global bootmem areas Mike Rapoport (Microsoft)
@ 2026-11-12 10:38 ` Mike Rapoport (Microsoft)
2026-11-12 10:38 ` [PATCH v3 5/5] kho: docs: update description of bootmem regions Mike Rapoport (Microsoft)
4 siblings, 0 replies; 6+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-11-12 10:38 UTC (permalink / raw)
To: Jonathan Corbet, Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Chen Pei, George Guo, Kexin Liu,
Randy Dunlap, Shuah Khan, Sourabh Jain, kexec, linux-doc,
linux-kernel, linux-mm, Jose A. Perez de Azpillaga,
Mukesh Pilaniya
Bootmem areas are sized from overlapping views of the memory reserved in
memblock:
* the global area is sized from all reserved memory, including memory
allocated on specific nodes, so on NUMA systems everything allocated
on a node is accounted in both the global area and the area of that
node.
* per-node sizes are calculated after the lowmem and the global areas
are reserved. With CONFIG_NUMA=n all memblock regions belong to node 0,
so the lowmem and the global areas are accounted in the size of node 0
area and scaled once again.
As a result, bootmem is about twice bigger than it should be on NUMA
systems and about four times bigger with CONFIG_NUMA=n. On small systems
reservation of the bootmem may fail and then KHO gets disabled.
Make the areas cover separate parts of the reserved memory: the lowmem
area covers allocations below ARCH_LOW_ADDRESS_LIMIT, per-node areas
cover allocations above it on each node and the global area covers
allocations that are not bound to any node. Calculate all sizes before
reserving any of the areas.
With CONFIG_NUMA=n the global area becomes empty and it is not reserved.
If ARCH_LOW_ADDRESS_LIMIT covers all memory, as may happen on arm64, any
area can serve lowmem allocations, so don't reserve the lowmem area.
Bootmem sizes in MiB in x86 QEMU VMs, before -> after:
CONFIG_NUMA=y CONFIG_NUMA=n
1G, 1 node 176 -> 92 320 -> 80
4G, 1 node 508 -> 332 1264 -> 316
16G, 2 nodes 1268 -> 704 2944 -> 736
32G, 4 nodes 2296 -> 1224 5196 -> 1300
64G, 4 nodes 4344 -> 2248 9664 -> 2420
Fixes: 3dc92c311498 ("kexec: add Kexec HandOver (KHO) generation helpers")
Reported-by: Kexin Liu <liukexin@kylinos.cn>
Reported-by: George Guo <guodongtai@kylinos.cn>
Closes: https://lore.kernel.org/all/20260904025101.9959-1-dongtai.guo@linux.dev
Reported-by: Sourabh Jain <sourabhjain@linux.ibm.com>
Closes: https://lore.kernel.org/all/20260922131217.698809-1-sourabhjain@linux.ibm.com
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
Reviewed-by: Mukesh Pilaniya <mpilaniy@redhat.com>
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
kernel/liveupdate/kexec_handover.c | 92 ++++++++++++++++++++++----------------
1 file changed, 54 insertions(+), 38 deletions(-)
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index bbc9e644addc9..e9cefe22a6e6f 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -685,6 +685,7 @@ static unsigned int bootmem_scale __initdata = 200;
static phys_addr_t bootmem_size_global __initdata;
static phys_addr_t bootmem_size_pernode __initdata;
static phys_addr_t bootmem_size_lowmem __initdata;
+static phys_addr_t bootmem_size_node[MAX_NUMNODES] __initdata;
static int __init kho_parse_bootmem_size(char *p)
{
@@ -760,29 +761,58 @@ static int __init kho_parse_bootmem_size(char *p)
}
early_param("kho_bootmem", kho_parse_bootmem_size);
+static phys_addr_t __init reserved_mem_size(phys_addr_t limit, int nid)
+{
+ return memblock_reserved_kern_size(limit, nid) -
+ memblock_reserved_hugetlb_size(limit, nid);
+}
+
+static phys_addr_t __init bootmem_scale_size(phys_addr_t size)
+{
+ return size * bootmem_scale / 100;
+}
+
+/*
+ * The lowmem area covers allocations below ARCH_LOW_ADDRESS_LIMIT, per-node
+ * areas cover allocations above it on each node and the global area covers
+ * allocations that are not bound to any node.
+ *
+ * The reserved memory on a node and the total reserved memory include
+ * lowmem, so it is subtracted from both.
+ *
+ * If ARCH_LOW_ADDRESS_LIMIT covers all memory, any area can serve lowmem
+ * allocations and there is no lowmem area.
+ *
+ * All sizes must be calculated before any area is reserved, otherwise the
+ * areas reserved first will be accounted in the sizes of the others.
+ */
static void __init bootmem_size_update(void)
{
- /*
- * If fixed sizes are not provided via command line, calculate them now.
- * Remove HugeTLB allocations from it because they never get allocated
- * from bootmem.
- */
+ int nid;
+
if (bootmem_scale) {
- phys_addr_t size;
-
- size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT,
- NUMA_NO_NODE);
- size -= memblock_reserved_hugetlb_size(ARCH_LOW_ADDRESS_LIMIT,
- NUMA_NO_NODE);
- size = size * bootmem_scale / 100;
- bootmem_size_lowmem = size;
-
- size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,
- NUMA_NO_NODE);
- size -= memblock_reserved_hugetlb_size(MEMBLOCK_ALLOC_ANYWHERE,
- NUMA_NO_NODE);
- size = size * bootmem_scale / 100 - bootmem_size_lowmem;
- bootmem_size_global = size;
+ phys_addr_t low_limit = ARCH_LOW_ADDRESS_LIMIT;
+ phys_addr_t lowmem, global, size;
+
+ if (low_limit >= memblock_end_of_DRAM() - 1)
+ low_limit = 0;
+
+ lowmem = reserved_mem_size(low_limit, NUMA_NO_NODE);
+ global = reserved_mem_size(MEMBLOCK_ALLOC_ANYWHERE, NUMA_NO_NODE);
+ global -= lowmem;
+
+ for_each_node_state(nid, N_MEMORY) {
+ size = reserved_mem_size(MEMBLOCK_ALLOC_ANYWHERE, nid) -
+ reserved_mem_size(low_limit, nid);
+ bootmem_size_node[nid] = bootmem_scale_size(size);
+ global -= size;
+ }
+
+ bootmem_size_lowmem = bootmem_scale_size(lowmem);
+ bootmem_size_global = bootmem_scale_size(global);
+ } else {
+ for_each_node_state(nid, N_MEMORY)
+ bootmem_size_node[nid] = bootmem_size_pernode;
}
/*
@@ -791,24 +821,10 @@ static void __init bootmem_size_update(void)
*/
bootmem_size_lowmem = round_up(bootmem_size_lowmem, BOOTMEM_ALIGNMENT_BYTES);
bootmem_size_global = round_up(bootmem_size_global, BOOTMEM_ALIGNMENT_BYTES);
-}
-
-static phys_addr_t __init bootmem_size_node(int nid)
-{
- phys_addr_t size;
-
- if (bootmem_scale) {
- size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,
- nid);
- /* Do not count HugeTLB pages. */
- size -= memblock_reserved_hugetlb_size(MEMBLOCK_ALLOC_ANYWHERE,
- nid);
- size = size * bootmem_scale / 100;
- } else {
- size = bootmem_size_pernode;
+ for_each_node_state(nid, N_MEMORY) {
+ bootmem_size_node[nid] = round_up(bootmem_size_node[nid],
+ BOOTMEM_ALIGNMENT_BYTES);
}
-
- return round_up(size, BOOTMEM_ALIGNMENT_BYTES);
}
bool kho_bootmem_overlap(phys_addr_t phys, size_t size)
@@ -905,7 +921,7 @@ static void __init kho_reserve_bootmem(void)
* memoryless nodes, as we can not allocate bootmem areas there.
*/
for_each_node_state(nid, N_MEMORY) {
- if (kho_reserve_bootmem_area(bootmem_size_node(nid),
+ if (kho_reserve_bootmem_area(bootmem_size_node[nid],
MEMBLOCK_ALLOC_ACCESSIBLE, nid)) {
pr_err("Failed to reserve nid %d bootmem\n", nid);
goto err_free_bootmem_areas;
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v3 5/5] kho: docs: update description of bootmem regions
2026-11-12 10:38 [PATCH v3 0/5] kho: fix bootmem sizing Mike Rapoport (Microsoft)
` (3 preceding siblings ...)
2026-11-12 10:38 ` [PATCH v3 4/5] kho: fix bootmem sizing Mike Rapoport (Microsoft)
@ 2026-11-12 10:38 ` Mike Rapoport (Microsoft)
4 siblings, 0 replies; 6+ messages in thread
From: Mike Rapoport (Microsoft) @ 2026-11-12 10:38 UTC (permalink / raw)
To: Jonathan Corbet, Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Chen Pei, George Guo, Kexin Liu,
Randy Dunlap, Shuah Khan, Sourabh Jain, kexec, linux-doc,
linux-kernel, linux-mm
The description of the boot memory regions does not mention the low
memory region and claims there is always a region per node plus a global
region, while the low memory and the global regions are not allocated
when they would be empty.
List all types of the boot memory regions and describe when they are not
allocated.
While on it, fix grammar and split the description of the regions size
and of the CMA usage into separate paragraphs.
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
Documentation/core-api/kho/index.rst | 31 ++++++++++++++++++++-----------
1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/Documentation/core-api/kho/index.rst b/Documentation/core-api/kho/index.rst
index e78a2bbe2be1d..ce2bf9cf66514 100644
--- a/Documentation/core-api/kho/index.rst
+++ b/Documentation/core-api/kho/index.rst
@@ -52,17 +52,26 @@ allocations before during boot up to the initialization of the page allocator.
We guarantee that we always have such regions through the boot memory regions:
On first boot KHO allocates several physically contiguous memory regions. Since
-after kexec these regions will be used by early memory allocations, there is a
-boot memory region per NUMA node plus a boot memory region to satisfy
-allocations requests that do not require particular NUMA node assignment. By
-default, size of the boot memory region is calculated based on amount of memory
-allocated during boot. The ``kho_bootmem`` kernel command line option may be
-used to explicitly define size of the boot memory regions. The boot memory
-regions are declared as CMA when page allocator is initialized so that their
-memory can be used during system lifetime. CMA gives us the guarantee that no
-handover pages land in that region, because handover pages must be at a static
-physical memory location and CMA enforces that only movable pages can be located
-inside.
+after kexec these regions will be used by early memory allocations, there are
+several types of such regions to ensure that all boot-time allocations work:
+
+* a low memory region for allocations below ``ARCH_LOW_ADDRESS_LIMIT``
+* a region per NUMA node
+* a global region for allocations that do not require a particular NUMA node
+
+Regions that would be empty are not allocated. For example, there is no low
+memory region when ``ARCH_LOW_ADDRESS_LIMIT`` covers all memory, and there is
+no global region when ``CONFIG_NUMA`` is disabled.
+
+By default, the size of the boot memory regions is calculated based on the
+amount of memory allocated during boot. The ``kho_bootmem`` kernel command line
+option may be used to explicitly define the size of the boot memory regions.
+
+The boot memory regions are declared as CMA when the page allocator is
+initialized so that their memory can be used during system lifetime. CMA gives
+us the guarantee that no handover pages land in that region, because handover
+pages must be at a static physical memory location and CMA enforces that only
+movable pages can be located inside.
After KHO kexec, we ignore the ``kho_bootmem`` kernel command line option and
instead reuse the exact same region that was originally allocated. This allows
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread