Proposal for run time memory allocation for crash kernel

From: Ronit halder
Date: Mon Jun 20 2016 - 11:48:45 EST


Hi,
Currenty linux kernel allocates memory at the boot time for crash
kernel booted using kexec system call in kernel crash. It will be very
useful if we can allocate memory in run time.
It is possible to allocate memory for crash kernel at the boot time
using CMA (Contiguous Memory Allocator). CMA is capable of allocating
big chunk of memory. At the boot time we will create one (if only low
memory is used) or two (if we use both high and low memory) CMA areas
of size given in "crashkernel" boot time command line parameter. Then
the user can allocate or free memory from those CMA areas using
"/sys/kernel/kexec_crash_size" sysfs entry.

I am attaching a prototype patch with the mail. I have made the patch
against kernel version v4.4.11.
Please give your opinions.diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index d2bbe34..42c7226 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -69,6 +69,7 @@
#include <linux/crash_dump.h>
#include <linux/tboot.h>
#include <linux/jiffies.h>
+#include <linux/cma.h>

#include <video/edid.h>

@@ -122,7 +123,8 @@
*/
unsigned long max_low_pfn_mapped;
unsigned long max_pfn_mapped;
-
+struct cma *crashk_cma = NULL;
+struct cma *crashk_cma_low = NULL;
#ifdef CONFIG_DMI
RESERVE_BRK(dmi_alloc, 65536);
#endif
@@ -532,9 +534,9 @@ static int __init reserve_crashkernel_low(void)
return -ENOMEM;
}

- ret = memblock_reserve(low_base, low_size);
+ ret = cma_declare_contiguous(low_base, low_size, 0, CRASH_ALIGN, 0, 1, &crashk_cma_low);
if (ret) {
- pr_err("%s: Error reserving crashkernel low memblock.\n", __func__);
+ pr_err("%s: Error reserving CMA area for crashkernel low.\n", __func__);
return ret;
}

@@ -543,9 +545,7 @@ static int __init reserve_crashkernel_low(void)
(unsigned long)(low_base >> 20),
(unsigned long)(total_low_mem >> 20));

- crashk_low_res.start = low_base;
- crashk_low_res.end = low_base + low_size - 1;
- insert_resource(&iomem_resource, &crashk_low_res);
+
#endif
return 0;
}
@@ -578,6 +578,8 @@ static void __init reserve_crashkernel(void)
high ? CRASH_ADDR_HIGH_MAX
: CRASH_ADDR_LOW_MAX,
crash_size, CRASH_ALIGN);
+ pr_info("ronit 0 crash_base %llu crash_size %llu \n", crash_base, crash_size);
+
if (!crash_base) {
pr_info("crashkernel reservation failed - No suitable area found.\n");
return;
@@ -589,30 +591,25 @@ static void __init reserve_crashkernel(void)
start = memblock_find_in_range(crash_base,
crash_base + crash_size,
crash_size, 1 << 20);
+ pr_info("ronit base_mentioned crash_base %llu crash_size %llu \n", crash_base, crash_size);
if (start != crash_base) {
pr_info("crashkernel reservation failed - memory is in use.\n");
return;
}
}
- ret = memblock_reserve(crash_base, crash_size);
- if (ret) {
- pr_err("%s: Error reserving crashkernel memblock.\n", __func__);
+ if (crash_base >= (1ULL << 32) && reserve_crashkernel_low()) {
return;
}
-
- if (crash_base >= (1ULL << 32) && reserve_crashkernel_low()) {
- memblock_free(crash_base, crash_size);
+ ret = cma_declare_contiguous(crash_base, crash_size, 0, CRASH_ALIGN, 0, 1, &crashk_cma);
+ if (ret) {
+ pr_err("%s: Error reserving CMA area for crashkernel.\n", __func__);
return;
}

- pr_info("Reserving %ldMB of memory at %ldMB for crashkernel (System RAM: %ldMB)\n",
+ pr_info("ronit halder Reserving %ldMB of memory at %ldMB for crashkernel (System RAM: %ldMB)\n",
(unsigned long)(crash_size >> 20),
(unsigned long)(crash_base >> 20),
(unsigned long)(total_mem >> 20));
-
- crashk_res.start = crash_base;
- crashk_res.end = crash_base + crash_size - 1;
- insert_resource(&iomem_resource, &crashk_res);
}
#else
static void __init reserve_crashkernel(void)
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index d140b1e..d6a24d6 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -307,7 +307,7 @@ extern size_t vmcoreinfo_max_size;

/* flag to track if kexec reboot is in progress */
extern bool kexec_in_progress;
-
+extern struct cma *crashk_cma, *crashk_cma_low;
int __init parse_crashkernel(char *cmdline, unsigned long long system_ram,
unsigned long long *crash_size, unsigned long long *crash_base);
int parse_crashkernel_high(char *cmdline, unsigned long long system_ram,
@@ -318,6 +318,13 @@ int crash_shrink_memory(unsigned long new_size);
size_t crash_get_memory_size(void);
void crash_free_reserved_phys_range(unsigned long begin, unsigned long end);

+//custom functions
+int crash_free_memory(unsigned int size);
+int crash_alloc_memory(unsigned int size);
+int crash_alloc_memory_low(void);
+int crash_free_memory_low(void);
+size_t crash_get_memory_size_low(void);
+
int __weak arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
unsigned long buf_len);
void * __weak arch_kexec_kernel_image_load(struct kimage *image);
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 11b64a6..434a8b4 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -39,6 +39,7 @@
#include <linux/compiler.h>
#include <linux/hugetlb.h>

+#include <linux/cma.h>
#include <asm/page.h>
#include <asm/sections.h>

@@ -46,7 +47,9 @@
#include <crypto/sha.h>
#include "kexec_internal.h"

+#define CRASH_ALIGN (16 << 20)
DEFINE_MUTEX(kexec_mutex);
+DEFINE_MUTEX(kexec_mutex_low);

/* Per cpu memory for storing cpu states in case of system crash. */
note_buf_t __percpu *crash_notes;
@@ -57,6 +60,7 @@ u32 vmcoreinfo_note[VMCOREINFO_NOTE_SIZE/4];
size_t vmcoreinfo_size;
size_t vmcoreinfo_max_size = sizeof(vmcoreinfo_data);

+static struct page *pages, *pages_low;
/* Flag to indicate we are going to kexec a new kernel */
bool kexec_in_progress = false;

@@ -887,6 +891,17 @@ size_t crash_get_memory_size(void)
return size;
}

+size_t crash_get_memory_size_low(void)
+{
+ size_t size = 0;
+
+ mutex_lock(&kexec_mutex_low);
+ if (crashk_low_res.end != crashk_low_res.start)
+ size = resource_size(&crashk_low_res);
+ mutex_unlock(&kexec_mutex_low);
+ return size;
+}
+
void __weak crash_free_reserved_phys_range(unsigned long begin,
unsigned long end)
{
@@ -946,6 +961,71 @@ unlock:
mutex_unlock(&kexec_mutex);
return ret;
}
+int crash_free_memory(unsigned int size)
+{
+ int ret;
+
+ if (!crashk_cma)
+ return 0;
+ ret = cma_release(crashk_cma, pages, size>>PAGE_SHIFT);
+
+ if (!ret) {
+ pr_info("Crash memory release failed");
+ return 0;
+ }
+ release_resource(&crashk_res);
+ return 1;
+}
+
+int crash_alloc_memory(unsigned int size)
+{
+ if (!crashk_cma)
+ return 0;
+ pages = cma_alloc(crashk_cma, size>>PAGE_SHIFT, KEXEC_CRASH_MEM_ALIGN);
+ pr_info("In crash_alloc_memory fucntion");
+ if (!pages){
+ pr_info("Memory for crash kernel not allocated");
+ return 0;
+ }
+
+ crashk_res.start = page_to_pfn(pages)<<PAGE_SHIFT;
+ crashk_res.end = crashk_res.start + size - 1;
+ insert_resource(&iomem_resource, &crashk_res);
+ return 1;
+}
+
+int crash_free_memory_low(void)
+{
+ int ret;
+
+ if (!crashk_cma_low)
+ return 0;
+ ret = cma_release(crashk_cma_low, pages_low, cma_get_size(crashk_cma_low)>>PAGE_SHIFT);
+
+ if (!ret) {
+ pr_info("Crash low memory release failed");
+ return 0;
+ }
+ release_resource(&crashk_low_res);
+ return 1;
+}
+
+int crash_alloc_memory_low(void)
+{
+ if (!crashk_cma_low)
+ return 0;
+ pages = cma_alloc(crashk_cma_low, cma_get_size(crashk_cma_low)>>PAGE_SHIFT, KEXEC_CRASH_MEM_ALIGN);
+ pr_info("In crash_alloc_memory fucntion");
+ if (!pages){
+ pr_info("Low memory for crash kernel not allocated");
+ return 0;
+ }
+
+ crashk_low_res.start = page_to_pfn(pages)<<PAGE_SHIFT;
+ crashk_low_res.end = crashk_low_res.start + cma_get_size(crashk_cma_low) - 1;
+ insert_resource(&iomem_resource, &crashk_low_res);
+ return 1;
+}

static u32 *append_elf_note(u32 *buf, char *name, unsigned type, void *data,
size_t data_len)
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
index e83b264..fbc5314 100644
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -116,12 +116,23 @@ static ssize_t kexec_crash_size_store(struct kobject *kobj,
{
unsigned long cnt;
int ret;
+ int size;

if (kstrtoul(buf, 0, &cnt))
return -EINVAL;

- ret = crash_shrink_memory(cnt);
- return ret < 0 ? ret : count;
+ size = cnt<<20;
+ if (cnt == 0) {
+ crash_free_memory_low();
+ ret = crash_free_memory(crash_get_memory_size());
+ }
+ else if (cnt > 0) {
+ if (crash_get_memory_size_low() == 0)
+ crash_alloc_memory_low();
+ ret = crash_free_memory(crash_get_memory_size());
+ ret = crash_alloc_memory(size);
+ }
+ return count;
}
KERNEL_ATTR_RW(kexec_crash_size);