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