Re: [PATCH 3/3] xfs: test regression in shrink when the new EOFS splits a sparse inode cluster

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jul 27, 2021 at 05:10:35PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@xxxxxxxxxx>
> 
> This is a targeted regression test for the patch "xfs: check for sparse
> inode clusters that cross new EOAG when shrinking", which was found by
> running the random-loopy shrink stresser xfs/168.
> 
> The original shrink implementation assumed that if we could allocate the
> last free extent in the filesystem, it was ok to proceed with the fs
> shrink.  Unfortunately, this isn't quite the case -- if there's a sparse
> inode cluster such that the blocks at the end of the cluster are free,
> it is not ok to shrink the fs to the point that part of the cluster
> hangs off the end of the filesystem.  Doing so results in repair and
> scrub marking the filesystem corrupt, so we must not.
> 
> (EOFS == "end of filesystem"; EOAG == "end of allocation group")
> 
> Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx>
> ---

Good to me, and test passed on my system.

Reviewed-by: Zorro Lang <zlang@xxxxxxxxxx>

>  tests/xfs/778     |  190 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/xfs/778.out |    2 +
>  2 files changed, 192 insertions(+)
>  create mode 100755 tests/xfs/778
>  create mode 100644 tests/xfs/778.out
> 
> 
> diff --git a/tests/xfs/778 b/tests/xfs/778
> new file mode 100755
> index 00000000..73cebaf1
> --- /dev/null
> +++ b/tests/xfs/778
> @@ -0,0 +1,190 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2021 Oracle.  All Rights Reserved.
> +#
> +# FS QA Test 778
> +#
> +# Ensure that online shrink does not let us shrink the fs such that the end
> +# of the filesystem is now in the middle of a sparse inode cluster.
> +#
> +. ./common/preamble
> +_begin_fstest auto quick shrink
> +
> +# Import common functions.
> +. ./common/filter
> +
> +# real QA test starts here
> +
> +# Modify as appropriate.
> +_supported_fs generic
> +_require_scratch
> +_require_xfs_sparse_inodes
> +_require_scratch_xfs_shrink
> +_require_xfs_io_command "falloc"
> +_require_xfs_io_command "fpunch"
> +
> +_scratch_mkfs "-d size=50m -m crc=1 -i sparse" |
> +	_filter_mkfs > /dev/null 2> $tmp.mkfs
> +. $tmp.mkfs	# for isize
> +cat $tmp.mkfs >> $seqres.full
> +
> +daddr_to_fsblocks=$((dbsize / 512))
> +
> +convert_units() {
> +	_scratch_xfs_db -f -c "$@" | sed -e 's/^.*(\([0-9]*\)).*$/\1/g'
> +}
> +
> +# Figure out the next possible inode number after the log, since we can't
> +# shrink or relocate the log
> +logstart=$(_scratch_xfs_get_metadata_field 'logstart' 'sb')
> +if [ $logstart -gt 0 ]; then
> +	logblocks=$(_scratch_xfs_get_metadata_field 'logblocks' 'sb')
> +	logend=$((logstart + logblocks))
> +	logend_agno=$(convert_units "convert fsb $logend agno")
> +	logend_agino=$(convert_units "convert fsb $logend agino")
> +else
> +	logend_agno=0
> +	logend_agino=0
> +fi
> +
> +_scratch_mount
> +_xfs_force_bdev data $SCRATCH_MNT
> +old_dblocks=$($XFS_IO_PROG -c 'statfs' $SCRATCH_MNT | grep geom.datablocks)
> +
> +mkdir $SCRATCH_MNT/save/
> +sino=$(stat -c '%i' $SCRATCH_MNT/save)
> +
> +_consume_freesp()
> +{
> +	file=$1
> +
> +	# consume nearly all available space (leave ~1MB)
> +	avail=`_get_available_space $SCRATCH_MNT`
> +	filesizemb=$((avail / 1024 / 1024 - 1))
> +	$XFS_IO_PROG -fc "falloc 0 ${filesizemb}m" $file
> +}
> +
> +# Allocate inodes in a directory until failure.
> +_alloc_inodes()
> +{
> +	dir=$1
> +
> +	i=0
> +	while [ true ]; do
> +		touch $dir/$i 2>> $seqres.full || break
> +		i=$((i + 1))
> +	done
> +}
> +
> +# Find a sparse inode cluster after logend_agno/logend_agino.
> +find_sparse_clusters()
> +{
> +	for ((agno = agcount - 1; agno >= logend_agno; agno--)); do
> +		_scratch_xfs_db -c "agi $agno" -c "addr root" -c "btdump" | \
> +			tr ':[,]' '    ' | \
> +			awk -v "agno=$agno" \
> +			    -v "agino=$logend_agino" \
> +'{if ($2 >= agino && and(strtonum($3), 0x8000)) {printf("%s %s %s\n", agno, $2, $3);}}' | \
> +			tac
> +	done
> +}
> +
> +# Calculate the fs inode chunk size based on the inode size and fixed 64-inode
> +# record. This value is used as the target level of free space fragmentation
> +# induced by the test (i.e., max size of free extents). We don't need to go
> +# smaller than a full chunk because the XFS block allocator tacks on alignment
> +# requirements to the size of the requested allocation. In other words, a chunk
> +# sized free chunk is not enough to guarantee a successful chunk sized
> +# allocation.
> +XFS_INODES_PER_CHUNK=64
> +CHUNK_SIZE=$((isize * XFS_INODES_PER_CHUNK))
> +
> +_consume_freesp $SCRATCH_MNT/spc
> +
> +# Now that the fs is nearly full, punch holes in every other $CHUNK_SIZE range
> +# of the space consumer file.  The goal here is to end up with a sparse cluster
> +# at the end of the fs (and past any internal log), where the chunks at the end
> +# of the cluster are sparse.
> +
> +offset=`_get_filesize $SCRATCH_MNT/spc`
> +offset=$((offset - $CHUNK_SIZE * 2))
> +nr=0
> +while [ $offset -ge 0 ]; do
> +	$XFS_IO_PROG -c "fpunch $offset $CHUNK_SIZE" $SCRATCH_MNT/spc \
> +		2>> $seqres.full || _fail "fpunch failed"
> +
> +	# allocate as many inodes as possible
> +	mkdir -p $SCRATCH_MNT/urk/offset.$offset > /dev/null 2>&1
> +	_alloc_inodes $SCRATCH_MNT/urk/offset.$offset
> +
> +	offset=$((offset - $CHUNK_SIZE * 2))
> +
> +	# Every five times through the loop, see if we got a sparse cluster
> +	nr=$((nr + 1))
> +	if [ $((nr % 5)) -eq 4 ]; then
> +		_scratch_unmount
> +		find_sparse_clusters > $tmp.clusters
> +		if [ -s $tmp.clusters ]; then
> +			break;
> +		fi
> +		_scratch_mount
> +	fi
> +done
> +
> +test -s $tmp.clusters || _notrun "Could not create a sparse inode cluster"
> +
> +echo clusters >> $seqres.full
> +cat $tmp.clusters >> $seqres.full
> +
> +# Figure out which inode numbers are in that last cluster.  We need to preserve
> +# that cluster but delete everything else ahead of shrinking.
> +icluster_agno=$(head -n 1 $tmp.clusters | cut -d ' ' -f 1)
> +icluster_agino=$(head -n 1 $tmp.clusters | cut -d ' ' -f 2)
> +icluster_ino=$(convert_units "convert agno $icluster_agno agino $icluster_agino ino")
> +
> +# Check that the save directory isn't going to prevent us from shrinking
> +test $sino -lt $icluster_ino || \
> +	echo "/save inode comes after target cluster, test may fail"
> +
> +# Save the inodes in the last cluster and delete everything else
> +_scratch_mount
> +rm -r $SCRATCH_MNT/spc
> +for ((ino = icluster_ino; ino < icluster_ino + XFS_INODES_PER_CHUNK; ino++)); do
> +	find $SCRATCH_MNT/urk/ -inum "$ino" -print0 | xargs -r -0 mv -t $SCRATCH_MNT/save/
> +done
> +rm -rf $SCRATCH_MNT/urk/ $SCRATCH_MNT/save/*/*
> +sync
> +$XFS_IO_PROG -c 'fsmap -vvvvv' $SCRATCH_MNT &>> $seqres.full
> +
> +# Propose shrinking the filesystem such that the end of the fs ends up in the
> +# sparse part of our sparse cluster.  Remember, the last block of that cluster
> +# ought to be free.
> +target_ino=$((icluster_ino + XFS_INODES_PER_CHUNK - 1))
> +for ((ino = target_ino; ino >= icluster_ino; ino--)); do
> +	found=$(find $SCRATCH_MNT/save/ -inum "$ino" | wc -l)
> +	test $found -gt 0 && break
> +
> +	ino_daddr=$(convert_units "convert ino $ino daddr")
> +	new_size=$((ino_daddr / daddr_to_fsblocks))
> +
> +	echo "Hope to fail at shrinking to $new_size" >> $seqres.full
> +	$XFS_GROWFS_PROG -D $new_size $SCRATCH_MNT &>> $seqres.full
> +	res=$?
> +
> +	# Make sure shrink did not work
> +	new_dblocks=$($XFS_IO_PROG -c 'statfs' $SCRATCH_MNT | grep geom.datablocks)
> +	if [ "$new_dblocks" != "$old_dblocks" ]; then
> +		echo "should not have shrank $old_dblocks -> $new_dblocks"
> +		break
> +	fi
> +
> +	if [ $res -eq 0 ]; then
> +		echo "shrink to $new_size (ino $ino) should have failed"
> +		break
> +	fi
> +done
> +
> +# success, all done
> +echo Silence is golden
> +status=0
> +exit
> diff --git a/tests/xfs/778.out b/tests/xfs/778.out
> new file mode 100644
> index 00000000..e80f72a3
> --- /dev/null
> +++ b/tests/xfs/778.out
> @@ -0,0 +1,2 @@
> +QA output created by 778
> +Silence is golden
> 




[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux