xfstests: 298: Test that FS sends discard requests only on free blocks

V1->V2: Change way of testing suggested by Dave Chinner
1. Create image file with FS on it
2. Call fstrim to discard free blocks
3. Check that every punched hole in the image file is in the area
   that is marked as free

Signed-off-by: Tomas Racek <tracek@redhat.com>
Reviewed-by: Rich Johnston <rjohnston@sgi.com>
Signed-off-by: Rich Johnston <rjohnston@sgi.com>
This commit is contained in:
Tomas Racek
2013-02-03 10:19:58 +00:00
committed by Rich Johnston
parent 29fe8007e7
commit 76765b7e4e
3 changed files with 196 additions and 0 deletions
+190
View File
@@ -0,0 +1,190 @@
#! /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 <tracek@redhat.com>
#
# 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
#-----------------------------------------------------------------------
#
# creator
owner=tracek@redhat.com
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
+5
View File
@@ -0,0 +1,5 @@
QA output created by 298
Generating garbage on loop...done.
Running fstrim...done.
Detecting interesting holes in image...done.
Comparing holes to the reported space from FS...done.
+1
View File
@@ -416,3 +416,4 @@ deprecated
295 auto logprint quick
296 dump auto quick
297 auto freeze
298 auto trim