From: Darrick J. Wong <djwong@xxxxxxxxxx> This is a regression test for: xfs: make xfs_rtalloc_query_range input parameters const xfs: fix off-by-one error when the last rt extent is in use xfs: make fsmap backend function key parameters const In which we synthesize an XFS with a realtime volume and a special realtime volume to trip the bugs fixed by all three patches that resulted in incomplete fsmap output. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- tests/xfs/922 | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/xfs/922.out | 2 + 2 files changed, 185 insertions(+) create mode 100755 tests/xfs/922 create mode 100644 tests/xfs/922.out diff --git a/tests/xfs/922 b/tests/xfs/922 new file mode 100755 index 00000000..95304d57 --- /dev/null +++ b/tests/xfs/922 @@ -0,0 +1,183 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2021 Oracle. All Rights Reserved. +# +# FS QA Test 922 +# +# Regression test for commits: +# +# c02f6529864a ("xfs: make xfs_rtalloc_query_range input parameters const") +# 9ab72f222774 ("xfs: fix off-by-one error when the last rt extent is in use") +# 7e1826e05ba6 ("xfs: make fsmap backend function key parameters const") +# +# These commits fix a bug in fsmap where the data device fsmap function would +# corrupt the high key passed to the rt fsmap function if the data device +# number is smaller than the rt device number and the data device itself is +# smaller than the rt device. +# +. ./common/preamble +_begin_fstest auto fsmap + +_cleanup() +{ + cd / + rm -r -f $tmp.* + _scratch_unmount + test -n "$ddloop" && _destroy_loop_device "$ddloop" + test -n "$rtloop" && _destroy_loop_device "$rtloop" + test -n "$ddfile" && rm -f "$ddfile" + test -n "$rtfile" && rm -f "$rtfile" + test -n "$old_use_external" && USE_EXTERNAL="$old_use_external" +} + +# real QA test starts here + +# Modify as appropriate. +_supported_fs generic +_require_test + +# Synthesize data and rt volumes that as needed to trigger the bug +ddfile=$TEST_DIR/data +rtfile=$TEST_DIR/rt +rm -f "$rtfile" "$ddfile" + +ddsize="$((100 * 1048576))" +rtsize="$((200 * 1048576))" + +truncate -s $ddsize $ddfile +truncate -s $rtsize $rtfile +ddloop="$(_create_loop_device $ddfile)" +rtloop="$(_create_loop_device $rtfile)" + +test -z "$ddloop" && _fail "Could not create data loop device" +test -z "$rtloop" && _fail "Could not create rt loop device" + +# dataloop must have a smaller device number than rtloop because fsmap sorts +# the output by device number +ddmajor=$(stat -c '%t' "$ddloop") +rtmajor=$(stat -c '%t' "$rtloop") +test $ddmajor -le $rtmajor || \ + _notrun "Data loopdev major $ddmajor larger than rt major $rtmajor" + +ddminor=$(stat -c '%T' "$ddloop") +rtminor=$(stat -c '%T' "$rtloop") +test $ddmajor -le $rtmajor || \ + _notrun "Data loopdev minor $ddminor larger than rt minor $rtminor" + +# Inject our custom-built devices as an rt-capable scratch device. +# We avoid touching "require_scratch" so that post-test fsck will not try to +# run on our synthesized scratch device. +old_use_external="$USE_EXTERNAL" +USE_EXTERNAL=yes +SCRATCH_RTDEV="$rtloop" +SCRATCH_DEV="$ddloop" + +_scratch_mkfs >> $seqres.full +_try_scratch_mount >> $seqres.full || \ + _notrun "mount with injected rt device failed" + +# Create a file that we'll use to seed fsmap entries for the rt device, +# and force the root directory to create only data device files +rtfile="$SCRATCH_MNT/rtfile" +$XFS_IO_PROG -R -f -c 'stat -v' $rtfile | grep -q 'fsxattr.*xflags.*realtime' || \ + _notrun "could not create realtime file" +$XFS_IO_PROG -c 'chattr -t' $SCRATCH_MNT +rtextsize="$(stat -c '%o' $rtfile)" + +# Make sure the data device is smaller than the rt device by at least a few +# realtime extents. +ddbytes="$(stat -f -c '%S * %b' $SCRATCH_MNT | bc)" +rtbytes="$(stat -f -c '%S * %b' $rtfile | bc)" + +test "$ddbytes" -lt "$((rtbytes + (10 * rtextsize) ))" || \ + echo "data device ($ddbytes) has more bytes than rt ($rtbytes)" + +# Craft the layout of the sole realtime file in such a way that the only +# allocated space on the realtime device is at a physical disk address that is +# higher than the size of the data device. For realtime files this is really +# easy because fallocate for the first rt file always starts allocating at +# physical offset zero. +alloc_rtx="$((rtbytes / rtextsize))" +$XFS_IO_PROG -c "falloc 0 $((alloc_rtx * rtextsize))" $rtfile + +expected_end="$(( (alloc_rtx * rtextsize - 1) / 512 ))" + +# Print extent mapping of rtfile in format: +# file_offset file_end physical_offset physical_end +rtfile_exts() { + $XFS_IO_PROG -c 'bmap -elp' $rtfile | \ + egrep -v '(^/|EXT:|hole)' | \ + tr ':.[]' ' ' | \ + while read junk foff fend physoff physend junk; do + echo "$foff $fend $physoff $physend" + done +} + +# Make sure that we actually got the entire device. +rtfile_exts | awk -v end=$expected_end ' +{ + if ($3 != 0) + printf("%s: unexpected physical offset %s, wanted 0\n", $0, $3); + if ($4 != end) + printf("%s: unexpected physical end %s, wanted %d\n", $0, $4, end); +}' + +# Now punch out a range that is slightly larger than the size of the data +# device. +punch_bytes="$((ddsize + rtextsize))" +punch_rtx="$((punch_bytes / rtextsize))" +$XFS_IO_PROG -c "fpunch 0 $((punch_rtx * rtextsize))" $rtfile + +expected_offset="$((punch_rtx * rtextsize / 512))" + +echo "rtfile should have physical extent from $expected_offset to $expected_end sectors" >> $seqres.full + +# Make sure that the realtime file now has only one extent at the end of the +# realtime device +rtfile_exts | awk -v offset=$expected_offset -v end=$expected_end ' +{ + if ($3 != offset) + printf("%s: unexpected physical offset %s, wanted %d\n", $0, $3, offset); + if ($4 != end) + printf("%s: unexpected physical end %s, wanted %d\n", $0, $4, end); +}' + +$XFS_IO_PROG -c 'bmap -elpv' $rtfile >> $seqres.full +$XFS_IO_PROG -c 'fsmap' $SCRATCH_MNT >> $seqres.full + +fsmap() { + $XFS_IO_PROG -c 'fsmap' $SCRATCH_MNT | \ + grep -v 'EXT:' | \ + tr ':.[]' ' ' | \ + while read junk major minor physoff physend junk; do + echo "$major:$minor $physoff $physend" + done +} + +# Check the fsmap output contains a record for the realtime device at a +# physical offset higher than end of the data device and corresponding to the +# beginning of the non-punched area. +fsmap | awk -v dev="$rtmajor:$rtminor" -v offset=$expected_offset -v end=$expected_end ' +BEGIN { + found_start = 0; + found_end = 0; +} +{ + if ($1 == dev && $2 >= offset) { + found_start = 1; + if ($3 == end) { + found_end = 1; + } + } +} +END { + if (found_start == 0) + printf("No fsmap record for rtdev %s above sector %d\n", dev, offset); + if (found_end == 0) + printf("No fsmap record for rtdev %s ending at sector %d\n", dev, end); +}' + +echo Silence is golden +# success, all done +status=0 +exit diff --git a/tests/xfs/922.out b/tests/xfs/922.out new file mode 100644 index 00000000..3f90539d --- /dev/null +++ b/tests/xfs/922.out @@ -0,0 +1,2 @@ +QA output created by 922 +Silence is golden