blob: e7b7b233de45f2370e13634e79921ee730fe0dc8 [file] [log] [blame]
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2013 Red Hat, Inc., Tomas Racek <tracek@redhat.com>
#
# FS QA Test No. 298
#
# Test that filesystem sends discard requests only on free blocks
#
seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"
status=1 # failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common/rc
_supported_fs ext4 xfs
_supported_os Linux
_require_test
_require_loop
_require_fstrim
_require_xfs_io_command "fiemap"
_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 -F -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_PROG $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 -r -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_dev $loop_dev
_mount $loop_dev $loop_mnt
echo -n "Generating garbage on loop..."
# Goal is to fill it up, ignore any errors.
for i in `seq 1 10`; do
mkdir $loop_mnt/$i &> /dev/null
cp -r $here/* $loop_mnt/$i &> /dev/null || break
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=$(_get_block_size $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