kdumpctl: Add kdumpctl estimate

Resolves: bz1951415
Upstream: fedora
Conflict: none

commit e9e6a2c745d41f5447c0062525c0e4f3f489b903
Author: Kairui Song <kasong@redhat.com>
Date:   Thu Apr 22 03:27:10 2021 +0800

    kdumpctl: Add kdumpctl estimate

    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>
    Acked-by: Pingfan Liu <piliu@redhat.com>

Signed-off-by: Kairui Song <kasong@redhat.com>
This commit is contained in:
Kairui Song 2021-04-22 03:27:10 +08:00
parent de1c56b637
commit a9fda5c885
3 changed files with 171 additions and 1 deletions

View File

@ -977,3 +977,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
}

View File

@ -1214,6 +1214,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 -lt $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
@ -1269,8 +1360,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
}

View File

@ -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),