为树莓派编译Kdump kernel

本文记录一下编译带Kdump功能的内核的过程中需要注意的点。首先要说的是,目前Pi的Linux Kernel对Kexec的支持还有些不稳定,对于各种设备的驱动的支持还有些问题,导致整个流程不稳定。目前仍然在漫长的解决过程中:

对应的Issue的地址在这里:

https://github.com/raspberrypi/linux/issues/27

有兴趣的小伙伴可以关注下这个issue的解决情况。

虽然这个功能目前还极其不稳定的状态,但是并不影响我们通过编译过程理解内核的配置方法。

关于内核的编译与交叉编译方法,之前写的三篇文章已经有介绍,这篇文章不再重复。本文的重点是要对编译内核前的配置做下介绍。

配置内核,最稳妥的方法是在kernel的source tree下执行menuconfig

$ make menuconfig

如果是交叉编译,需要添加平台选项,具体参考这篇文章:

https://www.raspberrypi.org/documentation/linux/kernel/configuring.md

执行menuconfig,会进入到kernel的配置菜单:

需要注意的是,这个配置菜单里面,有很多选项是互相制约,互相依赖的。比如:你选了A选项,那么可能B选项就不可以再被勾选。或者:你必须选择了X选项,Y选项才会出现。

这个依赖关系,在menuconfig里面是不会出现的,而是在内核的Kconfig文件里体现。

Kconfig文件在内核的每一级目录里面都存在,并且配置了各种内核编译选项,以及选项之间的依赖关系。下面是内核源码中的各个Kconfig文件:

$ find . | grep Kconfig$ | head
./usr/Kconfig
./block/partitions/Kconfig
./block/Kconfig
./certs/Kconfig
./lib/xz/Kconfig
./lib/fonts/Kconfig
./lib/Kconfig
./kernel/gcov/Kconfig
./kernel/irq/Kconfig
./kernel/power/Kconfig

可以看到,每一层目录里面都有Kconfig文件,整体形成一个树形结构。

因此,读懂Kconfig文件,是配置内核编译选项的关键所在,建议大家阅读相关文档进行系统学习。以下是推荐阅读的一些资料:

https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt

https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt

https://www.linuxjournal.com/content/kbuild-linux-kernel-build-system

对于编译Kdump内核,我们需要的关键内核选项是:

关于上面选项的相关文档在这里,供大家参考:

https://wiki.archlinux.org/index.php/Kdump

另外,在文档里没有写的是,我们还需要打开KEXEC选项。

Linux kernel里面,CONFIG_PROC_VMCORE是和CONFIG_CRASH_DUMP,所以可以不用操心(分析过程接下来给出)。因此我们需要自己设置的选项是:

这几个选项的依赖关系,我们可以在内核代码中检索一下。我们首先检索KEXEC选项。

因为我们是要给arm架构平台来做编译,因此要在架构目录里进行搜索:

[weli@2ac1fbd74634 arm]$ pwd
/home/weli/linux/arch/arm

使用下面的脚本在平台目录里搜索一下KEXEC

for f in $(find . | grep Kconfig)
do
	grep -n 'KEXEC' ${f}
	RESULT=$?
	if [ ${RESULT} -eq 0 ]; then
		echo ${f}
	fi
done

得到结果如下:

可以看到,只有./Kconfig这个文件里面包含KEXEC选项。于是我们打开这个配置文件,找到相关的配置选项:

可以看到它的依赖关系:

depends on (!SMP || PM_SLEEP_SMP)

这里面有一个重要信息,就是!SMP这个条件,也就是说,只有SMP选项不打开的时候,才能够在配置的时候选择KEXEC选项(后面的PM_SLEEP_SMP本文不讨论了,因为分析方法是一样的)。

我们用相同的搜索方法找到SMP选项,并查看选项描述:

可以看到这个选项对应menuconfig的菜单描述:

bool "Symmetric Multi-Processing"

如果我们网上看,就可以看到,这个选项位于Kernel Features这个菜单里面:

因此,我们进入menuconfig里面,要关掉相关的选项,然后KEXEC对应的选项才能显示出来。

我们可以验证上面的分析,重新进入menuconfig,查看Boot options里面的选项(也是通过分析Kconfig文件,可以得知KEXEC选项在Boot options的菜单里):

可以看到只有kdump的选项,并没有相关的kexec的选项。此时我们回到主菜单,然后按照之前的分析,进到Kernel features里面,并且关掉Symmetric Multi-Processing的选项:

此时重新查看Boot options

可以看到kexec的相关选项出现了。我们把这个选项和kdump的选项都够选上,选择菜单底部的Save,把内容保存进.config,并退出menuconfig。

此时比对新的.config和原始的版本(原始版本的.config,我之前保存为.config.org,用于比对):

[weli@2ac1fbd74634 linux]$ diff --unified=0 .config.orig .config
--- .config.orig	2018-06-16 12:34:42.321735961 +0000
+++ .config	2018-06-16 14:07:00.601815577 +0000
@@ -30,0 +31 @@
+CONFIG_BROKEN_ON_SMP=y
@@ -75,2 +75,0 @@
-CONFIG_ARCH_HAS_TICK_BROADCAST=y
-CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
@@ -105 +104 @@
-CONFIG_TREE_RCU=y
+CONFIG_TINY_RCU=y
@@ -108 +107 @@
-CONFIG_TREE_SRCU=y
+CONFIG_TINY_SRCU=y
@@ -110,2 +109,2 @@
-CONFIG_RCU_STALL_COMMON=y
-CONFIG_RCU_NEED_SEGCBLIST=y
+# CONFIG_RCU_STALL_COMMON is not set
+# CONFIG_RCU_NEED_SEGCBLIST is not set
@@ -116 +114,0 @@
-CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
@@ -133,2 +130,0 @@
-CONFIG_CPUSETS=y
-CONFIG_PROC_PID_CPUSET=y
@@ -212 +207,0 @@
-CONFIG_SLUB_CPU_PARTIAL=y
@@ -215,0 +211,2 @@
+CONFIG_CRASH_CORE=y
+CONFIG_KEXEC_CORE=y
@@ -357,3 +353,0 @@
-CONFIG_MUTEX_SPIN_ON_OWNER=y
-CONFIG_RWSEM_SPIN_ON_OWNER=y
-CONFIG_LOCK_SPIN_ON_OWNER=y
@@ -490 +484 @@
-CONFIG_SWP_EMULATE=y
+# CONFIG_SWP_EMULATE is not set
@@ -491,0 +486 @@
+# CONFIG_CPU_DCACHE_DISABLE is not set
@@ -503 +497,0 @@
-CONFIG_ARM_ERRATA_643719=y
@@ -506,2 +499,0 @@
-# CONFIG_ARM_ERRATA_754327 is not set
-# CONFIG_ARM_ERRATA_764369 is not set
@@ -509 +500,0 @@
-# CONFIG_ARM_ERRATA_798181 is not set
@@ -538,5 +529 @@
-CONFIG_SMP=y
-CONFIG_SMP_ON_UP=y
-CONFIG_ARM_CPU_TOPOLOGY=y
-# CONFIG_SCHED_MC is not set
-# CONFIG_SCHED_SMT is not set
+# CONFIG_SMP is not set
@@ -544,2 +530,0 @@
-# CONFIG_MCPM is not set
-# CONFIG_BIG_LITTLE is not set
@@ -551,2 +535,0 @@
-CONFIG_NR_CPUS=4
-# CONFIG_HOTPLUG_CPU is not set
@@ -591,0 +575 @@
+CONFIG_NEED_PER_CPU_KM=y
@@ -627 +611,2 @@
-# CONFIG_CRASH_DUMP is not set
+CONFIG_KEXEC=y
+CONFIG_CRASH_DUMP=y
@@ -653 +637,0 @@
-CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
@@ -659 +642,0 @@
-# CONFIG_ARM_BIG_LITTLE_CPUFREQ is not set
@@ -1273,3 +1255,0 @@
-CONFIG_RPS=y
-CONFIG_RFS_ACCEL=y
-CONFIG_XPS=y
@@ -1281 +1260,0 @@
-CONFIG_NET_FLOW_LIMIT=y
@@ -1509 +1487,0 @@
-CONFIG_GENERIC_ARCH_TOPOLOGY=y
@@ -5604,0 +5583 @@
+CONFIG_PROC_VMCORE=y
@@ -5837 +5815,0 @@
-# CONFIG_DEBUG_PER_CPU_MAPS is not set
@@ -5890 +5867,0 @@
-CONFIG_RCU_CPU_STALL_TIMEOUT=21
@@ -6052 +6028,0 @@
-# CONFIG_CRYPTO_PCRYPT is not set
@@ -6244 +6219,0 @@
-CONFIG_CPU_RMAP=y

可以看到,虽然我们只更改了3个选项,但是上面却有几十处连锁反应的修改,这些联动修改都是通过判断Kconfig中的依赖关系来完成的。

至此为止,我们还需要打开kernel的debug info选项。按照上面的方法分析Kconfig即可。为了方便分析,我们可以把上面用到的脚本改进一下,并且形成一个脚本文件:

[weli@2ac1fbd74634 linux]$ pwd
/home/weli/linux
[weli@2ac1fbd74634 linux]$ cat find_opt.sh
#!/usr/bin/env bash
for f in $(find . | grep Kconfig)
do
	grep -n "config ${1}" ${f}
	RESULT=$?
	if [ ${RESULT} -eq 0 ]; then
		echo ${f}
	fi
done

这样,我们可以用上面的文件进行对Kconfig文件一族的分析:

$ ./find_opt.sh 'DEBUG_INFO'
140:config DEBUG_INFO
153:config DEBUG_INFO_REDUCED
166:config DEBUG_INFO_SPLIT
181:config DEBUG_INFO_DWARF4
./lib/Kconfig.debug

可以看到,相关的配置是在Kconfig.debug里面。因此,我们重复上面的分析过程,找到DEBUG_INFO所在的menu item和相关的依赖关系,进行配置。

在这里直接给出分析结果,这个选项是在Kernel hacking这个条目里:

进到这个menu item里,找到Compile-time checks and compiler options

进到上面这个menu item里,勾选Compile the kernel with debug info

配置完成后,存盘推出menuconfig,检查.config中的DEBUG_INFO已经正确配置:

$ grep 'DEBUG_INFO=' .config
CONFIG_DEBUG_INFO=y

检查我们要的这个选项即可,其它相关的依赖会自动解决,不用操心。

以上配置全部完成后,就可以rebuild kernel了:

编译和安装内核的过程,在之前的几篇文章已经详细介绍过了,这里就不再展开了。此外,这篇参考文章要仔细读:

https://www.raspberrypi.org/documentation/linux/kernel/building.md

此外,以下有一个重要注意事项:

如果你使用交叉编译,一定要用Raspberry Pi的tools工具包里最新的gcc:

注意上面的arm编译包里,这个arm-rpi-4.9.3-linux-gnueabihf是最新的。可以在自己的.bashrc环境文件里配置好:

$ cat ~/.bashrc
PATH=$PATH:/home/weli/tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/

上面这样,确保交叉编译工具使用最新的版本。关于这个交叉编译器版本的问题,可以查看这个相关issue:

https://github.com/raspberrypi/tools/issues/52

等编译完成后,也是按照之前文章里面介绍过的流程,把内核和编译出来的模块都安装好:

最后就是把kernel文件和设备树文件都拷贝到指定位置:

[weli@2ac1fbd74634 linux]$ sudo cp arch/arm/boot/zImage /mnt/arch/boot/kernel7.img
[weli@2ac1fbd74634 linux]$ sudo cp arch/arm/boot/dts/*.dtb /mnt/arch/boot/
[weli@2ac1fbd74634 linux]$ sudo cp arch/arm/boot/dts/overlays/*.dtb* /mnt/arch/boot/overlays/
[weli@2ac1fbd74634 linux]$ sudo cp arch/arm/boot/dts/overlays/README /mnt/arch/boot/overlays/

以上编译全部完成之后,就可以重启树莓派,使用新的,带有Kdump的内核了。

本篇文章就介绍到这里,关于如何使用Kdump,我会开一篇新的文章专门介绍。