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