#!/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