Files
kdumpst/kdump-load.sh
Guilherme G. Piccoli d8815b1cd7 kdump-load/save-dumps: Use shell options to improve reliability
Add hereby "set -uo pipefail", with the goal of improving
reliability (suggested by Emil/@xexaxo). Notice that the
suggestion included "-e", but we make use of this, by checking
non-zero pipes, so instead of refactoring the code to just have
this option, the choice was to not have it.

Also, make use of bash as the shell to execute the tools - after
some analysis, we make use of few bashisms that are a bummer to
change, since a lot of scripts in SteamOS make use of bash and
in general it is a very common shell, let's just go along with it.

Signed-off-by: Guilherme G. Piccoli <gpiccoli@igalia.com>
2023-03-31 15:34:42 -03:00

180 lines
6.3 KiB
Bash

#!/bin/bash
set -uo pipefail
#
# SPDX-License-Identifier: LGPL-2.1+
#
# Copyright (c) 2021 Valve.
# Maintainer: Guilherme G. Piccoli <gpiccoli@igalia.com>
#
# Script that loads the panic kdump (from within a systemd service) and/or
# configures the Pstore-RAM mechanism. If the proper parameters are passed
# also, either it creates the minimal kdump initramfs for the running kernel
# or removes all the previously created ones. Since it runs on boot time,
# extra care is required to avoid boot hangs.
#
# This function has 2 purposes: if 'kdump' is passed as argument and we don't
# have crashkernel memory reserved, we edit grub config file and recreate
# grub.cfg, so next boot has it reserved; in this case, we also bail-out,
# since kdump can't be loaded anyway.
#
# If 'pstore' is passsed as argument, we try to unset crashkernel iff it's
# already set AND the pattern in grub config is the one added by us - if the
# users set crashkernel themselves, we don't mess with that.
grub_update() {
CRASHK="$(cat /sys/kernel/kexec_crash_size)"
SED_ADD="s/^GRUB_CMDLINE_LINUX_DEFAULT=\"/GRUB_CMDLINE_LINUX_DEFAULT=\"${GRUB_CMDLINE}/g"
if [ "${GRUB_AUTOSET}" -eq 1 ]; then
if [ "$1" = "kdump" ] && [ "${CRASHK}" -eq 0 ]; then
sed -i "${SED_ADD}" "${GRUB_CFG_FILE}"
if ! grub-mkconfig -o "${GRUB_BOOT_FILE}" 1>/dev/null; then
logger "kdump: failed to execute command \"${GRUB_CMD}\""
exit 1
fi
sync "${GRUB_BOOT_FILE}" 2>/dev/null
logger "kdump: kexec won't succeed, no reserved memory in this boot..."
logger "kdump: but we automatically set crashkernel for next boot."
exit 0 # this is considered a successful run
fi
if [ "$1" = "pstore" ] && [ "${CRASHK}" -ne 0 ]; then
sed -i "s/\"${GRUB_CMDLINE}/\"/g" "${GRUB_CFG_FILE}"
if ! grub-mkconfig -o "${GRUB_BOOT_FILE}" 1>/dev/null; then
logger "kdump: failed to execute command \"${GRUB_CMD}\""
exit 1
fi
sync "${GRUB_BOOT_FILE}" 2>/dev/null
logger "kdump: cleared crashkernel memory previously set."
fi
fi
}
# This function is responsible for creating the kdump initrd, either
# via command-line call or in case initrd doesn't exist during kdump load.
create_initrd() {
rm -f "${MOUNT_FOLDER}/kdump-initrd-$(uname -r).img"
echo "Creating the kdump initramfs for kernel \"$(uname -r)\" ..."
DRACUT_NO_XATTR=1 dracut --no-early-microcode --host-only -q -m\
"bash systemd systemd-initrd systemd-sysusers modsign dbus-daemon kdump dbus udev-rules dracut-systemd base fs-lib shutdown"\
--kver "$(uname -r)" "${MOUNT_FOLDER}/kdump-initrd-$(uname -r).img"
}
# This routine performs a clean-up by deleting the old/useless remaining
# kdump initrd files.
cleanup_unused_initrd() {
INSTALLED_KERNELS="${MOUNT_FOLDER}/.installed_kernels"
find /lib/modules/* -maxdepth 0 -type d -exec basename {} \; 1> "${INSTALLED_KERNELS}"
find "${MOUNT_FOLDER}"/* -name "kdump-initrd*" -type f -print0 2>/dev/null |\
while IFS= read -r -d '' file
do
FNAME="$(basename "${file}" .img)"
KVER="${FNAME#kdump-initrd-}"
if ! grep -q "${KVER}" "${INSTALLED_KERNELS}" ; then
rm -f "${MOUNT_FOLDER}/${FNAME}.img"
logger "kdump: removed unused file \"${FNAME}.img\""
fi
done
rm -f "${INSTALLED_KERNELS}"
}
# Load the necessary external variables, otherwise it'll fail later.
HAVE_CFG_FILES=0
shopt -s nullglob
for cfg in "/usr/share/kdump.d"/*; do
if [ -f "$cfg" ]; then
. "$cfg"
HAVE_CFG_FILES=1
fi
done
shopt -u nullglob
if [ ${HAVE_CFG_FILES} -eq 0 ]; then
logger "kdump: no config files in /usr/share/kdump.d/ - aborting."
exit 1
fi
# Find the proper mount point expected for kdump collection:
DEVN_MOUNTED="$(findmnt "${MOUNT_DEVNODE}" -fno TARGET)"
# Create the kdump main folder here, as soon as possible, given
# the importance of such directory in all kdump/pstore steps.
MOUNT_FOLDER="${DEVN_MOUNTED}/${MOUNT_FOLDER}"
mkdir -p "${MOUNT_FOLDER}"
echo "${MOUNT_FOLDER}" > "${MNT_TMP}"
sync "${MNT_TMP}"
# Notice that at this point it's required to have the full
# MOUNT_FOLDER, so this must remain after the DEVNODE operations above.
if [ "$1" = "initrd" ]; then
create_initrd
exit 0
fi
if [ "$1" = "clear" ]; then
rm -f "${MOUNT_FOLDER}"/kdump-initrd-*
exit 0
fi
# Pstore-RAM load; if it is configured via the config files and fails
# to configure pstore, we still try to load the kdump. We try to reserve
# here a ${MEM_REQUIRED} memory region.
# Notice that we assume ramoops is a module here - if built-in, users
# should properly load it through command-line parameters.
if [ "${USE_PSTORE_RAM}" -eq 1 ]; then
MEM_REQUIRED="${PSTORE_MEM_AMOUNT}"
RECORD_SIZE="${PSTORE_RECORD_SZ}"
RANGE=$(grep "RAM buffer" /proc/iomem | head -n1 | cut -f1 -d\ )
MEM_END=$(echo "$RANGE" | cut -f2 -d-)
MEM_START=$(echo "$RANGE" | cut -f1 -d-)
MEM_SIZE=$(( 16#${MEM_END} - 16#${MEM_START} ))
if [ ${MEM_SIZE} -ge "${MEM_REQUIRED}" ]; then
if modprobe ramoops mem_address=0x"${MEM_START}" mem_size="${MEM_REQUIRED}" record_size="${RECORD_SIZE}"; then
# If Pstore is set, update grub.cfg to avoid reserving crashkernel memory.
logger "kdump: pstore-RAM was loaded successfully"
cleanup_unused_initrd
grub_update pstore
exit 0
fi
logger "kdump: pstore-RAM load failed...will try kdump"
fi
# Fallback to kdump load - if we fail when configuring pstore, better
# trying kdump; in case we have crashkernel memory reserved, lucky us.
# If not, we're going to set that automatically on grub_update().
# Notice that if it's not set, we bail-out in grub_update() - there's
# no point in continuing since kdump cannot work.
fi
cleanup_unused_initrd
grub_update kdump
# Stolen from Debian kdump
KDUMP_CMDLINE=$(sed -re 's/(^| )(crashkernel|hugepages|hugepagesz)=[^ ]*//g;s/"/\\\\"/' /proc/cmdline)
KDUMP_CMDLINE="${KDUMP_CMDLINE} panic=-1 oops=panic fsck.mode=force fsck.repair=yes nr_cpus=1 reset_devices"
VMLINUX="$(grep -o 'BOOT_IMAGE=[^ ]*' /proc/cmdline)"
# In case we don't have a valid initrd, for some reason, try creating
# one before loading kdump (or else it will fail).
INITRD_FNAME="${MOUNT_FOLDER}/kdump-initrd-$(uname -r).img"
if [ ! -s "${INITRD_FNAME}" ]; then
create_initrd
fi
if ! kexec -s -p "${VMLINUX#*BOOT_IMAGE=}" --initrd "${INITRD_FNAME}" --append="${KDUMP_CMDLINE}"; then
logger "kdump: kexec load failed"
exit 1
fi
logger "kdump: panic kexec loaded successfully"