[PATCH v8 10/11] arm64: kexec_file: Fix CMA segment placement retries
Jinjie Ruan
ruanjinjie at huawei.com
Thu Nov 12 05:34:17 PST 2026
The file-based kexec system call places segments with kexec_add_buffer(),
which takes them from CMA when kexec_alloc_contig() succeeds, to avoid
copying the payload at kexec time.
When load_other_segments() cannot place the initrd or the DTB, the
segments of the attempt are dropped but their CMA pages are leaked.
Release them with kexec_rollback_segments() in the retry loop and in the
error path of load_other_segments().
The loop makes progress by advancing buf_min past a segment placed by the
walk. kexec_alloc_contig() ignores buf_min, so once the pages of a CMA
segment are released, the next attempt can get the same region again and
the retry makes no progress. Disable CMA for the retry.
A CMA segment can be anywhere in RAM, so moving buf_min to its end can
raise the walk's lower bound close to the top of memory and leave the
retry with too little space for the initrd and DTB, failing with
-EADDRNOTAVAIL. Only advance buf_min for segments found by the walk.
Tested with a non-crash kexec_file_load() on QEMU virt, 1 GiB RAM and
cma=64M, which places CMA at the top of RAM.
Before the fix:
The retry leaks the CMA pages and kexec_file_load() returns
-EADDRNOTAVAIL even though RAM below CMA is free.
After the fix:
The kernel and initrd are placed below CMA and the second
kernel boots.
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Will Deacon <will at kernel.org>
Cc: Breno Leitao <leitao at debian.org>
Cc: Pratyush Yadav <pratyush at kernel.org>
Cc: Andrew Morton <akpm at linux-foundation.org>
Cc: Yeoreum Yun <yeoreum.yun at arm.com>
Cc: Baoquan He <bhe at redhat.com>
Cc: stable at vger.kernel.org
Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation")
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
arch/arm64/kernel/kexec_image.c | 12 ++++++++----
arch/arm64/kernel/machine_kexec_file.c | 2 +-
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kernel/kexec_image.c b/arch/arm64/kernel/kexec_image.c
index a2a7c283b331..1cbb3c73c6e9 100644
--- a/arch/arm64/kernel/kexec_image.c
+++ b/arch/arm64/kernel/kexec_image.c
@@ -109,11 +109,15 @@ static void *image_load(struct kimage *image,
break;
/*
- * We couldn't find space for the other segments; erase the
- * kernel segment and try the next available hole.
+ * kexec_alloc_contig() ignores buf_min and always returns
+ * CMA memory, so disable it and keep buf_min.
*/
- image->nr_segments -= 1;
- kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz;
+ if (image->segment_cma[kernel_segment_number])
+ image->no_cma = true;
+ else
+ kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz;
+
+ kexec_rollback_segments(image, kernel_segment_number);
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
}
diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
index 82122ded1467..8747daa23079 100644
--- a/arch/arm64/kernel/machine_kexec_file.c
+++ b/arch/arm64/kernel/machine_kexec_file.c
@@ -132,7 +132,7 @@ int load_other_segments(struct kimage *image, unsigned long kernel_load_addr,
return 0;
out_err:
- image->nr_segments = orig_segments;
+ kexec_rollback_segments(image, orig_segments);
kvfree(dtb);
return ret;
}
--
2.34.1
More information about the linux-riscv
mailing list