2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2021, Oracle. All Rights Reserved.
7 # Regression test for commits:
9 # 9d5e8492eee0 ("xfs: adjust rt allocation minlen when extszhint > rtextsize")
10 # 676a659b60af ("xfs: retry allocations when locality-based search fails")
12 # The first bug occurs when an extent size hint is set on a realtime file.
13 # xfs_bmapi_rtalloc adjusts the offset and length of the allocation request to
14 # try to satisfy the hint, but doesn't adjust minlen to match. If the
15 # allocator finds free space that isn't large enough to map even a single block
16 # of the original request, bmapi_write will return ENOSPC and the write fails
17 # even though there's plenty of space.
19 # The second bug occurs when an extent size hint is set on a file, we ask to
20 # allocate blocks in an empty region immediately adjacent to a previous
21 # allocation, and the nearest available free space isn't anywhere near the
22 # previous allocation, the near allocator will give up and return ENOSPC, even
23 # if there's sufficient free realtime extents to satisfy the allocation
26 # Both bugs can be exploited by the same user call sequence, so here's a
27 # targeted test that runs in less time than the reproducers that are listed in
28 # the fix patches themselves.
31 _begin_fstest auto quick rw realtime
33 # Import common functions.
36 # real QA test starts here
40 _require_xfs_io_command "falloc"
41 _require_xfs_io_command "fpunch"
42 _require_test_program "punch-alternating"
48 filesize=`_get_available_space $SCRATCH_MNT`
49 $XFS_IO_PROG -f -c "truncate $filesize" -c "falloc 0 $filesize" $file
52 chunksizemb=$((filesize / chunks / 1048576))
53 seq 1 $chunks | while read f; do
54 echo "$((f * chunksizemb)) file size $f / 20"
55 $XFS_IO_PROG -fc "falloc -k $(( (f - 1) * chunksizemb))m ${chunksizemb}m" $file
59 chunksizemb=$((filesize / chunks / 1048576))
60 seq 80 $chunks | while read f; do
61 echo "$((f * chunksizemb)) file size $f / $chunks"
62 $XFS_IO_PROG -fc "falloc -k $(( (f - 1) * chunksizemb))m ${chunksizemb}m" $file
65 filesizemb=$((filesize / 1048576))
66 $XFS_IO_PROG -fc "falloc -k 0 ${filesizemb}m" $file
69 avail=`_get_available_space $SCRATCH_MNT`
70 $XFS_IO_PROG -fc "pwrite -S 0x65 0 $avail" ${file}
73 echo "Format and mount"
74 _scratch_mkfs > $seqres.full 2>&1
75 _scratch_mount >> $seqres.full 2>&1
77 # This is a test of the rt allocator; force all files to be created realtime
78 _xfs_force_bdev realtime $SCRATCH_MNT
80 # Set the extent size hint larger than the realtime extent size. This is
81 # necessary to exercise the minlen constraints on the realtime allocator.
82 fsbsize=$($XFS_IO_PROG -c 'statfs' $SCRATCH_MNT | grep geom.bsize | awk '{print $3}')
83 rtextsize_blks=$($XFS_IO_PROG -c 'statfs' $SCRATCH_MNT | grep geom.rtextsize | awk '{print $3}')
84 extsize=$((2 * rtextsize_blks * fsbsize))
86 echo "rtextsize_blks=$rtextsize_blks extsize=$extsize" >> $seqres.full
87 $XFS_IO_PROG -c "extsize $extsize" $SCRATCH_MNT
89 # Compute the geometry of the test files we're going to create. Realtime
90 # volumes are simple, which means that we can control the space allocations
91 # exactly to exploit bugs!
93 # Since this is a test of the near rt allocator, we need to set up the test to
94 # have a victim file with at least one rt extent allocated to it and enough
95 # free space to allocate at least one more rt extent at an adjacent file
96 # offset. The free space must not be immediately adjacent to the the first
97 # extent that we allocate to the victim file, and it must not be large enough
98 # to satisfy the entire allocation request all at once.
100 # Our free space fragmentation strategy is the usual fallocate-and-punch swiss
101 # cheese file, which means the free space is split into five sections:
103 # The first will be remapped into the victim file.
105 # The second section exists to prevent the free extents from being adjacent to
106 # the first section. It will be very large, since we allocate all the rt
109 # The last three sections will have every other rt extent punched out to create
111 remap_sz=$((extsize * 2))
112 required_sz=$((5 * remap_sz))
113 free_rtspace=$(_get_available_space $SCRATCH_MNT)
114 if [ $free_rtspace -lt $required_sz ]; then
115 _notrun "Insufficient free space on rt volume. Needed $required_sz, saw $free_rtspace."
118 # Allocate all the space on the rt volume so that we can control space
119 # allocations exactly.
120 fill_rtdev $SCRATCH_MNT/bigfile &>> $seqres.full
122 # We need at least 4 remap sections to proceed
123 bigfile_sz=$(stat -c '%s' $SCRATCH_MNT/bigfile)
124 if [ $bigfile_sz -lt $required_sz ]; then
125 _notrun "Free space control file needed $required_sz bytes, got $bigfile_sz."
128 # Remap the first remap section to a victim file.
129 $XFS_IO_PROG -c "fpunch 0 $remap_sz" $SCRATCH_MNT/bigfile
130 $XFS_IO_PROG -f -c "truncate $required_sz" -c "falloc 0 $remap_sz" $SCRATCH_MNT/victim
132 # Punch out every other extent of the last two sections, to fragment free space.
133 frag_sz=$((remap_sz * 3))
134 punch_off=$((bigfile_sz - frag_sz))
135 $here/src/punch-alternating $SCRATCH_MNT/bigfile -o $((punch_off / fsbsize)) -i $((rtextsize_blks * 2)) -s $rtextsize_blks
137 # Make sure we have some free rtextents.
138 free_rtx=$($XFS_IO_PROG -c 'statfs' $SCRATCH_MNT | grep counts.freertx | awk '{print $3}')
139 if [ $free_rtx -eq 0 ]; then
140 echo "Expected fragmented free rt space, found none."
143 # Try to double the amount of blocks in the victim file. On a buggy kernel,
144 # the rt allocator will fail immediately with ENOSPC even though we left enough
145 # free space for the write will complete fully.
146 echo "Try to write a bunch of stuff to the fragmented rt space"
147 $XFS_IO_PROG -c "pwrite -S 0x63 -b $remap_sz $remap_sz $remap_sz" -c stat $SCRATCH_MNT/victim >> $seqres.full
149 # The victim file should own at least two sections' worth of blocks.
150 victim_sectors=$(stat -c '%b' $SCRATCH_MNT/victim)
151 victim_space_usage=$((victim_sectors * 512))
152 expected_usage=$((remap_sz * 2))
154 if [ $victim_space_usage -lt $expected_usage ]; then
155 echo "Victim file should be using at least $expected_usage bytes, saw $victim_space_usage."