From 4562d80ec231e8b84089b17a3ca05889529c1bce Mon Sep 17 00:00:00 2001 From: Lichen Liu Date: Fri, 19 Sep 2025 16:43:19 +0800 Subject: [PATCH 1/2] mkdumprd: basic support for btrfs subvol The kdump scripts previously could not correctly handle cases where the dump target was located on a btrfs subvolume. The logic for finding the mount point did not account for the way subvolumes are represented, leading to failures in identifying the target device and checking for available space. This patch introduces support for btrfs subvolumes by: 1. Adding a new helper function `get_btrfs_subvol_from_mntopt` to extract the subvolume name from mount options. 2. Modifying `get_mntpoint_from_target` to accept a subvolume name. When a subvolume is provided, it correctly identifies the mount point by looking for the source entry that includes the subvolume name (e.g., `/dev/sda1[/subvol]`). 3. Updating various functions in `mkdumprd`, `kdump-lib.sh`, and the dracut module setup scripts to detect if the target is on a btrfs filesystem, parse the subvolume, and pass it to the relevant functions for mount point resolution and free space checks. This ensures that kdump can now be reliably configured with a dump target located on a btrfs subvolume. Signed-off-by: Lichen Liu --- dracut/99kdumpbase/module-setup.sh | 11 ++++++++--- kdump-lib-initramfs.sh | 22 ++++++++++++++++++---- kdump-lib.sh | 13 ++++++------- mkdumprd | 24 ++++++++++++++++-------- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/dracut/99kdumpbase/module-setup.sh b/dracut/99kdumpbase/module-setup.sh index 4dc11ac6..32819ab5 100755 --- a/dracut/99kdumpbase/module-setup.sh +++ b/dracut/99kdumpbase/module-setup.sh @@ -683,16 +683,21 @@ kdump_install_pre_post_conf() { } default_dump_target_install_conf() { - local _target _fstype + local _target _fstype _subvol _options local _mntpoint _save_path is_user_configured_dump_target && return _save_path=$(get_bind_mount_source "$(get_save_path)") _target=$(get_target_from_path "$_save_path") - _mntpoint=$(get_mntpoint_from_target "$_target") - + _options=$(get_mount_info OPTIONS target "$_save_path" -f) _fstype=$(get_fs_type_from_target "$_target") + if [[ $_fstype == btrfs ]]; then + _subvol=$(get_btrfs_subvol_from_mntopt "$_options") + fi + + _mntpoint=$(get_mntpoint_from_target "$_target" "$_subvol") + if is_fs_type_nfs "$_fstype"; then kdump_collect_netif_usage "$_target" _fstype="nfs" diff --git a/kdump-lib-initramfs.sh b/kdump-lib-initramfs.sh index c4bc6d2e..39e52a6f 100755 --- a/kdump-lib-initramfs.sh +++ b/kdump-lib-initramfs.sh @@ -110,15 +110,29 @@ get_fs_type_from_target() get_mntpoint_from_target() { - # get the first TARGET when SOURCE doesn't end with ]. - # In most cases, a SOURCE ends with ] when fsroot or subvol exists. - _mntpoint=$(get_mount_info TARGET,SOURCE source "$1" | grep -v "\]$" | awk 'NR==1 { print $1 }') - + _subvol="$2" + if [ -z "$_subvol" ]; then + # get the first TARGET when SOURCE doesn't end with ]. + # In most cases, a SOURCE ends with ] when fsroot or subvol exists. + _mntpoint=$(get_mount_info TARGET,SOURCE source "$1" | grep -v "\]$" | awk 'NR==1 { print $1 }') + else + # btrfs with subvol + _mntpoint=$(get_mount_info TARGET,SOURCE source "$1" | grep "\[$_subvol\]$" | awk 'NR==1 { print $1 }') + fi # fallback to the old way when _mntpoint is empty. [ -n "$_mntpoint" ] || _mntpoint=$(get_mount_info TARGET source "$1" -f) echo "$_mntpoint" } +get_btrfs_subvol_from_mntopt() +{ + _subvol=${1#*subvol=} + # mount option may not contain subvol + [ "$1" != "$_subvol" ] || return 0 + _subvol=${_subvol%%,*} + echo "$_subvol" +} + is_ssh_dump_target() { kdump_get_conf_val ssh | grep -q @ diff --git a/kdump-lib.sh b/kdump-lib.sh index 232d065f..4196b0e5 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -177,7 +177,7 @@ get_kdump_targets() # part is the bind mounted directory which quotes by bracket "[]". get_bind_mount_source() { - local _mnt _path _src _opt _fstype + local _mnt _path _src _opt _fstype _subvol local _fsroot _src_nofsroot _mnt=$(df "$1" | tail -1 | awk '{print $NF}') @@ -200,15 +200,13 @@ get_bind_mount_source() _fsroot=${_src#"${_src_nofsroot}"[} _fsroot=${_fsroot%]} - _mnt=$(get_mntpoint_from_target "$_src_nofsroot") - # for btrfs, _fsroot will also contain the subvol value as well, strip it if [[ $_fstype == btrfs ]]; then - local _subvol - _subvol=${_opt#*subvol=} - _subvol=${_subvol%,*} + _subvol=$(get_btrfs_subvol_from_mntopt "$_opt") _fsroot=${_fsroot#"$_subvol"} fi + _mnt=$(get_mntpoint_from_target "$_src_nofsroot" "$_subvol") + echo "$_mnt$_fsroot$_path" } @@ -219,11 +217,12 @@ get_mntopt_from_target() # Get the path where the target will be mounted in kdump kernel # $1: kdump target device +# $2: btrfs subvol get_kdump_mntpoint_from_target() { local _mntpoint - _mntpoint=$(get_mntpoint_from_target "$1") + _mntpoint=$(get_mntpoint_from_target "$1" "$2") # mount under /sysroot if dump to root disk or mount under # mount under /kdumproot if dump target is not mounted in first kernel # mount under /kdumproot/$_mntpoint in other cases in 2nd kernel. diff --git a/mkdumprd b/mkdumprd index e0392da9..06174de1 100644 --- a/mkdumprd +++ b/mkdumprd @@ -76,10 +76,13 @@ 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 == btrfs ]]; then + _subvol=$(get_btrfs_subvol_from_mntopt "$_options") + fi + _new_mntpoint=$(get_kdump_mntpoint_from_target "$_target" "$_subvol") if [[ $_fstype == "nfs"* ]]; then _pdev=$_target @@ -146,7 +149,7 @@ mkdir_save_path_ssh() #$1=dump target get_fs_size() { - df --output=avail "$(get_mntpoint_from_target "$1")/$SAVE_PATH" | tail -1 + df --output=avail "$(get_mntpoint_from_target "$1" "$2")/$SAVE_PATH" | tail -1 } #Function: get_raw_size @@ -159,6 +162,7 @@ get_raw_size() #Function: check_size #$1: dump type string ('raw', 'fs', 'ssh') #$2: dump target +#$3: btrfs subvol check_size() { local avail memtotal @@ -172,7 +176,7 @@ check_size() avail=$(get_ssh_size "$2") ;; fs) - avail=$(get_fs_size "$2") + avail=$(get_fs_size "$2" "$3") ;; *) return @@ -302,20 +306,24 @@ add_mount() #handle the case user does not specify the dump target explicitly handle_default_dump_target() { - local _target - local _mntpoint + local _target _mntpoint _fstype _subvol _options is_user_configured_dump_target && return check_save_path_fs "$SAVE_PATH" _save_path=$(get_bind_mount_source "$SAVE_PATH") + _options=$(get_mount_info OPTIONS target "$_save_path" -f) _target=$(get_target_from_path "$_save_path") - _mntpoint=$(get_mntpoint_from_target "$_target") + _fstype=$(get_fs_type_from_target "$_target") + if [[ $_fstype == btrfs ]]; then + _subvol=$(get_btrfs_subvol_from_mntopt "$_options") + fi + _mntpoint=$(get_mntpoint_from_target "$_target" "$_subvol") SAVE_PATH=${_save_path##"$_mntpoint"} - add_mount "$_target" - check_size fs "$_target" + add_mount "$_target" "$_fstype" "$_options" + check_size fs "$_target" "$_subvol" } # firstly get right SSH_KEY_LOCATION From 44601272651ea8eae26967b5b090442fb212885e Mon Sep 17 00:00:00 2001 From: Lichen Liu Date: Thu, 30 Oct 2025 16:59:56 +0800 Subject: [PATCH 2/2] unit tests: Add cases for btrfs subvol Add test cases to verify new and modified logic for handling btrfs subvolumes as kdump targets. Assisted-by: Claude (Anthropic) Signed-off-by: Lichen Liu --- spec/kdump-lib-initramfs_spec.sh | 50 ++++++++++++++++++++++++++++---- spec/kdump-lib_spec.sh | 48 ++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/spec/kdump-lib-initramfs_spec.sh b/spec/kdump-lib-initramfs_spec.sh index acabc2ff..d12b758d 100644 --- a/spec/kdump-lib-initramfs_spec.sh +++ b/spec/kdump-lib-initramfs_spec.sh @@ -67,6 +67,12 @@ Describe 'kdump-lib-initramfs' else printf '/var %s[/ostree/deploy/default/var]\n/sysroot %s\n' "$7" "$7" fi + elif [[ "$7" == '/dev/nvme0n1p3' ]]; then + if [[ "$8" == "-f" ]]; then + printf '/ /dev/nvme0n1p3[/root]\n' + else + printf '/ /dev/nvme0n1p3[/root]\n/var /dev/nvme0n1p3[/var]\n' + fi fi } @@ -76,19 +82,51 @@ Describe 'kdump-lib-initramfs' # - IPv6 NFS target also contain '[' in the export # - local dumping target that has '[' in the name # - has bind mint + # - has subvol Parameters - 'eng.redhat.com:/srv/[nfs]' '/mnt' - '[2620:52:0:a1:217:38ff:fe01:131]:/srv/[nfs]' '/mnt' - '/dev/mapper/rhel[disk]' '/' - '/dev/vda4' '/sysroot' + 'eng.redhat.com:/srv/[nfs]' '' '/mnt' + '[2620:52:0:a1:217:38ff:fe01:131]:/srv/[nfs]' '' '/mnt' + '/dev/mapper/rhel[disk]' '' '/' + '/dev/vda4' '' '/sysroot' + '/dev/nvme0n1p3' '/var' '/var' End It 'should handle all cases correctly' - When call get_mntpoint_from_target "$1" - The output should equal "$2" + When call get_mntpoint_from_target "$1" "$2" + The output should equal "$3" End End End + Describe 'Test get_btrfs_subvol_from_mntopt' + Context 'Given different mount option formats' + # Test the following cases: + # - Standard subvol option at the beginning + # - Standard subvol option in the middle + # - Standard subvol option at the end + # - Subvol with @ prefix (common btrfs pattern) + # - Subvol with complex path + # - No subvol option present + # - Empty mount options + # - Subvol option with no value + # - Similar but different option names + Parameters + "subvol=/home,rw,relatime" "/home" + "rw,subvol=/root,defaults" "/root" + "rw,relatime,subvol=/root" "/root" + "subvol=@home" "@home" + "subvol=/path/to/subvolume,rw" "/path/to/subvolume" + "rw,relatime,defaults" "" + true '' + "subvol=,rw" "" + "rw,subvolume=@home" "" + End + + It 'should extract subvolume correctly' + When call get_btrfs_subvol_from_mntopt "$1" + The output should equal "$2" + End + End + End End diff --git a/spec/kdump-lib_spec.sh b/spec/kdump-lib_spec.sh index 41755a40..8ced9172 100644 --- a/spec/kdump-lib_spec.sh +++ b/spec/kdump-lib_spec.sh @@ -133,4 +133,52 @@ Describe 'kdump-lib' End End + Describe "get_kdump_mntpoint_from_target with btrfs subvolume support" + # Mock get_mntpoint_from_target to simulate different mount scenarios + get_mntpoint_from_target() { + local device="$1" subvol="$2" + case "$device" in + '/dev/sda1') + if [[ "$subvol" == "@home" ]]; then + echo "/mnt/btrfs" + elif [[ "$subvol" == "/root" ]]; then + echo "/" + else + echo "/" + fi + ;; + '/dev/sdb1') + if [[ "$subvol" == "@backup" ]]; then + echo "/backup" + else + echo "/backup" + fi + ;; + *) + echo "/" + ;; + esac + } + + Context 'Given various mount point scenarios with subvolumes' + # Test the following cases: + # - Root filesystem with @ subvolume -> /sysroot + # - Home subvolume on non-root -> /kdumproot/mnt/btrfs + # - Backup subvolume -> /kdumproot/backup + # - Regular filesystem without subvolume -> /sysroot + Parameters + '/dev/sda1 /root /sysroot' + '/dev/sda1 @home /kdumproot/mnt/btrfs' + '/dev/sdb1 @backup /kdumproot/backup' + '/dev/vda2 "" /sysroot' + End + + It 'should handle btrfs subvolumes correctly' + # Parse parameters: device subvol expected_result + read -r device subvol expected_result <<< "$1" + When call get_kdump_mntpoint_from_target "$device" "$subvol" + The output should equal "$expected_result" + End + End + End End