xfs: test inode allocation with fragmented free space
[xfstests-dev.git] / tests / xfs / 076
1 #!/bin/bash
2 # FS QA Test No. xfs/076
3 #
4 # Verify that a filesystem with sparse inode support can allocate inodes in the
5 # event of free space fragmentation. This test is generic in nature but
6 # primarily relevant to filesystems that implement dynamic inode allocation
7 # (e.g., XFS).
8 #
9 # The test is inspired by inode allocation limitations on XFS when available
10 # free space is fragmented. XFS allocates inodes 64 at a time and thus requires
11 # an extent of length that depends on inode size (64 * isize / blksize).
12 #
13 # The test creates a small, sparse inode enabled filesystem. It fragments free
14 # space, allocates inodes to ENOSPC and then verifies that most of the available
15 # inodes (.i.e., free space) have been consumed.
16 #
17 #-----------------------------------------------------------------------
18 # Copyright (c) 2015 Red Hat, Inc.  All Rights Reserved.
19 #
20 # This program is free software; you can redistribute it and/or
21 # modify it under the terms of the GNU General Public License as
22 # published by the Free Software Foundation.
23 #
24 # This program is distributed in the hope that it would be useful,
25 # but WITHOUT ANY WARRANTY; without even the implied warranty of
26 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27 # GNU General Public License for more details.
28 #
29 # You should have received a copy of the GNU General Public License
30 # along with this program; if not, write the Free Software Foundation,
31 # Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
32 #
33 #-----------------------------------------------------------------------
34 #
35
36 seq=`basename $0`
37 seqres=$RESULT_DIR/$seq
38 echo "QA output created by $seq"
39
40 here=`pwd`
41 tmp=/tmp/$$
42 status=1        # failure is the default!
43
44 # get standard environment, filters and checks
45 . ./common/rc
46 . ./common/filter
47
48 _cleanup()
49 {
50         cd /
51         umount $SCRATCH_MNT 2>/dev/null
52         rm -f $tmp.*
53 }
54 trap "_cleanup; exit \$status" 0 1 2 3 15
55
56 _consume_freesp()
57 {
58         file=$1
59
60         # consume nearly all available space (leave ~1MB)
61         avail=`_get_available_space $SCRATCH_MNT`
62         filesizemb=$((avail / 1024 / 1024 - 1))
63         $XFS_IO_PROG -fc "falloc 0 ${filesizemb}m" $file
64 }
65
66 # Allocate inodes in a directory until failure.
67 _alloc_inodes()
68 {
69         dir=$1
70
71         i=0
72         while [ true ]; do
73                 touch $dir/$i 2>> $seqres.full || break
74                 i=$((i + 1))
75         done
76 }
77
78 # real QA test starts here
79 _supported_os Linux
80
81 _require_scratch
82 _require_xfs_io_command "falloc"
83 _require_xfs_io_command "fpunch"
84 _require_xfs_sparse_inodes
85
86 rm -f $seqres.full
87
88 _scratch_mkfs "-d size=50m -m crc=1 -i sparse" |
89         _filter_mkfs > /dev/null 2> $tmp.mkfs
90 . $tmp.mkfs     # for isize
91
92 _scratch_mount
93
94 # Calculate the fs inode chunk size based on the inode size and fixed 64-inode
95 # record. This value is used as the target level of free space fragmentation
96 # induced by the test (i.e., max size of free extents). We don't need to go
97 # smaller than a full chunk because the XFS block allocator tacks on alignment
98 # requirements to the size of the requested allocation. In other words, a chunk
99 # sized free chunk is not enough to guarantee a successful chunk sized
100 # allocation.
101 CHUNK_SIZE=$((isize * 64))
102
103 _consume_freesp $SCRATCH_MNT/spc
104
105 # Now that the fs is nearly full, punch holes in every other $CHUNK_SIZE range
106 # of the space consumer file. This should ensure that most freed extents are not
107 # contiguous with any others and thus sufficiently fragment free space. After
108 # each hole punch, allocate as many inodes as possible into the newly freed
109 # space. Note that we start at the end of the file and work backwards as a
110 # reverse allocation pattern increases the chances of both left and right sparse
111 # record merges.
112 offset=`stat -c "%s" $SCRATCH_MNT/spc`
113 offset=$((offset - $CHUNK_SIZE * 2))
114 while [ $offset -ge 0 ]; do
115         $XFS_IO_PROG -c "fpunch $offset $CHUNK_SIZE" $SCRATCH_MNT/spc \
116                 2>> $seqres.full || _fail "fpunch failed"
117
118         # allocate as many inodes as possible
119         mkdir -p $SCRATCH_MNT/offset.$offset > /dev/null 2>&1
120         _alloc_inodes $SCRATCH_MNT/offset.$offset
121
122         offset=$((offset - $CHUNK_SIZE * 2))
123 done
124
125 # check that we've hit at least 95% inode usage
126 iusepct=`_get_used_inode_percent $SCRATCH_MNT`
127 _within_tolerance "iusepct" $iusepct 100 5 0 -v
128
129 status=0
130 exit