xfstests: test speculative preallocation reclaim on ENOSPC/EDQUOT
[xfstests-dev.git] / tests / xfs / 014
1 #!/bin/bash
2 # FS QA Test No. xfs/014
3 #
4 # Test the behavior of XFS dynamic speculative preallocation at ENOSPC and
5 # EDQUOT conditions. Speculative preallocation allocates post-EOF space to files
6 # as they are extended. This test creates conditions where an fs is near a space
7 # limit with lingering, relatively significant preallocations and verifies that
8 # new writers reclaim said preallocations rather than prematurely fail with
9 # ENOSPC/EDQUOT.
10 #
11 #-----------------------------------------------------------------------
12 # Copyright (c) 2014 Red Hat, Inc.  All Rights Reserved.
13 #
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License as
16 # published by the Free Software Foundation.
17 #
18 # This program is distributed in the hope that it would be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write the Free Software Foundation,
25 # Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
26 #
27 #-----------------------------------------------------------------------
28 #
29
30 seq=`basename $0`
31 seqres=$RESULT_DIR/$seq
32 echo "QA output created by $seq"
33
34 here=`pwd`
35 tmp=/tmp/$$
36 status=1        # failure is the default!
37
38 # get standard environment, filters and checks
39 . ./common/rc
40 . ./common/filter
41 . ./common/quota
42
43 _cleanup()
44 {
45         cd /
46         umount $LOOP_MNT 2>/dev/null
47         umount $SCRATCH_MNT 2>/dev/null
48         rm -f $tmp.*
49 }
50 trap "_cleanup; exit \$status" 0 1 2 3 15
51
52 # Create a file using a repeated open, extending write and close pattern. This
53 # causes the preallocation to persist after the file is closed. Preallocation
54 # will not be reclaimed unless the inode is evicted or we hit an allocation
55 # failure.
56 _spec_prealloc_file()
57 {
58         file=$1
59
60         rm -f $file
61
62         # a few file extending open-write-close cycles should be enough to
63         # trigger the fs to retain preallocation. write 256k in 32k intervals to
64         # be sure
65         for i in $(seq 0 32768 262144); do
66                 $XFS_IO_PROG -f -c "pwrite $i 32k" $file >> $seqres.full
67         done
68
69         # write a 4k aligned amount of data to keep the calculations simple
70         $XFS_IO_PROG -c "pwrite 0 128m" $file >> $seqres.full
71
72         size=`stat -c "%s" $file`
73         blocks=`stat -c "%b" $file`
74         blocksize=`stat -c "%B" $file`
75
76         prealloc_size=$((blocks * blocksize - size))
77         if [ $prealloc_size -eq 0 ]; then
78                 echo "Warning: No speculative preallocation for $file." \
79                         "Check use of the allocsize= mount option."
80         fi
81
82         # keep a running total of how much preallocation we've created
83         TOTAL_PREALLOC=$((TOTAL_PREALLOC + prealloc_size))
84 }
85
86 _consume_free_space()
87 {
88         dir=$1
89
90         # allocate all but 10MB of available space
91         freesp=`df -m $dir | awk '/^\// { print $4 - 10 }'`
92         $XFS_IO_PROG -f -c "falloc 0 ${freesp}M" $dir/spc
93 }
94
95 # Create several files with preallocation and consume the remaining free space
96 # via fallocate to the put the fs at ENOSPC. Create a set of background writers
97 # to write into ENOSPC and cause the preallocation to be reclaimed and
98 # reallocated to the new writers.
99 _test_enospc()
100 {
101         dir=$1
102
103         rm -rf $dir/*
104
105         TOTAL_PREALLOC=0
106         for i in $(seq 0 3); do
107                 _spec_prealloc_file $dir/pre$i
108         done
109
110         _consume_free_space $dir
111
112         # consume 1/2 of the current preallocation across the set of 4 writers
113         write_size=$((TOTAL_PREALLOC / 2 / 4))
114         for i in $(seq 0 3); do
115                 $XFS_IO_PROG -f -c "pwrite 0 $write_size" $dir/file.$i \
116                         >> $seqres.full &
117         done
118
119         wait
120 }
121
122 # Create preallocations accounted by both user and group quotas. Set the
123 # associated quota hard limits to put them at EDQUOT. Verify that a new writer
124 # reclaims the preallocated space and proceeds without error.
125 _test_edquot()
126 {
127         dir=$1
128
129         rm -rf $dir/*
130
131         TOTAL_PREALLOC=0
132         _spec_prealloc_file $dir/user
133         chown $qa_user $dir/user
134
135         _spec_prealloc_file $dir/group
136         chgrp $qa_group $dir/group
137
138         # writing to a file under both quotas means both will be reclaimed on
139         # allocation failure
140         touch $dir/file
141         chown $qa_user $dir/file
142         chgrp $qa_group $dir/file
143
144         # put both quotas at EDQUOT
145         blks=`$XFS_QUOTA_PROG -xc "quota -u $qa_user" $dir | \
146                 tail -n 1 | awk '{ print $2 }'`
147         $XFS_QUOTA_PROG -xc "limit -u bhard=${blks}k $qa_user" $dir
148         blks=`$XFS_QUOTA_PROG -xc "quota -g $qa_grup" $dir | \
149                 tail -n 1 | awk '{ print $2 }'`
150         $XFS_QUOTA_PROG -xc "limit -g bhard=${blks}k $qa_group" $dir
151
152         # each quota has a single file worth of preallocation to reclaim. leave
153         # some wiggle room and write to 1/3 the total.
154         write_size=$((TOTAL_PREALLOC / 3))
155         $XFS_IO_PROG -c "pwrite 0 $write_size" $dir/file >> $seqres.full
156 }
157
158 # real QA test starts here
159 _supported_fs xfs
160 _supported_os Linux
161
162 _require_scratch
163 _require_xfs_io_command "falloc"
164 _require_loop
165 _require_quota
166 _require_user
167 _require_group
168
169 rm -f $seqres.full
170
171 echo "Silence is golden."
172
173 _scratch_mkfs_xfs >> $seqres.full 2>&1
174 _scratch_mount
175
176 # make sure the background eofblocks scanner doesn't interfere
177 orig_sp_time=`cat /proc/sys/fs/xfs/speculative_prealloc_lifetime`
178 echo 9999 > /proc/sys/fs/xfs/speculative_prealloc_lifetime
179
180 LOOP_FILE=$SCRATCH_MNT/$seq.fs
181 LOOP_MNT=$SCRATCH_MNT/$seq.mnt
182
183 $MKFS_XFS_PROG -d "file=1,name=$LOOP_FILE,size=10g" >> $seqres.full 2>&1
184
185 mkdir -p $LOOP_MNT
186 mount -t xfs -o loop,uquota,gquota $LOOP_FILE $LOOP_MNT || \
187         _fail "Failed to mount loop fs."
188
189 _test_enospc $LOOP_MNT
190 _test_edquot $LOOP_MNT
191
192 umount $LOOP_MNT
193
194 echo $orig_sp_time > /proc/sys/fs/xfs/speculative_prealloc_lifetime
195
196 umount $SCRATCH_MNT
197 _check_scratch_fs
198
199 status=0
200 exit