#! /bin/bash # FS QA Test No. 298 # # Test that filesystem sends discard requests only on free blocks # #----------------------------------------------------------------------- # Copyright (c) 2013 Red Hat, Inc., Tomas Racek # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #----------------------------------------------------------------------- # seq=`basename $0` echo "QA output created by $seq" status=1 # failure is the default! trap "_cleanup; exit \$status" 0 1 2 3 15 . common.config . common.rc _supported_fs ext4 xfs _supported_os Linux _require_fstrim _require_fs_space $TEST_DIR 307200 [ "$FSTYP" = "ext4" ] && _require_dumpe2fs _cleanup() { $UMOUNT_PROG $loop_dev &> /dev/null _destroy_loop_device $loop_dev if [ $status -eq 0 ]; then rm -rf $tmp rm $img_file fi } get_holes() { $XFS_IO_PROG -c fiemap $1 | grep hole | $SED_PROG 's/.*\[\(.*\)\.\.\(.*\)\].*/\1 \2/' } get_free_sectors() { case $FSTYP in ext4) $DUMPE2FS_PROG $img_file 2>&1 | grep " Free blocks" | cut -d ":" -f2- | \ tr ',' '\n' | $SED_PROG 's/^ //' | \ $AWK_PROG -v spb=$sectors_per_block 'BEGIN{FS="-"}; NF { if($2 != "") # range of blocks print spb * $1, spb * ($2 + 1) - 1; else # just single block print spb * $1, spb * ($1 + 1) - 1; }' ;; xfs) agsize=`xfs_info $loop_mnt | $SED_PROG -n 's/.*agsize=\(.*\) blks.*/\1/p'` # Convert free space (agno, block, length) to (start sector, end sector) $UMOUNT_PROG $loop_mnt $XFS_DB_PROG -c "freesp -d" $img_file | $SED_PROG '/^.*from/,$d'| \ $AWK_PROG -v spb=$sectors_per_block -v agsize=$agsize \ '{ print spb * ($1 * agsize + $2), spb * ($1 * agsize + $2 + $3) - 1 }' ;; esac } merge_ranges() { # Merges consecutive ranges from two input files file1=$1 file2=$2 tmp_file=$tmp/sectors.tmp cat $file1 $file2 | sort -n > $tmp_file read line < $tmp_file start=${line% *} end=${line#* } # Continue from second line sed -i "1d" $tmp_file while read line; do curr_start=${line% *} curr_end=${line#* } if [ `expr $end + 1` -ge $curr_start ]; then if [ $curr_end -gt $end ]; then end=$curr_end fi else echo $start $end start=$curr_start end=$curr_end fi done < $tmp_file # Print last line echo $start $end rm $tmp_file } here=`pwd` tmp=`mktemp -d` img_file=$TEST_DIR/$$.fs dd if=/dev/zero of=$img_file bs=1M count=300 &> /dev/null loop_dev=$(_create_loop_device $img_file) loop_mnt=$tmp/loop_mnt fiemap_ref="$tmp/reference" fiemap_after="$tmp/after" free_sectors="$tmp/free_sectors" merged_sectors="$tmp/merged_free_sectors" mkdir $loop_mnt [ "$FSTYP" = "xfs" ] && MKFS_OPTIONS="-f $MKFS_OPTIONS" $MKFS_PROG -t $FSTYP $MKFS_OPTIONS $loop_dev &> /dev/null $MOUNT_PROG $loop_dev $loop_mnt echo -n "Generating garbage on loop..." for i in `seq 1 10`; do mkdir $loop_mnt/$i cp -r $here/* $loop_mnt/$i done # Get reference fiemap, this can contain i.e. uninitialized inode table sync get_holes $img_file > $fiemap_ref # Delete some files find $loop_mnt -type f -print | $AWK_PROG \ 'BEGIN {srand()}; {if(rand() > 0.7) print $1;}' | xargs rm echo "done." echo -n "Running fstrim..." $FSTRIM_PROG $loop_mnt &> /dev/null echo "done." echo -n "Detecting interesting holes in image..." # Get after-trim fiemap sync get_holes $img_file > $fiemap_after echo "done." echo -n "Comparing holes to the reported space from FS..." # Get block size block_size=$(stat -f -c "%S" $loop_mnt/) sectors_per_block=`expr $block_size / 512` # Obtain free space from filesystem get_free_sectors > $free_sectors # Merge original holes with free sectors merge_ranges $fiemap_ref $free_sectors > $merged_sectors # Check that all holes after fstrim call were already present before or # that they match free space reported from FS while read line; do from=${line% *} to=${line#* } if ! $AWK_PROG -v s=$from -v e=$to \ '{ if ($1 <= s && e <= $2) found = 1}; END { if(found) exit 0; else exit 1}' $merged_sectors then echo "Sectors $from-$to are not marked as free!" exit fi done < $fiemap_after echo "done." status=0 exit