summaryrefslogtreecommitdiff
path: root/tests/generic/619
blob: c4bdfbced3f732a6f646b1ca133a74c5014593bb (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
187
188
189
190
191
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2020 IBM Corporation. All Rights Reserved.
#
# FS QA Test 619
#
# ENOSPC regression test in a multi-threaded scenario. Test allocation
# strategies of the file system and validate space anomalies as reported by
# the system versus the allocated by the program.
#
# The test is motivated by a bug in ext4 systems where-in ENOSPC is
# reported by the file system even though enough space for allocations is
# available[1].
# [1]: https://patchwork.ozlabs.org/patch/1294003
#
# Linux kernel patch series that fixes the above regression:
# 53f86b170dfa ("ext4: mballoc: add blocks to PA list under same spinlock
#               after allocating blocks")
# cf5e2ca6c990 ("ext4: mballoc: refactor ext4_mb_discard_preallocations()")
# 07b5b8e1ac40 ("ext4: mballoc: introduce pcpu seqcnt for freeing PA to
#               improve ENOSPC handling")
# 8ef123fe02ca ("ext4: mballoc: refactor ext4_mb_good_group()")
# 993778306e79 ("ext4: mballoc: use lock for checking free blocks while
#               retrying")
#
. ./common/preamble
_begin_fstest auto rw enospc prealloc

FS_SIZE=$((240*1024*1024)) # 240MB
DEBUG=1 # set to 0 to disable debug statements in shell and c-prog
FACT=0.7

# Disk allocation methods
FALLOCATE=1
FTRUNCATE=2

# Helps to build TEST_VECTORS
SMALL_FILE_SIZE=$((512 * 1024)) # in Bytes
BIG_FILE_SIZE=$((1536 * 1024))  # in Bytes
MIX_FILE_SIZE=$((2048 * 1024))  # (BIG + SMALL small file size)

# Import common functions.
. ./common/filter

# Modify as appropriate.
_supported_fs generic
_require_scratch
_require_test_program "t_enospc"
_require_xfs_io_command "falloc"

debug()
{
	if [ $DEBUG -eq 1 ]; then
		echo "$1" >> $seqres.full
	fi
}

# Calculate the number of threads needed to fill the disk space
# Arguments
# $1: the size of a file
# $2: ratio in which $1 file should be split into multiple files.
# $3: percentage of the disk space should be used during the test
# Calculate the number of threads needed to fill the disk space
calc_thread_cnt()
{
	local file_ratio_unit=$1
	local file_ratio=$2
	local disk_saturation=$3
	local tot_avail_size
	local avail_size
	local thread_cnt

	IFS=',' read -ra fratio <<< $file_ratio
	file_ratio_cnt=${#fratio[@]}

	tot_avail_size=$($DF_PROG --block-size=1 $SCRATCH_MNT | $AWK_PROG 'FNR == 2 { print $5 }')
	avail_size=$(echo $tot_avail_size*$disk_saturation | $BC_PROG)
	thread_cnt=$(echo "$file_ratio_cnt*($avail_size/$file_ratio_unit)" | $BC_PROG)

	debug "Total available size: $tot_avail_size"
	debug "Available size: $avail_size"
	debug "Thread count: $thread_cnt"

	echo ${thread_cnt}
}

# Arguments
# $1: a string containing test configuration separated by a colon.
#     $1 is treated as an array of arguments to the function.
#     Description of each array element is given below.
#
# @1: name of the test
# @2: thread in t_enospec exerciser will allocate file of @2 size
# @3: defines the proportion in which the file size defined in @2
#     should be divided into two files.
#     (valid @3: more than two values are not allowed)
#                values should be comma separated)
#                sum of all values must be 1)
# @4: define the percentage of available memory should be used to
#     during the test.
# @5: defines the disk allocation method (fallocate/ftruncate)
# @6: number of the test should run
run_testcase()
{
	IFS=':' read -ra args <<< $1
	local test_name=${args[0]}
	local file_ratio_unit=${args[1]}
	local file_ratio=${args[2]}
	local disk_saturation=${args[3]}
	local disk_alloc_method=${args[4]}
	local test_iteration_cnt=${args[5]}
	local extra_args=""
	local thread_cnt

	if [ "$disk_alloc_method" == "$FALLOCATE" ]; then
		extra_args="$extra_args -f"
	fi

	# enable the debug statements in c program
	if [ "$DEBUG" -eq 1 ]; then
		extra_args="$extra_args -v"
	fi

	debug "============ Test details start ============"
	debug "Test name: $test_name"
	debug "File ratio unit: $file_ratio_unit"
	debug "File ratio: $file_ratio"
	debug "Disk saturation $disk_saturation"
	debug "Disk alloc method $disk_alloc_method"
	debug "Test iteration count: $test_iteration_cnt"
	debug "Extra arg: $extra_args"

	for i in $(eval echo "{1..$test_iteration_cnt}"); do
		# Setup the device
		_scratch_mkfs_sized $FS_SIZE >> $seqres.full 2>&1
		_scratch_mount

		debug "===== Test: $test_name iteration: $i starts ====="
		thread_cnt=$(calc_thread_cnt $file_ratio_unit $file_ratio $disk_saturation)

		# Start the test
		$here/src/t_enospc -t $thread_cnt -s $file_ratio_unit -r $file_ratio -p $SCRATCH_MNT $extra_args >> $seqres.full

		status=$(echo $?)
		if [ $status -ne 0 ]; then
			use_per=$($DF_PROG -h | grep $SCRATCH_MNT | awk '{print substr($6, 1, length($6)-1)}' | $BC_PROG)
			alloc_per=$(echo "$FACT * 100" | $BC_PROG)
			# We are here since t_enospc failed with an error code.
			# If the used filesystem space is still < available space - that means
			# the test failed due to FS wrongly reported ENOSPC.
			if [ $(echo "$use_per < $alloc_per" | $BC_PROG) -ne 0 ]; then
				if [ $status -eq 134 ]; then
					# SIGABRT asserted exit code = 134
					echo "FAIL: Aborted assertion faliure"
				elif [ $status -eq 7 ]; then
					# SIGBUS asserted exit code = 7
					echo "FAIL: ENOSPC BUS faliure"
				fi
				echo "$test_name failed at iteration count: $i"
				echo "$($DF_PROG -h $SCRATCH_MNT)"
				echo "Allocated: $alloc_per% Used: $use_per%"
				exit
			fi
		fi

		# Make space for other tests
		_scratch_unmount

		debug "===== Test: $test_name iteration: $i ends ====="
	done
	debug "============ Test details end ============="
}

declare -a TEST_VECTORS=(
# test-name:file-ratio-unit:file-ratio:disk-saturation:disk-alloc-method:test-iteration-cnt
"Small-file-fallocate-test:$SMALL_FILE_SIZE:1:$FACT:$FALLOCATE:3"
"Big-file-fallocate-test:$BIG_FILE_SIZE:1:$FACT:$FALLOCATE:3"
"Mix-file-fallocate-test:$MIX_FILE_SIZE:0.75,0.25:$FACT:$FALLOCATE:3"
"Small-file-ftruncate-test:$SMALL_FILE_SIZE:1:$FACT:$FTRUNCATE:3"
"Big-file-ftruncate-test:$BIG_FILE_SIZE:1:$FACT:$FTRUNCATE:3"
"Mix-file-ftruncate-test:$MIX_FILE_SIZE:0.75,0.25:$FACT:$FTRUNCATE:3"
)

# real QA test starts here
for i in "${TEST_VECTORS[@]}"; do
	run_testcase $i
done

echo "Silence is golden"
status=0
exit