PSTORE ramoops 在Linux x86_64 与 arm64上使用
一、官方文档
kernel中关于ramoops的文档:
Documentation/admin-guide/ramoops.rst
二、操作过程
- kernel 配置选项:
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_RAM=y
CONFIG_PSTORE_COMPRESS=y
CONFIG_PSTORE_COMPRESS_DEFAULT="zstd"
CONFIG_PSTORE_ZSTD_COMPRESS=y
CONFIG_PSTORE_ZSTD_COMPRESS_DEFAULT=y
- 参数说明
主要的几个参数
2.1 参数的配置方式
从kernel中的ramoops的文档中可知,共有三种方式设置这些参数,
① 模块参数
② 设备树中配置参数
③ platform_data
2.1.1 模块参数 的方式
例如:
内存起始地址为0x20200000,内存大小为1M,dump(dmesg)的大小为64K,console的大小为64K:
insmod reed_solomon.ko
insmod ramoops.ko mem_size=0x100000 mem_address=0x20200000 record_size=0x10000 console_size=0x10000
2.1.2 设备树中配置参数的方式
该方式只能用于arm的方式,因为x86是没有设备树的。
Documentation/devicetree/bindings/reserved-memory/ramoops.txt
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
ramoops@8f000000 {
compatible = "ramoops";
reg = <0 0x20200000 0 0x100000>;
record-size = <0x10000>;
console-size = <0x10000>;
};
};
2.1.3 参数配置使用 platform_data 的方式
patch 如下
diff --git a/fs/pstore/ramoops_dev.c b/fs/pstore/ramoops_dev.c
new file mode 100644
index 000000000..3adf85bfa
--- /dev/null
+++ b/fs/pstore/ramoops_dev.c
@@ -0,0 +1,48 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pstore_ram.h>
+#include <linux/memblock.h>
+#include <linux/types.h>
+#include <linux/sizes.h>
+
+
+static struct ramoops_platform_data ramoops_data = {
+ .mem_size = 0x100000,
+ .mem_address = 0x20200000,
+ .mem_type = 0,
+ .record_size = 0x10000,
+ .console_size = 0x10000,
+ .max_reason = 2,
+};
+
+static struct platform_device ramoops_dev = {
+ .name = "ramoops",
+ .dev = {
+ .platform_data = &ramoops_data,
+ },
+};
+
+static int __init ramoops_device_init(void)
+{
+ int ret = 0;
+
+ pr_info("%s into %d\n", __func__, __LINE__);
+ ret = platform_device_register(&ramoops_dev);
+ if (ret) {
+ pr_err("%s %d into: unable to register platform device\n", __func__, __LINE__);
+ }
+
+ return ret;
+}
+
+static void __exit ramoops_device_exit(void)
+{
+ platform_device_unregister(&ramoops_dev);
+}
+
+postcore_initcall(ramoops_device_init);
+module_exit(ramoops_device_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RAM Oops/Panic logger/device");
三、 在x86_64上的使用
对于x86只能使用模块参数或者platform_data的方式,由于编译进kernel,所以选择platform_data的方式;
- 指定预留内存
1.1物理内存地址的选择
首先可以通过 /proc/iomem 中的 System RAM 了解到物理内存的映射,如下(2G的内存):
cat /proc/iomem | grep "System RAM"
00001000-0009ebff : System RAM 631K
00100000-1fffffff : System RAM 511M
20200000-40003fff : System RAM 510M
40005000-6a7c0fff : System RAM 679M
6a804000-6adecfff : System RAM 5M
6afaa000-7985cfff : System RAM 232M
79fff000-79ffffff : System RAM 4K
100000000-1005fffff : System RAM 5M
总大小为1943M,与 kernel 启动 log 中的总内存大小1945M相差不多;
[ 0.050303] Memory: 1854248K/1992680K available (14340K kernel code, 1770K rwdata, 5820K rodata, 1412K init, 3060K bss, 138176K reserved, 0K cma-reserved)
cat /proc/iomem
00000000-00000fff : Reserved
00001000-0009ebff : System RAM 631K
......
00100000-1fffffff : System RAM 511M
01000000-01e010a4 : Kernel code
02000000-025aefff : Kernel rodata
02600000-027ba47f : Kernel data
02d02000-02dfffff : Kernel bss
20000000-201fffff : Reserved
20000000-201fffff : pnp 00:0a
20200000-40003fff : System RAM 510M
......
由以上可知
00100000-1fffffff : System RAM 511M 段的内存中,kernel 使用了01000000-02dfffff 30M的内存;所以选择
20200000-40003fff : System RAM 510M 这段地址的内存;(只要找一块空闲的地址即可,内存大小使用1M足以)
1.2 设置保留地址
接着需要将选择的物理地址段设置为保留地址,在 arch/x86/kernel/setup.c 中的 setup_arch() 函数中稍微前一些的位置中调用 memblock_reserve() 来声明保留内存;(新建了个函数reserve_ramoops()函数,在reserve_crashkernel()函数之前调用)
patch如下:
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 065152d92..80892fd8d 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -409,6 +409,15 @@ static void __init memblock_x86_reserve_range_setup_data(void)
}
}
+static void __init reserve_ramoops(void)
+{
+#ifdef CONFIG_RAMOOPS_SPEC_MEM_TEST
+ pr_info("%s into %d before memblock_reserve ramoops\n", __func__, __LINE__);
+ memblock_reserve(0x20200000, SZ_1M);
+ pr_info("%s into %d after memblock_reserve ramoops\n", __func__, __LINE__);
+#endif
+}
+
/*
* --------- Crashkernel reservation ------------------------------
*/
@@ -1166,6 +1175,9 @@ void __init setup_arch(char **cmdline_p)
if (boot_cpu_has(X86_FEATURE_GBPAGES))
hugetlb_cma_reserve(PUD_SHIFT - PAGE_SHIFT);
+#ifdef CONFIG_PSTORE_RAM
+ reserve_ramoops();
+#endif
/*
* Reserve memory for crash kernel after SRAT is parsed so that it
* won't consume hotpluggable memory.
- 测试
2.1 测试前准备
2.1.1 挂载pstore文件系统
mount -t pstore pstore /sys/fs/pstore
2.1.2 确保驱动有被加载
dmesg | grep "ramoops"
2.1.3 开启panic_on_oops
echo 1 > /proc/sys/kernel/panic_on_oops
2.2 触发panic
echo c > /proc/sysrq-trigger
若这样没有触发panic,则可以使用以下patch:
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 7ca209d4e..a0f7d4319 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -599,6 +599,8 @@ void __handle_sysrq(int key, bool check_mask)
*/
if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
pr_info("%s\n", op_p->action_msg);
+ op_p = NULL;
+ pr_info("after %s\n", op_p->action_msg);
console_loglevel = orig_log_level;
op_p->handler(key);
} else {
2.3 查看dump
重启后,先挂载pstore文件系统,再加确保驱动有被加载,然后即可在 /sys/fs/pstore/下看见dump log
ls /sys/fs/pstore/
console-ramoops-0 dmesg-ramoops-1
- 指定预留内存 – 使用自动分配物理内存
3.1 原理
因为指定预留内存的方式,需要预先知道本地物理内存的映射,所以有了不需要预先知道本地物理内存的映射,而自动分配物理内存作为保留内存的需求;
参考了 arch/x86/kernel/setup.c 中 reserve_crashkernel() 函数的实现,其保留内存的方式,其便是通过调用memblock_phys_alloc_range() 函数来实现的;
memblock_phys_alloc_range() 在start 和end 之间分配size 字节,需要4个参数,如下:
成功时分配的内存块的物理地址,失败时返回0
static void __init reserve_ramoops(void)
{
#ifdef CONFIG_RAMOOPS_MEM_AUTO_ALLOC
extern phys_addr_t ram_mem_addr;
unsigned long long crash_size, crash_base, total_mem;
crash_size = SZ_1M;
total_mem = memblock_phys_mem_size();
crash_base = memblock_phys_alloc_range(crash_size,
SZ_1M, 0,
SZ_64T);
if (!crash_base) {
pr_info("ramoops reservation failed - No suitable area found.\n");
return;
}
pr_info("Reserving %ldMB of memory at %ldMB for ramoops (System RAM: %ldMB)\n",
(unsigned long)(crash_size >> 20),
(unsigned long)(crash_base >> 20),
(unsigned long)(total_mem >> 20));
ram_mem_addr = crash_base;
#else
#ifdef CONFIG_RAMOOPS_SPEC_MEM_TEST
pr_info("%s into %d before memblock_reserve ramoops\n", __func__, __LINE__);
memblock_reserve(0x20200000, SZ_1M);
pr_info("%s into %d after memblock_reserve ramoops\n", __func__, __LINE__);
#endif
#endif
}
完整patch如下:
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 065152d92..71f01a8cd 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -49,6 +49,9 @@
#include <asm/vsyscall.h>
#include <linux/vmalloc.h>
+#ifdef CONFIG_RAMOOPS_MEM_AUTO_ALLOC
+#include <linux/ramoops_dev.h>
+#endif
/*
* max_low_pfn_mapped: highest directly mapped pfn < 4 GB
* max_pfn_mapped: highest directly mapped pfn > 4 GB
@@ -409,6 +412,41 @@ static void __init memblock_x86_reserve_range_setup_data(void)
}
}
+static void __init reserve_ramoops(void)
+{
+#ifdef CONFIG_RAMOOPS_MEM_AUTO_ALLOC
+ extern phys_addr_t ram_mem_addr;
+ unsigned long long crash_size, crash_base, total_mem;
+
+ crash_size = SZ_1M;
+ total_mem = memblock_phys_mem_size();
+
+ crash_base = memblock_phys_alloc_range(crash_size,
+ SZ_1M, 0,
+ SZ_64T);
+ if (!crash_base) {
+ pr_info("ramoops reservation failed - No suitable area found.\n");
+ return;
+ }
+
+
+ pr_info("Reserving %ldMB of memory at %ldMB for ramoops (System RAM: %ldMB)\n",
+ (unsigned long)(crash_size >> 20),
+ (unsigned long)(crash_base >> 20),
+ (unsigned long)(total_mem >> 20));
+
+ ram_mem_addr = crash_base;
+#else
+
+#ifdef CONFIG_RAMOOPS_SPEC_MEM_TEST
+ pr_info("%s into %d before memblock_reserve ramoops\n", __func__, __LINE__);
+ memblock_reserve(0x20200000, SZ_1M);
+ pr_info("%s into %d after memblock_reserve ramoops\n", __func__, __LINE__);
+#endif
+
+#endif
+}
+
/*
* --------- Crashkernel reservation ------------------------------
*/
@@ -1166,6 +1204,9 @@ void __init setup_arch(char **cmdline_p)
if (boot_cpu_has(X86_FEATURE_GBPAGES))
hugetlb_cma_reserve(PUD_SHIFT - PAGE_SHIFT);
+#ifdef CONFIG_PSTORE_RAM
+ reserve_ramoops();
+#endif
/*
* Reserve memory for crash kernel after SRAT is parsed so that it
* won't consume hotpluggable memory.
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index 8efe60487..25a33d079 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -263,3 +263,11 @@ config PSTORE_BLK_FTRACE_SIZE
NOTE that, both Kconfig and module parameters can configure
pstore/blk, but module parameters have priority over Kconfig.
+
+config RAMOOPS_MEM_AUTO_ALLOC
+ bool "ramoops mem address use auto alloc in x86"
+ depends on PSTORE_RAM
+ default n
+ help
+ This will use the automatically allocated memory in setup.c
+ as the memory address for ramoops. in x86.
diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile
index c270467ae..9193e7bf4 100644
--- a/fs/pstore/Makefile
+++ b/fs/pstore/Makefile
@@ -4,6 +4,7 @@
#
obj-$(CONFIG_PSTORE) += pstore.o
+obj-$(CONFIG_RAMOOPS_MEM_AUTO_ALLOC) += ramoops_dev.o
pstore-objs += inode.o platform.o
pstore-$(CONFIG_PSTORE_FTRACE) += ftrace.o
diff --git a/fs/pstore/ramoops_dev.c b/fs/pstore/ramoops_dev.c
new file mode 100644
index 000000000..da272743c
--- /dev/null
+++ b/fs/pstore/ramoops_dev.c
@@ -0,0 +1,61 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pstore_ram.h>
+#include <linux/memblock.h>
+#include <linux/types.h>
+#include <linux/sizes.h>
+#include <linux/ramoops_dev.h>
+
+
+phys_addr_t ram_mem_addr = 0;
+
+static struct ramoops_platform_data ramoops_data = {
+ .mem_size = 0x100000,
+ .mem_address = 0x20200000,
+ .mem_type = 0,
+ .record_size = 0x10000,
+ .console_size = 0x10000,
+ .max_reason = 2,
+};
+
+static struct platform_device ramoops_dev = {
+ .name = "ramoops",
+ .dev = {
+ .platform_data = &ramoops_data,
+ },
+};
+
+static int __init ramoops_device_init(void)
+{
+ int ret = 0;
+ pr_info("%s into %d\n", __func__, __LINE__);
+
+ if (0 != ram_mem_addr) {
+ pr_info("%s %d into: ram_mem_addr = %llx, mem_address = %llx\n",
+ __func__, __LINE__, ram_mem_addr, ramoops_data.mem_address);
+ ramoops_data.mem_address = ram_mem_addr;
+ ramoops_data.mem_size = SZ_1M;
+ } else {
+ pr_err("%s %d into: ram_mem_addr is 0!\n", __func__, __LINE__);
+ return -1;
+ }
+
+ ret = platform_device_register(&ramoops_dev);
+ if (ret) {
+ pr_err("%s %d into: unable to register platform device\n", __func__, __LINE__);
+ }
+
+ return ret;
+}
+
+static void __exit ramoops_device_exit(void)
+{
+ platform_device_unregister(&ramoops_dev);
+}
+
+postcore_initcall(ramoops_device_init);
+module_exit(ramoops_device_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RAM Oops/Panic logger/device");
diff --git a/include/linux/ramoops_dev.h b/include/linux/ramoops_dev.h
new file mode 100644
index 000000000..eb4278898
--- /dev/null
+++ b/include/linux/ramoops_dev.h
@@ -0,0 +1,7 @@
+#ifndef _LINUX_RAMOOPS_DEV_H
+#define _LINUX_RAMOOPS_DEV_H
+
+
+extern phys_addr_t ram_mem_addr;
+
+#endif
3.3 测试
从启动log中获取分配到的保留内存地址,作为参数在使用加载模块的方式即可。
[ 0.019398] Reserving 1MB of memory at 4100MB for ramoops (System RAM: 1945MB)
[ 0.019400] reserve_ramoops 431 into: memblock_phys_alloc_range ramoops_base = 0x100400000
挂载文件系统
mount -t pstore pstore /sys/fs/pstore
触发panic
echo c /proc/sysrq-trigger
重启后检测log
ls /sys/fs/pstore/
console-ramoops-0 dmesg-ramoops-0 dmesg-ramoops-1
四、 在arm64上的使用
在arm上,三种参数的配置方式都支持,只有设置指定预留内存的方式与x86有差异;
1 指定预留内存 – 直接指定物理内存
1.1物理内存地址的选择
cat /proc/iomem
00200000-083fffff : System RAM
00200000-0148ffff : Kernel code
01490000-0166ffff : reserved
01670000-019dffff : Kernel data
08300000-08329fff : reserved
09400000-efffffff : System RAM
ec000000-efffffff : reserved
......
1f0000000-1ffffffff : System RAM
......
由以上可知,可以继续使用0x20200000,大小1M 这段地址的内存。
1.2 设置保留地址
arm64与x86_64 不太一样,arm64需要在 arch/arm64/mm/init.c 中的 bootmem_init() 函数中中调用 memblock_reserve() 来声明保留内存;
patch如下:
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 82cdb35ed..bddc93b10 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -95,6 +95,16 @@ phys_addr_t __ro_after_init arm64_dma_phys_limit = PHYS_MASK + 1;
*/
static bool disable_dma32 __ro_after_init;
+static void __init reserve_ramoops(void)
+{
+#ifdef CONFIG_RAMOOPS_SPEC_MEM_TEST
+
+ pr_info("%s into %d before memblock_reserve ramoops\n", __func__, __LINE__);
+ memblock_reserve(0x20200000, SZ_1M);
+ pr_info("%s into %d after memblock_reserve ramoops\n", __func__, __LINE__);
+#endif
+}
+
#ifdef CONFIG_KEXEC_CORE
/*
* reserve_crashkernel() - reserves memory for crash kernel
@@ -454,6 +464,12 @@ void __init arm64_memblock_init(void)
reserve_elfcorehdr();
+
+#ifdef CONFIG_PSTORE_RAM
+ if (!IS_ENABLED(CONFIG_ZONE_DMA) && !IS_ENABLED(CONFIG_ZONE_DMA32))
+ reserve_ramoops();
+#endif
+
if (!IS_ENABLED(CONFIG_ZONE_DMA) && !IS_ENABLED(CONFIG_ZONE_DMA32))
reserve_crashkernel();
@@ -500,6 +516,12 @@ void __init bootmem_init(void)
dma_contiguous_reserve(arm64_dma_phys_limit);
rk_dma_heap_cma_setup();
+
+#ifdef CONFIG_PSTORE_RAM
+ if (IS_ENABLED(CONFIG_ZONE_DMA) || IS_ENABLED(CONFIG_ZONE_DMA32))
+ reserve_ramoops();
+#endif
+
/*
* request_standard_resources() depends on crashkernel's memory being
* reserved, so do it here.
- 指定预留内存 – 使用自动分配物理内存
在arm64下不再是使用memblock_phys_alloc_range()函数,而是使用memblock_find_in_range()函数来分配内存;
参考了 arch/arm64/mm/init.c 中 reserve_crashkernel() 函数的实现,其保留内存的方式,便是通过调用memblock_find_in_range() 函数来实现的;
memblock_phys_alloc_range() 在start 和end 之间分配size 字节,需要4个参数,如下:
成功时返回找到的内存的物理地址,失败时返回0
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 82cdb35ed..a9679a89f 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -45,6 +45,10 @@
#include <asm/tlb.h>
#include <asm/alternative.h>
+#ifdef CONFIG_RAMOOPS_MEM_AUTO_ALLOC
+#include <linux/ramoops_dev.h>
+#endif
+
/*
* We need to be able to catch inadvertent references to memstart_addr
* that occur (potentially in generic code) before arm64_memblock_init()
@@ -95,6 +99,48 @@ phys_addr_t __ro_after_init arm64_dma_phys_limit = PHYS_MASK + 1;
*/
static bool disable_dma32 __ro_after_init;
+static void __init reserve_ramoops(void)
+{
+#ifdef CONFIG_RAMOOPS_MEM_AUTO_ALLOC
+ extern phys_addr_t ram_mem_addr;
+ unsigned long long crash_size, crash_base, total_mem;
+ int ret = 0;
+
+ crash_size = SZ_1M;
+ total_mem = memblock_phys_mem_size();
+
+ crash_base = memblock_find_in_range(0, arm64_dma_phys_limit, crash_size, SZ_1M);
+ if (!crash_base) {
+ pr_info("ramoops reservation failed - No suitable area found.\n");
+ return ;
+ }
+
+ pr_info("Reserving %ldMB of memory at %ldMB for ramoops (System RAM: %ldMB)\n",
+ (unsigned long)(crash_size >> 20),
+ (unsigned long)(crash_base >> 20),
+ (unsigned long)(total_mem >> 20));
+
+ pr_info("%s into %d before memblock_reserve ramoops\n", __func__, __LINE__);
+ ret = memblock_reserve(crash_base, crash_size);
+ if (ret < 0) {
+ pr_warn("ramoops reservation failed - memory is in use (0x%lx)\n",
+ (unsigned long)crash_base);
+ return ;
+ }
+
+ ram_mem_addr = crash_base;
+ pr_info("%s into %d after memblock_reserve ramoops\n", __func__, __LINE__);
+#else
+
+#ifdef CONFIG_RAMOOPS_SPEC_MEM_TEST
+ pr_info("%s into %d before memblock_reserve ramoops\n", __func__, __LINE__);
+ memblock_reserve(0x20200000, SZ_1M);
+ pr_info("%s into %d after memblock_reserve ramoops\n", __func__, __LINE__);
+#endif
+
+#endif
+}
+
#ifdef CONFIG_KEXEC_CORE
/*
* reserve_crashkernel() - reserves memory for crash kernel
@@ -454,6 +500,12 @@ void __init arm64_memblock_init(void)
reserve_elfcorehdr();
+
+#ifdef CONFIG_PSTORE_RAM
+ if (!IS_ENABLED(CONFIG_ZONE_DMA) && !IS_ENABLED(CONFIG_ZONE_DMA32))
+ reserve_ramoops();
+#endif
+
if (!IS_ENABLED(CONFIG_ZONE_DMA) && !IS_ENABLED(CONFIG_ZONE_DMA32))
reserve_crashkernel();
@@ -500,6 +552,12 @@ void __init bootmem_init(void)
dma_contiguous_reserve(arm64_dma_phys_limit);
rk_dma_heap_cma_setup();
+
+#ifdef CONFIG_PSTORE_RAM
+ if (IS_ENABLED(CONFIG_ZONE_DMA) || IS_ENABLED(CONFIG_ZONE_DMA32))
+ reserve_ramoops();
+#endif
+
/*
* request_standard_resources() depends on crashkernel's memory being
* reserved, so do it here.