[PATCH v1 5/7] xfs: Add realtime group shrink tests

Nirjhar Roy (IBM)
2026-02-19T06:12:04Z

From: "Nirjhar Roy (IBM)" <nirjhar.roy.lists@gmail.com>

Add unit tests to validate shrinking of realtime groups. This includes
last rtgroup's partial shrink case as well compelete rtgroup removals.

Signed-off-by: Nirjhar Roy (IBM) <nirjhar.roy.lists@gmail.com>
---
tests/xfs/539 | 190 ++++++++++++++++++++++++++++++++++++++++++++++
tests/xfs/539.out | 19 +++++
2 files changed, 209 insertions(+)
create mode 100755 tests/xfs/539
create mode 100644 tests/xfs/539.out

diff --git a/tests/xfs/539 b/tests/xfs/539
new file mode 100755
index 00000000..57e56168
--- /dev/null
+++ b/tests/xfs/539
@@ -0,0 +1,190 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Nirjhar Roy (IBM) <nirjhar.roy.lists@gmail.com>. All Rights Reserved.
+#
+# FS QA Test 539
+#
+# XFS realtime fs shrinkfs basic functionality test
+#
+# This test attempts to shrink the realtime filesystem by various sizes and
+# verifies if it works as expected.
+#
+. ./common/preamble
+_begin_fstest auto quick growfs shrinkfs
+
+# Import common functions.
+. ./common/filter
+_require_realtime_xfs_shrink
+
+AGCOUNT=16
+RGCOUNT="$AGCOUNT"
+FSSIZE=$((2 * 1024 * 1024 * 1024)) # 2G
+_require_scratch_size $((FSSIZE / 1024))
+
+_fixed_by_kernel_commit a65fd8120766 "xfs: Fix xfs_grow_last_rtg()"
+_fixed_by_kernel_commit xxxxxxxxxxxx "xfs: Fix in xfs_rtalloc_query_range()"
+
+reset()
+{
+ _scratch_unmount >> $seqres.full 2>&1
+ _scratch_mkfs -bsize=4k -dsize="$FSSIZE" -dagcount="$AGCOUNT" -rsize="$FSSIZE" \
+ -mmetadir=1 -rrgcount="$RGCOUNT" 2>&1 | \
+ tee -a $seqres.full | _filter_mkfs 2>$tmp.mkfs >/dev/null
+ . $tmp.mkfs
+ tot_rtextents=$rtextents
+ orgcount=$rgcount
+ _scratch_mount >> $seqres.full
+}
+
+# This functions converts a given number of rtextents to rg count.
+_rtextents_to_rg()
+{
+ local extents="$1"
+ local rgdiv=$((extents/rgextents))
+ local rgmod=$((extents%rgextents))
+ local rgc
+ if [[ "$rgmod" == 0 ]]; then
+ rgc="$rgdiv"
+ else
+ rgc=$(( rgdiv + 1 ))
+ fi
+ echo "$rgc"
+}
+
+test_shrink()
+{
+ $XFS_GROWFS_PROG -R"$1" $SCRATCH_MNT &> $tmp.growfs
+ ret=$?
+
+ _scratch_unmount
+ _check_scratch_fs
+ _scratch_mount
+
+ cat $tmp.growfs >> $seqres.full
+ $XFS_INFO_PROG $SCRATCH_MNT 2>&1 | \
+ _filter_mkfs 2>$tmp.xfsinfo >/dev/null
+ . $tmp.xfsinfo
+ [ $ret -eq 0 ]
+}
+
+# run_test <number of rtextents to remove> <test name> <optional: error message>
+# If the test is expected to succeed, then there is no need to provide an error
+# message.
+run_test_shrink()
+{
+ local delta_extents="$1"
+ local test_name="$2"
+ local error_message="$3"
+
+ echo "$test_name"
+ reset
+ local tsize=$((tot_rtextents-delta_extents))
+ local nrgcount=`_rtextents_to_rg "$tsize"`
+ echo "delta = $delta_extents new_rtextents = $tsize nrgcount = $nrgcount" \
+ >> $seqres.full
+ if [[ "$error_message" == "" ]]; then
+ # no error message means the test is expected to succeed
+ test_shrink "$tsize" || \
+ echo "Shrink $test_name failure"
+ # Verify the parameters
+ [[ "$rtextents" -ne "$tsize" ]] && \
+ echo "rtextents not changed properly after shrinking \"$test_name\"" \
+ && echo "expected $tsize got $rtextents"
+ [[ "$rgcount" -ne "$nrgcount" ]] && \
+ echo "rgcount not changed properly after shrinking \"$test_name\"" \
+ && echo "expected $nrgcount got $rgcount"
+ [[ "$(_scratch_xfs_get_rt_bitmap_count)" -ne "$nrgcount" ]] && \
+ echo "bitmap inode count not changed properly after shrinking \"$test_name\"" \
+ && echo "expected $nrgcount got $(_scratch_xfs_get_rt_bitmap_count)"
+ [[ "$(_scratch_xfs_get_rt_summary_count)" -ne "$nrgcount" ]] && \
+ echo "summary inode count not changed properly after shrinking \"$test_name\"" \
+ && echo "expected $nrgcount got $(_scratch_xfs_get_rt_summary_count)"
+ else
+ test_shrink "$tsize" && \
+ echo "Shrink \"$test_name\" succeeded unexpectedly"
+ grep -q "$error_message" $tmp.growfs || \
+ echo "Error message missing - shrinking \"$test_name\" (expected: $error_message)"
+ # Verify the parameters
+ [[ "$rtextents" -ne "$tot_rtextents" ]] && \
+ echo "rtextents changed after shrink failure \"$test_name\"" && \
+ echo "expected $tot_rtextents got $rtextents"
+ [[ "$rgcount" -ne "$orgcount" ]] && \
+ echo "rgcount changed after shrink failure \"$test_name\"" && \
+ echo "expected $orgcount got $rgcount"
+ [[ "$(_scratch_xfs_get_rt_bitmap_count)" -ne "$orgcount" ]] && \
+ echo "bitmap inode count changed after shrink failure \"$test_name\"" && \
+ echo "expected $orgcount got $(_scratch_xfs_get_rt_bitmap_count)"
+ [[ "$(_scratch_xfs_get_rt_summary_count)" -ne "$orgcount" ]] && \
+ echo "summary inode count changed after shrink failure \"$test_name\"" && \
+ echo "expected $orgcount got $(_scratch_xfs_get_rt_summary_count)"
+ fi
+}
+
+# This function, first shrinks the fs, then grows it. This is to make sure that
+# the fs is able to grow properly after a shrink has taken place.
+# Usage: run_test_shrink_grow <number of rtextents to remove> <test name>
+run_test_shrink_grow()
+{
+ local delta_extents="$1"
+ local test_name="$2"
+
+ echo "$test_name"
+ reset
+ local tsize=$((tot_rtextents-delta_extents))
+ test_shrink "$tsize" || \
+ echo "$test_name failure"
+ # Now grow the fs
+ test_shrink "$tot_rtextents" || \
+ echo "$test_name failure"
+ # Verify the parameters
+ [[ "$rtextents" -ne "$tot_rtextents" ]] && \
+ echo "rtextents not changed properly after \"$test_name\"" \
+ && echo "expected $tot_rtextents got $rtextents"
+ [[ "$rgcount" -ne "$orgcount" ]] && \
+ echo "rgcount not changed properly after \"$test_name\"" \
+ && echo "expected $orgcount got $rgcount"
+}
+
+reset
+
+# Tests expected to pass
+# run_test_shrink <number of rtextents to remove> <test name>
+echo "#Tests partial and multi-rg shrinking"
+run_test_shrink 1 "shrink by 1 rtextent"
+run_test_shrink $(( rgextents / 2 )) "shrink by 0.5 RG"
+run_test_shrink $(( rgextents)) "shrink by 1 RG"
+run_test_shrink $(( rgextents * 6 )) "shrink by 6 RG"
+run_test_shrink $(( rgextents * 3 + rgextents / 2)) "shrink by 3.5 RG"
+
+run_test_shrink $(( dbsize * 8 )) "shrink by 1 rmblocks"
+run_test_shrink $(( rgextents + dbsize * 8 )) "shrink by 1rtgroup + 1 rmblocks"
+
+echo
+
+reset
+# run_test_shrink_grow <number of rtextents to remove> <test name>
+echo "#Tests partial and multi-rg shink followed by growfs"
+run_test_shrink_grow $(( rgextents / 2 )) "shrink by 0.5 RG -> grow by 0.5 RG"
+run_test_shrink_grow $(( rgextents * 6 )) "shrink by 6 RG -> grow by 6 RG"
+run_test_shrink_grow $(( rgextents * 3 + rgextents / 2 )) \
+ "shrink by 3.5 RG -> grow by 3.5 RG"
+run_test_shrink_grow $(( dbsize * 8 )) "shrink by 1 rmblocks -> grow by 1 rmblocks"
+run_test_shrink_grow $(( rgextents + dbsize * 8 )) \
+ "shrink by 1rtgroup + 1 rmblocks -> grow by 1rtgroup + 1 rmblocks"
+
+echo
+
+reset
+echo "#Test out of bounds rg shrink: Below tests are expected to fail"
+# Tests expected to fail
+# run_test_shrink_grow <number of rtextents to remove> <test name> <error message>
+run_test_shrink "$(( 99999999 ))" "shrink to > dev size" \
+ "File too large"
+
+echo "printing seqres" >> $seqres.full
+cat $tmp.growfs >> $seqres.full
+
+$XFS_INFO_PROG $SCRATCH_MNT >> $seqres.full
+
+# success, all done
+_exit 0
diff --git a/tests/xfs/539.out b/tests/xfs/539.out
new file mode 100644
index 00000000..59d0557f
--- /dev/null
+++ b/tests/xfs/539.out
@@ -0,0 +1,19 @@
+QA output created by 539
+#Tests partial and multi-rg shrinking
+shrink by 1 rtextent
+shrink by 0.5 RG
+shrink by 1 RG
+shrink by 6 RG
+shrink by 3.5 RG
+shrink by 1 rmblocks
+shrink by 1rtgroup + 1 rmblocks
+
+#Tests partial and multi-rg shink followed by growfs
+shrink by 0.5 RG -> grow by 0.5 RG
+shrink by 6 RG -> grow by 6 RG
+shrink by 3.5 RG -> grow by 3.5 RG
+shrink by 1 rmblocks -> grow by 1 rmblocks
+shrink by 1rtgroup + 1 rmblocks -> grow by 1rtgroup + 1 rmblocks
+
+#Test out of bounds rg shrink: Below tests are expected to fail
+shrink to > dev size
--
2.34.1