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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
|
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2021 Facebook. All Rights Reserved.
#
# FS QA Test 250
#
# Test the new /sys/fs/btrfs/<uuid>/allocation/<block-type>/chunk_size
# setting. This setting allows the admin to change the chunk size
# setting for the next allocation.
#
# Test 1:
# Allocate storage for all three block types (data, metadata and system)
# with the default chunk size.
#
# Test 2:
# Set a new chunk size to double the default size and allocate space
# for all new block types with the new chunk size.
#
# Test 3:
# Pick an allocation size that is used in a loop and make sure the last
# allocation cannot be partially fullfilled.
#
# Note: Variable naming uses the following convention: if a variable name
# ends in "_B" then its a byte value, if it ends in "_MB" then the
# value is in megabytes.
#
. ./common/preamble
_begin_fstest auto
seq=`basename $0`
seqres="${RESULT_DIR}/${seq}"
# Parse a size string which is in the format "XX.XXMib".
#
# Parameters:
# - (IN) Block group type (Data, Metadata, System)
# - (INOUT) Variable to store block group size in MB
#
parse_size_string() {
local SIZE=$(echo "$1" | $AWK_PROG 'match($0, /([0-9.]+)/) { print substr($0, RSTART, RLENGTH) }')
eval $2="${SIZE%.*}"
}
# Determine the size of the device in MB.
#
# Parameters:
# - (INOUT) Variable to store device size in MB
#
device_size() {
btrfs fi show ${SCRATCH_MNT} --mbytes | grep devid >> $seqres.full
local SIZE=$($BTRFS_UTIL_PROG filesystem show ${SCRATCH_MNT} --mbytes | grep devid)
parse_size_string $(echo "${SIZE}" | $AWK_PROG '{print $4}') SIZE_ALLOC
eval $1=${SIZE_ALLOC%.*}
}
# Determine the free space of a block group in MB.
#
# Parameters:
# - (INOUT) Variable to store free space in MB
#
free_space() {
local SIZE=$($BTRFS_UTIL_PROG filesystem show ${SCRATCH_MNT} --mbytes | grep devid)
parse_size_string $(echo "${SIZE}" | $AWK_PROG '{print $4}') SIZE_ALLOC
parse_size_string $(echo "${SIZE}" | $AWK_PROG '{print $6}') SIZE_USED
eval $1=$(expr ${SIZE_ALLOC} - ${SIZE_USED})
}
# Determine how much space in MB has been allocated to a block group.
#
# Parameters:
# - (IN) Block group type (Data, Metadata, System)
# - (INOUT) Variable to store block group size in MB
#
alloc_size() {
local SIZE_STRING=$($BTRFS_UTIL_PROG filesystem df ${SCRATCH_MNT} -m | grep "$1" | $AWK_PROG '{print $3}')
parse_size_string ${SIZE_STRING} BLOCK_GROUP_SIZE
eval $2="${BLOCK_GROUP_SIZE}"
}
. ./common/filter
_supported_fs btrfs
_require_test
_require_scratch
# The chunk size on zoned mode is fixed to the zone size
_require_non_zoned_device "$SCRATCH_DEV"
# Delete log file if it exists.
rm -f "${seqres}.full"
# Make filesystem. 10GB is needed to test different chunk sizes for
# metadata and data and the default size for volumes > 5GB is different.
_scratch_mkfs_sized $((10 * 1024 * 1024 * 1024)) >> $seqres.full 2>&1
_scratch_mount >> $seqres.full 2>&1
# Check if there is sufficient sysfs support.
_require_fs_sysfs allocation/metadata/chunk_size
_require_fs_sysfs allocation/metadata/force_chunk_alloc
# Get free space.
free_space FREE_SPACE_MB
device_size DEVICE_SIZE_MB
echo "free space = ${FREE_SPACE_MB}MB" >> ${seqres}.full
# If device is a symbolic link, get block device.
SCRATCH_BDEV=$(_real_dev $SCRATCH_DEV)
# Get chunk sizes.
echo "Capture default chunk sizes."
# The sysfs interface has only exported chunk_size (10G by default),
# but no stripe_size (1G by default) exported.
#
# Btrfs calculate the real chunk to be allocated using both limits,
# Thus the default 10G chunk size can only be fulfilled by something
# like 10 disk RAID0
#
# The proper solution should be exporting the stripe_size limit too,
# but that may take several cycles, here we just use hard coded 1G
# as the expected chunk size.
FIRST_DATA_CHUNK_SIZE_B=$((1024 * 1024 * 1024))
# For metadata, we are safe to use the exported value, as the default
# metadata chunk size limit is already smaller than its stripe size.
FIRST_METADATA_CHUNK_SIZE_B=$(_get_fs_sysfs_attr ${SCRATCH_BDEV} allocation/metadata/chunk_size)
echo "Orig Data chunk size = ${FIRST_DATA_CHUNK_SIZE_B}" >> ${seqres}.full
echo "Orig Metaata chunk size = ${FIRST_METADATA_CHUNK_SIZE_B}" >> ${seqres}.full
INIT_ALLOC_SIZE_MB=$(expr \( ${FIRST_DATA_CHUNK_SIZE_B} + ${FIRST_METADATA_CHUNK_SIZE_B} \) / 1024 / 1024)
echo "Allocation size for initial allocation = ${INIT_ALLOC_SIZE_MB}MB" >> $seqres.full
#
# Do first allocation with the default chunk sizes for the different block
# types.
#
echo "First allocation."
alloc_size "Data" DATA_SIZE_START_MB
alloc_size "Metadata" METADATA_SIZE_START_MB
echo "Block group Data alloc size = ${DATA_SIZE_START_MB}MB" >> $seqres.full
echo "Block group Metadata alloc size = ${METADATA_SIZE_START_MB}MB" >> $seqres.full
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/data/force_chunk_alloc 1
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/metadata/force_chunk_alloc 1
alloc_size "Data" FIRST_DATA_SIZE_MB
alloc_size "Metadata" FIRST_METADATA_SIZE_MB
echo "First block group Data alloc size = ${FIRST_DATA_SIZE_MB}MB" >> ${seqres}.full
echo "First block group Metadata alloc size = ${FIRST_METADATA_SIZE_MB}MB" >> ${seqres}.full
free_space FREE_SPACE_AFTER
echo "Free space after first allocation = ${FREE_SPACE_AFTER}MB" >> ${seqres}.full
#
# Do allocation with the doubled chunk sizes for the different block types.
#
echo "Second allocation."
SECOND_DATA_CHUNK_SIZE_B=$(expr ${FIRST_DATA_CHUNK_SIZE_B} \* 2)
SECOND_METADATA_CHUNK_SIZE_B=$(expr ${FIRST_METADATA_CHUNK_SIZE_B} \* 2)
echo "Second block group Data calc alloc size = ${SECOND_DATA_CHUNK_SIZE_B}" >> $seqres.full
echo "Second block group Metadata calc alloc size = ${SECOND_METADATA_CHUNK_SIZE_B}" >> $seqres.full
# Stripe size is limited to 10% of device size.
if [[ ${SECOND_DATA_CHUNK_SIZE_B} -gt $(expr ${DEVICE_SIZE_MB} \* 10 \* 1024 \* 1024 / 100) ]]; then
SECOND_DATA_CHUNK_SIZE_B=$(expr ${DEVICE_SIZE_MB} \* 10 / 100 \* 1024 \* 1024)
fi
if [[ ${SECOND_METADATA_CHUNK_SIZE_B} -gt $(expr ${DEVICE_SIZE_MB} \* 10 \* 1024 \* 1024 / 100) ]]; then
SECOND_METADATA_CHUNK_SIZE_B=$(expr ${DEVICE_SIZE_MB} \* 10 / 100 \* 1024 \* 1024)
fi
echo "Second block group Data alloc size = ${SECOND_DATA_CHUNK_SIZE_B}" >> $seqres.full
echo "Second block group Metadata alloc size = ${SECOND_METADATA_CHUNK_SIZE_B}" >> $seqres.full
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/data/chunk_size ${SECOND_DATA_CHUNK_SIZE_B}
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/metadata/chunk_size ${SECOND_METADATA_CHUNK_SIZE_B}
SECOND_DATA_CHUNK_SIZE_READ_B=$(_get_fs_sysfs_attr ${SCRATCH_BDEV} allocation/data/chunk_size)
SECOND_METADATA_CHUNK_SIZE_READ_B=$(_get_fs_sysfs_attr ${SCRATCH_BDEV} allocation/metadata/chunk_size)
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/data/force_chunk_alloc 1
echo "Allocated data chunk" >> $seqres.full
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/metadata/force_chunk_alloc 1
echo "Allocated metadata chunk" >> $seqres.full
alloc_size "Data" SECOND_DATA_SIZE_MB
alloc_size "Metadata" SECOND_METADATA_SIZE_MB
alloc_size "System" SECOND_SYSTEM_SIZE_MB
echo "Calculate request size so last memory allocation cannot be completely fullfilled."
free_space FREE_SPACE_MB
# Find request size whose space allocation cannot be completely fullfilled.
THIRD_DATA_CHUNK_SIZE_MB=$(expr 256)
until [ ${THIRD_DATA_CHUNK_SIZE_MB} -gt $(expr 7 \* 1024) ]; do
if [ $((FREE_SPACE_MB%THIRD_DATA_CHUNK_SIZE_MB)) -ne 0 ]; then
break
fi
THIRD_DATA_CHUNK_SIZE_MB=$((THIRD_DATA_CHUNK_SIZE_MB+256))
done
if [[ ${THIRD_DATA_CHUNK_SIZE_MB} -eq $(expr 7 \* 1024) ]]; then
_fail "Cannot find allocation size for partial block allocation."
fi
THIRD_DATA_CHUNK_SIZE_B=$(expr ${THIRD_DATA_CHUNK_SIZE_MB} \* 1024 \* 1024)
echo "Allocation size in mb = ${THIRD_DATA_CHUNK_SIZE_MB}" >> ${seqres}.full
echo "Allocation size in bytes = ${THIRD_DATA_CHUNK_SIZE_B}" >> ${seqres}.full
#
# Do allocation until free space is exhausted.
#
echo "Third allocation."
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/data/chunk_size ${THIRD_DATA_CHUNK_SIZE_B}
free_space FREE_SPACE_MB
while [ $FREE_SPACE_MB -gt $THIRD_DATA_CHUNK_SIZE_MB ]; do
alloc_size "Data" THIRD_DATA_SIZE_MB
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/data/force_chunk_alloc 1
free_space FREE_SPACE_MB
done
alloc_size "Data" FOURTH_DATA_SIZE_MB
#
# Force chunk allocation of system block type must fail.
#
echo "Force allocation of system block type must fail."
_set_fs_sysfs_attr ${SCRATCH_BDEV} allocation/system/force_chunk_alloc 1 2>/dev/null
#
# Verification of initial allocation.
#
echo "Verify first allocation."
FIRST_DATA_CHUNK_SIZE_MB=$(expr ${FIRST_DATA_CHUNK_SIZE_B} / 1024 / 1024)
FIRST_METADATA_CHUNK_SIZE_MB=$(expr ${FIRST_METADATA_CHUNK_SIZE_B} / 1024 / 1024)
# if [[ $(expr ${FIRST_DATA_CHUNK_SIZE_MB} + ${DATA_SIZE_START_MB}) -ne $(expr ${FIRST_DATA_SIZE_MB}) ]]; then
if [[ $(expr ${FIRST_DATA_CHUNK_SIZE_MB} + ${DATA_SIZE_START_MB}) -ne ${FIRST_DATA_SIZE_MB} ]]; then
echo "Initial data allocation not correct."
fi
if [[ $(expr ${FIRST_METADATA_CHUNK_SIZE_MB} + ${METADATA_SIZE_START_MB}) -ne ${FIRST_METADATA_SIZE_MB} ]]; then
echo "Initial metadata allocation not correct."
fi
#
# Verification of second allocation.
#
echo "Verify second allocation."
SECOND_DATA_CHUNK_SIZE_MB=$(expr ${SECOND_DATA_CHUNK_SIZE_B} / 1024 / 1024)
SECOND_METADATA_CHUNK_SIZE_MB=$(expr ${SECOND_METADATA_CHUNK_SIZE_B} / 1024 / 1024)
if [[ ${SECOND_DATA_CHUNK_SIZE_B} -ne ${SECOND_DATA_CHUNK_SIZE_READ_B} ]]; then
echo "Value written to allocation/data/chunk_size and read value are different."
fi
if [[ ${SECOND_METADATA_CHUNK_SIZE_B} -ne ${SECOND_METADATA_CHUNK_SIZE_READ_B} ]]; then
echo "Value written to allocation/metadata/chunk_size and read value are different."
fi
if [[ $(expr ${SECOND_DATA_CHUNK_SIZE_MB} + ${FIRST_DATA_SIZE_MB}) -ne ${SECOND_DATA_SIZE_MB} ]]; then
echo "Data allocation after chunk size change not correct."
fi
if [[ $(expr ${SECOND_METADATA_CHUNK_SIZE_MB} + ${FIRST_METADATA_SIZE_MB}) -ne ${SECOND_METADATA_SIZE_MB} ]]; then
echo "Metadata allocation after chunk size change not correct."
fi
#
# Verification of third allocation.
#
echo "Verify third allocation."
if [[ ${FREE_SPACE_MB} -gt ${THIRD_DATA_CHUNK_SIZE_MB} ]]; then
echo "Free space after allocating over memlimit is too high."
fi
# The + 1 is required as 1MB is always kept as free space.
if [[ $(expr ${THIRD_DATA_CHUNK_SIZE_MB} + ${THIRD_DATA_SIZE_MB} + 1) -le $(expr ${FOURTH_DATA_SIZE_MB}) ]]; then
echo "Allocation until out of memory: last memory allocation size is not correct."
fi
status=0
exit
|