3c43fe746917183843eca55562ebf83cb3d797ec
[xfstests-dev.git] / tests / generic / 619
1 #! /bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2020 IBM Corporation. All Rights Reserved.
4 #
5 # FS QA Test 619
6 #
7 # ENOSPC regression test in a multi-threaded scenario. Test allocation
8 # strategies of the file system and validate space anomalies as reported by
9 # the system versus the allocated by the program.
10 #
11 # The test is motivated by a bug in ext4 systems where-in ENOSPC is
12 # reported by the file system even though enough space for allocations is
13 # available[1].
14 # [1]: https://patchwork.ozlabs.org/patch/1294003
15 #
16 # Linux kernel patch series that fixes the above regression:
17 # 53f86b170dfa ("ext4: mballoc: add blocks to PA list under same spinlock
18 #               after allocating blocks")
19 # cf5e2ca6c990 ("ext4: mballoc: refactor ext4_mb_discard_preallocations()")
20 # 07b5b8e1ac40 ("ext4: mballoc: introduce pcpu seqcnt for freeing PA to
21 #               improve ENOSPC handling")
22 # 8ef123fe02ca ("ext4: mballoc: refactor ext4_mb_good_group()")
23 # 993778306e79 ("ext4: mballoc: use lock for checking free blocks while
24 #               retrying")
25 #
26 seq=`basename $0`
27 seqres=$RESULT_DIR/$seq
28 echo "QA output created by $seq"
29
30 here=`pwd`
31 tmp=/tmp/$$
32 status=1        # failure is the default!
33 trap "_cleanup; exit \$status" 0 1 2 3 15
34
35 FS_SIZE=$((240*1024*1024)) # 240MB
36 DEBUG=1 # set to 0 to disable debug statements in shell and c-prog
37 FACT=0.7
38
39 # Disk allocation methods
40 FALLOCATE=1
41 FTRUNCATE=2
42
43 # Helps to build TEST_VECTORS
44 SMALL_FILE_SIZE=$((512 * 1024)) # in Bytes
45 BIG_FILE_SIZE=$((1536 * 1024))  # in Bytes
46 MIX_FILE_SIZE=$((2048 * 1024))  # (BIG + SMALL small file size)
47
48 _cleanup()
49 {
50         cd /
51         rm -f $tmp.*
52 }
53
54 # get standard environment, filters and checks
55 . ./common/rc
56 . ./common/filter
57
58 # remove previous $seqres.full before test
59 rm -f $seqres.full
60
61 # Modify as appropriate.
62 _supported_fs generic
63 _require_scratch
64 _require_test_program "t_enospc"
65 _require_xfs_io_command "falloc"
66
67 debug()
68 {
69         if [ $DEBUG -eq 1 ]; then
70                 echo "$1" >> $seqres.full
71         fi
72 }
73
74 # Calculate the number of threads needed to fill the disk space
75 # Arguments
76 # $1: the size of a file
77 # $2: ratio in which $1 file should be split into multiple files.
78 # $3: percentage of the disk space should be used during the test
79 # Calculate the number of threads needed to fill the disk space
80 calc_thread_cnt()
81 {
82         local file_ratio_unit=$1
83         local file_ratio=$2
84         local disk_saturation=$3
85         local tot_avail_size
86         local avail_size
87         local thread_cnt
88
89         IFS=',' read -ra fratio <<< $file_ratio
90         file_ratio_cnt=${#fratio[@]}
91
92         tot_avail_size=$($DF_PROG --block-size=1 $SCRATCH_DEV | awk 'FNR == 2 { print $5 }')
93         avail_size=$(echo $tot_avail_size*$disk_saturation | $BC_PROG)
94         thread_cnt=$(echo "$file_ratio_cnt*($avail_size/$file_ratio_unit)" | $BC_PROG)
95
96         debug "Total available size: $tot_avail_size"
97         debug "Available size: $avail_size"
98         debug "Thread count: $thread_cnt"
99
100         echo ${thread_cnt}
101 }
102
103 # Arguments
104 # $1: a string containing test configuration separated by a colon.
105 #     $1 is treated as an array of arguments to the function.
106 #     Description of each array element is given below.
107 #
108 # @1: name of the test
109 # @2: thread in t_enospec exerciser will allocate file of @2 size
110 # @3: defines the proportion in which the file size defined in @2
111 #     should be divided into two files.
112 #     (valid @3: more than two values are not allowed)
113 #                values should be comma separated)
114 #                sum of all values must be 1)
115 # @4: define the percentage of available memory should be used to
116 #     during the test.
117 # @5: defines the disk allocation method (fallocate/ftruncate)
118 # @6: number of the test should run
119 run_testcase()
120 {
121         IFS=':' read -ra args <<< $1
122         local test_name=${args[0]}
123         local file_ratio_unit=${args[1]}
124         local file_ratio=${args[2]}
125         local disk_saturation=${args[3]}
126         local disk_alloc_method=${args[4]}
127         local test_iteration_cnt=${args[5]}
128         local extra_args=""
129         local thread_cnt
130
131         if [ "$disk_alloc_method" == "$FALLOCATE" ]; then
132                 extra_args="$extra_args -f"
133         fi
134
135         # enable the debug statements in c program
136         if [ "$DEBUG" -eq 1 ]; then
137                 extra_args="$extra_args -v"
138         fi
139
140         debug "============ Test details start ============"
141         debug "Test name: $test_name"
142         debug "File ratio unit: $file_ratio_unit"
143         debug "File ratio: $file_ratio"
144         debug "Disk saturation $disk_saturation"
145         debug "Disk alloc method $disk_alloc_method"
146         debug "Test iteration count: $test_iteration_cnt"
147         debug "Extra arg: $extra_args"
148
149         for i in $(eval echo "{1..$test_iteration_cnt}"); do
150                 # Setup the device
151                 _scratch_mkfs_sized $FS_SIZE >> $seqres.full 2>&1
152                 _scratch_mount
153
154                 debug "===== Test: $test_name iteration: $i starts ====="
155                 thread_cnt=$(calc_thread_cnt $file_ratio_unit $file_ratio $disk_saturation)
156
157                 # Start the test
158                 $here/src/t_enospc -t $thread_cnt -s $file_ratio_unit -r $file_ratio -p $SCRATCH_MNT $extra_args >> $seqres.full
159
160                 status=$(echo $?)
161                 if [ $status -ne 0 ]; then
162                         use_per=$($DF_PROG -h | grep $SCRATCH_MNT | awk '{print substr($6, 1, length($6)-1)}' | $BC_PROG)
163                         alloc_per=$(echo "$FACT * 100" | $BC_PROG)
164                         # We are here since t_enospc failed with an error code.
165                         # If the used filesystem space is still < available space - that means
166                         # the test failed due to FS wrongly reported ENOSPC.
167                         if [ $(echo "$use_per < $alloc_per" | $BC_PROG) -ne 0 ]; then
168                                 if [ $status -eq 134 ]; then
169                                         # SIGABRT asserted exit code = 134
170                                         echo "FAIL: Aborted assertion faliure"
171                                 elif [ $status -eq 7 ]; then
172                                         # SIGBUS asserted exit code = 7
173                                         echo "FAIL: ENOSPC BUS faliure"
174                                 fi
175                                 echo "$test_name failed at iteration count: $i"
176                                 echo "$($DF_PROG -h $SCRATCH_MNT)"
177                                 echo "Allocated: $alloc_per% Used: $use_per%"
178                                 exit
179                         fi
180                 fi
181
182                 # Make space for other tests
183                 _scratch_unmount
184
185                 debug "===== Test: $test_name iteration: $i ends ====="
186         done
187         debug "============ Test details end ============="
188 }
189
190 declare -a TEST_VECTORS=(
191 # test-name:file-ratio-unit:file-ratio:disk-saturation:disk-alloc-method:test-iteration-cnt
192 "Small-file-fallocate-test:$SMALL_FILE_SIZE:1:$FACT:$FALLOCATE:3"
193 "Big-file-fallocate-test:$BIG_FILE_SIZE:1:$FACT:$FALLOCATE:3"
194 "Mix-file-fallocate-test:$MIX_FILE_SIZE:0.75,0.25:$FACT:$FALLOCATE:3"
195 "Small-file-ftruncate-test:$SMALL_FILE_SIZE:1:$FACT:$FTRUNCATE:3"
196 "Big-file-ftruncate-test:$BIG_FILE_SIZE:1:$FACT:$FTRUNCATE:3"
197 "Mix-file-ftruncate-test:$MIX_FILE_SIZE:0.75,0.25:$FACT:$FTRUNCATE:3"
198 )
199
200 # real QA test starts here
201 for i in "${TEST_VECTORS[@]}"; do
202         run_testcase $i
203 done
204
205 echo "Silence is golden"
206 status=0
207 exit