[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <50693f0c-c610-4347-86aa-40d50dc681fc@linux.ibm.com>
Date: Tue, 10 Feb 2026 18:00:21 +0530
From: Sourabh Jain <sourabhjain@...ux.ibm.com>
To: Jinjie Ruan <ruanjinjie@...wei.com>, corbet@....net,
catalin.marinas@....com, will@...nel.org, chenhuacai@...nel.org,
kernel@...0n.name, maddy@...ux.ibm.com, mpe@...erman.id.au,
npiggin@...il.com, chleroy@...nel.org, pjw@...nel.org,
palmer@...belt.com, aou@...s.berkeley.edu, alex@...ti.fr,
tglx@...nel.org, mingo@...hat.com, bp@...en8.de,
dave.hansen@...ux.intel.com, hpa@...or.com, akpm@...ux-foundation.org,
bhe@...hat.com, vgoyal@...hat.com, dyoung@...hat.com,
rdunlap@...radead.org, pmladek@...e.com, feng.tang@...ux.alibaba.com,
pawan.kumar.gupta@...ux.intel.com, kees@...nel.org, elver@...gle.com,
arnd@...db.de, lirongqing@...du.com, fvdl@...gle.com,
leitao@...ian.org, rppt@...nel.org, ardb@...nel.org, jbohac@...e.cz,
osandov@...com, ryan.roberts@....com, cfsworks@...il.com,
tangyouling@...inos.cn, ritesh.list@...il.com, thuth@...hat.com,
hbathini@...ux.ibm.com, eajames@...ux.ibm.com, bjorn@...osinc.com,
songshuaishuai@...ylab.org, kevin.brodsky@....com,
samuel.holland@...ive.com, vishal.moola@...il.com,
junhui.liu@...moral.tech, dwmw@...zon.co.uk, pbonzini@...hat.com,
thomas.lendacky@....com, kai.huang@...el.com, ubizjak@...il.com,
coxu@...hat.com, liaoyuanhong@...o.com, fuqiang.wang@...ystack.cn,
brgerst@...il.com, x86@...nel.org, linux-doc@...r.kernel.org,
linux-kernel@...r.kernel.org, linux-arm-kernel@...ts.infradead.org,
loongarch@...ts.linux.dev, linuxppc-dev@...ts.ozlabs.org,
linux-riscv@...ts.infradead.org, kexec@...ts.infradead.org
Subject: Re: [PATCH v4 1/3] crash: Exclude crash kernel memory in crash core
Hello Jinjie,
On 09/02/26 15:29, Jinjie Ruan wrote:
> The exclude of crashk_res, crashk_low_res and crashk_cma memory
> are almost identical across different architectures, handling them
> in the crash core would eliminate a lot of duplication, so do
> them in the common code.
>
> And move the size calculation (and the realloc if needed) into the
> generic crash core so that:
>
> - New CMA regions or future crash-memory types can automatically
> accounted for in crash core;
>
> - Each architecture no longer has to play whack-a-mole with
> its private array size.
>
> To achieve the above goal, 4 architecture-specific functions are
> introduced:
>
> - arch_get_system_nr_ranges() and arch_prepare_elf64_ram_headers().
> The 1st function pre-counts the number of memory ranges, and
> the 2st function fill the memory ranges into the cmem->ranges[] array,
> and count the actual number of ranges filled. The default implementation
> is consistent with arm64 and loongson.
>
> - arch_crash_exclude_mem_range(). Realloc for powerpc. The default
> implementation is crash_exclude_mem_range(), and use
> crash_exclude_mem_range_guarded() to implement the arch version
> for powerpc.
>
> - arch_get_crash_memory_ranges(). Get crash memory ranges for arch and
> the default implementation is generic across x86, arm64, riscv, and
> loongson by using the first two arch functions above. powerpc has its
> own implementation by calling get_crash_memory_ranges().
>
> Tested on x86, arm64 and riscv with QEMU.
>
> Signed-off-by: Jinjie Ruan <ruanjinjie@...wei.com>
> ---
> arch/arm64/kernel/machine_kexec_file.c | 47 +--------
> arch/loongarch/kernel/machine_kexec_file.c | 45 +-------
> arch/powerpc/include/asm/kexec.h | 13 +++
> arch/powerpc/kexec/crash.c | 52 ++++++----
> arch/powerpc/kexec/file_load_64.c | 17 ++-
> arch/powerpc/kexec/ranges.c | 18 +---
> arch/riscv/include/asm/kexec.h | 10 ++
> arch/riscv/kernel/machine_kexec_file.c | 37 ++-----
> arch/x86/include/asm/kexec.h | 10 ++
> arch/x86/kernel/crash.c | 104 ++-----------------
> include/linux/crash_core.h | 114 +++++++++++++++++++--
> kernel/crash_core.c | 71 +++++++++++--
> 12 files changed, 269 insertions(+), 269 deletions(-)
>
> diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
> index 410060ebd86d..8a29449e992d 100644
> --- a/arch/arm64/kernel/machine_kexec_file.c
> +++ b/arch/arm64/kernel/machine_kexec_file.c
> @@ -39,50 +39,6 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
> return kexec_image_post_load_cleanup_default(image);
> }
>
> -#ifdef CONFIG_CRASH_DUMP
> -static int prepare_elf_headers(void **addr, unsigned long *sz)
> -{
> - struct crash_mem *cmem;
> - unsigned int nr_ranges;
> - int ret;
> - u64 i;
> - phys_addr_t start, end;
> -
> - nr_ranges = 2; /* for exclusion of crashkernel region */
> - for_each_mem_range(i, &start, &end)
> - nr_ranges++;
> -
> - cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL);
> - if (!cmem)
> - return -ENOMEM;
> -
> - cmem->max_nr_ranges = nr_ranges;
> - cmem->nr_ranges = 0;
> - for_each_mem_range(i, &start, &end) {
> - cmem->ranges[cmem->nr_ranges].start = start;
> - cmem->ranges[cmem->nr_ranges].end = end - 1;
> - cmem->nr_ranges++;
> - }
> -
> - /* Exclude crashkernel region */
> - ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
> - if (ret)
> - goto out;
> -
> - if (crashk_low_res.end) {
> - ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);
> - if (ret)
> - goto out;
> - }
> -
> - ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
> -
> -out:
> - kfree(cmem);
> - return ret;
> -}
> -#endif
> -
> /*
> * Tries to add the initrd and DTB to the image. If it is not possible to find
> * valid locations, this function will undo changes to the image and return non
> @@ -109,7 +65,8 @@ int load_other_segments(struct kimage *image,
> void *headers;
> unsigned long headers_sz;
> if (image->type == KEXEC_TYPE_CRASH) {
> - ret = prepare_elf_headers(&headers, &headers_sz);
> + ret = crash_prepare_elf64_headers(true, &headers, &headers_sz,
> + NULL, NULL, NULL);
> if (ret) {
> pr_err("Preparing elf core header failed\n");
> goto out_err;
> diff --git a/arch/loongarch/kernel/machine_kexec_file.c b/arch/loongarch/kernel/machine_kexec_file.c
> index fb57026f5f25..93555b92bbeb 100644
> --- a/arch/loongarch/kernel/machine_kexec_file.c
> +++ b/arch/loongarch/kernel/machine_kexec_file.c
> @@ -56,48 +56,6 @@ static void cmdline_add_initrd(struct kimage *image, unsigned long *cmdline_tmpl
> }
>
> #ifdef CONFIG_CRASH_DUMP
> -
> -static int prepare_elf_headers(void **addr, unsigned long *sz)
> -{
> - int ret, nr_ranges;
> - uint64_t i;
> - phys_addr_t start, end;
> - struct crash_mem *cmem;
> -
> - nr_ranges = 2; /* for exclusion of crashkernel region */
> - for_each_mem_range(i, &start, &end)
> - nr_ranges++;
> -
> - cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL);
> - if (!cmem)
> - return -ENOMEM;
> -
> - cmem->max_nr_ranges = nr_ranges;
> - cmem->nr_ranges = 0;
> - for_each_mem_range(i, &start, &end) {
> - cmem->ranges[cmem->nr_ranges].start = start;
> - cmem->ranges[cmem->nr_ranges].end = end - 1;
> - cmem->nr_ranges++;
> - }
> -
> - /* Exclude crashkernel region */
> - ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
> - if (ret < 0)
> - goto out;
> -
> - if (crashk_low_res.end) {
> - ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);
> - if (ret < 0)
> - goto out;
> - }
> -
> - ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
> -
> -out:
> - kfree(cmem);
> - return ret;
> -}
> -
> /*
> * Add the "mem=size@...rt" command line parameter to command line, indicating the
> * memory region the new kernel can use to boot into.
> @@ -163,7 +121,8 @@ int load_other_segments(struct kimage *image,
> void *headers;
> unsigned long headers_sz;
>
> - ret = prepare_elf_headers(&headers, &headers_sz);
> + ret = crash_prepare_elf64_headers(true, &headers, &headers_sz,
> + NULL, NULL, NULL);
> if (ret < 0) {
> pr_err("Preparing elf core header failed\n");
> goto out_err;
> diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
> index bd4a6c42a5f3..ec64c26ca81a 100644
> --- a/arch/powerpc/include/asm/kexec.h
> +++ b/arch/powerpc/include/asm/kexec.h
> @@ -123,6 +123,11 @@ static inline void kdump_cma_reserve(void) { }
> #endif
>
> #if defined(CONFIG_CRASH_DUMP)
> +#include <asm/kexec_ranges.h>
> +
> +struct crash_mem;
> +struct memory_notify;
> +
> /*
> * This function is responsible for capturing register states if coming
> * via panic or invoking dump using sysrq-trigger.
> @@ -147,6 +152,14 @@ unsigned int arch_crash_get_elfcorehdr_size(void);
> #define crash_get_elfcorehdr_size arch_crash_get_elfcorehdr_size
> #endif /* CONFIG_CRASH_HOTPLUG */
>
> +int arch_crash_exclude_mem_range(struct crash_mem **mem, unsigned long long mstart,
> + unsigned long long mend);
> +#define arch_crash_exclude_mem_range arch_crash_exclude_mem_range
> +
> +int arch_get_crash_memory_ranges(struct crash_mem **cmem, unsigned long *nr_mem_ranges,
> + struct kimage *image, struct memory_notify *mn);
> +#define arch_get_crash_memory_ranges arch_get_crash_memory_ranges
> +
> extern int crashing_cpu;
> extern void crash_send_ipi(void (*crash_ipi_callback)(struct pt_regs *));
> extern void crash_ipi_callback(struct pt_regs *regs);
> diff --git a/arch/powerpc/kexec/crash.c b/arch/powerpc/kexec/crash.c
> index a325c1c02f96..5ade9a853fb0 100644
> --- a/arch/powerpc/kexec/crash.c
> +++ b/arch/powerpc/kexec/crash.c
> @@ -419,30 +419,21 @@ unsigned int arch_crash_get_elfcorehdr_size(void)
> return sizeof(struct elfhdr) + (phdr_cnt * sizeof(Elf64_Phdr));
> }
>
> -/**
> - * update_crash_elfcorehdr() - Recreate the elfcorehdr and replace it with old
> - * elfcorehdr in the kexec segment array.
> - * @image: the active struct kimage
> - * @mn: struct memory_notify data handler
> - */
> -static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify *mn)
> +int arch_get_crash_memory_ranges(struct crash_mem **cmem, unsigned long *nr_mem_ranges,
> + struct kimage *image, struct memory_notify *mn)
> {
> + unsigned long base_addr, size;
> int ret;
> - struct crash_mem *cmem = NULL;
> - struct kexec_segment *ksegment;
> - void *ptr, *mem, *elfbuf = NULL;
> - unsigned long elfsz, memsz, base_addr, size;
>
> - ksegment = &image->segment[image->elfcorehdr_index];
> - mem = (void *) ksegment->mem;
> - memsz = ksegment->memsz;
> -
> - ret = get_crash_memory_ranges(&cmem);
> + ret = get_crash_memory_ranges(cmem);
> if (ret) {
> pr_err("Failed to get crash mem range\n");
> - return;
> + return ret;
> }
>
> + if (!image || !mn)
> + return 0;
> +
> /*
> * The hot unplugged memory is part of crash memory ranges,
> * remove it here.
> @@ -450,14 +441,34 @@ static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify *
> if (image->hp_action == KEXEC_CRASH_HP_REMOVE_MEMORY) {
> base_addr = PFN_PHYS(mn->start_pfn);
> size = mn->nr_pages * PAGE_SIZE;
> - ret = remove_mem_range(&cmem, base_addr, size);
> + ret = remove_mem_range(cmem, base_addr, size);
I like the overall design for handling crashkernel memory exclusion
in this patch series, especially the way you managed to free the
crash_mem object (mem) in the generic code (crash_prepare_elf64_headers()).
However, the way crash memory is prepared after a memory hotplug
event on powerpc by calling remove_mem_range(), can leave the crash
memory ranges unsorted. This can cause issues in the generic code
when excluding crashkernel memory, because crash_exclude_mem_range()
expects crash_mem to be sorted.
So I wrote a simple patch to cover this scenario. Including the
patch below as the first patch in this series would be helpful.
https://lore.kernel.org/all/20260210120803.433978-1-sourabhjain@linux.ibm.com/
Jinjie, will it be possible for you to include the above patch in this
patch series?
> if (ret) {
> pr_err("Failed to remove hot-unplugged memory from crash memory ranges\n");
> - goto out;
> + return ret;
> }
> }
>
> - ret = crash_prepare_elf64_headers(cmem, false, &elfbuf, &elfsz);
> + return 0;
> +}
> +
> +/**
> + * update_crash_elfcorehdr() - Recreate the elfcorehdr and replace it with old
> + * elfcorehdr in the kexec segment array.
> + * @image: the active struct kimage
> + * @mn: struct memory_notify data handler
> + */
> +static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify *mn)
> +{
> + void *ptr, *mem, *elfbuf = NULL;
> + struct kexec_segment *ksegment;
> + unsigned long elfsz, memsz;
> + int ret;
> +
> + ksegment = &image->segment[image->elfcorehdr_index];
> + mem = (void *) ksegment->mem;
> + memsz = ksegment->memsz;
> +
> + ret = crash_prepare_elf64_headers(false, &elfbuf, &elfsz, NULL, image, mn);
> if (ret) {
> pr_err("Failed to prepare elf header\n");
> goto out;
> @@ -486,7 +497,6 @@ static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify *
> xchg(&kexec_crash_image, image);
> }
> out:
> - kvfree(cmem);
> kvfree(elfbuf);
> }
>
> diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
> index e7ef8b2a2554..6fe13031236c 100644
> --- a/arch/powerpc/kexec/file_load_64.c
> +++ b/arch/powerpc/kexec/file_load_64.c
> @@ -401,17 +401,17 @@ static void update_backup_region_phdr(struct kimage *image, Elf64_Ehdr *ehdr)
> }
> }
>
> -static unsigned int kdump_extra_elfcorehdr_size(struct crash_mem *cmem)
> +static unsigned int kdump_extra_elfcorehdr_size(unsigned long nr_mem_ranges)
> {
> #if defined(CONFIG_CRASH_HOTPLUG) && defined(CONFIG_MEMORY_HOTPLUG)
> unsigned int extra_sz = 0;
>
> if (CONFIG_CRASH_MAX_MEMORY_RANGES > (unsigned int)PN_XNUM)
> pr_warn("Number of Phdrs %u exceeds max\n", CONFIG_CRASH_MAX_MEMORY_RANGES);
> - else if (cmem->nr_ranges >= CONFIG_CRASH_MAX_MEMORY_RANGES)
> + else if (nr_mem_ranges >= CONFIG_CRASH_MAX_MEMORY_RANGES)
> pr_warn("Configured crash mem ranges may not be enough\n");
> else
> - extra_sz = (CONFIG_CRASH_MAX_MEMORY_RANGES - cmem->nr_ranges) * sizeof(Elf64_Phdr);
> + extra_sz = (CONFIG_CRASH_MAX_MEMORY_RANGES - nr_mem_ranges) * sizeof(Elf64_Phdr);
>
> return extra_sz;
> #endif
> @@ -428,17 +428,13 @@ static unsigned int kdump_extra_elfcorehdr_size(struct crash_mem *cmem)
> */
> static int load_elfcorehdr_segment(struct kimage *image, struct kexec_buf *kbuf)
> {
> - struct crash_mem *cmem = NULL;
> + unsigned long nr_mem_ranges;
> unsigned long headers_sz;
> void *headers = NULL;
> int ret;
>
> - ret = get_crash_memory_ranges(&cmem);
> - if (ret)
> - goto out;
> -
> /* Setup elfcorehdr segment */
> - ret = crash_prepare_elf64_headers(cmem, false, &headers, &headers_sz);
> + ret = crash_prepare_elf64_headers(false, &headers, &headers_sz, &nr_mem_ranges, NULL, NULL);
> if (ret) {
> pr_err("Failed to prepare elf headers for the core\n");
> goto out;
> @@ -450,7 +446,7 @@ static int load_elfcorehdr_segment(struct kimage *image, struct kexec_buf *kbuf)
> kbuf->buffer = headers;
> kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
> kbuf->bufsz = headers_sz;
> - kbuf->memsz = headers_sz + kdump_extra_elfcorehdr_size(cmem);
> + kbuf->memsz = headers_sz + kdump_extra_elfcorehdr_size(nr_mem_ranges);
> kbuf->top_down = false;
>
> ret = kexec_add_buffer(kbuf);
> @@ -463,7 +459,6 @@ static int load_elfcorehdr_segment(struct kimage *image, struct kexec_buf *kbuf)
> image->elf_headers_sz = headers_sz;
> image->elf_headers = headers;
> out:
> - kfree(cmem);
> return ret;
> }
>
> diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c
> index 867135560e5c..34e3058ff1d5 100644
> --- a/arch/powerpc/kexec/ranges.c
> +++ b/arch/powerpc/kexec/ranges.c
> @@ -553,9 +553,9 @@ int get_usable_memory_ranges(struct crash_mem **mem_ranges)
> #endif /* CONFIG_KEXEC_FILE */
>
> #ifdef CONFIG_CRASH_DUMP
> -static int crash_exclude_mem_range_guarded(struct crash_mem **mem_ranges,
> - unsigned long long mstart,
> - unsigned long long mend)
> +int arch_crash_exclude_mem_range(struct crash_mem **mem_ranges,
> + unsigned long long mstart,
> + unsigned long long mend)
> {
> struct crash_mem *tmem = *mem_ranges;
>
> @@ -604,18 +604,6 @@ int get_crash_memory_ranges(struct crash_mem **mem_ranges)
> sort_memory_ranges(*mem_ranges, true);
> }
>
> - /* Exclude crashkernel region */
> - ret = crash_exclude_mem_range_guarded(mem_ranges, crashk_res.start, crashk_res.end);
> - if (ret)
> - goto out;
> -
> - for (i = 0; i < crashk_cma_cnt; ++i) {
> - ret = crash_exclude_mem_range_guarded(mem_ranges, crashk_cma_ranges[i].start,
> - crashk_cma_ranges[i].end);
> - if (ret)
> - goto out;
> - }
> -
> /*
> * FIXME: For now, stay in parity with kexec-tools but if RTAS/OPAL
> * regions are exported to save their context at the time of
> diff --git a/arch/riscv/include/asm/kexec.h b/arch/riscv/include/asm/kexec.h
> index b9ee8346cc8c..daee8388a26d 100644
> --- a/arch/riscv/include/asm/kexec.h
> +++ b/arch/riscv/include/asm/kexec.h
> @@ -75,4 +75,14 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start,
> unsigned long cmdline_len);
> #endif
>
> +#ifdef CONFIG_CRASH_DUMP
> +struct crash_mem;
> +
> +int arch_get_system_nr_ranges(unsigned int *nr_ranges);
> +#define arch_get_system_nr_ranges arch_get_system_nr_ranges
> +
> +int arch_prepare_elf64_ram_headers(struct crash_mem *cmem);
> +#define arch_prepare_elf64_ram_headers arch_prepare_elf64_ram_headers
> +#endif
> +
> #endif
> diff --git a/arch/riscv/kernel/machine_kexec_file.c b/arch/riscv/kernel/machine_kexec_file.c
> index dd9d92a96517..2f0e7bbeb2f0 100644
> --- a/arch/riscv/kernel/machine_kexec_file.c
> +++ b/arch/riscv/kernel/machine_kexec_file.c
> @@ -44,6 +44,13 @@ static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
> return 0;
> }
>
> +int arch_get_system_nr_ranges(unsigned int *nr_ranges)
> +{
> + walk_system_ram_res(0, -1, nr_ranges, get_nr_ram_ranges_callback);
> +
> + return 0;
> +}
> +
> static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
> {
> struct crash_mem *cmem = arg;
> @@ -55,33 +62,10 @@ static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
> return 0;
> }
>
> -static int prepare_elf_headers(void **addr, unsigned long *sz)
> +int arch_prepare_elf64_ram_headers(struct crash_mem *cmem)
> {
> - struct crash_mem *cmem;
> - unsigned int nr_ranges;
> - int ret;
> -
> - nr_ranges = 1; /* For exclusion of crashkernel region */
> - walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
> -
> - cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL);
> - if (!cmem)
> - return -ENOMEM;
> -
> - cmem->max_nr_ranges = nr_ranges;
> cmem->nr_ranges = 0;
> - ret = walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
> - if (ret)
> - goto out;
> -
> - /* Exclude crashkernel region */
> - ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
> - if (!ret)
> - ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
> -
> -out:
> - kfree(cmem);
> - return ret;
> + return walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
> }
>
> static char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
> @@ -273,7 +257,8 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start,
> if (image->type == KEXEC_TYPE_CRASH) {
> void *headers;
> unsigned long headers_sz;
> - ret = prepare_elf_headers(&headers, &headers_sz);
> +
> + ret = crash_prepare_elf64_headers(true, &headers, &headers_sz, NULL, NULL, NULL);
> if (ret) {
> pr_err("Preparing elf core header failed\n");
> goto out;
> diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
> index 5cfb27f26583..9939dd8715df 100644
> --- a/arch/x86/include/asm/kexec.h
> +++ b/arch/x86/include/asm/kexec.h
> @@ -232,6 +232,16 @@ unsigned int arch_crash_get_elfcorehdr_size(void);
> #define crash_get_elfcorehdr_size arch_crash_get_elfcorehdr_size
> #endif
>
> +#ifdef CONFIG_CRASH_DUMP
> +struct crash_mem;
> +
> +int arch_get_system_nr_ranges(unsigned int *nr_ranges);
> +#define arch_get_system_nr_ranges arch_get_system_nr_ranges
> +
> +int arch_prepare_elf64_ram_headers(struct crash_mem *cmem);
> +#define arch_prepare_elf64_ram_headers arch_prepare_elf64_ram_headers
> +#endif
> +
> #endif /* __ASSEMBLER__ */
>
> #endif /* _ASM_X86_KEXEC_H */
> diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
> index 335fd2ee9766..3fbbd518604a 100644
> --- a/arch/x86/kernel/crash.c
> +++ b/arch/x86/kernel/crash.c
> @@ -152,73 +152,15 @@ static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
> return 0;
> }
>
> -/* Gather all the required information to prepare elf headers for ram regions */
> -static struct crash_mem *fill_up_crash_elf_data(void)
> +int arch_get_system_nr_ranges(unsigned int *nr_ranges)
> {
> - unsigned int nr_ranges = 0;
> - struct crash_mem *cmem;
> + int ret;
>
> - walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
> + ret = walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
> if (!nr_ranges)
> - return NULL;
> -
> - /*
> - * Exclusion of crash region, crashk_low_res and/or crashk_cma_ranges
> - * may cause range splits. So add extra slots here.
> - *
> - * Exclusion of low 1M may not cause another range split, because the
> - * range of exclude is [0, 1M] and the condition for splitting a new
> - * region is that the start, end parameters are both in a certain
> - * existing region in cmem and cannot be equal to existing region's
> - * start or end. Obviously, the start of [0, 1M] cannot meet this
> - * condition.
> - *
> - * But in order to lest the low 1M could be changed in the future,
> - * (e.g. [start, 1M]), add a extra slot.
> - */
> - nr_ranges += 3 + crashk_cma_cnt;
> - cmem = vzalloc(struct_size(cmem, ranges, nr_ranges));
> - if (!cmem)
> - return NULL;
> -
> - cmem->max_nr_ranges = nr_ranges;
> -
> - return cmem;
> -}
> -
> -/*
> - * Look for any unwanted ranges between mstart, mend and remove them. This
> - * might lead to split and split ranges are put in cmem->ranges[] array
> - */
> -static int elf_header_exclude_ranges(struct crash_mem *cmem)
> -{
> - int ret = 0;
> - int i;
> -
> - /* Exclude the low 1M because it is always reserved */
> - ret = crash_exclude_mem_range(cmem, 0, SZ_1M - 1);
> - if (ret)
> - return ret;
> -
> - /* Exclude crashkernel region */
> - ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
> - if (ret)
> - return ret;
> -
> - if (crashk_low_res.end)
> - ret = crash_exclude_mem_range(cmem, crashk_low_res.start,
> - crashk_low_res.end);
> - if (ret)
> - return ret;
> -
> - for (i = 0; i < crashk_cma_cnt; ++i) {
> - ret = crash_exclude_mem_range(cmem, crashk_cma_ranges[i].start,
> - crashk_cma_ranges[i].end);
> - if (ret)
> - return ret;
> - }
> + return -ENOMEM;
>
> - return 0;
> + return ret;
> }
>
> static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
> @@ -232,35 +174,9 @@ static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
> return 0;
> }
>
> -/* Prepare elf headers. Return addr and size */
> -static int prepare_elf_headers(void **addr, unsigned long *sz,
> - unsigned long *nr_mem_ranges)
> +int arch_prepare_elf64_ram_headers(struct crash_mem *cmem)
> {
> - struct crash_mem *cmem;
> - int ret;
> -
> - cmem = fill_up_crash_elf_data();
> - if (!cmem)
> - return -ENOMEM;
> -
> - ret = walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
> - if (ret)
> - goto out;
> -
> - /* Exclude unwanted mem ranges */
> - ret = elf_header_exclude_ranges(cmem);
> - if (ret)
> - goto out;
> -
> - /* Return the computed number of memory ranges, for hotplug usage */
> - *nr_mem_ranges = cmem->nr_ranges;
> -
> - /* By default prepare 64bit headers */
> - ret = crash_prepare_elf64_headers(cmem, IS_ENABLED(CONFIG_X86_64), addr, sz);
> -
> -out:
> - vfree(cmem);
> - return ret;
> + return walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
> }
> #endif
>
> @@ -418,7 +334,8 @@ int crash_load_segments(struct kimage *image)
> .buf_max = ULONG_MAX, .top_down = false };
>
> /* Prepare elf headers and add a segment */
> - ret = prepare_elf_headers(&kbuf.buffer, &kbuf.bufsz, &pnum);
> + ret = crash_prepare_elf64_headers(IS_ENABLED(CONFIG_X86_64), &kbuf.buffer,
> + &kbuf.bufsz, &pnum, NULL, NULL);
> if (ret)
> return ret;
>
> @@ -529,7 +446,8 @@ void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
> * Create the new elfcorehdr reflecting the changes to CPU and/or
> * memory resources.
> */
> - if (prepare_elf_headers(&elfbuf, &elfsz, &nr_mem_ranges)) {
> + if (crash_prepare_elf64_headers(IS_ENABLED(CONFIG_X86_64), &elfbuf, &elfsz,
> + &nr_mem_ranges, NULL, NULL)) {
> pr_err("unable to create new elfcorehdr");
> goto out;
> }
> diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h
> index d35726d6a415..8d92cd16b625 100644
> --- a/include/linux/crash_core.h
> +++ b/include/linux/crash_core.h
> @@ -2,11 +2,15 @@
> #ifndef LINUX_CRASH_CORE_H
> #define LINUX_CRASH_CORE_H
>
> -#include <linux/linkage.h>
> #include <linux/elfcore.h>
> #include <linux/elf.h>
> +#include <linux/kexec.h>
> +#include <linux/linkage.h>
> +#include <linux/memblock.h>
> +#include <linux/vmalloc.h>
>
> struct kimage;
> +struct memory_notify;
>
> struct crash_mem {
> unsigned int max_nr_ranges;
> @@ -54,6 +58,104 @@ static inline int arch_crash_hotplug_support(struct kimage *image, unsigned long
> }
> #endif
>
> +#ifndef arch_get_system_nr_ranges
> +static inline int arch_get_system_nr_ranges(unsigned int *nr_ranges)
> +{
> + phys_addr_t start, end;
> + u64 i;
> +
> + for_each_mem_range(i, &start, &end)
> + (*nr_ranges)++;
> +
> + return 0;
> +}
> +#endif
> +
> +#ifndef arch_prepare_elf64_ram_headers
> +static inline int arch_prepare_elf64_ram_headers(struct crash_mem *cmem)
> +{
> + phys_addr_t start, end;
> + u64 i;
> +
> + cmem->nr_ranges = 0;
> + for_each_mem_range(i, &start, &end) {
> + cmem->ranges[cmem->nr_ranges].start = start;
> + cmem->ranges[cmem->nr_ranges].end = end - 1;
> + cmem->nr_ranges++;
> + }
> +
> + for (i = 0; i < crashk_cma_cnt; i++) {
> + cmem->ranges[cmem->nr_ranges].start = crashk_cma_ranges[i].start;
> + cmem->ranges[cmem->nr_ranges].end = crashk_cma_ranges[i].end;
> + cmem->nr_ranges++;
> + }
> +
> + return 0;
> +}
> +#endif
> +
> +extern int crash_exclude_mem_range(struct crash_mem *mem,
> + unsigned long long mstart,
> + unsigned long long mend);
> +
> +#ifndef arch_crash_exclude_mem_range
> +static __always_inline int arch_crash_exclude_mem_range(struct crash_mem **mem_ranges,
> + unsigned long long mstart,
> + unsigned long long mend)
> +{
> + return crash_exclude_mem_range(*mem_ranges, mstart, mend);
> +}
> +#endif
> +
> +#ifndef arch_get_crash_memory_ranges
> +static inline int arch_get_crash_memory_ranges(struct crash_mem **cmem,
> + unsigned long *nr_mem_ranges,
> + struct kimage *image,
> + struct memory_notify *mn)
> +{
> + unsigned int nr_ranges;
> + int ret;
> +
> +#if defined(CONFIG_X86_64) || defined(CONFIG_X86_32)
> + /*
> + * Exclusion of crash region, crashk_low_res and/or crashk_cma_ranges
> + * may cause range splits. So add extra slots here.
> + *
> + * Exclusion of low 1M may not cause another range split, because the
> + * range of exclude is [0, 1M] and the condition for splitting a new
> + * region is that the start, end parameters are both in a certain
> + * existing region in cmem and cannot be equal to existing region's
> + * start or end. Obviously, the start of [0, 1M] cannot meet this
> + * condition.
> + *
> + * But in order to lest the low 1M could be changed in the future,
> + * (e.g. [start, 1M]), add a extra slot.
> + */
> + nr_ranges = 3 + crashk_cma_cnt;
> +#else
> + /* For exclusion of crashkernel region*/
> + nr_ranges = 1 + (crashk_low_res.end != 0) + crashk_cma_cnt;
> +#endif
> +
> + ret = arch_get_system_nr_ranges(&nr_ranges);
> + if (ret)
> + return ret;
> +
> + *cmem = kvzalloc(struct_size(*cmem, ranges, nr_ranges), GFP_KERNEL);
> + if (!(*cmem))
> + return -ENOMEM;
> +
> + (*cmem)->max_nr_ranges = nr_ranges;
> + ret = arch_prepare_elf64_ram_headers(*cmem);
> + if (ret) {
> + kvfree(*cmem);
> + return ret;
> + }
> +
> + return 0;
> +}
> +#endif
> +
> #ifndef crash_get_elfcorehdr_size
> static inline unsigned int crash_get_elfcorehdr_size(void) { return 0; }
> #endif
> @@ -61,11 +163,11 @@ static inline unsigned int crash_get_elfcorehdr_size(void) { return 0; }
> /* Alignment required for elf header segment */
> #define ELF_CORE_HEADER_ALIGN 4096
>
> -extern int crash_exclude_mem_range(struct crash_mem *mem,
> - unsigned long long mstart,
> - unsigned long long mend);
> -extern int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
> - void **addr, unsigned long *sz);
> +extern int crash_prepare_elf64_headers(int need_kernel_map,
> + void **addr, unsigned long *sz,
> + unsigned long *nr_mem_ranges,
> + struct kimage *image,
> + struct memory_notify *mn);
>
> struct kimage;
> struct kexec_segment;
> diff --git a/kernel/crash_core.c b/kernel/crash_core.c
> index 99dac1aa972a..39048c87d7a2 100644
> --- a/kernel/crash_core.c
> +++ b/kernel/crash_core.c
> @@ -18,6 +18,7 @@
> #include <linux/memblock.h>
> #include <linux/kmemleak.h>
> #include <linux/crash_core.h>
> +#include <linux/crash_reserve.h>
> #include <linux/reboot.h>
> #include <linux/btf.h>
> #include <linux/objtool.h>
> @@ -161,19 +162,66 @@ static inline resource_size_t crash_resource_size(const struct resource *res)
> return !res->end ? 0 : resource_size(res);
> }
>
> +static int crash_exclude_mem_ranges(struct crash_mem *cmem,
> + unsigned long *nr_mem_ranges)
> +{
> + int ret, i;
> +
> +#if defined(CONFIG_X86_64) || defined(CONFIG_X86_32)
> + /* Exclude the low 1M because it is always reserved */
> + ret = crash_exclude_mem_range(cmem, 0, SZ_1M - 1);
> + if (ret)
> + return ret;
> +#endif
>
> + /* Exclude crashkernel region */
> + ret = arch_crash_exclude_mem_range(&cmem, crashk_res.start, crashk_res.end);
> + if (ret)
> + return ret;
> +
> + if (crashk_low_res.end) {
> + ret = arch_crash_exclude_mem_range(&cmem, crashk_low_res.start, crashk_low_res.end);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < crashk_cma_cnt; ++i) {
> + ret = arch_crash_exclude_mem_range(&cmem, crashk_cma_ranges[i].start,
> + crashk_cma_ranges[i].end);
> + if (ret)
> + return ret;
> + }
>
> + /* Return the computed number of memory ranges, for hotplug usage */
> + if (nr_mem_ranges)
> + *nr_mem_ranges = cmem->nr_ranges;
>
> -int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
> - void **addr, unsigned long *sz)
> + return 0;
> +}
> +
> +int crash_prepare_elf64_headers(int need_kernel_map, void **addr,
> + unsigned long *sz, unsigned long *nr_mem_ranges,
> + struct kimage *image, struct memory_notify *mn)
> {
> - Elf64_Ehdr *ehdr;
> - Elf64_Phdr *phdr;
> unsigned long nr_cpus = num_possible_cpus(), nr_phdr, elf_sz;
> - unsigned char *buf;
> - unsigned int cpu, i;
> unsigned long long notes_addr;
> + struct crash_mem *mem = NULL;
> unsigned long mstart, mend;
> + unsigned int cpu, i;
> + unsigned char *buf;
> + Elf64_Ehdr *ehdr;
> + Elf64_Phdr *phdr;
> + int ret = 0;
> +
> + ret = arch_get_crash_memory_ranges(&mem, nr_mem_ranges, image, mn);
> + if (ret)
> + return ret;
> +
> + if (mem) {
> + ret = crash_exclude_mem_ranges(mem, nr_mem_ranges);
> + if (ret)
> + goto out;
> + }
>
> /* extra phdr for vmcoreinfo ELF note */
> nr_phdr = nr_cpus + 1;
> @@ -192,8 +240,10 @@ int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
> elf_sz = ALIGN(elf_sz, ELF_CORE_HEADER_ALIGN);
>
> buf = vzalloc(elf_sz);
> - if (!buf)
> - return -ENOMEM;
> + if (!buf) {
> + ret = -ENOMEM;
> + goto out;
> + }
>
> ehdr = (Elf64_Ehdr *)buf;
> phdr = (Elf64_Phdr *)(ehdr + 1);
> @@ -262,7 +312,10 @@ int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
>
> *addr = buf;
> *sz = elf_sz;
> - return 0;
> +
> +out:
> + kvfree(mem);
> + return ret;
> }
>
> /**
Powered by blists - more mailing lists