The exclude of crashk_res, crashk_low_res and crashk_cma memory are almost identical across different architectures, so 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 are automatically accounted for; - Each architecture no longer has to play whack-a-mole with its private array size. This patch is tested ok on arm64 and riscv with QEMU. Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com> --- arch/arm64/include/asm/kexec.h | 9 ++ arch/arm64/kernel/machine_kexec_file.c | 41 ++++------ arch/loongarch/include/asm/kexec.h | 9 +- arch/loongarch/kernel/machine_kexec_file.c | 41 ++++------ arch/powerpc/include/asm/kexec.h | 13 +++ arch/powerpc/kexec/crash.c | 48 ++++++----- arch/powerpc/kexec/file_load_64.c | 17 ++-- arch/powerpc/kexec/ranges.c | 18 +--- arch/riscv/include/asm/kexec.h | 8 ++ arch/riscv/kernel/machine_kexec_file.c | 37 +++------ arch/x86/include/asm/kexec.h | 7 ++ arch/x86/kernel/crash.c | 95 +++------------------- include/linux/crash_core.h | 89 ++++++++++++++++++-- kernel/crash_core.c | 71 ++++++++++++++-- 14 files changed, 282 insertions(+), 221 deletions(-) diff --git a/arch/arm64/include/asm/kexec.h b/arch/arm64/include/asm/kexec.h index 892e5bebda95..a6c58dd1cf6e 100644 --- a/arch/arm64/include/asm/kexec.h +++ b/arch/arm64/include/asm/kexec.h @@ -93,6 +93,7 @@ static inline void crash_prepare_suspend(void) {} static inline void crash_post_resume(void) {} #endif +struct crash_mem; struct kimage; #if defined(CONFIG_KEXEC_CORE) @@ -130,6 +131,14 @@ extern int load_other_segments(struct kimage *image, char *cmdline); #endif +#ifdef CONFIG_CRASH_DUMP +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 diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c index 410060ebd86d..377980c282be 100644 --- a/arch/arm64/kernel/machine_kexec_file.c +++ b/arch/arm64/kernel/machine_kexec_file.c @@ -40,23 +40,22 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image) } #ifdef CONFIG_CRASH_DUMP -static int prepare_elf_headers(void **addr, unsigned long *sz) +int arch_get_system_nr_ranges(unsigned int *nr_ranges) { - struct crash_mem *cmem; - unsigned int nr_ranges; - int ret; - u64 i; phys_addr_t start, end; + u64 i; - nr_ranges = 2; /* for exclusion of crashkernel region */ for_each_mem_range(i, &start, &end) - nr_ranges++; + (*nr_ranges)++; + + return 0; +} - cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL); - if (!cmem) - return -ENOMEM; +int arch_prepare_elf64_ram_headers(struct crash_mem *cmem) +{ + phys_addr_t start, end; + u64 i; - cmem->max_nr_ranges = nr_ranges; cmem->nr_ranges = 0; for_each_mem_range(i, &start, &end) { cmem->ranges[cmem->nr_ranges].start = start; @@ -64,22 +63,12 @@ static int prepare_elf_headers(void **addr, unsigned long *sz) 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); + return 0; +} -out: - kfree(cmem); - return ret; +static int prepare_elf_headers(void **addr, unsigned long *sz) +{ + return crash_prepare_elf64_headers(true, addr, sz, NULL, NULL, NULL); } #endif diff --git a/arch/loongarch/include/asm/kexec.h b/arch/loongarch/include/asm/kexec.h index 209fa43222e1..a3f0119e2382 100644 --- a/arch/loongarch/include/asm/kexec.h +++ b/arch/loongarch/include/asm/kexec.h @@ -34,7 +34,6 @@ static inline void crash_setup_regs(struct pt_regs *newregs, } #define ARCH_HAS_KIMAGE_ARCH - struct kimage_arch { unsigned long efi_boot; unsigned long cmdline_ptr; @@ -42,12 +41,20 @@ struct kimage_arch { }; #ifdef CONFIG_KEXEC_FILE +struct crash_mem; + extern const struct kexec_file_ops kexec_efi_ops; extern const struct kexec_file_ops kexec_elf_ops; int arch_kimage_file_post_load_cleanup(struct kimage *image); #define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup +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 + extern int load_other_segments(struct kimage *image, unsigned long kernel_load_addr, unsigned long kernel_size, char *initrd, unsigned long initrd_len, char *cmdline, unsigned long cmdline_len); diff --git a/arch/loongarch/kernel/machine_kexec_file.c b/arch/loongarch/kernel/machine_kexec_file.c index fb57026f5f25..eac8544c5cc9 100644 --- a/arch/loongarch/kernel/machine_kexec_file.c +++ b/arch/loongarch/kernel/machine_kexec_file.c @@ -56,23 +56,22 @@ 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 arch_get_system_nr_ranges(unsigned int *nr_ranges) { - int ret, nr_ranges; - uint64_t i; phys_addr_t start, end; - struct crash_mem *cmem; + uint64_t i; - nr_ranges = 2; /* for exclusion of crashkernel region */ for_each_mem_range(i, &start, &end) - nr_ranges++; + (*nr_ranges)++; + + return 0; +} - cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL); - if (!cmem) - return -ENOMEM; +int arch_prepare_elf64_ram_headers(struct crash_mem *cmem) +{ + phys_addr_t start, end; + uint64_t i; - cmem->max_nr_ranges = nr_ranges; cmem->nr_ranges = 0; for_each_mem_range(i, &start, &end) { cmem->ranges[cmem->nr_ranges].start = start; @@ -80,22 +79,12 @@ static int prepare_elf_headers(void **addr, unsigned long *sz) 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); + return 0; +} -out: - kfree(cmem); - return ret; +static int prepare_elf_headers(void **addr, unsigned long *sz) +{ + return crash_prepare_elf64_headers(true, addr, sz, NULL, NULL, NULL); } /* diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index bd4a6c42a5f3..42a76779ccf2 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_crash_prepare_cmem(struct crash_mem *cmem, unsigned long *nr_mem_ranges, + struct kimage *image, struct memory_notify *mn); +#define arch_crash_prepare_cmem arch_crash_prepare_cmem + 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..ff798e3144fa 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_crash_prepare_cmem(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); 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. @@ -453,11 +444,31 @@ static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify * ret = remove_mem_range(&cmem, base_addr, size); 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..87307026084e 100644 --- a/arch/riscv/include/asm/kexec.h +++ b/arch/riscv/include/asm/kexec.h @@ -55,6 +55,8 @@ typedef void (*riscv_kexec_method)(unsigned long first_ind_entry, extern riscv_kexec_method riscv_kexec_norelocate; #ifdef CONFIG_KEXEC_FILE +struct crash_mem; + extern const struct kexec_file_ops elf_kexec_ops; extern const struct kexec_file_ops image_kexec_ops; @@ -69,6 +71,12 @@ struct kimage; int arch_kimage_file_post_load_cleanup(struct kimage *image); #define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup +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 + int load_extra_segments(struct kimage *image, unsigned long kernel_start, unsigned long kernel_len, char *initrd, unsigned long initrd_len, char *cmdline, diff --git a/arch/riscv/kernel/machine_kexec_file.c b/arch/riscv/kernel/machine_kexec_file.c index dd9d92a96517..b3faf0dfac9b 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,15 @@ 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); + return walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback); +} -out: - kfree(cmem); - return ret; +static int prepare_elf_headers(void **addr, unsigned long *sz) +{ + return crash_prepare_elf64_headers(true, addr, sz, NULL, NULL, NULL); } static char *setup_kdump_cmdline(struct kimage *image, char *cmdline, diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h index 5cfb27f26583..d527de588aaa 100644 --- a/arch/x86/include/asm/kexec.h +++ b/arch/x86/include/asm/kexec.h @@ -34,6 +34,7 @@ #include <asm/page.h> #include <asm/ptrace.h> +struct crash_mem; struct kimage; /* @@ -219,6 +220,12 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image); #endif #endif +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 + extern void kdump_nmi_shootdown_cpus(void); #ifdef CONFIG_CRASH_HOTPLUG diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index 335fd2ee9766..ece2764506e9 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -152,72 +152,14 @@ 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 0; } @@ -232,35 +174,18 @@ static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg) return 0; } +int arch_prepare_elf64_ram_headers(struct crash_mem *cmem) +{ + return walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback); +} + /* Prepare elf headers. Return addr and size */ static int prepare_elf_headers(void **addr, unsigned long *sz, unsigned long *nr_mem_ranges) { - 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 crash_prepare_elf64_headers(IS_ENABLED(CONFIG_X86_64), addr, sz, + nr_mem_ranges, NULL, NULL); } #endif diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h index d35726d6a415..d2ea5b1a071d 100644 --- a/include/linux/crash_core.h +++ b/include/linux/crash_core.h @@ -2,11 +2,14 @@ #ifndef LINUX_CRASH_CORE_H #define LINUX_CRASH_CORE_H +#include <linux/kexec.h> #include <linux/linkage.h> #include <linux/elfcore.h> #include <linux/elf.h> +#include <linux/vmalloc.h> struct kimage; +struct memory_notify; struct crash_mem { unsigned int max_nr_ranges; @@ -54,6 +57,82 @@ 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) +{ + return -EINVAL; +} +#endif + +#ifndef arch_prepare_elf64_ram_headers +static inline int arch_prepare_elf64_ram_headers(struct crash_mem *cmem) +{ + return -EINVAL; +} +#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_crash_prepare_cmem +static inline int arch_crash_prepare_cmem(struct crash_mem **cmem, + unsigned long *nr_mem_ranges, + struct kimage *image, + struct memory_notify *mn) +{ + unsigned int nr_ranges; + int ret; + +#ifdef CONFIG_X86_64 + /* + * 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 +140,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..71a1dba7b96f 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; + +#ifdef CONFIG_X86_64 + /* 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_crash_prepare_cmem(&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; } /** -- 2.34.1