On Tue, May 18, 2021 at 6:32 PM Pingfan Liu <piliu(a)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(a)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(a)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.
--
Best Regards,
Kairui Song