btrfs: test balance and send running in parallel
[xfstests-dev.git] / tests / btrfs / 252
1 #! /bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (C) 2021 SUSE Linux Products GmbH. All Rights Reserved.
4 #
5 # FS QA Test No. 252
6 #
7 # Test that send and balance can run in parallel, without failures and producing
8 # correct results.
9 #
10 # Before kernel 5.3 it was possible to run both operations in parallel, however
11 # it was buggy and caused sporadic failures due to races, so it was disabled in
12 # kernel 5.3 by commit 9e967495e0e0ae ("Btrfs: prevent send failures and crashes
13 # due to concurrent relocation"). There is a now a patch that enables both
14 # operations to safely run in parallel, and it has the following subject:
15 #
16 #      "btrfs: make send work with concurrent block group relocation"
17 #
18 # This also serves the purpose of testing a succession of incremental send
19 # operations, where we have a bunch of snapshots of the same subvolume and we
20 # keep doing an incremental send using the previous snapshot as the parent.
21 #
22 . ./common/preamble
23 _begin_fstest auto send balance stress
24
25 _cleanup()
26 {
27         if [ ! -z $balance_pid ]; then
28                 kill $balance_pid &> /dev/null
29                 wait $balance_pid
30         fi
31         cd /
32         rm -r -f $tmp.*
33 }
34
35 # Import common functions.
36 . ./common/filter
37
38 # real QA test starts here
39
40 _supported_fs btrfs
41 # The size needed is variable as it depends on the specific randomized
42 # operations from fsstress and on the value of $LOAD_FACTOR. But require
43 # at least $LOAD_FACTOR * 6G, as we do the receive operations to the same
44 # filesystem, do relocation and store snapshot checksums from fssum in the
45 # filesystem as well (can be hundreds of megabytes for $LOAD_FACTOR > 3).
46 _require_scratch_size $(($LOAD_FACTOR * 6 * 1024 * 1024))
47 _require_fssum
48
49 balance_loop()
50 {
51         trap "wait; exit" SIGTERM
52
53         while true; do
54                 _run_btrfs_balance_start $SCRATCH_MNT > /dev/null
55                 [ $? -eq 0 ] || echo "Balance failed: $?"
56         done
57 }
58
59 _scratch_mkfs >> $seqres.full 2>&1
60 _scratch_mount
61
62 num_snapshots=$((10 + $LOAD_FACTOR * 2))
63 avg_ops_per_snapshot=$((1000 * LOAD_FACTOR))
64 total_fsstress_ops=$((num_snapshots * avg_ops_per_snapshot))
65
66 data_subvol="$SCRATCH_MNT/data"
67 snapshots_dir="$SCRATCH_MNT/snapshots"
68 dest_dir="$SCRATCH_MNT/received"
69 fssum_dir="$SCRATCH_MNT/fssum"
70
71 mkdir -p "$snapshots_dir"
72 mkdir -p "$dest_dir"
73 mkdir -p "$fssum_dir"
74 $BTRFS_UTIL_PROG subvolume create "$data_subvol" >> $seqres.full
75
76 snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r \"$data_subvol\""
77 snapshot_cmd="$snapshot_cmd \"$snapshots_dir/snap_\`date +'%s%N'\`\""
78
79 # Use a single fsstress process so that in case of a failure we can grab the seed
80 # number from the .full log file and deterministically reproduce a failure.
81 # Also disable subvolume and snapshot creation, since send is not recursive, so
82 # it's pointless to have them.
83 #
84 echo "Running fsstress..." >> $seqres.full
85 $FSSTRESS_PROG $FSSTRESS_AVOID -d "$data_subvol" -p 1 -w \
86                -f subvol_create=0 -f subvol_delete=0 -f snapshot=0 \
87                -x "$snapshot_cmd" -X $num_snapshots \
88                -n $total_fsstress_ops >> $seqres.full
89
90 snapshots=(`IFS=$'\n' ls -1 "$snapshots_dir"`)
91
92 # Compute the checksums for every snapshot.
93 for i in "${!snapshots[@]}"; do
94         snap="${snapshots_dir}/${snapshots[$i]}"
95         echo "Computing checksum for snapshot: ${snapshots[$i]}" >> $seqres.full
96         $FSSUM_PROG -A -f -w "${fssum_dir}/${i}.fssum" "$snap"
97 done
98
99 # Now leave a process constantly running balance.
100 balance_loop &
101 balance_pid=$!
102
103 # Now send and receive all snapshots to our destination directory.
104 # We send and receive from/to the same same filesystem using a pipe, because
105 # this is the most stressful scenario and it could lead (and has lead to during
106 # development) to deadlocks - the sending task blocking on a full pipe while
107 # holding some lock, while the receiving side was not reading from the pipe
108 # because it was waiting for a transaction commit, which could not happen due
109 # to the lock held by the sending task.
110 #
111 for i in "${!snapshots[@]}"; do
112         snap="${snapshots_dir}/${snapshots[$i]}"
113         prev_snap="${snapshots_dir}/${snapshots[$i - 1]}"
114
115         # For the first snapshot we do a full incremental send, for all the
116         # others we do an incremental send, using the previous snapshot as the
117         # parent.
118         #
119         # We redirect stderr of the send command because the commands prints to
120         # stderr a message like "At subvol ...", and the number of snapshots and
121         # snapshot names we have depends on LOAD_FACTOR and timestamps, so we
122         # don't use them in the golden output. Recent versions of btrfs-progs
123         # have the --quiet option to eliminate these messages.
124         #
125         # We also redirect stderr and stdout of the receive command. Note that
126         # when receiving a full stream, the command prints a message like
127         # "At subvol ..." to stderr, but when receiving an incremental stream it
128         # prints a message to stdout like "At snapshot ...". Just like for the
129         # send command, new versions of btrfs-progs have the --quiet option to
130         # eliminate these messages.
131         #
132         # Further the send command prints the messages with a full snapshot path,
133         # while receive prints only the snapshot name.
134         #
135         # We redirect all these messages to $seqres.full and then manually check
136         # if the commands succeeded. This is just so that the test is able to
137         # run with older versions of btrfs-progs.
138         #
139         if [ $i -eq 0 ]; then
140                 echo "Full send of the snapshot at: $snap" >>$seqres.full
141                 $BTRFS_UTIL_PROG send "$snap" 2>>$seqres.full | \
142                         $BTRFS_UTIL_PROG receive "$dest_dir" 2>>$seqres.full
143         else
144                 echo "Incremental send of the snapshot at: $snap" >>$seqres.full
145                 $BTRFS_UTIL_PROG send -p "$prev_snap" "$snap" 2>>$seqres.full | \
146                         $BTRFS_UTIL_PROG receive "$dest_dir" >>$seqres.full
147         fi
148
149         retvals=( "${PIPESTATUS[@]}" )
150
151         [ ${retvals[0]} -eq 0 ] || \
152                 echo "Send of snapshot $snap failed: ${retvals[0]}"
153         [ ${retvals[1]} -eq 0 ] || \
154                 echo "Receive of snapshot $snap failed: ${retvals[1]}"
155
156         if [ $i -gt 0 ]; then
157                 # We don't need the previous snapshot anymore, so delete it.
158                 # This makes balance not so slow and triggers the cleaner kthread
159                 # to run and delete the snapshot tree in parallel, while we are
160                 # also running balance and send/receive, adding additional test
161                 # coverage and stress.
162                 $BTRFS_UTIL_PROG subvolume delete "$prev_snap" >> $seqres.full
163         fi
164 done
165
166 # We are done with send/receive, send a signal to the balance job and verify
167 # the snapshot checksums while it terminates. We do the wait at _cleanup() so
168 # that we do some useful work while it terminates.
169 kill $balance_pid
170
171 # Now verify that received snapshots have the expected checksums.
172 for i in "${!snapshots[@]}"; do
173         snap_csum="${fssum_dir}/${i}.fssum"
174         snap_copy="${dest_dir}/${snapshots[$i]}"
175
176         echo "Verifying checksum for snapshot at: $snap_copy" >> $seqres.full
177         # On success, fssum outputs only a single line with "OK" to stdout, and
178         # on error it outputs several lines to stdout. Since the number of
179         # snapshots in the test depends on $LOAD_FACTOR, filter out the success
180         # case, so we don't have a mismatch with the golden output in case we
181         # run with a non default $LOAD_FACTOR (default is 1). We only want the
182         # mismatch with the golden output in case there's a checksum failure.
183         $FSSUM_PROG -r "$snap_csum" "$snap_copy" | egrep -v '^OK$'
184 done
185
186 echo "Silence is golden"
187
188 # success, all done
189 status=0
190 exit