Re: [PATCH] xfstests/xfs: test inode allocation with fragmented free space

Dave Chinner
2015-06-03T22:33:47Z

On Wed, Jun 03, 2015 at 03:08:26PM -0400, Brian Foster wrote:
> XFS dynamic inode allocation has a fundamental limitation in that an
> inode chunk requires a contiguous extent of a minimum size. Depending on
> the level of free space fragmentation, inode allocation can fail with
> ENOSPC where the filesystem might not be near 100% usage.
>
> The sparse inodes feature was implemented to provide an inode allocation
> strategy that maximizes the ability to allocate inodes under free space
> fragmentation. This test fragments free space and verifies that
> filesystems that support sparse inode allocation can allocate a minimum
> percentage of inodes on the fs.
>
> Signed-off-by: Brian Foster <bfoster@redhat.com>
> ---
>
> This is a test I've been using to verify the effectiveness of the XFS
> sparse inodes feature. I put some effort into trying to make it generic
> and have kind of gone back and forth on the matter. That said, it's kind
> of pointless on ext4, doesn't work well on btrfs because it doesn't
> track inode counts, and is generally expected to fail on XFS filesystems
> without sparse inodes support.
>
> Given all of that, I kind of went in the other direction and let it run
> only for XFS when sparse inodes is supported. We still have broader
> functional sparse inodes testability by virtue of DEBUG, of course.
> Thoughts?

I think it's fine as an xfs specific test given the nature of what
it is testing....
> +
> +_avail_bytes()
> +{
> + avail_kb=`$DF_PROG $SCRATCH_MNT | tail -n1 | awk '{ print $5 }'`
> + echo $((avail_kb * 1024))

DF_PROG --output=avail ?

And maybe should go into common/rc as _get_available_space, along
with the _get_*_inode functions?

> +
> +_consume_freesp()
> +{
> + file=$1
> +
> + # consume nearly all available space (leave ~1MB)
> + avail=`_avail_bytes`
> + filesizemb=$((avail / 1024 / 1024 - 1))
> + $XFS_IO_PROG -fc "falloc 0 ${filesizemb}m" $file \
> + 2>> $seqres.full || _fail "falloc failed"
> +}

Don't use _fail here - just have xfs_io output the error message
and let the golden image catch it. It's only a small filesystem,
if it fails it won't blow the runtime out badly, and we'll still get
sparse inode test coverage...

> +# 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
> +}
> +
> +# real QA test starts here
> +_supported_os Linux
> +
> +_require_scratch
> +_require_xfs_io_command "falloc"
> +_require_xfs_io_command "fpunch"
> +
> +rm -f $seqres.full
> +
> +echo "Silence is golden."
> +
> +_scratch_mkfs_sized $((1024*1024*50)) | _filter_mkfs > /dev/null 2> $tmp.mkfs ||
> + _fail "mkfs failed"
> +. $tmp.mkfs # for isize

That should pass "-i sparse" to _scratch_mkfs_sized, after....

> +
> +# This test runs a workload that is known to fail on XFS unless the sparse
> +# inodes chunk feature is enabled. Skip the test if it is not supported to avoid
> +# unnecessary and expected test failures.
> +$XFS_DB_PROG -c version $SCRATCH_DEV | grep SPARSE_INODES > /dev/null ||
> + _notrun "requires sparse inodes support"

... calling this:

_require_xfs_sparse_inodes()
{
_scratch_mkfs_xfs_supported -m crc=1 -i sparse > /dev/null 2>&1 \
|| _notrun "mkfs.xfs does not support sparse inodes"
_scratch_mkfs_xfs -m crc=1 -i sparse > /dev/null 2>&1
_scratch_mount >/dev/null 2>&1 \
|| _notrun "kernel does not support sparse inodes"
umount $SCRATCH_MOUNT
}

> +
> +_scratch_mount
> +
> +# magic heuristic value of 64 - # of inodes in an XFS inode record
> +FRAG_FACTOR=$((isize * 64))
> +[ $FRAG_FACTOR != 0 ] || _fail "could not calculate fragmentation factor"

Why would isize be zero? And what does this magic hueristic do?

> +_consume_freesp $SCRATCH_MNT/spc
> +
> +offset=`stat -c "%s" $SCRATCH_MNT/spc`
> +offset=$((offset - $FRAG_FACTOR * 2))

I dislike having to look up man pages to understand what code does.
It is grabbing the file size, and stepping back a magic number from
the EOF. I think it is punching inode chunk size holes in the file,
but I could be mistaken...

> +while [ $offset -ge 0 ]; do
> + # punch small holes in the file to fragment free space
> + $XFS_IO_PROG -c "fpunch $offset $FRAG_FACTOR" $SCRATCH_MNT/spc \
> + 2>> $seqres.full || _fail "fpunch failed"
> +
> + # allocate as many inodes as possible
> + mkdir -p $SCRATCH_MNT/offset.$offset > /dev/null 2>&1
> + _alloc_inodes $SCRATCH_MNT/offset.$offset
> +
> + offset=$((offset - $FRAG_FACTOR * 2))
> +done
> +
> +# check the inode % usage
> +iusepct=`$DF_PROG -i $SCRATCH_MNT | tail -n 1 | awk '{ print $6 }' |
> + sed -e 's/%//'`

_get_inode_used_percent()
{
$DF_PROG --output=ipcent $SCRATCH_MNT | tail -1 | sed -e 's/%//'
}

> +if [ $iusepct -lt 95 ]; then

if [ ! _within_tolerance .... ]

> + $DF_PROG -i $SCRATCH_MNT
> + _fail "could not allocate enough inodes"
> +fi
> +
> +_scratch_unmount

No need to unmount scratch here.

Cheers,

Dave.
--
Dave Chinner
david@fromorbit.com