[PATCH 1/1] swapext: make sure that we don't swap unwritten extents unless they're part of a rt extent(??)

Darrick J. Wong
2024-01-01T00:44:57Z

tests/xfs/1213 | 73 ++++++++++++++++
tests/xfs/1213.out | 2
tests/xfs/1214 | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++
tests/xfs/1214.out | 2
4 files changed, 309 insertions(+)
create mode 100755 tests/xfs/1213
create mode 100644 tests/xfs/1213.out
create mode 100755 tests/xfs/1214
create mode 100644 tests/xfs/1214.out

diff --git a/tests/xfs/1213 b/tests/xfs/1213
new file mode 100755
index 0000000000..40bf3838af
--- /dev/null
+++ b/tests/xfs/1213
@@ -0,0 +1,73 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 1213
+#
+# Make sure that the XFS_EXCH_RANGE_FILE1_WRITTEN actually skips holes and
+# unwritten extents on the data device and the rt device when the rextsize
+# is 1 fsblock.
+#
+. ./common/preamble
+_begin_fstest auto fiexchange swapext
+
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs generic
+_require_xfs_io_command "falloc"
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+# This test doesn't deal with the unwritten extents that must be created when
+# the realtime file allocation unit is larger than the fs blocksize.
+file_blksz=$(_get_file_block_size $SCRATCH_MNT)
+fs_blksz=$(_get_block_size $SCRATCH_MNT)
+test "$file_blksz" -eq "$fs_blksz" || \
+ _notrun "test requires file alloc unit ($file_blksz) == fs block size ($fs_blksz)"
+
+swap_and_check_contents() {
+ local a="$1"
+ local b="$2"
+ local tag="$3"
+
+ local a_md5_before=$(md5sum $a | awk '{print $1}')
+ local b_md5_before=$(md5sum $b | awk '{print $1}')
+
+ # Test swapext. -h means skip holes in /b, and -e means operate to EOF
+ echo "swap $tag" >> $seqres.full
+ $XFS_IO_PROG -c fsync -c 'bmap -elpvvvv' $a $b >> $seqres.full
+ $XFS_IO_PROG -c "swapext -v exchrange -f -u -h -e -a $b" $a >> $seqres.full
+ $XFS_IO_PROG -c 'bmap -elpvvvv' $a $b >> $seqres.full
+ _scratch_cycle_mount
+
+ local a_md5_after=$(md5sum $a | awk '{print $1}')
+ local b_md5_after=$(md5sum $b | awk '{print $1}')
+
+ test "$a_md5_before" != "$a_md5_after" && \
+ echo "$a: md5 $a_md5_before -> $a_md5_after in $tag"
+
+ test "$b_md5_before" != "$b_md5_after" && \
+ echo "$b: md5 $b_md5_before -> $b_md5_after in $tag"
+}
+
+# plain preallocations on the data device
+$XFS_IO_PROG -c 'extsize 0' $SCRATCH_MNT
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/dar >> $seqres.full
+$XFS_IO_PROG -f -c 'truncate 1m' -c "falloc 640k 64k" $SCRATCH_MNT/dbr
+swap_and_check_contents $SCRATCH_MNT/dar $SCRATCH_MNT/dbr "plain prealloc"
+
+# extent size hints on the data device
+$XFS_IO_PROG -c 'extsize 1m' $SCRATCH_MNT
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/dae >> $seqres.full
+$XFS_IO_PROG -f -c 'truncate 1m' -c "falloc 640k 64k" $SCRATCH_MNT/dbe
+swap_and_check_contents $SCRATCH_MNT/dae $SCRATCH_MNT/dbe "data dev extsize prealloc"
+
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/1213.out b/tests/xfs/1213.out
new file mode 100644
index 0000000000..5a28b8b45f
--- /dev/null
+++ b/tests/xfs/1213.out
@@ -0,0 +1,2 @@
+QA output created by 1213
+Silence is golden
diff --git a/tests/xfs/1214 b/tests/xfs/1214
new file mode 100755
index 0000000000..5b78b5e348
--- /dev/null
+++ b/tests/xfs/1214
@@ -0,0 +1,232 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 1214
+#
+# Make sure that the XFS_EXCH_RANGE_FILE1_WRITTEN actually skips holes and
+# unwritten extents on the realtime device when the rextsize is larger than 1
+# fs block.
+#
+. ./common/preamble
+_begin_fstest auto fiexchange swapext
+
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs generic
+_require_xfs_io_command "falloc"
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_realtime
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+# This test only deals with the unwritten extents that must be created when
+# the realtime file allocation unit is larger than the fs blocksize.
+file_blksz=$(_get_file_block_size $SCRATCH_MNT)
+fs_blksz=$(_get_block_size $SCRATCH_MNT)
+test "$file_blksz" -ge "$((3 * fs_blksz))" || \
+ _notrun "test requires file alloc unit ($file_blksz) >= 3 * fs block size ($fs_blksz)"
+
+swap_and_check_contents() {
+ local a="$1"
+ local b="$2"
+ local tag="$3"
+
+ sync
+
+ # Test swapext. -h means skip holes in /b, and -e means operate to EOF
+ echo "swap $tag" >> $seqres.full
+ $XFS_IO_PROG -c 'bmap -elpvvvv' $a $b >> $seqres.full
+ $XFS_IO_PROG -c "swapext -v exchrange -f -u -h -e -a $b" $a >> $seqres.full
+ $XFS_IO_PROG -c 'bmap -elpvvvv' $a $b >> $seqres.full
+
+ local a_md5_before=$(md5sum $a | awk '{print $1}')
+ local b_md5_before=$(md5sum $b | awk '{print $1}')
+
+ _scratch_cycle_mount
+
+ local a_md5_check=$(md5sum $a.chk | awk '{print $1}')
+ local b_md5_check=$(md5sum $b.chk | awk '{print $1}')
+
+ local a_md5_after=$(md5sum $a | awk '{print $1}')
+ local b_md5_after=$(md5sum $b | awk '{print $1}')
+
+ test "$a_md5_before" != "$a_md5_after" && \
+ echo "$a: md5 $a_md5_before -> $a_md5_after in $tag"
+
+ test "$b_md5_before" != "$b_md5_after" && \
+ echo "$b: md5 $b_md5_before -> $b_md5_after in $tag"
+
+ if [ "$a_md5_check" != "$a_md5_after" ]; then
+ echo "$a: md5 $a_md5_after, expected $a_md5_check in $tag" | tee -a $seqres.full
+ echo "$a contents" >> $seqres.full
+ od -tx1 -Ad -c $a >> $seqres.full
+ echo "$a.chk contents" >> $seqres.full
+ od -tx1 -Ad -c $a.chk >> $seqres.full
+ fi
+
+ if [ "$b_md5_check" != "$b_md5_after" ]; then
+ echo "$b: md5 $b_md5_after, expected $b_md5_check in $tag" | tee -a $seqres.full
+ echo "$b contents" >> $seqres.full
+ od -tx1 -Ad -c $b >> $seqres.full
+ echo "$b.chk contents" >> $seqres.full
+ od -tx1 -Ad -c $b.chk >> $seqres.full
+ fi
+}
+
+filesz=$((5 * file_blksz))
+
+# first rtblock of the second rtextent is unwritten
+rm -f $SCRATCH_MNT/da $SCRATCH_MNT/db $SCRATCH_MNT/*.chk
+_pwrite_byte 0x58 0 $filesz $SCRATCH_MNT/da >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x59 $((file_blksz + fs_blksz)) $((file_blksz - fs_blksz))" \
+ $SCRATCH_MNT/db >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 0 $file_blksz" \
+ -c "pwrite -S 0x00 $file_blksz $fs_blksz" \
+ -c "pwrite -S 0x59 $((file_blksz + fs_blksz)) $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x58 $((file_blksz * 2)) $((filesz - (file_blksz * 2) ))" \
+ $SCRATCH_MNT/da.chk >> /dev/null
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 $file_blksz $file_blksz" \
+ $SCRATCH_MNT/db.chk >> /dev/null
+swap_and_check_contents $SCRATCH_MNT/da $SCRATCH_MNT/db \
+ "first rtb of second rtx"
+
+# second rtblock of the second rtextent is unwritten
+rm -f $SCRATCH_MNT/da $SCRATCH_MNT/db $SCRATCH_MNT/*.chk
+_pwrite_byte 0x58 0 $filesz $SCRATCH_MNT/da >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x59 $file_blksz $fs_blksz" \
+ -c "pwrite -S 0x59 $((file_blksz + (2 * fs_blksz) )) $((file_blksz - (2 * fs_blksz) ))" \
+ $SCRATCH_MNT/db >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 0 $file_blksz" \
+ -c "pwrite -S 0x59 $file_blksz $fs_blksz" \
+ -c "pwrite -S 0x00 $((file_blksz + fs_blksz)) $fs_blksz" \
+ -c "pwrite -S 0x59 $((file_blksz + (2 * fs_blksz) )) $((file_blksz - (2 * fs_blksz) ))" \
+ -c "pwrite -S 0x58 $((file_blksz * 2)) $((filesz - (file_blksz * 2) ))" \
+ $SCRATCH_MNT/da.chk >> /dev/null
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 $file_blksz $file_blksz" \
+ $SCRATCH_MNT/db.chk >> /dev/null
+swap_and_check_contents $SCRATCH_MNT/da $SCRATCH_MNT/db \
+ "second rtb of second rtx"
+
+# last rtblock of the second rtextent is unwritten
+rm -f $SCRATCH_MNT/da $SCRATCH_MNT/db $SCRATCH_MNT/*.chk
+_pwrite_byte 0x58 0 $filesz $SCRATCH_MNT/da >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x59 $file_blksz $((file_blksz - fs_blksz))" \
+ $SCRATCH_MNT/db >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 0 $file_blksz" \
+ -c "pwrite -S 0x59 $file_blksz $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x00 $(( (2 * file_blksz) - fs_blksz)) $fs_blksz" \
+ -c "pwrite -S 0x58 $((file_blksz * 2)) $((filesz - (file_blksz * 2) ))" \
+ $SCRATCH_MNT/da.chk >> /dev/null
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 $file_blksz $file_blksz" \
+ $SCRATCH_MNT/db.chk >> /dev/null
+swap_and_check_contents $SCRATCH_MNT/da $SCRATCH_MNT/db \
+ "last rtb of second rtx"
+
+# last rtb of the 2nd rtx and first rtb of the 3rd rtx is unwritten
+rm -f $SCRATCH_MNT/da $SCRATCH_MNT/db $SCRATCH_MNT/*.chk
+_pwrite_byte 0x58 0 $filesz $SCRATCH_MNT/da >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "falloc $file_blksz $((2 * file_blksz))" \
+ -c "pwrite -S 0x59 $file_blksz $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x59 $(( (2 * file_blksz) + fs_blksz)) $((file_blksz - fs_blksz))" \
+ $SCRATCH_MNT/db >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 0 $file_blksz" \
+ -c "pwrite -S 0x59 $file_blksz $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x00 $(( (2 * file_blksz) - fs_blksz)) $((2 * fs_blksz))" \
+ -c "pwrite -S 0x59 $(( (2 * file_blksz) + fs_blksz)) $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x58 $((file_blksz * 3)) $((filesz - (file_blksz * 3) ))" \
+ $SCRATCH_MNT/da.chk >> /dev/null
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 $file_blksz $((2 * file_blksz))" \
+ $SCRATCH_MNT/db.chk >> /dev/null
+swap_and_check_contents $SCRATCH_MNT/da $SCRATCH_MNT/db \
+ "last rtb of 2nd rtx and first rtb of 3rd rtx"
+
+# last rtb of the 2nd rtx and first rtb of the 4th rtx is unwritten; 3rd rtx
+# is a hole
+rm -f $SCRATCH_MNT/da $SCRATCH_MNT/db $SCRATCH_MNT/*.chk
+_pwrite_byte 0x58 0 $filesz $SCRATCH_MNT/da >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x59 $file_blksz $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x59 $(( (3 * file_blksz) + fs_blksz)) $((file_blksz - fs_blksz))" \
+ -c "fpunch $((2 * file_blksz)) $file_blksz" \
+ $SCRATCH_MNT/db >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 0 $file_blksz" \
+ -c "pwrite -S 0x59 $file_blksz $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x00 $(( (2 * file_blksz) - fs_blksz)) $fs_blksz" \
+ -c "pwrite -S 0x58 $((file_blksz * 2)) $file_blksz" \
+ -c "pwrite -S 0x00 $((3 * file_blksz)) $fs_blksz" \
+ -c "pwrite -S 0x59 $(( (3 * file_blksz) + fs_blksz)) $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x58 $((file_blksz * 4)) $((filesz - (file_blksz * 4) ))" \
+ $SCRATCH_MNT/da.chk >> /dev/null
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 $file_blksz $file_blksz" \
+ -c "pwrite -S 0x58 $((file_blksz * 3)) $file_blksz" \
+ $SCRATCH_MNT/db.chk >> /dev/null
+swap_and_check_contents $SCRATCH_MNT/da $SCRATCH_MNT/db \
+ "last rtb of 2nd rtx and first rtb of 4th rtx; 3rd rtx is hole"
+
+# last rtb of the 2nd rtx and first rtb of the 4th rtx is unwritten; 3rd rtx
+# is preallocated
+rm -f $SCRATCH_MNT/da $SCRATCH_MNT/db $SCRATCH_MNT/*.chk
+_pwrite_byte 0x58 0 $filesz $SCRATCH_MNT/da >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "falloc $file_blksz $((file_blksz * 3))" \
+ -c "pwrite -S 0x59 $file_blksz $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x59 $(( (3 * file_blksz) + fs_blksz)) $((file_blksz - fs_blksz))" \
+ $SCRATCH_MNT/db >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 0 $file_blksz" \
+ -c "pwrite -S 0x59 $file_blksz $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x00 $(( (2 * file_blksz) - fs_blksz)) $fs_blksz" \
+ -c "pwrite -S 0x58 $((file_blksz * 2)) $file_blksz" \
+ -c "pwrite -S 0x00 $((3 * file_blksz)) $fs_blksz" \
+ -c "pwrite -S 0x59 $(( (3 * file_blksz) + fs_blksz)) $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x58 $((file_blksz * 4)) $((filesz - (file_blksz * 4) ))" \
+ $SCRATCH_MNT/da.chk >> /dev/null
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 $file_blksz $file_blksz" \
+ -c "pwrite -S 0x58 $((file_blksz * 3)) $file_blksz" \
+ $SCRATCH_MNT/db.chk >> /dev/null
+swap_and_check_contents $SCRATCH_MNT/da $SCRATCH_MNT/db \
+ "last rtb of 2nd rtx and first rtb of 4th rtx; 3rd rtx is prealloc"
+
+# 2nd rtx is preallocated and first rtb of 3rd rtx is unwritten
+rm -f $SCRATCH_MNT/da $SCRATCH_MNT/db $SCRATCH_MNT/*.chk
+_pwrite_byte 0x58 0 $filesz $SCRATCH_MNT/da >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "falloc $file_blksz $((file_blksz * 2))" \
+ -c "pwrite -S 0x59 $(( (2 * file_blksz) + fs_blksz)) $((file_blksz - fs_blksz))" \
+ $SCRATCH_MNT/db >> $seqres.full
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 0 $((2 * file_blksz))" \
+ -c "pwrite -S 0x00 $((2 * file_blksz)) $fs_blksz" \
+ -c "pwrite -S 0x59 $(( (2 * file_blksz) + fs_blksz)) $((file_blksz - fs_blksz))" \
+ -c "pwrite -S 0x58 $((file_blksz * 3)) $((filesz - (file_blksz * 3) ))" \
+ $SCRATCH_MNT/da.chk >> /dev/null
+$XFS_IO_PROG -f -c "truncate $filesz" \
+ -c "pwrite -S 0x58 $((2 * file_blksz)) $file_blksz" \
+ $SCRATCH_MNT/db.chk >> /dev/null
+swap_and_check_contents $SCRATCH_MNT/da $SCRATCH_MNT/db \
+ "2nd rtx is prealloc and first rtb of 3rd rtx is unwritten"
+
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/1214.out b/tests/xfs/1214.out
new file mode 100644
index 0000000000..a529e42333
--- /dev/null
+++ b/tests/xfs/1214.out
@@ -0,0 +1,2 @@
+QA output created by 1214
+Silence is golden