#!/bin/bash # FS QA Test No. xfs/014 # # Test the behavior of XFS dynamic speculative preallocation at ENOSPC and # EDQUOT conditions. Speculative preallocation allocates post-EOF space to files # as they are extended. This test creates conditions where an fs is near a space # limit with lingering, relatively significant preallocations and verifies that # new writers reclaim said preallocations rather than prematurely fail with # ENOSPC/EDQUOT. # #----------------------------------------------------------------------- # Copyright (c) 2014 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 . ./common/quota _cleanup() { cd / umount $LOOP_MNT 2>/dev/null _scratch_unmount 2>/dev/null rm -f $tmp.* } trap "_cleanup; exit \$status" 0 1 2 3 15 # Create a file using a repeated open, extending write and close pattern. This # causes the preallocation to persist after the file is closed. Preallocation # will not be reclaimed unless the inode is evicted or we hit an allocation # failure. _spec_prealloc_file() { file=$1 rm -f $file # a few file extending open-write-close cycles should be enough to # trigger the fs to retain preallocation. write 256k in 32k intervals to # be sure for i in $(seq 0 32768 262144); do $XFS_IO_PROG -f -c "pwrite $i 32k" $file >> $seqres.full done # write a 4k aligned amount of data to keep the calculations simple $XFS_IO_PROG -c "pwrite 0 128m" $file >> $seqres.full size=`stat -c "%s" $file` blocks=`stat -c "%b" $file` blocksize=`stat -c "%B" $file` prealloc_size=$((blocks * blocksize - size)) if [ $prealloc_size -eq 0 ]; then echo "Warning: No speculative preallocation for $file." \ "Check use of the allocsize= mount option." fi # keep a running total of how much preallocation we've created TOTAL_PREALLOC=$((TOTAL_PREALLOC + prealloc_size)) } _consume_free_space() { dir=$1 # allocate all but 10MB of available space freesp=`$DF_PROG -m $dir | $AWK_PROG '/^\// { print $5 - 10 }'` $XFS_IO_PROG -f -c "falloc 0 ${freesp}M" $dir/spc } # Create several files with preallocation and consume the remaining free space # via fallocate to the put the fs at ENOSPC. Create a set of background writers # to write into ENOSPC and cause the preallocation to be reclaimed and # reallocated to the new writers. _test_enospc() { dir=$1 rm -rf $dir/* TOTAL_PREALLOC=0 for i in $(seq 0 3); do _spec_prealloc_file $dir/pre$i done _consume_free_space $dir # consume 1/2 of the current preallocation across the set of 4 writers write_size=$((TOTAL_PREALLOC / 2 / 4)) for i in $(seq 0 3); do touch $dir/file.$i done for i in $(seq 0 3); do $XFS_IO_PROG -f -c "pwrite 0 $write_size" $dir/file.$i \ >> $seqres.full & done wait } # Create preallocations accounted by both user and group quotas. Set the # associated quota hard limits to put them at EDQUOT. Verify that a new writer # reclaims the preallocated space and proceeds without error. _test_edquot() { dir=$1 rm -rf $dir/* TOTAL_PREALLOC=0 _spec_prealloc_file $dir/user chown $qa_user $dir/user _spec_prealloc_file $dir/group chgrp $qa_group $dir/group # writing to a file under both quotas means both will be reclaimed on # allocation failure touch $dir/file chown $qa_user $dir/file chgrp $qa_group $dir/file # put both quotas at EDQUOT blks=`$XFS_QUOTA_PROG -xc "quota -u $qa_user" $dir | \ tail -n 1 | awk '{ print $2 }'` $XFS_QUOTA_PROG -xc "limit -u bhard=${blks}k $qa_user" $dir blks=`$XFS_QUOTA_PROG -xc "quota -g $qa_group" $dir | \ tail -n 1 | awk '{ print $2 }'` $XFS_QUOTA_PROG -xc "limit -g bhard=${blks}k $qa_group" $dir # each quota has a single file worth of preallocation to reclaim. leave # some wiggle room and write to 1/3 the total. write_size=$((TOTAL_PREALLOC / 3)) $XFS_IO_PROG -c "pwrite 0 $write_size" $dir/file >> $seqres.full } # real QA test starts here _supported_fs xfs _supported_os Linux _require_scratch _require_xfs_io_command "falloc" _require_loop _require_quota _require_user _require_group rm -f $seqres.full echo "Silence is golden." _scratch_mkfs_xfs >> $seqres.full 2>&1 _scratch_mount # make sure the background eofblocks scanner doesn't interfere orig_sp_time=`cat /proc/sys/fs/xfs/speculative_prealloc_lifetime` echo 9999 > /proc/sys/fs/xfs/speculative_prealloc_lifetime LOOP_FILE=$SCRATCH_MNT/$seq.fs LOOP_MNT=$SCRATCH_MNT/$seq.mnt $MKFS_XFS_PROG -d "file=1,name=$LOOP_FILE,size=10g" >> $seqres.full 2>&1 mkdir -p $LOOP_MNT mount -t xfs -o loop,uquota,gquota $LOOP_FILE $LOOP_MNT || \ _fail "Failed to mount loop fs." _test_enospc $LOOP_MNT _test_edquot $LOOP_MNT umount $LOOP_MNT echo $orig_sp_time > /proc/sys/fs/xfs/speculative_prealloc_lifetime _scratch_unmount status=0 exit