summaryrefslogtreecommitdiff
path: root/tests/xfs/014
blob: be25c17645c69f0616b67cf13c898ea6c6b7220e (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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2014 Red Hat, Inc.  All Rights Reserved.
#
# FS QA Test No. xfs/014
#
# Test the behavior of XFS dynamic speculative preallocation at ENOSPC and
# EDQUOT conditions. Speculative preallocation allocates post-EOF space to files
# as they are extended. This test creates conditions where an fs is near a space
# limit with lingering, relatively significant preallocations and verifies that
# new writers reclaim said preallocations rather than prematurely fail with
# ENOSPC/EDQUOT.
#
. ./common/preamble
_begin_fstest auto enospc quick quota prealloc

# Import common functions.
. ./common/filter
. ./common/quota

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

# Create a file using a repeated open, extending write and close pattern. This
# causes the preallocation to persist after the file is closed. Preallocation
# will not be reclaimed unless the inode is evicted or we hit an allocation
# failure.
_spec_prealloc_file()
{
	local file=$1
	local prealloc_size=0
	local i=0

	# Now that we have background garbage collection processes that can be
	# triggered by low space/quota conditions, it's possible that we won't
	# succeed in creating a speculative preallocation on the first try.
	for ((tries = 0; tries < 5 && prealloc_size == 0; tries++)); do
		rm -f $file

		# a few file extending open-write-close cycles should be enough
		# to trigger the fs to retain preallocation. write 256k in 32k
		# intervals to be sure
		for i in $(seq 0 32768 262144); do
			$XFS_IO_PROG -f -c "pwrite $i 32k" $file >> $seqres.full
		done

		# write a 4k aligned amount of data to keep the calculations
		# simple
		$XFS_IO_PROG -c "pwrite 0 128m" $file >> $seqres.full

		size=`_get_filesize $file`
		blocks=`stat -c "%b" $file`
		blocksize=`stat -c "%B" $file`

		prealloc_size=$((blocks * blocksize - size))
	done

	if [ $prealloc_size -eq 0 ]; then
		echo "Warning: No speculative preallocation for $file after $tries iterations." \
			"Check use of the allocsize= mount option."
	fi

	# keep a running total of how much preallocation we've created
	TOTAL_PREALLOC=$((TOTAL_PREALLOC + prealloc_size))
}

_consume_free_space()
{
	dir=$1

	# allocate all but 10MB of available space
	freesp=`$DF_PROG -m $dir | $AWK_PROG '/^\// { print $5 - 10 }'`
	$XFS_IO_PROG -f -c "falloc 0 ${freesp}M" $dir/spc
}

# Create several files with preallocation and consume the remaining free space
# via fallocate to the put the fs at ENOSPC. Create a set of background writers
# to write into ENOSPC and cause the preallocation to be reclaimed and
# reallocated to the new writers.
_test_enospc()
{
	dir=$1

	rm -rf $dir/*

	TOTAL_PREALLOC=0
	for i in $(seq 0 3); do
		_spec_prealloc_file $dir/pre$i
	done

	_consume_free_space $dir

	# consume 1/2 of the current preallocation across the set of 4 writers
	write_size=$((TOTAL_PREALLOC / 2 / 4))
	for i in $(seq 0 3); do
		touch $dir/file.$i
	done
	for i in $(seq 0 3); do
		$XFS_IO_PROG -f -c "pwrite 0 $write_size" $dir/file.$i \
			>> $seqres.full &
	done

	wait
}

# Create preallocations accounted by both user and group quotas. Set the
# associated quota hard limits to put them at EDQUOT. Verify that a new writer
# reclaims the preallocated space and proceeds without error.
_test_edquot()
{
	dir=$1

	rm -rf $dir/*

	TOTAL_PREALLOC=0
	_spec_prealloc_file $dir/user
	chown $qa_user $dir/user

	_spec_prealloc_file $dir/group
	chgrp $qa_group $dir/group

	# writing to a file under both quotas means both will be reclaimed on
	# allocation failure
	touch $dir/file
	chown $qa_user $dir/file
	chgrp $qa_group $dir/file

	# put both quotas at EDQUOT
	blks=`$XFS_QUOTA_PROG -xc "quota -u $qa_user" $dir | \
		tail -n 1 | awk '{ print $2 }'`
	$XFS_QUOTA_PROG -xc "limit -u bhard=${blks}k $qa_user" $dir
	blks=`$XFS_QUOTA_PROG -xc "quota -g $qa_group" $dir | \
		tail -n 1 | awk '{ print $2 }'`
	$XFS_QUOTA_PROG -xc "limit -g bhard=${blks}k $qa_group" $dir

	# each quota has a single file worth of preallocation to reclaim. leave
	# some wiggle room and write to 1/3 the total.
	write_size=$((TOTAL_PREALLOC / 3))
	$XFS_IO_PROG -c "pwrite 0 $write_size" $dir/file >> $seqres.full
}

# real QA test starts here
_supported_fs xfs

_require_scratch
_require_xfs_io_command "falloc"
_require_loop
_require_quota
_require_user
_require_group

echo "Silence is golden."

_scratch_mkfs_xfs >> $seqres.full 2>&1
_scratch_mount

# make sure the background eofblocks scanner doesn't interfere
orig_sp_time=`cat /proc/sys/fs/xfs/speculative_prealloc_lifetime`
echo 9999 > /proc/sys/fs/xfs/speculative_prealloc_lifetime

LOOP_FILE=$SCRATCH_MNT/$seq.fs
LOOP_MNT=$SCRATCH_MNT/$seq.mnt

$MKFS_XFS_PROG -d "file=1,name=$LOOP_FILE,size=10g" >> $seqres.full 2>&1

mkdir -p $LOOP_MNT
mount -t xfs -o loop,uquota,gquota $LOOP_FILE $LOOP_MNT || \
	_fail "Failed to mount loop fs."

_test_enospc $LOOP_MNT
_test_edquot $LOOP_MNT

umount $LOOP_MNT

echo $orig_sp_time > /proc/sys/fs/xfs/speculative_prealloc_lifetime

_scratch_unmount

status=0
exit