| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright (c) 2014 Red Hat, Inc. All Rights Reserved. |
| # |
| # 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. |
| # |
| 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=`_get_filesize $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 |
| |
| _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 |