shared/298: Wire btrfs support in get_free_sectors
[xfstests-dev.git] / tests / shared / 298
1 #! /bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2013 Red Hat, Inc., Tomas Racek <tracek@redhat.com>
4 #
5 # FS QA Test No. 298
6 #
7 # Test that filesystem sends discard requests only on free blocks
8 #
9 seq=`basename $0`
10 seqres=$RESULT_DIR/$seq
11 echo "QA output created by $seq"
12
13 status=1        # failure is the default!
14 trap "_cleanup; exit \$status" 0 1 2 3 15
15
16 . ./common/rc
17
18 _supported_fs ext4 xfs btrfs
19 _supported_os Linux
20 _require_test
21 _require_loop
22 _require_fstrim
23 _require_xfs_io_command "fiemap"
24 if [ "$FSTYP" = "btrfs" ]; then
25         # 3g for btrfs to have distinct bgs
26         _require_fs_space $TEST_DIR 3145728
27         fssize=3000
28 else
29         _require_fs_space $TEST_DIR 307200
30         fssize=300
31 fi
32
33 [ "$FSTYP" = "ext4" ] && _require_dumpe2fs
34 [ "$FSTYP" = "btrfs" ] && _require_btrfs_command inspect-internal dump-super
35 [ "$FSTYP" = "btrfs" ] && _require_btrfs_command inspect-internal dump-tree
36
37 _cleanup()
38 {
39         $UMOUNT_PROG $loop_dev &> /dev/null
40         _destroy_loop_device $loop_dev
41         if [ $status -eq 0 ]; then
42                 rm -rf $tmp
43                 rm $img_file
44         fi
45 }
46
47 get_holes()
48 {
49         $XFS_IO_PROG -F -c fiemap $1 | grep hole | $SED_PROG 's/.*\[\(.*\)\.\.\(.*\)\].*/\1 \2/'
50 }
51
52 get_free_sectors()
53 {
54         case $FSTYP in
55         ext4)
56         $DUMPE2FS_PROG $img_file  2>&1 | grep " Free blocks" | cut -d ":" -f2- | \
57                 tr ',' '\n' | $SED_PROG 's/^ //' | \
58                 $AWK_PROG -v spb=$sectors_per_block 'BEGIN{FS="-"};
59                      NF {
60                         if($2 != "") # range of blocks
61                                 print spb * $1, spb * ($2 + 1) - 1;
62                         else            # just single block
63                                 print spb * $1, spb * ($1 + 1) - 1;
64                      }'
65         ;;
66         xfs)
67         agsize=`$XFS_INFO_PROG $loop_mnt | $SED_PROG -n 's/.*agsize=\(.*\) blks.*/\1/p'`
68         # Convert free space (agno, block, length) to (start sector, end sector)
69         $UMOUNT_PROG $loop_mnt
70         $XFS_DB_PROG -r -c "freesp -d" $img_file | $SED_PROG '/^.*from/,$d'| \
71                  $AWK_PROG -v spb=$sectors_per_block -v agsize=$agsize \
72                 '{ print spb * ($1 * agsize + $2), spb * ($1 * agsize + $2 + $3) - 1 }'
73         ;;
74         btrfs)
75                 local device_size=$($BTRFS_UTIL_PROG filesystem show --raw $loop_mnt 2>&1 \
76                         | sed -n "s/^.*size \([0-9]*\).*$/\1/p")
77
78                 local nodesize=$($BTRFS_UTIL_PROG inspect-internal dump-super $img_file  \
79                         | sed -n 's/nodesize\s*\(.*\)/\1/p')
80
81                 # Get holes within block groups
82                 $BTRFS_UTIL_PROG inspect-internal dump-tree -t extent $img_file \
83                         | $AWK_PROG -v sectorsize=512 -v nodesize=$nodesize -f $here/src/parse-extent-tree.awk
84
85                 # Get holes within unallocated space on disk
86                 $BTRFS_UTIL_PROG inspect-internal dump-tree -t dev $img_file \
87                         | $AWK_PROG -v sectorsize=512 -v devsize=$device_size -f $here/src/parse-dev-tree.awk
88
89         ;;
90         esac
91 }
92
93 merge_ranges()
94 {
95         # Merges consecutive ranges from two input files
96         file1=$1
97         file2=$2
98
99         tmp_file=$tmp/sectors.tmp
100
101         cat $file1 $file2 | sort -n > $tmp_file
102
103         read line < $tmp_file
104         start=${line% *}
105         end=${line#* }
106
107         # Continue from second line
108         sed -i "1d" $tmp_file
109         while read line; do
110                 curr_start=${line% *}
111                 curr_end=${line#* }
112
113                 if [ `expr $end + 1` -ge $curr_start ]; then
114                         if [ $curr_end -gt $end ]; then
115                                 end=$curr_end
116                         fi
117                 else
118                         echo $start $end
119                         start=$curr_start
120                         end=$curr_end
121                 fi
122         done < $tmp_file
123
124         # Print last line
125         echo $start $end
126
127         rm $tmp_file
128 }
129
130 here=`pwd`
131 tmp=`mktemp -d`
132
133 img_file=$TEST_DIR/$$.fs
134 dd if=/dev/zero of=$img_file bs=1M count=$fssize &> /dev/null
135
136 loop_dev=$(_create_loop_device $img_file)
137 loop_mnt=$tmp/loop_mnt
138
139 fiemap_ref="$tmp/reference"
140 fiemap_after="$tmp/after"
141 free_sectors="$tmp/free_sectors"
142 merged_sectors="$tmp/merged_free_sectors"
143
144 mkdir $loop_mnt
145
146 [ "$FSTYP" = "xfs" ] && MKFS_OPTIONS="-f $MKFS_OPTIONS"
147 [ "$FSTYP" = "btrfs" ] && MKFS_OPTIONS="$MKFS_OPTIONS -f -dsingle -msingle"
148
149 _mkfs_dev $loop_dev
150 _mount $loop_dev $loop_mnt
151
152 echo -n "Generating garbage on loop..."
153 # Goal is to fill it up, ignore any errors.
154 for i in `seq 1 10`; do
155         mkdir $loop_mnt/$i &> /dev/null
156         cp -r $here/* $loop_mnt/$i &> /dev/null || break
157 done
158
159 # Get reference fiemap, this can contain i.e. uninitialized inode table
160 sync
161 get_holes $img_file > $fiemap_ref
162
163 # Delete some files
164 find $loop_mnt -type f -print | $AWK_PROG \
165         'BEGIN {srand()}; {if(rand() > 0.7) print $1;}' | xargs rm
166 echo "done."
167
168 echo -n "Running fstrim..."
169 $FSTRIM_PROG $loop_mnt &> /dev/null
170 echo "done."
171
172 echo -n "Detecting interesting holes in image..."
173 # Get after-trim fiemap
174 sync
175 get_holes $img_file > $fiemap_after
176 echo "done."
177
178 echo -n "Comparing holes to the reported space from FS..."
179 # Get block size
180 block_size=$(_get_block_size $loop_mnt/)
181 sectors_per_block=`expr $block_size / 512`
182
183 # Obtain free space from filesystem
184 get_free_sectors > $free_sectors
185 # Merge original holes with free sectors
186 merge_ranges $fiemap_ref $free_sectors > $merged_sectors
187
188 # Check that all holes after fstrim call were already present before or
189 # that they match free space reported from FS
190 while read line; do
191         from=${line% *}
192         to=${line#* }
193         if ! $AWK_PROG -v s=$from -v e=$to \
194                 '{ if ($1 <= s && e <= $2) found = 1};
195                 END { if(found) exit 0; else exit 1}' $merged_sectors
196         then
197                 echo "Sectors $from-$to are not marked as free!"
198                 exit
199         fi
200 done < $fiemap_after
201 echo "done."
202
203 status=0
204 exit