kexec-tools/mkdumprd

446 lines
12 KiB
Plaintext
Raw Normal View History

#!/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
dlog_init
if [ $? -ne 0 ]; then
echo "failed to initiate the kdump logger."
exit 1
fi
conf_file="/etc/kdump.conf"
SSH_KEY_LOCATION="/root/.ssh/kdump_id_rsa"
SAVE_PATH=$(get_save_path)
OVERRIDE_RESETTABLE=0
extra_modules=""
Reduce kdump memory consumption by only installing needed NIC drivers Resolves: bz1958587 Upstream: Fedora Conflict: 1. RHEL8's mkdumprd has different dracut_args from upstream's. 2. RHEL8's kdump_install_net is different from upstream's and we should install needed NIC drivers in the end. commit a65dde2d1083a57824aecd1840dea417c98c553d 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>
2022-11-25 01:43:05 +00:00
dracut_args="--add kdumpbase --quiet --hostonly --hostonly-cmdline --hostonly-i18n --hostonly-mode strict --hostonly-nics '' -o \"plymouth dash resume ifcfg earlykdump\" --compress=xz"
readonly MKDUMPRD_TMPDIR="$(mktemp -d -t mkdumprd.XXXXXX)"
[ -d "$MKDUMPRD_TMPDIR" ] || perror_exit "dracut: mktemp -p -d -t dracut.XXXXXX failed."
readonly 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="$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 _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
_options=$(echo $_options | sed 's/,\(mount\)\?addr=[^,]*//g')
_options=$(echo $_options | sed 's/,\(mount\)\?proto=[^,]*//g')
_options=$(echo $_options | sed '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
_options=$(echo $_options | sed 's/\(^\|,\)ro\($\|,\)/\1rw\2/g')
# with 'noauto' in fstab nfs and non-root disk mount will fail in 2nd
# kernel, filter it out here.
_options=$(echo $_options | sed 's/\(^\|,\)noauto\($\|,\)/\1/g')
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 _opt _out _size
_opt="-i $SSH_KEY_LOCATION -o BatchMode=yes -o StrictHostKeyChecking=yes"
_out=$(ssh -q -n $_opt $1 "df -P $SAVE_PATH")
[ $? -ne 0 ] && {
perror_exit "checking remote ssh server available size failed."
}
#ssh output removed the line break, so print field NF-2
_size=$(echo -n $_out| awk '{avail=NF-2; print $avail}')
echo -n $_size
}
#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 2>&1 > /dev/null
_ret=$?
if [ $_ret -ne 0 ]; then
perror_exit "mkdir failed on $1:$SAVE_PATH"
fi
#check whether user has write permission on $1:$SAVE_PATH
_dir=$(ssh -qn $_opt $1 mktemp -dqp $SAVE_PATH 2>/dev/null)
_ret=$?
if [ $_ret -ne 0 ]; then
perror_exit "Could not create temporary directory on $1:$SAVE_PATH. Make sure user has write permission on destination"
fi
ssh -qn $_opt $1 rmdir $_dir
return 0
}
#Function: get_fs_size
#$1=dump target
get_fs_size() {
local _mnt=$(get_mntpoint_from_target $1)
echo -n $(df -P "${_mnt}/$SAVE_PATH"|tail -1|awk '{print $4}')
}
#Function: get_raw_size
#$1=dump target
get_raw_size() {
echo -n $(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
if [ $? -ne 0 ]; then
perror_exit "Check dump target size failed"
fi
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
}
check_user_configured_target()
{
local _target=$1 _cfg_fs_type=$2 _mounted
local _mnt=$(get_mntpoint_from_target $_target)
local _opt=$(get_mntopt_from_target $_target)
local _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
[ $? -ne 0 ] && perror_exit "Failed to mount $_target on $_mnt for kdump preflight check."
_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
[ $? -ne 0 ] && perror_exit "Failed to mount $_target for kdump preflight check."
_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
if ! $_cmd --check-params $_params; then
perror_exit "makedumpfile parameter check failed."
fi
}
add_mount() {
local _mnt=$(to_mount $@)
if [ $? -ne 0 ]; then
exit 1
fi
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
}
get_override_resettable()
{
local override_resettable
override_resettable=$(grep "^override_resettable" $conf_file)
if [ -n "$override_resettable" ]; then
OVERRIDE_RESETTABLE=$(echo $override_resettable | cut -d' ' -f2)
if [ "$OVERRIDE_RESETTABLE" != "0" ] && [ "$OVERRIDE_RESETTABLE" != "1" ];then
perror_exit "override_resettable value $OVERRIDE_RESETTABLE is invalid"
fi
fi
}
# $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="/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"
local resettable=1
if [ -f "$path" ]
then
resettable="$(cat $path)"
[ $resettable -eq 0 -a "$OVERRIDE_RESETTABLE" -eq 0 ] && {
local 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 _ret _target
get_override_resettable
for_each_block_target is_unresettable
_ret=$?
[ $_ret -eq 0 ] && 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. User will prompted for password during second kernel boot."
fi
# firstly get right SSH_KEY_LOCATION
keyfile=$(awk '/^sshkey/ {print $2}' $conf_file)
if [ -f "$keyfile" ]; then
# canonicalize the path
SSH_KEY_LOCATION=$(/usr/bin/readlink -m $keyfile)
fi
while read 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)
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)
add_dracut_arg $config_val
;;
*)
;;
esac
done <<< "$(read_strip_comments $conf_file)"
handle_default_dump_target
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
echo "$dracut_args $@" | xargs dracut