xfs: test inode allocation with fragmented free space

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>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
Brian Foster
2015-08-04 14:10:48 +10:00
committed by Dave Chinner
parent 4273348818
commit 46e088e7be
4 changed files with 170 additions and 0 deletions
+37
View File
@@ -1449,6 +1449,18 @@ _require_xfs_sysfs()
fi fi
} }
# this test requires the xfs sparse inode feature
#
_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_MNT
}
# this test requires that external log/realtime devices are not in use # this test requires that external log/realtime devices are not in use
# #
_require_nonexternal() _require_nonexternal()
@@ -2724,6 +2736,18 @@ _get_used_inode()
echo $nr_inode echo $nr_inode
} }
_get_used_inode_percent()
{
if [ -z "$1" ]; then
echo "Usage: _get_used_inode_percent <mnt>"
exit 1
fi
local pct_inode;
pct_inode=`$DF_PROG -i $1 | tail -1 | awk '{ print $6 }' | \
sed -e 's/%//'`
echo $pct_inode
}
_get_free_inode() _get_free_inode()
{ {
if [ -z "$1" ]; then if [ -z "$1" ]; then
@@ -2735,6 +2759,19 @@ _get_free_inode()
echo $nr_inode echo $nr_inode
} }
# get the available space in bytes
#
_get_available_space()
{
if [ -z "$1" ]; then
echo "Usage: _get_available_space <mnt>"
exit 1
fi
local avail_kb;
avail_kb=`$DF_PROG $1 | tail -n1 | awk '{ print $5 }'`
echo $((avail_kb * 1024))
}
# get btrfs profile configs being tested # get btrfs profile configs being tested
# #
# A set of pre-set profile configs are exported via _btrfs_profile_configs # A set of pre-set profile configs are exported via _btrfs_profile_configs
Executable
+130
View File
@@ -0,0 +1,130 @@
#!/bin/bash
# FS QA Test No. xfs/076
#
# Verify that a filesystem with sparse inode support can allocate inodes in the
# event of free space fragmentation. This test is generic in nature but
# primarily relevant to filesystems that implement dynamic inode allocation
# (e.g., XFS).
#
# The test is inspired by inode allocation limitations on XFS when available
# free space is fragmented. XFS allocates inodes 64 at a time and thus requires
# an extent of length that depends on inode size (64 * isize / blksize).
#
# The test creates a small, sparse inode enabled filesystem. It fragments free
# space, allocates inodes to ENOSPC and then verifies that most of the available
# inodes (.i.e., free space) have been consumed.
#
#-----------------------------------------------------------------------
# Copyright (c) 2015 Red Hat, Inc. All Rights Reserved.
#
# 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`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
# get standard environment, filters and checks
. ./common/rc
. ./common/filter
_cleanup()
{
cd /
umount $SCRATCH_MNT 2>/dev/null
rm -f $tmp.*
}
trap "_cleanup; exit \$status" 0 1 2 3 15
_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
}
# real QA test starts here
_supported_os Linux
_require_scratch
_require_xfs_io_command "falloc"
_require_xfs_io_command "fpunch"
_require_xfs_sparse_inodes
rm -f $seqres.full
_scratch_mkfs "-d size=50m -m crc=1 -i sparse" |
_filter_mkfs > /dev/null 2> $tmp.mkfs
. $tmp.mkfs # for isize
_scratch_mount
# 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.
CHUNK_SIZE=$((isize * 64))
_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. This should ensure that most freed extents are not
# contiguous with any others and thus sufficiently fragment free space. After
# each hole punch, allocate as many inodes as possible into the newly freed
# space. Note that we start at the end of the file and work backwards as a
# reverse allocation pattern increases the chances of both left and right sparse
# record merges.
offset=`stat -c "%s" $SCRATCH_MNT/spc`
offset=$((offset - $CHUNK_SIZE * 2))
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/offset.$offset > /dev/null 2>&1
_alloc_inodes $SCRATCH_MNT/offset.$offset
offset=$((offset - $CHUNK_SIZE * 2))
done
# check that we've hit at least 95% inode usage
iusepct=`_get_used_inode_percent $SCRATCH_MNT`
_within_tolerance "iusepct" $iusepct 100 5 0 -v
status=0
exit
+2
View File
@@ -0,0 +1,2 @@
QA output created by 076
iusepct is in range
+1
View File
@@ -73,6 +73,7 @@
073 copy auto 073 copy auto
074 quick auto prealloc rw 074 quick auto prealloc rw
075 auto quick mount 075 auto quick mount
076 auto enospc
078 growfs auto quick 078 growfs auto quick
080 rw ioctl 080 rw ioctl
081 deprecated # log logprint quota 081 deprecated # log logprint quota