generic: test MADV_POPULATE_READ with IO errors
[xfstests-dev.git] / tests / xfs / 187
1 #! /bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2021, Oracle.  All Rights Reserved.
4 #
5 # FS QA Test No. 187
6 #
7 # Regression test for commits:
8 #
9 # 9d5e8492eee0 ("xfs: adjust rt allocation minlen when extszhint > rtextsize")
10 # 676a659b60af ("xfs: retry allocations when locality-based search fails")
11 #
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.
18 #
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
24 # request.
25 #
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.
29 #
30 . ./common/preamble
31 _begin_fstest auto quick rw realtime
32
33 # Import common functions.
34 . ./common/filter
35
36 # real QA test starts here
37 _supported_fs xfs
38 _require_scratch
39 _require_realtime
40 _require_xfs_io_command "falloc"
41 _require_xfs_io_command "fpunch"
42 _require_test_program "punch-alternating"
43
44 fill_rtdev()
45 {
46         file=$1
47
48         filesize=`_get_available_space $SCRATCH_MNT`
49         $XFS_IO_PROG -f -c "truncate $filesize" -c "falloc 0 $filesize" $file
50
51         chunks=20
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
56         done
57
58         chunks=100
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
63         done
64
65         filesizemb=$((filesize / 1048576))
66         $XFS_IO_PROG -fc "falloc -k 0 ${filesizemb}m" $file
67
68         # Try again anyway
69         avail=`_get_available_space $SCRATCH_MNT`
70         $XFS_IO_PROG -fc "pwrite -S 0x65 0 $avail" ${file}
71 }
72
73 echo "Format and mount"
74 _scratch_mkfs > $seqres.full 2>&1
75 _scratch_mount >> $seqres.full 2>&1
76
77 # This is a test of the rt allocator; force all files to be created realtime
78 _xfs_force_bdev realtime $SCRATCH_MNT
79
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))
85
86 echo "rtextsize_blks=$rtextsize_blks extsize=$extsize" >> $seqres.full
87 $XFS_IO_PROG -c "extsize $extsize" $SCRATCH_MNT
88
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!
92 #
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.
99 #
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:
102 #
103 # The first will be remapped into the victim file.
104 #
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
107 # space.
108 #
109 # The last three sections will have every other rt extent punched out to create
110 # some free space.
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."
116 fi
117
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
121
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."
126 fi
127
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
131
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
136
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."
141 fi
142
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
148
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))
153
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."
156 fi
157
158 status=0
159 exit