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
add crash_prepare_headers() helper to handle 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.

Reviewed-by: Sourabh Jain <[email protected]>
Acked-by: Baoquan He <[email protected]>
Acked-by: Mike Rapoport (Microsoft) <[email protected]>
Signed-off-by: Jinjie Ruan <[email protected]>
---
 include/linux/crash_core.h |  5 +++
 kernel/crash_core.c        | 91 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 93 insertions(+), 3 deletions(-)

diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h
index c1dee3f971a9..583ffcc703d4 100644
--- a/include/linux/crash_core.h
+++ b/include/linux/crash_core.h
@@ -59,6 +59,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;
@@ -76,6 +78,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 4f21fc3b108b..d28be3890efb 100644
--- a/kernel/crash_core.c
+++ b/kernel/crash_core.c
@@ -16,6 +16,7 @@
 #include <linux/mm.h>
 #include <linux/cpuhotplug.h>
 #include <linux/memblock.h>
+#include <linux/memory_hotplug.h>
 #include <linux/kmemleak.h>
 #include <linux/crash_core.h>
 #include <linux/reboot.h>
@@ -168,9 +169,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)
 {
@@ -272,6 +270,93 @@ 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;
+
+       get_online_mems();
+       max_nr_ranges = arch_get_system_nr_ranges();
+       if (!max_nr_ranges) {
+               put_online_mems();
+               return -ENOMEM;
+       }
+
+       cmem = alloc_cmem(max_nr_ranges);
+       if (!cmem) {
+               put_online_mems();
+               return -ENOMEM;
+       }
+
+       ret = arch_crash_populate_cmem(cmem);
+       if (ret) {
+               put_online_mems();
+               goto out;
+       }
+
+       put_online_mems();
+       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