The crash memory alloc, and 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.

To achieve the above goal, three architecture-specific functions are
introduced:

- arch_get_system_nr_ranges(). Pre-counts the max number of memory ranges.

- arch_crash_populate_cmem(). Collects the memory ranges and fills them
  into cmem.

- arch_crash_exclude_ranges(). Architecture's additional crash memory
  ranges exclusion, defaulting to empty.

Signed-off-by: Jinjie Ruan <[email protected]>
---
 arch/arm64/kernel/machine_kexec_file.c     | 39 +++-------
 arch/loongarch/kernel/machine_kexec_file.c | 39 +++-------
 arch/riscv/kernel/machine_kexec_file.c     | 38 +++------
 arch/x86/kernel/crash.c                    | 89 +++-------------------
 include/linux/crash_core.h                 |  5 ++
 kernel/crash_core.c                        | 82 +++++++++++++++++++-
 6 files changed, 132 insertions(+), 160 deletions(-)

diff --git a/arch/arm64/kernel/machine_kexec_file.c 
b/arch/arm64/kernel/machine_kexec_file.c
index fba260ad87a9..c338506a580b 100644
--- a/arch/arm64/kernel/machine_kexec_file.c
+++ b/arch/arm64/kernel/machine_kexec_file.c
@@ -40,23 +40,23 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
 }
 
 #ifdef CONFIG_CRASH_DUMP
-static int prepare_elf_headers(void **addr, unsigned long *sz)
+unsigned int arch_get_system_nr_ranges(void)
 {
-       struct crash_mem *cmem;
-       unsigned int nr_ranges;
-       int ret;
-       u64 i;
+       unsigned int nr_ranges = 2; /* for exclusion of crashkernel region */
        phys_addr_t start, end;
+       u64 i;
 
-       nr_ranges = 2; /* for exclusion of crashkernel region */
        for_each_mem_range(i, &start, &end)
                nr_ranges++;
 
-       cmem = kmalloc_flex(*cmem, ranges, nr_ranges);
-       if (!cmem)
-               return -ENOMEM;
+       return nr_ranges;
+}
+
+int arch_crash_populate_cmem(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 +64,7 @@ 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);
-
-out:
-       kfree(cmem);
-       return ret;
+       return 0;
 }
 #endif
 
@@ -109,7 +94,7 @@ 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_headers(true, &headers, &headers_sz, 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 5584b798ba46..4b318a94b564 100644
--- a/arch/loongarch/kernel/machine_kexec_file.c
+++ b/arch/loongarch/kernel/machine_kexec_file.c
@@ -56,23 +56,23 @@ 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)
+unsigned int arch_get_system_nr_ranges(void)
 {
-       int ret, nr_ranges;
-       uint64_t i;
+       int nr_ranges = 2; /* for exclusion of crashkernel region */
        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++;
 
-       cmem = kmalloc_flex(*cmem, ranges, nr_ranges);
-       if (!cmem)
-               return -ENOMEM;
+       return nr_ranges;
+}
+
+int arch_crash_populate_cmem(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 +80,7 @@ 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);
-
-out:
-       kfree(cmem);
-       return ret;
+       return 0;
 }
 
 /*
@@ -163,7 +148,7 @@ int load_other_segments(struct kimage *image,
                void *headers;
                unsigned long headers_sz;
 
-               ret = prepare_elf_headers(&headers, &headers_sz);
+               ret = crash_prepare_headers(true, &headers, &headers_sz, NULL);
                if (ret < 0) {
                        pr_err("Preparing elf core header failed\n");
                        goto out_err;
diff --git a/arch/riscv/kernel/machine_kexec_file.c 
b/arch/riscv/kernel/machine_kexec_file.c
index 54e2d9552e93..d0e331d87155 100644
--- a/arch/riscv/kernel/machine_kexec_file.c
+++ b/arch/riscv/kernel/machine_kexec_file.c
@@ -44,6 +44,15 @@ static int get_nr_ram_ranges_callback(struct resource *res, 
void *arg)
        return 0;
 }
 
+unsigned int arch_get_system_nr_ranges(void)
+{
+       unsigned int nr_ranges = 1; /* For exclusion of crashkernel region */
+
+       walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
+
+       return nr_ranges;
+}
+
 static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
 {
        struct crash_mem *cmem = arg;
@@ -55,33 +64,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_crash_populate_cmem(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_flex(*cmem, ranges, nr_ranges);
-       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 +259,7 @@ 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_headers(true, &headers, &headers_sz, NULL);
                if (ret) {
                        pr_err("Preparing elf core header failed\n");
                        goto out;
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index 335fd2ee9766..3ad3f8b758a4 100644
--- a/arch/x86/kernel/crash.c
+++ b/arch/x86/kernel/crash.c
@@ -152,16 +152,8 @@ 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)
+unsigned int arch_get_system_nr_ranges(void)
 {
-       unsigned int nr_ranges = 0;
-       struct crash_mem *cmem;
-
-       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.
@@ -176,49 +168,16 @@ static struct crash_mem *fill_up_crash_elf_data(void)
         * 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;
+       unsigned int nr_ranges = 3 + crashk_cma_cnt;
 
-       return cmem;
+       walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
+       return nr_ranges;
 }
 
-/*
- * 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 arch_crash_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;
+       return crash_exclude_mem_range(cmem, 0, SZ_1M - 1);
 }
 
 static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
@@ -232,35 +191,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_crash_populate_cmem(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 +351,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_headers(IS_ENABLED(CONFIG_X86_64), &kbuf.buffer,
+                                   &kbuf.bufsz, &pnum);
        if (ret)
                return ret;
 
@@ -529,7 +463,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_headers(IS_ENABLED(CONFIG_X86_64), &elfbuf, &elfsz,
+                                 &nr_mem_ranges)) {
                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..033b20204aca 100644
--- a/include/linux/crash_core.h
+++ b/include/linux/crash_core.h
@@ -66,6 +66,8 @@ extern int crash_exclude_mem_range(struct crash_mem *mem,
                                   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_headers(int need_kernel_map, void **addr,
+                                unsigned long *sz, unsigned long 
*nr_mem_ranges);
 
 struct kimage;
 struct kexec_segment;
@@ -83,6 +85,9 @@ int kexec_should_crash(struct task_struct *p);
 int kexec_crash_loaded(void);
 void crash_save_cpu(struct pt_regs *regs, int cpu);
 extern int kimage_crash_copy_vmcoreinfo(struct kimage *image);
+extern unsigned int arch_get_system_nr_ranges(void);
+extern int arch_crash_populate_cmem(struct crash_mem *cmem);
+extern int arch_crash_exclude_ranges(struct crash_mem *cmem);
 
 #else /* !CONFIG_CRASH_DUMP*/
 struct pt_regs;
diff --git a/kernel/crash_core.c b/kernel/crash_core.c
index 2c1a3791e410..96a96e511f5a 100644
--- a/kernel/crash_core.c
+++ b/kernel/crash_core.c
@@ -170,9 +170,6 @@ static inline resource_size_t crash_resource_size(const 
struct resource *res)
        return !res->end ? 0 : resource_size(res);
 }
 
-
-
-
 int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
                          void **addr, unsigned long *sz)
 {
@@ -274,6 +271,85 @@ int crash_prepare_elf64_headers(struct crash_mem *mem, int 
need_kernel_map,
        return 0;
 }
 
+static struct crash_mem *alloc_cmem(unsigned int nr_ranges)
+{
+       struct crash_mem *cmem;
+
+       cmem = kvzalloc_flex(*cmem, ranges, nr_ranges);
+       if (!cmem)
+               return NULL;
+
+       cmem->max_nr_ranges = nr_ranges;
+       return cmem;
+}
+
+unsigned int __weak arch_get_system_nr_ranges(void) { return 0; }
+int __weak arch_crash_populate_cmem(struct crash_mem *cmem) { return -1; }
+int __weak arch_crash_exclude_ranges(struct crash_mem *cmem) { return 0; }
+
+static int crash_exclude_core_ranges(struct crash_mem *cmem)
+{
+       int ret, i;
+
+       /* 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;
+}
+
+int crash_prepare_headers(int need_kernel_map, void **addr, unsigned long *sz,
+                         unsigned long *nr_mem_ranges)
+{
+       unsigned int max_nr_ranges;
+       struct crash_mem *cmem;
+       int ret;
+
+       max_nr_ranges = arch_get_system_nr_ranges();
+       if (!max_nr_ranges)
+               return -ENOMEM;
+
+       cmem = alloc_cmem(max_nr_ranges);
+       if (!cmem)
+               return -ENOMEM;
+
+       ret = arch_crash_populate_cmem(cmem);
+       if (ret)
+               goto out;
+
+       ret = crash_exclude_core_ranges(cmem);
+       if (ret)
+               goto out;
+
+       ret = arch_crash_exclude_ranges(cmem);
+       if (ret)
+               goto out;
+
+       /* Return the computed number of memory ranges, for hotplug usage */
+       if (nr_mem_ranges)
+               *nr_mem_ranges = cmem->nr_ranges;
+
+       ret = crash_prepare_elf64_headers(cmem, need_kernel_map, addr, sz);
+
+out:
+       kvfree(cmem);
+       return ret;
+}
+
 /**
  * crash_exclude_mem_range - exclude a mem range for existing ranges
  * @mem: mem->range contains an array of ranges sorted in ascending order
-- 
2.34.1


Reply via email to