summaryrefslogtreecommitdiff
path: root/tests/xfs/076
blob: a6ec0568b922114551c2129bdafc9e058c476d3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2015 Red Hat, Inc.  All Rights Reserved.
#
# FS QA Test No. xfs/076
#
# Verify that a filesystem with sparse inode support can allocate inodes in the
# event of free space fragmentation. This test is generic in nature but
# primarily relevant to filesystems that implement dynamic inode allocation
# (e.g., XFS).
#
# The test is inspired by inode allocation limitations on XFS when available
# free space is fragmented. XFS allocates inodes 64 at a time and thus requires
# an extent of length that depends on inode size (64 * isize / blksize).
#
# The test creates a small, sparse inode enabled filesystem. It fragments free
# space, allocates inodes to ENOSPC and then verifies that most of the available
# inodes (.i.e., free space) have been consumed.
#
. ./common/preamble
_begin_fstest auto enospc punch prealloc

# Import common functions.
. ./common/filter

# Override the default cleanup function.
_cleanup()
{
	cd /
	_scratch_unmount 2>/dev/null
	rm -f $tmp.*
}

_consume_freesp()
{
	file=$1

	# consume nearly all available space (leave ~1MB)
	avail=`_get_available_space $SCRATCH_MNT`
	filesizemb=$((avail / 1024 / 1024 - 1))
	$XFS_IO_PROG -fc "falloc 0 ${filesizemb}m" $file
}

# Allocate inodes in a directory until failure.
_alloc_inodes()
{
	dir=$1

	i=0
	while [ true ]; do
		touch $dir/$i 2>> $seqres.full || break
		i=$((i + 1))
	done
}

# real QA test starts here

if [ -n "$SCRATCH_RTDEV" ]; then
	# ./check won't know we unset SCRATCH_RTDEV
	_require_scratch_nocheck
else
	_require_scratch
fi
_require_xfs_io_command "falloc"
_require_xfs_io_command "fpunch"
_require_xfs_sparse_inodes

# Disable the scratch rt device to avoid test failures relating to the rt
# bitmap consuming all the free space in our small data device.
unset SCRATCH_RTDEV

_scratch_mkfs "-d size=96m -m crc=1 -i sparse" | tee -a $seqres.full |
	_filter_mkfs > /dev/null 2> $tmp.mkfs
. $tmp.mkfs	# for isize

_scratch_mount

# Calculate the fs inode chunk size based on the inode size and fixed 64-inode
# record. This value is used as the target level of free space fragmentation
# induced by the test (i.e., max size of free extents). We don't need to go
# smaller than a full chunk because the XFS block allocator tacks on alignment
# requirements to the size of the requested allocation. In other words, a chunk
# sized free chunk is not enough to guarantee a successful chunk sized
# allocation.
CHUNK_SIZE=$((isize * 64))

_consume_freesp $SCRATCH_MNT/spc

# Now that the fs is nearly full, punch holes in every other $CHUNK_SIZE range
# of the space consumer file. This should ensure that most freed extents are not
# contiguous with any others and thus sufficiently fragment free space. After
# each hole punch, allocate as many inodes as possible into the newly freed
# space. Note that we start at the end of the file and work backwards as a
# reverse allocation pattern increases the chances of both left and right sparse
# record merges.
offset=`_get_filesize $SCRATCH_MNT/spc`
offset=$((offset - $CHUNK_SIZE * 2))
while [ $offset -ge 0 ]; do
	$XFS_IO_PROG -c "fpunch $offset $CHUNK_SIZE" $SCRATCH_MNT/spc \
		2>> $seqres.full || _fail "fpunch failed"

	# allocate as many inodes as possible
	mkdir -p $SCRATCH_MNT/offset.$offset > /dev/null 2>&1
	_alloc_inodes $SCRATCH_MNT/offset.$offset

	offset=$((offset - $CHUNK_SIZE * 2))
done

# check that we've hit at least 95% inode usage
iusepct=`_get_used_inode_percent $SCRATCH_MNT`
_within_tolerance "iusepct" $iusepct 100 5 0 -v

status=0
exit