81b414d100
Resolves: bz2076416
Upstream: Fedora
Conflict: None
commit a65dde2d10
Author: Coiby Xu <coxu@redhat.com>
Date: Thu May 19 11:39:25 2022 +0800
Reduce kdump memory consumption by only installing needed NIC drivers
Even after having asked NM to stop managing a unneeded NIC, a NIC driver
may still waste memory. For example, mlx5_core uses a substantial amount
of memory during driver initialization,
======== Report format module_summary: ========
Module mlx5_core using 350.2MB (89650 pages), peak allocation 367.4MB (94056 pages)
Module squashfs using 13.1MB (3360 pages), peak allocation 13.1MB (3360 pages)
Module overlay using 2.1MB (550 pages), peak allocation 2.2MB (555 pages)
Module dns_resolver using 0.9MB (219 pages), peak allocation 5.2MB (1338 pages)
Module mlxfw using 0.7MB (172 pages), peak allocation 5.3MB (1349 pages)
======== Report format module_summary END ========
======== Report format module_top: ========
Top stack usage of module mlx5_core:
(null) Pages: 89650 (peak: 94056)
ret_from_fork (0xffffda088b4165f8) Pages: 60007 (peak: 60007)
kthread (0xffffda088b4bd7e4) Pages: 60007 (peak: 60007)
worker_thread (0xffffda088b4b48d0) Pages: 60007 (peak: 60007)
process_one_work (0xffffda088b4b3f40) Pages: 60007 (peak: 60007)
work_for_cpu_fn (0xffffda088b4aef00) Pages: 53906 (peak: 53906)
local_pci_probe (0xffffda088b9e1e44) Pages: 53906 (peak: 53906)
probe_one mlx5_core (0xffffda084f899cc8) Pages: 53518 (peak: 53518)
mlx5_init_one mlx5_core (0xffffda084f8994ac) Pages: 49756 (peak: 49756)
mlx5_function_setup.constprop.0 mlx5_core (0xffffda084f899100) Pages: 44434 (eak: 44434)
mlx5_satisfy_startup_pages mlx5_core (0xffffda084f8a4f24) Pages: 44434 (peak: 44434)
mlx5_function_setup.constprop.0 mlx5_core (0xffffda084f899078) Pages: 5285 (peak: 5285)
mlx5_cmd_init mlx5_core (0xffffda084f89e414) Pages: 4818 (peak: 4818)
mlx5_alloc_cmd_msg mlx5_core (0xffffda084f89aaa0) Pages: 4403 (peak: 4403)
This memory consumption is completely unnecessary when kdump doesn't need
this NIC. Only install needed NIC drivers to prevent this kind of waste.
Note
1. this patch depends on [1] to ask dracut to not install NIC drivers.
2. "ethtool -i" somehow fails to get the vlan driver
3. team.ko doesn't depend on the team mode drivers so we need to install
the team mode drivers manually.
[1] https://github.com/dracutdevs/dracut/pull/1789
Signed-off-by: Coiby Xu <coxu@redhat.com>
Reviewed-by: Thomas Haller <thaller@redhat.com>
Reviewed-by: Philipp Rudo <prudo@redhat.com>
Signed-off-by: Coiby Xu <coxu@redhat.com>
469 lines
12 KiB
Bash
469 lines
12 KiB
Bash
#!/bin/bash --norc
|
|
# New mkdumprd
|
|
#
|
|
# Copyright 2011 Red Hat, Inc.
|
|
#
|
|
# Written by Cong Wang <amwang@redhat.com>
|
|
#
|
|
|
|
if [[ -f /etc/sysconfig/kdump ]]; then
|
|
. /etc/sysconfig/kdump
|
|
fi
|
|
|
|
[[ $dracutbasedir ]] || dracutbasedir=/usr/lib/dracut
|
|
. $dracutbasedir/dracut-functions.sh
|
|
. /lib/kdump/kdump-lib.sh
|
|
. /lib/kdump/kdump-logger.sh
|
|
export IN_KDUMP=1
|
|
|
|
#initiate the kdump logger
|
|
if ! dlog_init; then
|
|
echo "failed to initiate the kdump logger."
|
|
exit 1
|
|
fi
|
|
|
|
SSH_KEY_LOCATION="/root/.ssh/kdump_id_rsa"
|
|
SAVE_PATH=$(get_save_path)
|
|
OVERRIDE_RESETTABLE=0
|
|
|
|
extra_modules=""
|
|
dracut_args=(--add kdumpbase --quiet --hostonly --hostonly-cmdline --hostonly-i18n --hostonly-mode strict --hostonly-nics '' -o "plymouth resume ifcfg earlykdump")
|
|
|
|
MKDUMPRD_TMPDIR="$(mktemp -d -t mkdumprd.XXXXXX)"
|
|
[ -d "$MKDUMPRD_TMPDIR" ] || perror_exit "dracut: mktemp -p -d -t dracut.XXXXXX failed."
|
|
MKDUMPRD_TMPMNT="$MKDUMPRD_TMPDIR/target"
|
|
|
|
trap '
|
|
ret=$?;
|
|
is_mounted $MKDUMPRD_TMPMNT && umount -f $MKDUMPRD_TMPMNT;
|
|
[[ -d $MKDUMPRD_TMPDIR ]] && rm --one-file-system -rf -- "$MKDUMPRD_TMPDIR";
|
|
exit $ret;
|
|
' EXIT
|
|
|
|
# clean up after ourselves no matter how we die.
|
|
trap 'exit 1;' SIGINT
|
|
|
|
add_dracut_arg()
|
|
{
|
|
dracut_args+=("$@")
|
|
}
|
|
|
|
add_dracut_mount()
|
|
{
|
|
add_dracut_arg "--mount" "$1"
|
|
}
|
|
|
|
add_dracut_sshkey()
|
|
{
|
|
add_dracut_arg "--sshkey" "$1"
|
|
}
|
|
|
|
# caller should ensure $1 is valid and mounted in 1st kernel
|
|
to_mount()
|
|
{
|
|
local _target=$1 _fstype=$2 _options=$3 _sed_cmd _new_mntpoint _pdev
|
|
|
|
_new_mntpoint=$(get_kdump_mntpoint_from_target "$_target")
|
|
_fstype="${_fstype:-$(get_fs_type_from_target "$_target")}"
|
|
_options="${_options:-$(get_mntopt_from_target "$_target")}"
|
|
_options="${_options:-defaults}"
|
|
|
|
if [[ $_fstype == "nfs"* ]]; then
|
|
_pdev=$_target
|
|
_sed_cmd+='s/,\(mount\)\?addr=[^,]*//g;'
|
|
_sed_cmd+='s/,\(mount\)\?proto=[^,]*//g;'
|
|
_sed_cmd+='s/,clientaddr=[^,]*//;'
|
|
else
|
|
# for non-nfs _target converting to use udev persistent name
|
|
_pdev="$(kdump_get_persistent_dev "$_target")"
|
|
if [[ -z $_pdev ]]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# mount fs target as rw in 2nd kernel
|
|
_sed_cmd+='s/\(^\|,\)ro\($\|,\)/\1rw\2/g;'
|
|
# with 'noauto' in fstab nfs and non-root disk mount will fail in 2nd
|
|
# kernel, filter it out here.
|
|
_sed_cmd+='s/\(^\|,\)noauto\($\|,\)/\1/g;'
|
|
# drop nofail or nobootwait
|
|
_sed_cmd+='s/\(^\|,\)nofail\($\|,\)/\1/g;'
|
|
_sed_cmd+='s/\(^\|,\)nobootwait\($\|,\)/\1/g;'
|
|
|
|
_options=$(echo "$_options" | sed "$_sed_cmd")
|
|
|
|
echo "$_pdev $_new_mntpoint $_fstype $_options"
|
|
}
|
|
|
|
#Function: get_ssh_size
|
|
#$1=dump target
|
|
#called from while loop and shouldn't read from stdin, so we're using "ssh -n"
|
|
get_ssh_size()
|
|
{
|
|
local _out
|
|
local _opt=("-i" "$SSH_KEY_LOCATION" "-o" "BatchMode=yes" "-o" "StrictHostKeyChecking=yes")
|
|
|
|
if ! _out=$(ssh -q -n "${_opt[@]}" "$1" "df" "--output=avail" "$SAVE_PATH"); then
|
|
perror_exit "checking remote ssh server available size failed."
|
|
fi
|
|
|
|
echo -n "$_out" | tail -1
|
|
}
|
|
|
|
#mkdir if save path does not exist on ssh dump target
|
|
#$1=ssh dump target
|
|
#caller should ensure write permission on $1:$SAVE_PATH
|
|
#called from while loop and shouldn't read from stdin, so we're using "ssh -n"
|
|
mkdir_save_path_ssh()
|
|
{
|
|
local _opt _dir
|
|
_opt=(-i "$SSH_KEY_LOCATION" -o BatchMode=yes -o StrictHostKeyChecking=yes)
|
|
ssh -qn "${_opt[@]}" "$1" mkdir -p "$SAVE_PATH" &> /dev/null ||
|
|
perror_exit "mkdir failed on $1:$SAVE_PATH"
|
|
|
|
# check whether user has write permission on $1:$SAVE_PATH
|
|
_dir=$(ssh -qn "${_opt[@]}" "$1" mktemp -dqp "$SAVE_PATH" 2> /dev/null) ||
|
|
perror_exit "Could not create temporary directory on $1:$SAVE_PATH. Make sure user has write permission on destination"
|
|
ssh -qn "${_opt[@]}" "$1" rmdir "$_dir"
|
|
|
|
return 0
|
|
}
|
|
|
|
#Function: get_fs_size
|
|
#$1=dump target
|
|
get_fs_size()
|
|
{
|
|
df --output=avail "$(get_mntpoint_from_target "$1")/$SAVE_PATH" | tail -1
|
|
}
|
|
|
|
#Function: get_raw_size
|
|
#$1=dump target
|
|
get_raw_size()
|
|
{
|
|
fdisk -s "$1"
|
|
}
|
|
|
|
#Function: check_size
|
|
#$1: dump type string ('raw', 'fs', 'ssh')
|
|
#$2: dump target
|
|
check_size()
|
|
{
|
|
local avail memtotal
|
|
|
|
memtotal=$(awk '/MemTotal/{print $2}' /proc/meminfo)
|
|
case "$1" in
|
|
raw)
|
|
avail=$(get_raw_size "$2")
|
|
;;
|
|
ssh)
|
|
avail=$(get_ssh_size "$2")
|
|
;;
|
|
fs)
|
|
avail=$(get_fs_size "$2")
|
|
;;
|
|
*)
|
|
return
|
|
;;
|
|
esac || perror_exit "Check dump target size failed"
|
|
|
|
if [[ $avail -lt $memtotal ]]; then
|
|
dwarn "Warning: There might not be enough space to save a vmcore."
|
|
dwarn " The size of $2 should be greater than $memtotal kilo bytes."
|
|
fi
|
|
}
|
|
|
|
check_save_path_fs()
|
|
{
|
|
local _path=$1
|
|
|
|
if [[ ! -d $_path ]]; then
|
|
perror_exit "Dump path $_path does not exist."
|
|
fi
|
|
}
|
|
|
|
mount_failure()
|
|
{
|
|
local _target=$1
|
|
local _mnt=$2
|
|
local _fstype=$3
|
|
local msg="Failed to mount $_target"
|
|
|
|
if [[ -n $_mnt ]]; then
|
|
msg="$msg on $_mnt"
|
|
fi
|
|
|
|
msg="$msg for kdump preflight check."
|
|
|
|
if [[ $_fstype == "nfs" ]]; then
|
|
msg="$msg Please make sure nfs-utils has been installed."
|
|
fi
|
|
|
|
perror_exit "$msg"
|
|
}
|
|
|
|
check_user_configured_target()
|
|
{
|
|
local _target=$1 _cfg_fs_type=$2 _mounted
|
|
local _mnt _opt _fstype
|
|
|
|
_mnt=$(get_mntpoint_from_target "$_target")
|
|
_opt=$(get_mntopt_from_target "$_target")
|
|
_fstype=$(get_fs_type_from_target "$_target")
|
|
|
|
if [[ -n $_fstype ]]; then
|
|
# In case of nfs4, nfs should be used instead, nfs* options is deprecated in kdump.conf
|
|
[[ $_fstype == "nfs"* ]] && _fstype=nfs
|
|
|
|
if [[ -n $_cfg_fs_type ]] && [[ $_fstype != "$_cfg_fs_type" ]]; then
|
|
perror_exit "\"$_target\" have a wrong type config \"$_cfg_fs_type\", expected \"$_fstype\""
|
|
fi
|
|
else
|
|
_fstype="$_cfg_fs_type"
|
|
_fstype="$_cfg_fs_type"
|
|
fi
|
|
|
|
# For noauto mount, mount it inplace with default value.
|
|
# Else use the temporary target directory
|
|
if [[ -n $_mnt ]]; then
|
|
if ! is_mounted "$_mnt"; then
|
|
if [[ $_opt == *",noauto"* ]]; then
|
|
mount "$_mnt" || mount_failure "$_target" "$_mnt" "$_fstype"
|
|
_mounted=$_mnt
|
|
else
|
|
perror_exit "Dump target \"$_target\" is neither mounted nor configured as \"noauto\""
|
|
fi
|
|
fi
|
|
else
|
|
_mnt=$MKDUMPRD_TMPMNT
|
|
mkdir -p "$_mnt"
|
|
mount "$_target" "$_mnt" -t "$_fstype" -o defaults || mount_failure "$_target" "" "$_fstype"
|
|
_mounted=$_mnt
|
|
fi
|
|
|
|
# For user configured target, use $SAVE_PATH as the dump path within the target
|
|
if [[ ! -d "$_mnt/$SAVE_PATH" ]]; then
|
|
perror_exit "Dump path \"$SAVE_PATH\" does not exist in dump target \"$_target\""
|
|
fi
|
|
|
|
check_size fs "$_target"
|
|
|
|
# Unmount it early, if function is interrupted and didn't reach here, the shell trap will clear it up anyway
|
|
if [[ -n $_mounted ]]; then
|
|
umount -f -- "$_mounted"
|
|
fi
|
|
}
|
|
|
|
# $1: core_collector config value
|
|
verify_core_collector()
|
|
{
|
|
local _cmd="${1%% *}"
|
|
local _params="${1#* }"
|
|
|
|
if [[ $_cmd != "makedumpfile" ]]; then
|
|
if is_raw_dump_target; then
|
|
dwarn "Warning: specifying a non-makedumpfile core collector, you will have to recover the vmcore manually."
|
|
fi
|
|
return
|
|
fi
|
|
|
|
if is_ssh_dump_target || is_raw_dump_target; then
|
|
if ! strstr "$_params" "-F"; then
|
|
perror_exit 'The specified dump target needs makedumpfile "-F" option.'
|
|
fi
|
|
_params="$_params vmcore"
|
|
else
|
|
_params="$_params vmcore dumpfile"
|
|
fi
|
|
|
|
# shellcheck disable=SC2086
|
|
if ! $_cmd --check-params $_params; then
|
|
perror_exit "makedumpfile parameter check failed."
|
|
fi
|
|
}
|
|
|
|
add_mount()
|
|
{
|
|
local _mnt
|
|
|
|
_mnt=$(to_mount "$@") || exit 1
|
|
|
|
add_dracut_mount "$_mnt"
|
|
}
|
|
|
|
#handle the case user does not specify the dump target explicitly
|
|
handle_default_dump_target()
|
|
{
|
|
local _target
|
|
local _mntpoint
|
|
|
|
is_user_configured_dump_target && return
|
|
|
|
check_save_path_fs "$SAVE_PATH"
|
|
|
|
_save_path=$(get_bind_mount_source "$SAVE_PATH")
|
|
_target=$(get_target_from_path "$_save_path")
|
|
_mntpoint=$(get_mntpoint_from_target "$_target")
|
|
|
|
SAVE_PATH=${_save_path##"$_mntpoint"}
|
|
add_mount "$_target"
|
|
check_size fs "$_target"
|
|
}
|
|
|
|
# $1: function name
|
|
for_each_block_target()
|
|
{
|
|
local dev majmin
|
|
|
|
for dev in $(get_kdump_targets); do
|
|
[[ -b $dev ]] || continue
|
|
majmin=$(get_maj_min "$dev")
|
|
check_block_and_slaves "$1" "$majmin" && return 1
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
#judge if a specific device with $1 is unresettable
|
|
#return false if unresettable.
|
|
is_unresettable()
|
|
{
|
|
local path device resettable=1
|
|
|
|
path="/sys/$(udevadm info --query=all --path="/sys/dev/block/$1" | awk '/^P:/ {print $2}' | sed -e 's/\(cciss[0-9]\+\/\).*/\1/g' -e 's/\/block\/.*$//')/resettable"
|
|
if [[ -f $path ]]; then
|
|
resettable="$(< "$path")"
|
|
[[ $resettable -eq 0 ]] && [[ $OVERRIDE_RESETTABLE -eq 0 ]] && {
|
|
device=$(udevadm info --query=all --path="/sys/dev/block/$1" | awk -F= '/DEVNAME/{print $2}')
|
|
derror "Error: Can not save vmcore because device $device is unresettable"
|
|
return 0
|
|
}
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
#check if machine is resettable.
|
|
#return true if resettable
|
|
check_resettable()
|
|
{
|
|
local _target _override_resettable
|
|
|
|
_override_resettable=$(kdump_get_conf_val override_resettable)
|
|
OVERRIDE_RESETTABLE=${_override_resettable:-$OVERRIDE_RESETTABLE}
|
|
if [ "$OVERRIDE_RESETTABLE" != "0" ] && [ "$OVERRIDE_RESETTABLE" != "1" ]; then
|
|
perror_exit "override_resettable value '$OVERRIDE_RESETTABLE' is invalid"
|
|
fi
|
|
|
|
for_each_block_target is_unresettable && return
|
|
|
|
return 1
|
|
}
|
|
|
|
check_crypt()
|
|
{
|
|
local _dev
|
|
|
|
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
|
|
exit 1
|
|
fi
|
|
|
|
if ! check_crypt; then
|
|
dwarn "Warning: Encrypted device is in dump path, which is not recommended, see kexec-kdump-howto.txt for more details."
|
|
fi
|
|
|
|
# firstly get right SSH_KEY_LOCATION
|
|
keyfile=$(kdump_get_conf_val sshkey)
|
|
if [[ -f $keyfile ]]; then
|
|
# canonicalize the path
|
|
SSH_KEY_LOCATION=$(/usr/bin/readlink -m "$keyfile")
|
|
fi
|
|
|
|
while read -r config_opt config_val; do
|
|
# remove inline comments after the end of a directive.
|
|
case "$config_opt" in
|
|
extra_modules)
|
|
extra_modules="$extra_modules $config_val"
|
|
;;
|
|
ext[234] | xfs | btrfs | minix | nfs | virtiofs)
|
|
check_user_configured_target "$config_val" "$config_opt"
|
|
add_mount "$config_val" "$config_opt"
|
|
;;
|
|
raw)
|
|
# checking raw disk writable
|
|
dd if="$config_val" count=1 of=/dev/null > /dev/null 2>&1 || {
|
|
perror_exit "Bad raw disk $config_val"
|
|
}
|
|
_praw=$(persistent_policy="by-id" kdump_get_persistent_dev "$config_val")
|
|
if [[ -z $_praw ]]; then
|
|
exit 1
|
|
fi
|
|
add_dracut_arg "--device" "$_praw"
|
|
check_size raw "$config_val"
|
|
;;
|
|
ssh)
|
|
if strstr "$config_val" "@"; then
|
|
mkdir_save_path_ssh "$config_val"
|
|
check_size ssh "$config_val"
|
|
add_dracut_sshkey "$SSH_KEY_LOCATION"
|
|
else
|
|
perror_exit "Bad ssh dump target $config_val"
|
|
fi
|
|
;;
|
|
core_collector)
|
|
verify_core_collector "$config_val"
|
|
;;
|
|
dracut_args)
|
|
while read -r dracut_arg; do
|
|
add_dracut_arg "$dracut_arg"
|
|
done <<< "$(echo "$config_val" | xargs -n 1 echo)"
|
|
;;
|
|
*) ;;
|
|
|
|
esac
|
|
done <<< "$(kdump_read_conf)"
|
|
|
|
handle_default_dump_target
|
|
|
|
if ! have_compression_in_dracut_args; then
|
|
if is_squash_available && dracut_have_option "--squash-compressor"; then
|
|
add_dracut_arg "--squash-compressor" "zstd"
|
|
elif is_zstd_command_available; then
|
|
add_dracut_arg "--compress" "zstd"
|
|
fi
|
|
fi
|
|
|
|
if [[ -n $extra_modules ]]; then
|
|
add_dracut_arg "--add-drivers" "$extra_modules"
|
|
fi
|
|
|
|
# TODO: The below check is not needed anymore with the introduction of
|
|
# 'zz-fadumpinit' module, that isolates fadump's capture kernel initrd,
|
|
# but still sysroot.mount unit gets generated based on 'root=' kernel
|
|
# parameter available in fadump case. So, find a way to fix that first
|
|
# before removing this check.
|
|
if ! is_fadump_capable; then
|
|
# The 2nd rootfs mount stays behind the normal dump target mount,
|
|
# so it doesn't affect the logic of check_dump_fs_modified().
|
|
is_dump_to_rootfs && add_mount "$(to_dev_name "$(get_root_fs_device)")"
|
|
|
|
add_dracut_arg "--no-hostonly-default-device"
|
|
fi
|
|
|
|
# This is RHEL-only to work around nvme problem, then real fix should go to dracut
|
|
if [[ -d /sys/module/nvme ]]; then
|
|
add_dracut_arg "--add-drivers" "nvme"
|
|
fi
|
|
|
|
dracut "${dracut_args[@]}" "$@"
|
|
|
|
_rc=$?
|
|
sync
|
|
exit $_rc
|