Patch 1, 2 add a helper to detect and list crypt devices. Patch 3 simplify some existing code with the helper. Patch 4 adds kdumpctl estimate command and updates man page.
Following contents are copied from Patch 4:
Add a rough esitimation support, currently, following memory usage are checked by this sub command:
- System RAM - Kdump Initramfs size - Kdump Kernel image size - Kdump Kernel module size - Kdump userspace user and other runtime allocated memory (currently simply using a fixed value: 64M) - LUKS encryption memory usage
The output of kdumpctl estimate looks like this: # kdumpctl estimate Reserved crashkernel: 256M Recommanded crashkernel: 160M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M Large modules: xfs: 1892352 nouveau: 2318336
And if the kdump target is encrypted: # kdumpctl estimate Encrypted kdump target requires extra memory, assuming using the keyslot with minimun memory requirement
Reserved crashkernel: 256M Recommanded crashkernel: 655M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M LUKS required size: 512M Large modules: xfs: 1892352 nouveau: 2318336 WARNING: Current crashkernel size is lower than recommanded size 655M.
The "Recommanded" value is calculated based on memory usages mentioned above, and will be adjusted accodingly to be no less than the value provided by kdump_get_arch_recommend_size.
--
Update from v3: - Simplify the code, and rebase. - Also simpify the output to make it easier to understand.
Update from v2: - If binutils or kernel decompression tools are not installed, will get kernel size from iomem. - Add support for detecting LUKS memory requirement.
Update from v1: - Fix typos - Remove "Baseline crashkernel:" from output
Kairui Song (4): kdump-lib.sh: introduce a helper to get underlying crypt device kdump-lib.sh: introduce a helper to get all crypt dev used by kdump mkdumprd: make use of the new get_luks_crypt_dev helper kdumpctl: Add kdumpctl estimate
kdump-lib.sh | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++ kdumpctl | 96 ++++++++++++++++++++++++++++++++++++++++++++- kdumpctl.8 | 7 ++++ mkdumprd | 32 +++------------ 4 files changed, 217 insertions(+), 27 deletions(-)
Signed-off-by: Kairui Song kasong@redhat.com --- kdump-lib.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/kdump-lib.sh b/kdump-lib.sh index 4dcd134..ecb2721 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -958,3 +958,20 @@ kdump_get_arch_recommend_size() echo $result return 0 } + +# Print all underlying crypt devices of a block device +# print nothing if device is not on top of a crypt device +# $1: the block device to be checked in maj:min format +get_luks_crypt_dev() +{ + [[ -b /dev/block/$1 ]] || return 1 + + local _type=$(eval "$(blkid -u filesystem,crypto -o export -- /dev/block/$1); echo $TYPE") + [[ $_type == "crypto_LUKS" ]] && echo $1 + + for _x in /sys/dev/block/$1/slaves/*; do + [[ -f $_x/dev ]] || continue + [[ $_x/subsystem -ef /sys/class/block ]] || continue + get_luks_crypt_dev "$(< "$_x/dev")" + done +}
--- kdump-lib.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/kdump-lib.sh b/kdump-lib.sh index ecb2721..e00ea43 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -975,3 +975,24 @@ get_luks_crypt_dev() get_luks_crypt_dev "$(< "$_x/dev")" done } + +# kdump_get_maj_min <device> +# Prints the major and minor of a device node. +# Example: +# $ get_maj_min /dev/sda2 +# 8:2 +kdump_get_maj_min() { + local _majmin + _majmin="$(stat -L -c '%t:%T' "$1" 2> /dev/null)" + printf "%s" "$((0x${_majmin%:*})):$((0x${_majmin#*:}))" +} + +get_all_kdump_crypt_dev() +{ + local _dev _crypt + + for _dev in $(get_block_dump_target); do + _crypt=$(get_luks_crypt_dev $(kdump_get_maj_min "$_dev")) + [[ -n "$_crypt" ]] && echo $_crypt + done +}
Simplfy the code and also improve the performance. udevadm call is heavy.
Signed-off-by: Kairui Song kasong@redhat.com --- mkdumprd | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-)
diff --git a/mkdumprd b/mkdumprd index 50ebdc8..b518078 100644 --- a/mkdumprd +++ b/mkdumprd @@ -325,7 +325,6 @@ get_override_resettable() fi }
- # $1: function name for_each_block_target() { @@ -340,8 +339,6 @@ for_each_block_target() return 0 }
- - #judge if a specific device with $1 is unresettable #return false if unresettable. is_unresettable() @@ -378,32 +375,15 @@ check_resettable() return 1 }
-# $1: maj:min -is_crypt() -{ - local majmin=$1 dev line ID_FS_TYPE="" - - line=$(udevadm info --query=property --path=/sys/dev/block/$majmin \ - | grep "^ID_FS_TYPE") - eval "$line" - [[ "$ID_FS_TYPE" = "crypto_LUKS" ]] && { - dev=$(udevadm info --query=all --path=/sys/dev/block/$majmin | awk -F= '/DEVNAME/{print $2}') - derror "Device $dev is encrypted." - return 0 - } - return 1 -} - check_crypt() { - local _ret _target - - for_each_block_target is_crypt - _ret=$? - - [ $_ret -eq 0 ] && return + local _dev
- return 1 + for _dev in $(get_kdump_targets); do + if [[ -n $(get_luks_crypt_dev "$(get_maj_min "$_dev")") ]]; then + derror "Device $_dev is encrypted." && return 1 + fi + done }
if ! check_resettable; then
Add a rough esitimation support, currently, following memory usage are checked by this sub command:
- System RAM - Kdump Initramfs size - Kdump Kernel image size - Kdump Kernel module size - Kdump userspace user and other runtime allocated memory (currently simply using a fixed value: 64M) - LUKS encryption memory usage
The output of kdumpctl estimate looks like this: # kdumpctl estimate Reserved crashkernel: 256M Recommanded crashkernel: 160M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M Large modules: xfs: 1892352 nouveau: 2318336
And if the kdump target is encrypted: # kdumpctl estimate Encrypted kdump target requires extra memory, assuming using the keyslot with minimun memory requirement
Reserved crashkernel: 256M Recommanded crashkernel: 655M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M LUKS required size: 512M Large modules: xfs: 1892352 nouveau: 2318336 WARNING: Current crashkernel size is lower than recommanded size 655M.
The "Recommanded" value is calculated based on memory usages mentioned above, and will be adjusted accodingly to be no less than the value provided by kdump_get_arch_recommend_size.
Signed-off-by: Kairui Song kasong@redhat.com --- kdump-lib.sh | 71 ++++++++++++++++++++++++++++++++++++++ kdumpctl | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++- kdumpctl.8 | 5 +++ 3 files changed, 171 insertions(+), 1 deletion(-)
diff --git a/kdump-lib.sh b/kdump-lib.sh index e00ea43..74072c5 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -996,3 +996,74 @@ get_all_kdump_crypt_dev() [[ -n "$_crypt" ]] && echo $_crypt done } + +check_vmlinux() +{ + # Use readelf to check if it's a valid ELF + readelf -h $1 &>/dev/null || return 1 +} + +get_vmlinux_size() +{ + local size=0 + + while read _type _offset _virtaddr _physaddr _fsize _msize _flg _aln; do + size=$(( $size + $_msize )) + done <<< $(readelf -l -W $1 | grep "^ LOAD" 2>/dev/stderr) + + echo $size +} + +try_decompress() +{ + # The obscure use of the "tr" filter is to work around older versions of + # "grep" that report the byte offset of the line instead of the pattern. + + # Try to find the header ($1) and decompress from here + for pos in `tr "$1\n$2" "\n$2=" < "$4" | grep -abo "^$2"` + do + if ! type -P $3 > /dev/null; then + ddebug "Signiature detected but '$3' is missing, skip this decompressor" + break + fi + + pos=${pos%%:*} + tail -c+$pos "$img" | $3 > $5 2> /dev/null + if check_vmlinux $5; then + ddebug "Kernel is extracted with '$3'" + return 0 + fi + done + + return 1 +} + +# Borrowed from linux/scripts/extract-vmlinux +get_kernel_size() +{ + # Prepare temp files: + local img=$1 tmp=$(mktemp /tmp/vmlinux-XXX) + trap "rm -f $tmp" 0 + + # Try to check if it's a vmlinux already + check_vmlinux $img && get_vmlinux_size $img && return 0 + + # That didn't work, so retry after decompression. + try_decompress '\037\213\010' xy gunzip $img $tmp || \ + try_decompress '\3757zXZ\000' abcde unxz $img $tmp || \ + try_decompress 'BZh' xy bunzip2 $img $tmp || \ + try_decompress '\135\0\0\0' xxx unlzma $img $tmp || \ + try_decompress '\211\114\132' xy 'lzop -d' $img $tmp || \ + try_decompress '\002!L\030' xxx 'lz4 -d' $img $tmp || \ + try_decompress '(\265/\375' xxx unzstd $img $tmp + + # Finally check for uncompressed images or objects: + [[ $? -eq 0 ]] && get_vmlinux_size $tmp && return 0 + + # Fallback to use iomem + local _size=0 + for _seg in $(cat /proc/iomem | grep -E "Kernel (code|rodata|data|bss)" | cut -d ":" -f 1); do + _size=$(( $_size + 0x${_seg#*-} - 0x${_seg%-*} )) + done + echo $_size +} diff --git a/kdumpctl b/kdumpctl index 009d259..978dae5 100755 --- a/kdumpctl +++ b/kdumpctl @@ -1249,6 +1249,97 @@ rebuild() { return $? }
+do_estimate() { + local kdump_mods + local -A large_mods + local baseline + local kernel_size mod_size initrd_size baseline_size runtime_size reserved_size estimated_size recommanded_size + local size_mb=$(( 1024 * 1024 )) + + setup_initrd + if [ ! -f "$TARGET_INITRD" ]; then + derror "kdumpctl estimate: kdump initramfs is not built yet." + exit 1 + fi + + kdump_mods="$(lsinitrd "$TARGET_INITRD" -f /usr/lib/dracut/hostonly-kernel-modules.txt | tr '\n' ' ')" + baseline=$(kdump_get_arch_recommend_size) + if [[ "${baseline: -1}" == "M" ]]; then + baseline=${baseline%M} + elif [[ "${baseline: -1}" == "G" ]]; then + baseline=$(( ${baseline%G} * 1024 )) + elif [[ "${baseline: -1}" == "T" ]]; then + baseline=$(( ${baseline%Y} * 1048576 )) + fi + + # The default value when using crashkernel=auto + baseline_size=$((baseline * size_mb)) + # Current reserved crashkernel size + reserved_size=$(cat /sys/kernel/kexec_crash_size) + # A pre-estimated value for userspace usage and kernel + # runtime allocation, 64M should good for most cases + runtime_size=$((64 * size_mb)) + # Kernel image size + kernel_size=$(get_kernel_size "$KDUMP_KERNEL") + # Kdump initramfs size + initrd_size=$(du -b "$TARGET_INITRD" | awk '{print $1}') + # Kernel modules static size after loaded + mod_size=0 + while read -r _name _size _; do + if [[ ! " $kdump_mods " == *" $_name "* ]]; then + continue + fi + mod_size=$((mod_size + _size)) + + # Mark module with static size larger than 2M as large module + if [[ $((_size / size_mb)) -ge 1 ]]; then + large_mods[$_name]=$_size + fi + done <<< "$(< /proc/modules)" + + # Extra memory usage required for LUKS2 decryption + crypt_size=0 + for _dev in $(get_all_kdump_crypt_dev); do + _crypt_info=$(cryptsetup luksDump "/dev/block/$_dev") + [[ $(echo "$_crypt_info" | sed -n "s/^Version:\s*(.*)/\1/p" ) == "2" ]] || continue + for _mem in $(echo "$_crypt_info" | sed -n "s/\sMemory:\s*(.*)/\1/p" | sort -n ); do + crypt_size=$((crypt_size + _mem * 1024)) + break + done + done + [[ $crypt_size -ne 0 ]] && echo -e "Encrypted kdump target requires extra memory, assuming using the keyslot with minimun memory requirement\n" + + estimated_size=$((kernel_size + mod_size + initrd_size + runtime_size + crypt_size)) + if [[ $baseline_size -gt $estimated_size ]]; then + recommanded_size=$baseline_size + else + recommanded_size=$estimated_size + fi + + echo "Reserved crashkernel: $((reserved_size / size_mb))M" + echo "Recommanded crashkernel: $((recommanded_size / size_mb))M" + echo + echo "Kernel image size: $((kernel_size / size_mb))M" + echo "Kernel modules size: $((mod_size / size_mb))M" + echo "Initramfs size: $((initrd_size / size_mb))M" + echo "Runtime reservation: $((runtime_size / size_mb))M" + [[ $crypt_size -ne 0 ]] && \ + echo "LUKS required size: $((crypt_size / size_mb))M" + echo -n "Large modules:" + if [[ "${#large_mods[@]}" -eq 0 ]]; then + echo " <none>" + else + echo "" + for _mod in "${!large_mods[@]}"; do + echo " $_mod: ${large_mods[$_mod]}" + done + fi + + if [[ $reserved_size -le $recommanded_size ]]; then + echo "WARNING: Current crashkernel size is lower than recommanded size $((recommanded_size / size_mb))M." + fi +} + if [ ! -f "$KDUMP_CONFIG_FILE" ]; then derror "Error: No kdump config file found!" exit 1 @@ -1304,8 +1395,11 @@ main () showmem) show_reserved_mem ;; + estimate) + do_estimate + ;; *) - dinfo $"Usage: $0 {start|stop|status|restart|reload|rebuild|propagate|showmem}" + dinfo $"Usage: $0 {estimate|start|stop|status|restart|reload|rebuild|propagate|showmem}" exit 1 esac } diff --git a/kdumpctl.8 b/kdumpctl.8 index ae97af7..a32a972 100644 --- a/kdumpctl.8 +++ b/kdumpctl.8 @@ -44,6 +44,11 @@ impossible to use password authentication during kdump. .TP .I showmem Prints the size of reserved memory for crash kernel in megabytes. +.TP +.I estimate +Estimate a suitable crashkernel value for current machine. This is a +best-effort estimate. It will print a recommanded crashkernel value +based on current kdump setup, and list some details of memory usage.
.SH "SEE ALSO" .BR kdump.conf (5),
On Tue, May 18, 2021 at 05:04:16PM +0800, Kairui Song wrote:
Add a rough esitimation support, currently, following memory usage are checked by this sub command:
- System RAM
- Kdump Initramfs size
- Kdump Kernel image size
- Kdump Kernel module size
- Kdump userspace user and other runtime allocated memory (currently simply using a fixed value: 64M)
- LUKS encryption memory usage
The output of kdumpctl estimate looks like this: # kdumpctl estimate Reserved crashkernel: 256M Recommanded crashkernel: 160M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M Large modules: xfs: 1892352 nouveau: 2318336
And if the kdump target is encrypted: # kdumpctl estimate Encrypted kdump target requires extra memory, assuming using the keyslot with minimun memory requirement
Reserved crashkernel: 256M Recommanded crashkernel: 655M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M LUKS required size: 512M Large modules: xfs: 1892352 nouveau: 2318336 WARNING: Current crashkernel size is lower than recommanded size 655M.
The "Recommanded" value is calculated based on memory usages mentioned above, and will be adjusted accodingly to be no less than the value provided by kdump_get_arch_recommend_size.
Signed-off-by: Kairui Song kasong@redhat.com
kdump-lib.sh | 71 ++++++++++++++++++++++++++++++++++++++ kdumpctl | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++- kdumpctl.8 | 5 +++ 3 files changed, 171 insertions(+), 1 deletion(-)
diff --git a/kdump-lib.sh b/kdump-lib.sh index e00ea43..74072c5 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -996,3 +996,74 @@ get_all_kdump_crypt_dev() [[ -n "$_crypt" ]] && echo $_crypt done }
+check_vmlinux() +{
- # Use readelf to check if it's a valid ELF
- readelf -h $1 &>/dev/null || return 1
+}
+get_vmlinux_size() +{
- local size=0
- while read _type _offset _virtaddr _physaddr _fsize _msize _flg _aln; do
size=$(( $size + $_msize ))
- done <<< $(readelf -l -W $1 | grep "^ LOAD" 2>/dev/stderr)
- echo $size
+}
+try_decompress() +{
- # The obscure use of the "tr" filter is to work around older versions of
- # "grep" that report the byte offset of the line instead of the pattern.
- # Try to find the header ($1) and decompress from here
- for pos in `tr "$1\n$2" "\n$2=" < "$4" | grep -abo "^$2"`
- do
if ! type -P $3 > /dev/null; then
ddebug "Signiature detected but '$3' is missing, skip this decompressor"
break
fi
pos=${pos%%:*}
tail -c+$pos "$img" | $3 > $5 2> /dev/null
if check_vmlinux $5; then
ddebug "Kernel is extracted with '$3'"
return 0
fi
- done
- return 1
+}
+# Borrowed from linux/scripts/extract-vmlinux +get_kernel_size() +{
- # Prepare temp files:
- local img=$1 tmp=$(mktemp /tmp/vmlinux-XXX)
- trap "rm -f $tmp" 0
- # Try to check if it's a vmlinux already
- check_vmlinux $img && get_vmlinux_size $img && return 0
- # That didn't work, so retry after decompression.
- try_decompress '\037\213\010' xy gunzip $img $tmp || \
- try_decompress '\3757zXZ\000' abcde unxz $img $tmp || \
- try_decompress 'BZh' xy bunzip2 $img $tmp || \
- try_decompress '\135\0\0\0' xxx unlzma $img $tmp || \
- try_decompress '\211\114\132' xy 'lzop -d' $img $tmp || \
- try_decompress '\002!L\030' xxx 'lz4 -d' $img $tmp || \
- try_decompress '(\265/\375' xxx unzstd $img $tmp
- # Finally check for uncompressed images or objects:
- [[ $? -eq 0 ]] && get_vmlinux_size $tmp && return 0
- # Fallback to use iomem
- local _size=0
- for _seg in $(cat /proc/iomem | grep -E "Kernel (code|rodata|data|bss)" | cut -d ":" -f 1); do
_size=$(( $_size + 0x${_seg#*-} - 0x${_seg%-*} ))
- done
- echo $_size
+} diff --git a/kdumpctl b/kdumpctl index 009d259..978dae5 100755 --- a/kdumpctl +++ b/kdumpctl @@ -1249,6 +1249,97 @@ rebuild() { return $? }
+do_estimate() {
- local kdump_mods
- local -A large_mods
- local baseline
- local kernel_size mod_size initrd_size baseline_size runtime_size reserved_size estimated_size recommanded_size
- local size_mb=$(( 1024 * 1024 ))
- setup_initrd
- if [ ! -f "$TARGET_INITRD" ]; then
derror "kdumpctl estimate: kdump initramfs is not built yet."
exit 1
- fi
- kdump_mods="$(lsinitrd "$TARGET_INITRD" -f /usr/lib/dracut/hostonly-kernel-modules.txt | tr '\n' ' ')"
- baseline=$(kdump_get_arch_recommend_size)
- if [[ "${baseline: -1}" == "M" ]]; then
baseline=${baseline%M}
- elif [[ "${baseline: -1}" == "G" ]]; then
baseline=$(( ${baseline%G} * 1024 ))
- elif [[ "${baseline: -1}" == "T" ]]; then
baseline=$(( ${baseline%Y} * 1048576 ))
- fi
- # The default value when using crashkernel=auto
- baseline_size=$((baseline * size_mb))
- # Current reserved crashkernel size
- reserved_size=$(cat /sys/kernel/kexec_crash_size)
- # A pre-estimated value for userspace usage and kernel
- # runtime allocation, 64M should good for most cases
- runtime_size=$((64 * size_mb))
- # Kernel image size
- kernel_size=$(get_kernel_size "$KDUMP_KERNEL")
- # Kdump initramfs size
- initrd_size=$(du -b "$TARGET_INITRD" | awk '{print $1}')
- # Kernel modules static size after loaded
- mod_size=0
- while read -r _name _size _; do
if [[ ! " $kdump_mods " == *" $_name "* ]]; then
continue
fi
mod_size=$((mod_size + _size))
# Mark module with static size larger than 2M as large module
if [[ $((_size / size_mb)) -ge 1 ]]; then
large_mods[$_name]=$_size
fi
- done <<< "$(< /proc/modules)"
- # Extra memory usage required for LUKS2 decryption
- crypt_size=0
- for _dev in $(get_all_kdump_crypt_dev); do
_crypt_info=$(cryptsetup luksDump "/dev/block/$_dev")
[[ $(echo "$_crypt_info" | sed -n "s/^Version:\s*\(.*\)/\1/p" ) == "2" ]] || continue
for _mem in $(echo "$_crypt_info" | sed -n "s/\sMemory:\s*\(.*\)/\1/p" | sort -n ); do
crypt_size=$((crypt_size + _mem * 1024))
break
done
- done
- [[ $crypt_size -ne 0 ]] && echo -e "Encrypted kdump target requires extra memory, assuming using the keyslot with minimun memory requirement\n"
- estimated_size=$((kernel_size + mod_size + initrd_size + runtime_size + crypt_size))
- if [[ $baseline_size -gt $estimated_size ]]; then
recommanded_size=$baseline_size
- else
recommanded_size=$estimated_size
- fi
- echo "Reserved crashkernel: $((reserved_size / size_mb))M"
- echo "Recommanded crashkernel: $((recommanded_size / size_mb))M"
- echo
- echo "Kernel image size: $((kernel_size / size_mb))M"
- echo "Kernel modules size: $((mod_size / size_mb))M"
- echo "Initramfs size: $((initrd_size / size_mb))M"
- echo "Runtime reservation: $((runtime_size / size_mb))M"
- [[ $crypt_size -ne 0 ]] && \
- echo "LUKS required size: $((crypt_size / size_mb))M"
- echo -n "Large modules:"
- if [[ "${#large_mods[@]}" -eq 0 ]]; then
echo " <none>"
- else
echo ""
for _mod in "${!large_mods[@]}"; do
echo " $_mod: ${large_mods[$_mod]}"
done
- fi
- if [[ $reserved_size -le $recommanded_size ]]; then
echo "WARNING: Current crashkernel size is lower than recommanded size $((recommanded_size / size_mb))M."
- fi
+}
if [ ! -f "$KDUMP_CONFIG_FILE" ]; then derror "Error: No kdump config file found!" exit 1 @@ -1304,8 +1395,11 @@ main () showmem) show_reserved_mem ;;
estimate)
do_estimate
*);;
dinfo $"Usage: $0 {start|stop|status|restart|reload|rebuild|propagate|showmem}"
exit 1 esacdinfo $"Usage: $0 {estimate|start|stop|status|restart|reload|rebuild|propagate|showmem}"
} diff --git a/kdumpctl.8 b/kdumpctl.8 index ae97af7..a32a972 100644 --- a/kdumpctl.8 +++ b/kdumpctl.8 @@ -44,6 +44,11 @@ impossible to use password authentication during kdump. .TP .I showmem Prints the size of reserved memory for crash kernel in megabytes. +.TP +.I estimate +Estimate a suitable crashkernel value for current machine. This is a +best-effort estimate. It will print a recommanded crashkernel value +based on current kdump setup, and list some details of memory usage.
.SH "SEE ALSO" .BR kdump.conf (5), -- 2.31.1
Hi Kairui,
Thanks for bringing up this. It is an interesting topic. and Look into future, is it possible to estimate runtime_size based on the info available in 1st kernel: -1. kmod -2. squashfs ?
For this patch, LGTM,
Acked-by: Pingfan Liu piliu@redhat.com
On Tue, May 18, 2021 at 6:32 PM Pingfan Liu piliu@redhat.com wrote:
On Tue, May 18, 2021 at 05:04:16PM +0800, Kairui Song wrote:
Add a rough esitimation support, currently, following memory usage are checked by this sub command:
- System RAM
- Kdump Initramfs size
- Kdump Kernel image size
- Kdump Kernel module size
- Kdump userspace user and other runtime allocated memory (currently simply using a fixed value: 64M)
- LUKS encryption memory usage
The output of kdumpctl estimate looks like this: # kdumpctl estimate Reserved crashkernel: 256M Recommanded crashkernel: 160M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M Large modules: xfs: 1892352 nouveau: 2318336
And if the kdump target is encrypted: # kdumpctl estimate Encrypted kdump target requires extra memory, assuming using the keyslot with minimun memory requirement
Reserved crashkernel: 256M Recommanded crashkernel: 655M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M LUKS required size: 512M Large modules: xfs: 1892352 nouveau: 2318336 WARNING: Current crashkernel size is lower than recommanded size 655M.
The "Recommanded" value is calculated based on memory usages mentioned above, and will be adjusted accodingly to be no less than the value provided by kdump_get_arch_recommend_size.
Signed-off-by: Kairui Song kasong@redhat.com
kdump-lib.sh | 71 ++++++++++++++++++++++++++++++++++++++ kdumpctl | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++- kdumpctl.8 | 5 +++ 3 files changed, 171 insertions(+), 1 deletion(-)
diff --git a/kdump-lib.sh b/kdump-lib.sh index e00ea43..74072c5 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -996,3 +996,74 @@ get_all_kdump_crypt_dev() [[ -n "$_crypt" ]] && echo $_crypt done }
+check_vmlinux() +{
- # Use readelf to check if it's a valid ELF
- readelf -h $1 &>/dev/null || return 1
+}
+get_vmlinux_size() +{
- local size=0
- while read _type _offset _virtaddr _physaddr _fsize _msize _flg _aln; do
size=$(( $size + $_msize ))
- done <<< $(readelf -l -W $1 | grep "^ LOAD" 2>/dev/stderr)
- echo $size
+}
+try_decompress() +{
- # The obscure use of the "tr" filter is to work around older versions of
- # "grep" that report the byte offset of the line instead of the pattern.
- # Try to find the header ($1) and decompress from here
- for pos in `tr "$1\n$2" "\n$2=" < "$4" | grep -abo "^$2"`
- do
if ! type -P $3 > /dev/null; then
ddebug "Signiature detected but '$3' is missing, skip this decompressor"
break
fi
pos=${pos%%:*}
tail -c+$pos "$img" | $3 > $5 2> /dev/null
if check_vmlinux $5; then
ddebug "Kernel is extracted with '$3'"
return 0
fi
- done
- return 1
+}
+# Borrowed from linux/scripts/extract-vmlinux +get_kernel_size() +{
- # Prepare temp files:
- local img=$1 tmp=$(mktemp /tmp/vmlinux-XXX)
- trap "rm -f $tmp" 0
- # Try to check if it's a vmlinux already
- check_vmlinux $img && get_vmlinux_size $img && return 0
- # That didn't work, so retry after decompression.
- try_decompress '\037\213\010' xy gunzip $img $tmp || \
- try_decompress '\3757zXZ\000' abcde unxz $img $tmp || \
- try_decompress 'BZh' xy bunzip2 $img $tmp || \
- try_decompress '\135\0\0\0' xxx unlzma $img $tmp || \
- try_decompress '\211\114\132' xy 'lzop -d' $img $tmp || \
- try_decompress '\002!L\030' xxx 'lz4 -d' $img $tmp || \
- try_decompress '(\265/\375' xxx unzstd $img $tmp
- # Finally check for uncompressed images or objects:
- [[ $? -eq 0 ]] && get_vmlinux_size $tmp && return 0
- # Fallback to use iomem
- local _size=0
- for _seg in $(cat /proc/iomem | grep -E "Kernel (code|rodata|data|bss)" | cut -d ":" -f 1); do
_size=$(( $_size + 0x${_seg#*-} - 0x${_seg%-*} ))
- done
- echo $_size
+} diff --git a/kdumpctl b/kdumpctl index 009d259..978dae5 100755 --- a/kdumpctl +++ b/kdumpctl @@ -1249,6 +1249,97 @@ rebuild() { return $? }
+do_estimate() {
local kdump_mods
local -A large_mods
local baseline
local kernel_size mod_size initrd_size baseline_size runtime_size reserved_size estimated_size recommanded_size
local size_mb=$(( 1024 * 1024 ))
setup_initrd
if [ ! -f "$TARGET_INITRD" ]; then
derror "kdumpctl estimate: kdump initramfs is not built yet."
exit 1
fi
kdump_mods="$(lsinitrd "$TARGET_INITRD" -f /usr/lib/dracut/hostonly-kernel-modules.txt | tr '\n' ' ')"
baseline=$(kdump_get_arch_recommend_size)
if [[ "${baseline: -1}" == "M" ]]; then
baseline=${baseline%M}
elif [[ "${baseline: -1}" == "G" ]]; then
baseline=$(( ${baseline%G} * 1024 ))
elif [[ "${baseline: -1}" == "T" ]]; then
baseline=$(( ${baseline%Y} * 1048576 ))
fi
# The default value when using crashkernel=auto
baseline_size=$((baseline * size_mb))
# Current reserved crashkernel size
reserved_size=$(cat /sys/kernel/kexec_crash_size)
# A pre-estimated value for userspace usage and kernel
# runtime allocation, 64M should good for most cases
runtime_size=$((64 * size_mb))
# Kernel image size
kernel_size=$(get_kernel_size "$KDUMP_KERNEL")
# Kdump initramfs size
initrd_size=$(du -b "$TARGET_INITRD" | awk '{print $1}')
# Kernel modules static size after loaded
mod_size=0
while read -r _name _size _; do
if [[ ! " $kdump_mods " == *" $_name "* ]]; then
continue
fi
mod_size=$((mod_size + _size))
# Mark module with static size larger than 2M as large module
if [[ $((_size / size_mb)) -ge 1 ]]; then
large_mods[$_name]=$_size
fi
done <<< "$(< /proc/modules)"
# Extra memory usage required for LUKS2 decryption
crypt_size=0
for _dev in $(get_all_kdump_crypt_dev); do
_crypt_info=$(cryptsetup luksDump "/dev/block/$_dev")
[[ $(echo "$_crypt_info" | sed -n "s/^Version:\s*\(.*\)/\1/p" ) == "2" ]] || continue
for _mem in $(echo "$_crypt_info" | sed -n "s/\sMemory:\s*\(.*\)/\1/p" | sort -n ); do
crypt_size=$((crypt_size + _mem * 1024))
break
done
done
[[ $crypt_size -ne 0 ]] && echo -e "Encrypted kdump target requires extra memory, assuming using the keyslot with minimun memory requirement\n"
estimated_size=$((kernel_size + mod_size + initrd_size + runtime_size + crypt_size))
if [[ $baseline_size -gt $estimated_size ]]; then
recommanded_size=$baseline_size
else
recommanded_size=$estimated_size
fi
echo "Reserved crashkernel: $((reserved_size / size_mb))M"
echo "Recommanded crashkernel: $((recommanded_size / size_mb))M"
echo
echo "Kernel image size: $((kernel_size / size_mb))M"
echo "Kernel modules size: $((mod_size / size_mb))M"
echo "Initramfs size: $((initrd_size / size_mb))M"
echo "Runtime reservation: $((runtime_size / size_mb))M"
[[ $crypt_size -ne 0 ]] && \
echo "LUKS required size: $((crypt_size / size_mb))M"
echo -n "Large modules:"
if [[ "${#large_mods[@]}" -eq 0 ]]; then
echo " <none>"
else
echo ""
for _mod in "${!large_mods[@]}"; do
echo " $_mod: ${large_mods[$_mod]}"
done
fi
if [[ $reserved_size -le $recommanded_size ]]; then
echo "WARNING: Current crashkernel size is lower than recommanded size $((recommanded_size / size_mb))M."
fi
+}
if [ ! -f "$KDUMP_CONFIG_FILE" ]; then derror "Error: No kdump config file found!" exit 1 @@ -1304,8 +1395,11 @@ main () showmem) show_reserved_mem ;;
estimate)
do_estimate
;; *)
dinfo $"Usage: $0 {start|stop|status|restart|reload|rebuild|propagate|showmem}"
dinfo $"Usage: $0 {estimate|start|stop|status|restart|reload|rebuild|propagate|showmem}" exit 1 esac
} diff --git a/kdumpctl.8 b/kdumpctl.8 index ae97af7..a32a972 100644 --- a/kdumpctl.8 +++ b/kdumpctl.8 @@ -44,6 +44,11 @@ impossible to use password authentication during kdump. .TP .I showmem Prints the size of reserved memory for crash kernel in megabytes. +.TP +.I estimate +Estimate a suitable crashkernel value for current machine. This is a +best-effort estimate. It will print a recommanded crashkernel value +based on current kdump setup, and list some details of memory usage.
.SH "SEE ALSO" .BR kdump.conf (5), -- 2.31.1
Hi Kairui,
Thanks for bringing up this. It is an interesting topic. and Look into future, is it possible to estimate runtime_size based on the info available in 1st kernel: -1. kmod -2. squashfs ?
For this patch, LGTM,
Acked-by: Pingfan Liu piliu@redhat.com
Thanks for the review, yes, the runtime_size estimation could be improved in many ways, I think we may even introduce a kdumpctl estimate --reboot later, to let kdumpctl do a scripted panic reboot and collect real and accurate usage info. This command could be a good start.
On 5/18/21 5:04 PM, Kairui Song wrote:
Patch 1, 2 add a helper to detect and list crypt devices. Patch 3 simplify some existing code with the helper. Patch 4 adds kdumpctl estimate command and updates man page.
Following contents are copied from Patch 4:
Add a rough esitimation support, currently, following memory usage are checked by this sub command:
- System RAM
- Kdump Initramfs size
- Kdump Kernel image size
- Kdump Kernel module size
- Kdump userspace user and other runtime allocated memory (currently simply using a fixed value: 64M)
- LUKS encryption memory usage
The output of kdumpctl estimate looks like this: # kdumpctl estimate Reserved crashkernel: 256M Recommanded crashkernel: 160M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M Large modules: xfs: 1892352 nouveau: 2318336
And if the kdump target is encrypted: # kdumpctl estimate Encrypted kdump target requires extra memory, assuming using the keyslot with minimun memory requirement
Reserved crashkernel: 256M Recommanded crashkernel: 655M
Kernel image size: 47M Kernel modules size: 12M Initramfs size: 19M Runtime reservation: 64M LUKS required size: 512M Large modules: xfs: 1892352 nouveau: 2318336 WARNING: Current crashkernel size is lower than recommanded size 655M.
The "Recommanded" value is calculated based on memory usages mentioned above, and will be adjusted accodingly to be no less than the value provided by kdump_get_arch_recommend_size.
--
Update from v3:
- Simplify the code, and rebase.
- Also simpify the output to make it easier to understand.
Update from v2:
- If binutils or kernel decompression tools are not installed, will get kernel size from iomem.
- Add support for detecting LUKS memory requirement.
Update from v1:
- Fix typos
- Remove "Baseline crashkernel:" from output
Kairui Song (4): kdump-lib.sh: introduce a helper to get underlying crypt device kdump-lib.sh: introduce a helper to get all crypt dev used by kdump mkdumprd: make use of the new get_luks_crypt_dev helper kdumpctl: Add kdumpctl estimate
kdump-lib.sh | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++ kdumpctl | 96 ++++++++++++++++++++++++++++++++++++++++++++- kdumpctl.8 | 7 ++++ mkdumprd | 32 +++------------ 4 files changed, 217 insertions(+), 27 deletions(-)
For the series,
Acked-by: Pingfan Liu piliu@redhat.com