[PATCH v1 5/8] s390/kdump: Exclude crashkernel CMA memory from vmcore

From: Mikhail Zaslonko

Date: Thu Nov 12 2026 - 06:33:52 EST


The kdump kernel will be using the crashkernel CMA memory of the
crashed kernel, so it must not be a part of the dump.

Keep the CMA ranges handed over by the crashed kernel in oldmem_data.
Add them to oldmem_type next to the crashkernel memory, so that no
PT_LOAD headers are created for them.

Merge adjacent regions, such as CMA areas placed back to back or next
to the crashkernel memory. Otherwise for_each_physmem_range() returns an
empty range between them, which ends up as an empty PT_LOAD header.

Use the original ranges even if mem= limits usable memory in the kdump
kernel. Build the list without depending on CONFIG_CMA in that kernel.

Co-developed-by: Vasily Gorbik <gor@xxxxxxxxxxxxx>
Signed-off-by: Vasily Gorbik <gor@xxxxxxxxxxxxx>
Signed-off-by: Mikhail Zaslonko <zaslonko@xxxxxxxxxxxxx>
---
arch/s390/boot/startup.c | 1 +
arch/s390/include/asm/setup.h | 1 +
arch/s390/kernel/crash_dump.c | 48 ++++++++++++++++++++++++++++-------
3 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c
index 95543656d1b8..111b146a3332 100644
--- a/arch/s390/boot/startup.c
+++ b/arch/s390/boot/startup.c
@@ -529,6 +529,7 @@ void startup_kernel(void)
physmem_reserve(RR_INITRD, parmarea.initrd_start, parmarea.initrd_size);
oldmem_data.start = parmarea.oldmem_base;
oldmem_data.size = parmarea.oldmem_size;
+ memcpy(oldmem_data.crashk_cma, parmarea.crashk_cma, sizeof(oldmem_data.crashk_cma));

read_ipl_report();
sclp_early_read_info();
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h
index 7e992f3953b1..7048985ec014 100644
--- a/arch/s390/include/asm/setup.h
+++ b/arch/s390/include/asm/setup.h
@@ -100,6 +100,7 @@ extern void (*_machine_power_off)(void);
struct oldmem_data {
unsigned long start;
unsigned long size;
+ struct crashk_cma_range crashk_cma[CRASHK_CMA_ENTRIES];
};
extern struct oldmem_data oldmem_data;

diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index d4839de8ce9d..f2f4a5f40d60 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -19,6 +19,7 @@
#include <linux/uio.h>
#include <asm/asm-offsets.h>
#include <asm/os_info.h>
+#include <asm/setup.h>
#include <asm/elf.h>
#include <asm/ipl.h>
#include <asm/sclp.h>
@@ -29,13 +30,14 @@
#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
#define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))

-static struct memblock_region oldmem_region;
+/* crashkernel memory and crashkernel CMA memory, sorted by address */
+static struct memblock_region oldmem_regions[1 + CRASHK_CMA_ENTRIES];

static struct memblock_type oldmem_type = {
.cnt = 1,
- .max = 1,
+ .max = ARRAY_SIZE(oldmem_regions),
.total_size = 0,
- .regions = &oldmem_region,
+ .regions = oldmem_regions,
.name = "oldmem",
};

@@ -478,6 +480,37 @@ static int get_cpu_cnt(void)
return cpus;
}

+/* Exclude the original crashkernel and CMA reservations, independent of mem=. */
+static void oldmem_type_init(void)
+{
+ struct memblock_region *regions = oldmem_regions;
+ unsigned long base, size;
+ int i, j;
+
+ regions[0].base = oldmem_data.start;
+ regions[0].size = oldmem_data.size;
+ oldmem_type.cnt = 1;
+ oldmem_type.total_size = oldmem_data.size;
+ for (i = 0; i < ARRAY_SIZE(oldmem_data.crashk_cma) && oldmem_data.crashk_cma[i].size; i++) {
+ base = oldmem_data.crashk_cma[i].start;
+ size = oldmem_data.crashk_cma[i].size;
+ for (j = oldmem_type.cnt; j > 0 && regions[j - 1].base > base; j--)
+ regions[j] = regions[j - 1];
+ regions[j].base = base;
+ regions[j].size = size;
+ oldmem_type.cnt++;
+ oldmem_type.total_size += size;
+ }
+ /* memblock iterators expect separate regions, merge adjacent ones */
+ for (i = 1, j = 0; i < oldmem_type.cnt; i++) {
+ if (regions[j].base + regions[j].size == regions[i].base)
+ regions[j].size += regions[i].size;
+ else
+ regions[++j] = regions[i];
+ }
+ oldmem_type.cnt = j + 1;
+}
+
/*
* Return memory chunk count for ELF header (new kernel)
*/
@@ -626,12 +659,9 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
if (is_ipl_type_dump() && !sclp.hsa_size)
return -ENODEV;

- /* For kdump, exclude previous crashkernel memory */
- if (oldmem_data.start) {
- oldmem_region.base = oldmem_data.start;
- oldmem_region.size = oldmem_data.size;
- oldmem_type.total_size = oldmem_data.size;
- }
+ /* For kdump, exclude memory used by the kdump kernel */
+ if (oldmem_data.start)
+ oldmem_type_init();

mem_chunk_cnt = get_mem_chunk_cnt();
phdr_text_cnt = os_info_has_vm() ? 1 : 0;
--
2.55.0