2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2013 Red Hat, Inc., Tomas Racek <tracek@redhat.com>
7 # Test that filesystem sends discard requests only on free blocks
10 seqres=$RESULT_DIR/$seq
11 echo "QA output created by $seq"
13 status=1 # failure is the default!
14 trap "_cleanup; exit \$status" 0 1 2 3 15
18 _supported_fs ext4 xfs btrfs
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
29 _require_fs_space $TEST_DIR 307200
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
39 $UMOUNT_PROG $loop_dev &> /dev/null
40 _destroy_loop_device $loop_dev
41 if [ $status -eq 0 ]; then
49 # It's not a good idea to be running tools against the image file
50 # backing a live filesystem because the filesystem could be maintaining
51 # in-core state that will perturb the free space map on umount. Stick
52 # to established convention which requires the filesystem to be
53 # unmounted while we probe the underlying file.
54 $UMOUNT_PROG $loop_mnt
55 $XFS_IO_PROG -F -c fiemap $1 | grep hole | $SED_PROG 's/.*\[\(.*\)\.\.\(.*\)\].*/\1 \2/'
56 _mount $loop_dev $loop_mnt
63 $UMOUNT_PROG $loop_mnt
64 $DUMPE2FS_PROG $img_file 2>&1 | grep " Free blocks" | cut -d ":" -f2- | \
65 tr ',' '\n' | $SED_PROG 's/^ //' | \
66 $AWK_PROG -v spb=$sectors_per_block 'BEGIN{FS="-"};
68 if($2 != "") # range of blocks
69 print spb * $1, spb * ($2 + 1) - 1;
70 else # just single block
71 print spb * $1, spb * ($1 + 1) - 1;
75 agsize=`$XFS_INFO_PROG $loop_mnt | $SED_PROG -n 's/.*agsize=\(.*\) blks.*/\1/p'`
76 # Convert free space (agno, block, length) to (start sector, end sector)
77 $UMOUNT_PROG $loop_mnt
78 $XFS_DB_PROG -r -c "freesp -d" $img_file | $SED_PROG '/^.*from/,$d'| \
79 $AWK_PROG -v spb=$sectors_per_block -v agsize=$agsize \
80 '{ print spb * ($1 * agsize + $2), spb * ($1 * agsize + $2 + $3) - 1 }'
83 local device_size=$($BTRFS_UTIL_PROG filesystem show --raw $loop_mnt 2>&1 \
84 | sed -n "s/^.*size \([0-9]*\).*$/\1/p")
86 local nodesize=$($BTRFS_UTIL_PROG inspect-internal dump-super $img_file \
87 | sed -n 's/nodesize\s*\(.*\)/\1/p')
89 # Get holes within block groups
90 $BTRFS_UTIL_PROG inspect-internal dump-tree -t extent $img_file \
91 | $AWK_PROG -v sectorsize=512 -v nodesize=$nodesize -f $here/src/parse-extent-tree.awk
93 # Get holes within unallocated space on disk
94 $BTRFS_UTIL_PROG inspect-internal dump-tree -t dev $img_file \
95 | $AWK_PROG -v sectorsize=512 -v devsize=$device_size -f $here/src/parse-dev-tree.awk
103 # Merges consecutive ranges from two input files
107 tmp_file=$tmp/sectors.tmp
109 cat $file1 $file2 | sort -n > $tmp_file
111 read line < $tmp_file
115 # Continue from second line
116 sed -i "1d" $tmp_file
118 curr_start=${line% *}
121 if [ `expr $end + 1` -ge $curr_start ]; then
122 if [ $curr_end -gt $end ]; then
141 img_file=$TEST_DIR/$$.fs
142 dd if=/dev/zero of=$img_file bs=1M count=$fssize &> /dev/null
144 loop_dev=$(_create_loop_device $img_file)
145 loop_mnt=$tmp/loop_mnt
147 fiemap_ref="$tmp/reference"
148 fiemap_after="$tmp/after"
149 free_sectors="$tmp/free_sectors"
150 merged_sectors="$tmp/merged_free_sectors"
154 [ "$FSTYP" = "xfs" ] && MKFS_OPTIONS="-f $MKFS_OPTIONS"
155 [ "$FSTYP" = "btrfs" ] && MKFS_OPTIONS="$MKFS_OPTIONS -f -dsingle -msingle"
158 _mount $loop_dev $loop_mnt
160 echo -n "Generating garbage on loop..."
161 # Goal is to fill it up, ignore any errors.
162 for i in `seq 1 10`; do
163 mkdir $loop_mnt/$i &> /dev/null
164 cp -r $here/* $loop_mnt/$i &> /dev/null || break
167 # Get reference fiemap, this can contain i.e. uninitialized inode table
169 get_holes $img_file > $fiemap_ref
172 find $loop_mnt -type f -print | $AWK_PROG \
173 'BEGIN {srand()}; {if(rand() > 0.7) print $1;}' | xargs rm
176 echo -n "Running fstrim..."
177 $FSTRIM_PROG $loop_mnt &> /dev/null
180 echo -n "Detecting interesting holes in image..."
181 # Get after-trim fiemap
183 get_holes $img_file > $fiemap_after
186 echo -n "Comparing holes to the reported space from FS..."
188 block_size=$(_get_block_size $loop_mnt/)
189 sectors_per_block=`expr $block_size / 512`
191 # Obtain free space from filesystem
192 get_free_sectors > $free_sectors
193 # Merge original holes with free sectors
194 merge_ranges $fiemap_ref $free_sectors > $merged_sectors
196 # Check that all holes after fstrim call were already present before or
197 # that they match free space reported from FS
201 if ! $AWK_PROG -v s=$from -v e=$to \
202 '{ if ($1 <= s && e <= $2) found = 1};
203 END { if(found) exit 0; else exit 1}' $merged_sectors
205 echo "Sectors $from-$to are not marked as free!"
207 # Dump the state to make it easier to debug this...
208 echo free_sectors >> $seqres.full
209 sort -g < $free_sectors >> $seqres.full
210 echo fiemap_ref >> $seqres.full
211 sort -g < $fiemap_ref >> $seqres.full
212 echo merged_sectors >> $seqres.full
213 sort -g < $merged_sectors >> $seqres.full
214 echo fiemap_after >> $seqres.full
215 sort -g < $fiemap_after >> $seqres.full