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)
This will provide user three values as reference, The output of kdumpctl estimate looks like this:
# kdumpctl estimate Reserved crashkernel: 128M Estimated crashkernel: 143M Recommanded crashkernel: 160M
Kernel image size: 47M Kernel modules size: 9M Initramfs size: 22M Runtime reservation: 64M
Large modules: nouveau: 2281472 xfs: 1515520
WARNING: Current crashkernel size is lower than recommanded size 160M.
"Reserved crashkernel" is currently reserved crashkernel value. "Baseline crashkernel" is the crashkernel size when `crashkernel=auto` is used, based on crashkernel auto rules from RHEL. "Estimated crashkernel" is the estimate value based on the memory usage items listed above. "Recommanded crashkernel" will use the largest value of estimated crashkernel and the baseline crashkernel. All values are rounded up and converted to MB.
All modules with static size larger than 1M will be listed as large module.
Signed-off-by: Kairui Song kasong@redhat.com
--- Update from v1: - Fix typos - Remove "Baseline crashkernel:" from output --- kdump-lib.sh | 67 +++++++++++++++++++++++++++++++++++++++ kdumpctl | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 2 deletions(-)
diff --git a/kdump-lib.sh b/kdump-lib.sh index 21271cf..d56622a 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -900,3 +900,70 @@ kdump_get_arch_recommend_size() echo $result return 0 } + +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: + get_vmlinux_size $tmp && return 0 + + derror "Failed to get kernel image size" + return 1 +} diff --git a/kdumpctl b/kdumpctl index c3311ad..4109539 100755 --- a/kdumpctl +++ b/kdumpctl @@ -1246,6 +1246,87 @@ 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 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)" + + estimated_size=$(( $kernel_size + $mod_size + $initrd_size + $runtime_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 - 1 ) / $size_mb ))M" + echo "Estimated crashkernel: $(( ( $estimated_size + $size_mb - 1 ) / $size_mb ))M" + echo "Recommanded crashkernel: $(( ( $recommanded_size + $size_mb - 1 ) / $size_mb ))M" + echo + echo "Kernel image size: $(( ( $kernel_size + $size_mb - 1 ) / $size_mb ))M" + echo "Kernel modules size: $(( ( $mod_size + $size_mb - 1 ) / $size_mb ))M" + echo "Initramfs size: $(( ( $initrd_size + $size_mb - 1 ) / $size_mb ))M" + echo "Runtime reservation: $(( ( $runtime_size + $size_mb - 1 ) / $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 + 1023 ) / $size_mb ))M." + fi +} + if [ ! -f "$KDUMP_CONFIG_FILE" ]; then derror "Error: No kdump config file found!" exit 1 @@ -1301,8 +1382,11 @@ main () showmem) show_reserved_mem ;; + estimate) + do_estimate $2 + ;; *) - dinfo $"Usage: $0 {start|stop|status|restart|reload|rebuild|propagate|showmem}" + dinfo $"Usage: $0 {start|stop|status|restart|reload|rebuild|propagate|showmem|estimate}" exit 1 esac } @@ -1312,6 +1396,6 @@ single_instance_lock
# To avoid fd 9 leaking, we invoke a subshell, close fd 9 and call main. # So that fd isn't leaking when main is invoking a subshell. -(exec 9<&-; main $1) +(exec 9<&-; main $@)
exit $?
On Thu, Mar 11, 2021 at 12:31 PM Kairui Song kasong@redhat.com 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)
This will provide user three values as reference, The output of kdumpctl estimate looks like this:
# kdumpctl estimate Reserved crashkernel: 128M Estimated crashkernel: 143M Recommanded crashkernel: 160M Kernel image size: 47M Kernel modules size: 9M Initramfs size: 22M Runtime reservation: 64M Large modules: nouveau: 2281472 xfs: 1515520 WARNING: Current crashkernel size is lower than recommanded size 160M.
"Reserved crashkernel" is currently reserved crashkernel value. "Baseline crashkernel" is the crashkernel size when `crashkernel=auto` is used, based on crashkernel auto rules from RHEL. "Estimated crashkernel" is the estimate value based on the memory usage items listed above. "Recommanded crashkernel" will use the largest value of estimated crashkernel and the baseline crashkernel. All values are rounded up and converted to MB.
All modules with static size larger than 1M will be listed as large module.
Signed-off-by: Kairui Song kasong@redhat.com
Update from v1:
- Fix typos
- Remove "Baseline crashkernel:" from output
kdump-lib.sh | 67 +++++++++++++++++++++++++++++++++++++++ kdumpctl | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 2 deletions(-)
diff --git a/kdump-lib.sh b/kdump-lib.sh index 21271cf..d56622a 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -900,3 +900,70 @@ kdump_get_arch_recommend_size() echo $result return 0 }
+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:
- get_vmlinux_size $tmp && return 0
- derror "Failed to get kernel image size"
- return 1
+} diff --git a/kdumpctl b/kdumpctl index c3311ad..4109539 100755 --- a/kdumpctl +++ b/kdumpctl @@ -1246,6 +1246,87 @@ 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 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)"
estimated_size=$(( $kernel_size + $mod_size + $initrd_size + $runtime_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 - 1 ) / $size_mb ))M"
echo "Estimated crashkernel: $(( ( $estimated_size + $size_mb - 1 ) / $size_mb ))M"
echo "Recommanded crashkernel: $(( ( $recommanded_size + $size_mb - 1 ) / $size_mb ))M"
echo
echo "Kernel image size: $(( ( $kernel_size + $size_mb - 1 ) / $size_mb ))M"
echo "Kernel modules size: $(( ( $mod_size + $size_mb - 1 ) / $size_mb ))M"
echo "Initramfs size: $(( ( $initrd_size + $size_mb - 1 ) / $size_mb ))M"
echo "Runtime reservation: $(( ( $runtime_size + $size_mb - 1 ) / $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 + 1023 ) / $size_mb ))M."
fi
+}
if [ ! -f "$KDUMP_CONFIG_FILE" ]; then derror "Error: No kdump config file found!" exit 1 @@ -1301,8 +1382,11 @@ main () showmem) show_reserved_mem ;;
estimate)
do_estimate $2
;; *)
dinfo $"Usage: $0 {start|stop|status|restart|reload|rebuild|propagate|showmem}"
dinfo $"Usage: $0 {start|stop|status|restart|reload|rebuild|propagate|showmem|estimate}" exit 1 esac
} @@ -1312,6 +1396,6 @@ single_instance_lock
# To avoid fd 9 leaking, we invoke a subshell, close fd 9 and call main. # So that fd isn't leaking when main is invoking a subshell. -(exec 9<&-; main $1) +(exec 9<&-; main $@)
exit $?
2.29.2
Just sent V3, so Nack this one.