btrfs/011: umount device in _cleanup
[xfstests-dev.git] / tests / btrfs / 011
1 #! /bin/bash
2 # FSQA Test No. btrfs/011
3 #
4 # Test of the btrfs replace operation.
5 #
6 # The amount of tests done depends on the number of devices in the
7 # SCRATCH_DEV_POOL. For full test coverage, at least 5 devices should
8 # be available (e.g. 5 partitions).
9 #
10 # The source and target devices for the replace operation are
11 # arbitrarily chosen out of SCRATCH_DEV_POOl. Since the target device
12 # mustn't be smaller than the source device, the requirement for this
13 # test is that all devices have _exactly_ the same size. If this is
14 # not the case, this test is not run.
15 #
16 # To check the filesystems after replacing a device, a scrub run is
17 # performed, a btrfsck run, and finally the filesystem is remounted.
18 #
19 #-----------------------------------------------------------------------
20 # Copyright (C) 2013 STRATO.  All rights reserved.
21 #
22 # This program is free software; you can redistribute it and/or
23 # modify it under the terms of the GNU General Public License as
24 # published by the Free Software Foundation.
25 #
26 # This program is distributed in the hope that it would be useful,
27 # but WITHOUT ANY WARRANTY; without even the implied warranty of
28 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29 # GNU General Public License for more details.
30 #
31 # You should have received a copy of the GNU General Public License
32 # along with this program; if not, write the Free Software Foundation,
33 # Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
34 #
35 #-----------------------------------------------------------------------
36 #
37
38 seq=`basename $0`
39 seqres=$RESULT_DIR/$seq
40 echo "QA output created by $seq"
41
42 here=`pwd`
43 tmp=/tmp/$$
44 status=1
45 noise_pid=0
46
47 _cleanup()
48 {
49         if [ $noise_pid -ne 0 ] && ps -p $noise_pid | grep -q $noise_pid; then
50                 kill -TERM $noise_pid
51         fi
52         wait
53         rm -f $tmp.tmp
54         # we need this umount and couldn't rely on _require_scratch to umount
55         # it from next test, because we would replace SCRATCH_DEV, which is
56         # needed by _require_scratch, and make it umounted.
57         _scratch_unmount > /dev/null 2>&1
58 }
59 trap "_cleanup; exit \$status" 0 1 2 3 15
60
61 # get standard environment, filters and checks
62 . ./common/rc
63 . ./common/filter
64
65 # real QA test starts here
66 _supported_fs btrfs
67 _require_scratch_nocheck
68 _require_scratch_dev_pool 4
69 _require_btrfs_dump_super
70
71 rm -f $seqres.full
72 rm -f $tmp.tmp
73
74 echo "*** test btrfs replace"
75
76 workout()
77 {
78         local mkfs_options="$1"
79         local num_devs4raid="$2"
80         local with_cancel="$3"
81         local quick="$4"
82         local source_dev="`echo ${SCRATCH_DEV_POOL} | awk '{print $1}'`"
83         local target_dev="`echo ${SCRATCH_DEV_POOL} | awk '{print $NF}'`"
84         local fssize
85
86         echo >> $seqres.full
87         echo "---------workout \"$1\" $2 $3 $4-----------" >> $seqres.full
88
89         if [ "`echo $SCRATCH_DEV_POOL | wc -w`" -lt `expr $num_devs4raid + 1` ]; then
90                 echo "Skip workout $1 $2 $3 $4" >> $seqres.full
91                 echo "Too few devices in SCRATCH_DEV_POOL $SCRATCH_DEV_POOL, required: " `expr $num_devs4raid + 1` >> $seqres.full
92                 return 0
93         fi
94
95         # use min number of disks in order to fill up the disk to replace
96         # as much as possible
97         local used_devs_without_1st="`echo $SCRATCH_DEV_POOL | \
98                 awk '{ORS=\" \"; for (i = 2; i <= (NF - 1 < '$num_devs4raid' ? NF - 1 : '$num_devs4raid'); i++) print $i}'`"
99
100         # _scratch_mkfs adds the 1st device again (which is $SCRATCH_DEV)
101         _scratch_mkfs $mkfs_options $used_devs_without_1st >> $seqres.full 2>&1 || _fail "mkfs failed"
102
103         # create a filesystem on the target device just for the sake of
104         # being able to query its size with btrfs-show-super
105         $MKFS_BTRFS_PROG $MKFS_OPTIONS $target_dev >> $seqres.full 2>&1 || _fail "mkfs target_dev failed"
106
107         # The source and target devices for the replace operation are
108         # arbitrarily chosen out of the pool. Since the target device mustn't
109         # be smaller than the source device, the requirement for this test is
110         # that all devices have _exactly_ the same size. If this is not the
111         # case, this test is not run.
112         local num_lines=`$BTRFS_SHOW_SUPER_PROG $SCRATCH_DEV $used_devs_without_1st $target_dev | grep dev_item.total_bytes | uniq | wc -l`
113         if [ $num_lines -gt 1 ]; then
114                 _notrun "Different device sizes detected"
115         fi
116
117         if [ `$BTRFS_SHOW_SUPER_PROG $SCRATCH_DEV | grep dev_item.total_bytes | awk '{print $2}'` -lt 2500000000 ]; then
118                 _notrun "device size too small"
119         fi
120
121         _scratch_mount
122
123         echo "$BTRFS_UTIL_PROG filesystem show" >> $seqres.full
124         $BTRFS_UTIL_PROG filesystem show >> $seqres.full
125
126         # Generate metadata and some minimal user data, generate 500 times
127         # 20K extents in the data chunk and fill up metadata with inline
128         # extents.
129         for i in `seq 1 500`; do
130                 _ddt of=$SCRATCH_MNT/l$i bs=16385 count=1
131                 _ddt of=$SCRATCH_MNT/s$i bs=3800 count=1
132         done > /dev/null 2>&1
133
134         # Generate a template once and quickly copy it multiple times.
135         _ddt of=$SCRATCH_MNT/t0 bs=1M count=1 > /dev/null 2>&1
136
137         if [ "${quick}Q" = "thoroughQ" ]; then
138                 # The intention of this "thorough" test is to increase
139                 # the probability of random errors, in particular in
140                 # conjunction with the background noise generator and
141                 # a sync call while the replace operation is ongoing.
142                 fssize=2048
143         elif [ "${with_cancel}Q" = "cancelQ" ]; then
144                 # The goal is to produce enough data to prevent that the
145                 # replace operation finishes before the cancel request
146                 # is started.
147                 fssize=1024
148         else
149                 fssize=64
150         fi
151
152         # since the available size was tested before, do not tolerate
153         # any failures
154         for i in `seq $fssize`; do
155                 cp $SCRATCH_MNT/t0 $SCRATCH_MNT/t$i || _fail "cp failed"
156         done > /dev/null 2>> $seqres.full
157         sync; sync
158
159         btrfs_replace_test $source_dev $target_dev "" $with_cancel $quick
160         _scratch_unmount > /dev/null 2>&1
161
162         if echo $mkfs_options | egrep -qv "raid1|raid5|raid6|raid10" || \
163            [ "${with_cancel}Q" = "cancelQ" ]; then
164                 # the -r option has no effect without mirrors, skip -r test
165                 # in this case, and if only the canceling should be tested
166                 # as well
167                 return 0
168         fi
169
170         # One more time with the '-r' option this time. Instead of wasting
171         # time to populate the filesystem with data again, use the
172         # existing filesystem in the state as it is after the previous
173         # replace operation.
174         # If possible, use a strategy to select the source and target
175         # device so that we really change bits on the target disk, see
176         # below.
177
178         # The default: For the 2nd run, the new target drive is the old
179         # source drive, and the new source drive is the old target drive.
180         # Since except for the noise data, the copied data is already on
181         # the new target disk (which is the old source disk), this is not
182         # optimal to check whether data is copied correctly.
183         local tmp_dev="$source_dev"
184         source_dev="$target_dev"
185         target_dev="$tmp_dev"
186
187         # If we have at least one more device in the SCRATCH_DEV_POOL than
188         # used so far, use one of those for the new target devive.
189         if [ "`echo $SCRATCH_DEV_POOL | wc -w`" -gt `expr $num_devs4raid + 1` ]; then
190                 target_dev="`echo ${SCRATCH_DEV_POOL} | awk '{print $(NF-1)}'`"
191         fi
192
193         # If the filesystem is built out of more than one devices, use a
194         # different source device for this round.
195         if [ $num_devs4raid -gt 1 ]; then
196                 source_dev="`echo ${SCRATCH_DEV_POOL} | awk '{print $2}'`"
197         fi
198
199         # Mount similar to _scratch_mount, but since the SCRATCH_DEV (the
200         # 1st device in SCRATCH_DEV_POOL) was replaced by the previous
201         # btrfs replace operation, substitute SCRATCH_DEV with a device
202         # that is known to be part of the SCRATCH_MNT filesystem.
203         _mount -t $FSTYP `_scratch_mount_options | sed "s&${SCRATCH_DEV}&${source_dev}&"`
204         if [ $? -ne 0 ]; then
205                 echo "mount failed"
206                 return 1
207         fi
208
209         btrfs_replace_test $source_dev $target_dev "-r" $with_cancel $quick
210         _scratch_unmount > /dev/null 2>&1
211 }
212
213 btrfs_replace_test()
214 {
215         local source_dev="$1"
216         local target_dev="$2"
217         local replace_options="$3"
218         local with_cancel="$4"
219         local quick="$5"
220
221         # generate some (slow) background traffic in parallel to the
222         # replace operation. It is not a problem if cat fails early
223         # with ENOSPC.
224         cat /dev/urandom | od > $SCRATCH_MNT/noise 2>> $seqres.full &
225         noise_pid=$!
226
227         if [ "${with_cancel}Q" = "cancelQ" ]; then
228                 # background the replace operation (no '-B' option given)
229                 echo "$BTRFS_UTIL_PROG replace start -f $replace_options $source_dev $target_dev $SCRATCH_MNT" >> $seqres.full
230                 $BTRFS_UTIL_PROG replace start -f $replace_options $source_dev $target_dev $SCRATCH_MNT >> $seqres.full 2>&1 || _fail "btrfs replace start failed"
231                 sleep 1
232                 echo "$BTRFS_UTIL_PROG replace cancel $SCRATCH_MNT" >> $seqres.full
233                 $BTRFS_UTIL_PROG replace cancel $SCRATCH_MNT >> $seqres.full 2>&1 || _fail "btrfs replace cancel failed"
234
235                 # 'replace status' waits for the replace operation to finish
236                 # before the status is printed
237                 $BTRFS_UTIL_PROG replace status $SCRATCH_MNT > $tmp.tmp 2>&1
238                 cat $tmp.tmp >> $seqres.full
239                 grep -q canceled $tmp.tmp || _fail "btrfs replace status (canceled) failed"
240         else
241                 if [ "${quick}Q" = "thoroughQ" ]; then
242                         # On current hardware, the thorough test runs
243                         # more than a second. This is a chance to force
244                         # a sync in the middle of the replace operation.
245                         (sleep 1; sync) > /dev/null 2>&1 &
246                 fi
247                 echo "$BTRFS_UTIL_PROG replace start -Bf $replace_options $source_dev $target_dev $SCRATCH_MNT" >> $seqres.full
248                 $BTRFS_UTIL_PROG replace start -Bf $replace_options $source_dev $target_dev $SCRATCH_MNT >> $seqres.full 2>&1 || _fail "btrfs replace start failed"
249
250                 $BTRFS_UTIL_PROG replace status $SCRATCH_MNT > $tmp.tmp 2>&1
251                 cat $tmp.tmp >> $seqres.full
252                 grep -q finished $tmp.tmp || _fail "btrfs replace status (finished) failed"
253         fi
254
255         if ps -p $noise_pid | grep -q $noise_pid; then
256                 kill -TERM $noise_pid 2> /dev/null
257         fi
258         noise_pid=0
259         wait
260
261         # scrub tests on-disk data, that's the reason for the sync.
262         # With the '-B' option (don't background), any type of error causes
263         # exit values != 0, including detected correctable and uncorrectable
264         # errors on the device.
265         sync; sync
266         $BTRFS_UTIL_PROG scrub start -B $SCRATCH_MNT >> $seqres.full 2>&1 || _fail "btrfs scrub failed"
267
268         # Two tests are performed, the 1st is to btrfsck the filesystem,
269         # and the 2nd test is to mount the filesystem.
270         # Usually _check_btrfs_filesystem would perform the mount test,
271         # but it gets confused by the mount output that shows SCRATCH_MNT
272         # mounted but not being mounted to SCRATCH_DEV. This happens
273         # because in /proc/mounts the 2nd device of the filesystem is
274         # shown after the replace operation. Let's just do the mount
275         # test manually after _check_btrfs_filesystem is finished.
276         _scratch_unmount > /dev/null 2>&1
277         if [ "${with_cancel}Q" != "cancelQ" ]; then
278                 # after the replace operation, use the target_dev for everything
279                 echo "$BTRFS_UTIL_PROG filesystem show -d" >> $seqres.full
280                 $BTRFS_UTIL_PROG filesystem show -d >> $seqres.full
281                 echo "$BTRFS_UTIL_PROG filesystem show" >> $seqres.full
282                 $BTRFS_UTIL_PROG filesystem show >> $seqres.full
283                 echo "_check_btrfs_filesystem $target_dev" >> $seqres.full
284                 _check_btrfs_filesystem $target_dev
285                 _mount -t $FSTYP `_scratch_mount_options | sed "s&${SCRATCH_DEV}&${target_dev}&"`
286         else
287                 _check_btrfs_filesystem $source_dev
288                 _scratch_mount
289         fi
290 }
291
292 workout "-m single -d single" 1 no quick
293 workout "-m single -d single -M" 1 no quick
294 workout "-m dup -d single" 1 no quick
295 workout "-m dup -d single" 1 cancel quick
296 workout "-m dup -d dup -M" 1 no quick
297 workout "-m raid0 -d raid0" 2 no quick
298 workout "-m raid1 -d raid1" 2 no thorough
299 workout "-m raid5 -d raid5" 2 no quick
300 workout "-m raid6 -d raid6" 3 no quick
301 workout "-m raid10 -d raid10" 4 no quick
302
303 echo "*** done"
304 status=0
305 exit