xfs: force file creation to the data device for certain layout tests
[xfstests-dev.git] / tests / xfs / 016
1 #! /bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
4 #
5 # FS QA Test No. 016
6 #
7 # test end of log overwrite bug #796141
8 #
9 #
10 # pv 796141
11 #
12 # create a new FS, mostly fill the log. Then wrap the log back to the
13 # start bit by bit to force wiping of stale blocks near the end of the
14 # log. Check the block after the log ends to check for corruption
15 #
16 # assumptions :
17 #    - given we're only touching a single inode, the block after the
18 #      log which is in the middle ag should never be touched.
19 #      if it changes, we assume the log is writing over it
20 #
21
22 seq=`basename $0`
23 seqres=$RESULT_DIR/$seq
24 echo "QA output created by $seq"
25
26 here=`pwd`
27 tmp=/tmp/$$
28 status=1
29
30 trap "_cleanup; exit \$status" 0 1 2 3 15
31
32 _cleanup()
33 {
34     cd /
35     rm -f $tmp.*
36     echo "*** unmount"
37     _scratch_unmount 2>/dev/null
38 }
39
40 _block_filter()
41 {
42     sed -e 's/[0-9][0-9]*\.\.[0-9][0-9]*/BLOCKRANGE/g'
43 }
44
45 _init()
46 {
47     echo "*** determine log size"
48     local sz_mb=50
49     local dsize="-d size=${sz_mb}m"
50     local lsize="-l size=$(_scratch_find_xfs_min_logblocks $dsize)b"
51     local force_opts="$dsize $lsize"
52     _scratch_mkfs_xfs $force_opts >> $seqres.full 2>&1
53
54     # set log_size and log_size_bb globally
55     log_size_bb=`_log_size`
56     log_size=$((log_size_bb * 512))
57     echo "log_size_bb = $log_size_bb log_size = $log_size" >> $seqres.full
58
59     echo "*** reset partition"
60     $here/src/devzero -b 2048 -n $sz_mb -v 198 $SCRATCH_DEV # write 0xc6
61     echo "*** mkfs"
62     #
63     # Do not discard blocks as we check for patterns in free space.
64     # 
65     # First, make sure that mkfs supports '-K' option by using its
66     # dry run (-N option) and then add it to the force_opts.
67     #
68     if _scratch_mkfs_xfs -N -K $force_opts >/dev/null 2>&1; then
69         force_opts="-K $force_opts"
70     fi
71     echo mkfs_xfs $force_opts $SCRATCH_DEV >>$seqres.full
72     _scratch_mkfs_xfs $force_opts >$tmp.mkfs0 2>&1
73     [ $? -ne 0 ] && \
74         _notrun "Cannot mkfs for this test using MKFS_OPTIONS specified"
75     _filter_mkfs <$tmp.mkfs0 >/dev/null 2>$tmp.mkfs
76     . $tmp.mkfs
77     [ $logsunit -ne 0 ] && \
78         _notrun "Cannot run this test using log MKFS_OPTIONS specified"
79
80     # quotas generate extra log traffic so force it off
81     _qmount_option noquota
82 }
83
84 _log_traffic()
85 {
86     count=$1
87     echo "*** generate log traffic"
88
89     out=$SCRATCH_MNT/$$.tmp
90
91     echo "   *** mount"
92     if ! _try_scratch_mount
93     then
94         echo "failed to mount $SCRATCH_DEV"
95         exit 1
96     fi
97
98     # having any quota enabled (acct/enfd) means extra log traffic - evil!
99     $here/src/feature -U $SCRATCH_DEV && \
100                 _notrun "UQuota are enabled, test needs controlled log traffic"
101     $here/src/feature -G $SCRATCH_DEV && \
102                 _notrun "GQuota are enabled, test needs controlled log traffic"
103     $here/src/feature -P $SCRATCH_DEV && \
104                 _notrun "PQuota are enabled, test needs controlled log traffic"
105
106     echo "   *** fiddle"
107     while [ $count -ge 0 ]
108     do
109         touch $out
110         sync
111         rm $out
112         sync
113         let "count = count - 1"
114     done
115
116     echo "   *** unmount"
117     if ! _scratch_unmount
118     then
119         echo "failed to unmount $SCRATCH_DEV"
120         exit 1
121     fi
122 }
123
124 _log_size()
125 {
126     _scratch_xfs_logprint -tb | $AWK_PROG '
127         /log file: / || /log device: / { print $7}
128     '
129 }
130
131 _log_head()
132 {
133     _scratch_xfs_logprint -tb | $AWK_PROG '
134         /head:/ { print $5 }
135     '
136 }
137
138 # Get log stripe unit for v2 logs; if none specified,
139 # (or v1 log) just return "1" block
140
141 _log_sunit()
142 {
143     if [ ${lsunit:-0} -eq 0 ]; then
144         echo $dbsize
145     else
146         expr $lsunit \* $dbsize
147     fi
148 }
149
150 _after_log()
151 {
152     _scratch_xfs_db -r -c "sb" -c "print" | $AWK_PROG '
153         /logstart/  { logstart = $3 }
154         /logblocks/ { logblocks = $3 }
155         END {
156             print logstart + logblocks
157         }
158     '
159 }
160
161 _check_corrupt()
162 {
163     f="c6c6c6c6"
164     echo "*** check for corruption"
165     echo "expect $f..." >>$seqres.full
166     _scratch_xfs_db -r -c "fsblock $2" -c "print" | head | tee -a $seqres.full | \
167         grep -q -v "$f $f $f $f $f $f $f $f" && \
168             _fail "!!! block $2 corrupted!"
169 }
170
171 # get standard environment, filters and checks
172 . ./common/rc
173 . ./common/filter
174 . ./common/quota
175
176 # real QA test starts here
177 _supported_fs xfs
178
179 rm -f $seqres.full
180
181 _require_scratch
182 _init
183
184 block=`_after_log $SCRATCH_DEV`
185 echo "fsblock after log = $block"               >>$seqres.full
186 _check_corrupt $SCRATCH_DEV $block
187
188 actual_log_size=`_log_size`
189 echo "log size = $actual_log_size BB"                      >>$seqres.full
190 head=`_log_head`
191 echo "log position = $head"                     >>$seqres.full
192 lsunit=`_log_sunit`
193 echo "log sunit = $lsunit"                      >>$seqres.full
194
195 # sanity checks
196 [ $actual_log_size -eq $log_size_bb ] || \
197     _fail "!!! unexpected log size $size"
198 [ $head -eq 2 -o $head -eq $((lsunit/512)) ] || \
199     _fail "!!! unexpected initial log position $head vs. $((lsunit/512))"
200
201 # Step 1: Run 200 ops to estimate how how many log blocks are used for each op.
202 # Ignore the fact that it will also include an unmount record; this should be
203 # small overall.
204 echo "    lots of traffic for sampling" >>$seqres.full
205 sample_size_ops=200
206 _log_traffic $sample_size_ops
207 head1=`_log_head`
208 num_blocks=`expr $head1 - $head`
209 blocks_per_op=`echo "scale=3; $num_blocks / $sample_size_ops" | bc`
210 echo "log position = $head1; old log position: $head" >> $seqres.full
211 echo "blocks_per_op = $blocks_per_op" >>$seqres.full
212
213 # Step 2: Quickly advance the log from wherever step 1 left us to the point
214 # where the log is now 80% full on its first cycle.
215
216 # Estimate the number of ops needed to get the log head close to but not past
217 # near_end_min for a single mount.  We'd rather fall short and have to step our
218 # way closer to the end than run past the end, so our target for this second
219 # step is to fill 80% of the first cycle of the log.
220 num_expected_ops=$(( 8 * $(echo "$log_size_bb / $blocks_per_op" | bc) / 10))
221 echo "num_expected_ops = $num_expected_ops" >>$seqres.full
222
223 # Compute the number of ops needed to get from wherever we are right now in
224 # the log cycle to the 80% point.
225 num_expected_to_go=`echo "$num_expected_ops - $sample_size_ops" | bc`
226 echo "num_expected_to_go = $num_expected_to_go" >>$seqres.full
227
228 echo "    lots more traffic" >>$seqres.full
229 _log_traffic $num_expected_to_go
230 head=`_log_head`
231 echo "log position = $head"                     >>$seqres.full
232
233 # Step 3: Gradually advance log traffic to get us from wherever step 2 left us
234 # to the point where the log is within approximately 20 ops of wrapping into
235 # the second cycle.
236
237 # Since this is a log wrapping test, it's critical to push the log head to the
238 # point where it will wrap around within twenty rounds of ops.  Compute the
239 # expected value of the log head when we get to this point.  This "/ 1" piece
240 # tricks bc into printing integer numbers.
241 near_end_min=$(echo "$log_size_bb - (20 * $blocks_per_op / 1)" | bc)
242 echo "near_end_min = $near_end_min" >>$seqres.full
243
244 # Step us (in batches of 10 ops) to our goal.
245 while [ $head -lt $near_end_min ]; do
246         echo "    bump traffic from $head towards $near_end_min" >> $seqres.full
247         _log_traffic 10 > /dev/null 2>&1
248         head=$(_log_head)
249 done
250
251 [ $head -gt $near_end_min -a $head -lt $log_size_bb ] || \
252     _fail "!!! unexpected near end log position $head"
253
254 # Step 4: Try to wrap the log, checking for corruption with each advance.
255 # This is the functionality that we're actually trying to test.  We will try
256 # 40 ops (in batches of 2) to try to wrap the log.
257 for c in `seq 0 20`
258 do
259     echo "   little traffic"            >>$seqres.full
260     _log_traffic 2
261     head=`_log_head`
262     echo "log position = $head"         >>$seqres.full
263     _check_corrupt $SCRATCH_DEV $block
264 done
265
266 [ $head -lt 1000 ] || \
267     _fail "!!! unexpected wrapped log position $head"
268
269 # success, all done
270 status=0
271 exit