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