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
|
#!/usr/bin/env bash
# group: rw
#
# Test preallocated growth of qcow2 images
#
# Copyright (C) 2017 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=mreitz@redhat.com
seq=$(basename $0)
echo "QA output created by $seq"
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
get_image_size_on_host()
{
echo $(($(stat -c '%b * %B' "$TEST_IMG_FILE")))
}
# get standard environment and filters
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto file
# Growing a file with a backing file (without preallocation=full or
# =falloc) requires zeroing the newly added area, which is impossible
# to do quickly for v2 images, and hence is unsupported.
_unsupported_imgopts 'compat=0.10'
if [ -z "$TEST_IMG_FILE" ]; then
TEST_IMG_FILE=$TEST_IMG
fi
# Test whether we are running on a broken XFS version. There is this
# bug:
# $ rm -f foo
# $ touch foo
# $ block_size=4096 # Your FS's block size
# $ fallocate -o $((block_size / 2)) -l $block_size foo
# $ LANG=C xfs_bmap foo | grep hole
# 1: [8..15]: hole
#
# The problem is that the XFS driver rounds down the offset and
# rounds up the length to the block size, but independently. As
# such, it only allocates the first block in the example above,
# even though it should allocate the first two blocks (because our
# request is to fallocate something that touches both the first
# two blocks).
#
# This means that when you then write to the beginning of the
# second block, the disk usage of the first two blocks grows.
#
# That is precisely what fallocate() promises, though: That when you
# write to an area that you have fallocated, no new blocks will have
# to be allocated.
touch "$TEST_IMG_FILE"
# Assuming there is no FS with a block size greater than 64k
fallocate -o 65535 -l 2 "$TEST_IMG_FILE"
len0=$(get_image_size_on_host)
# Write to something that in theory we have just fallocated
# (Thus, the on-disk size should not increase)
poke_file "$TEST_IMG_FILE" 65536 42
len1=$(get_image_size_on_host)
if [ $len1 -gt $len0 ]; then
_notrun "the test filesystem's fallocate() is broken"
fi
rm -f "$TEST_IMG_FILE"
# Generally, we create some image with or without existing preallocation and
# then resize it. Then we write some data into the image and verify that its
# size does not change if we have used preallocation.
# With a cluster size of 512 B, one L2 table covers 64 * 512 B = 32 kB.
# One cluster of the L1 table covers 64 * 32 kB = 2 MB.
# There are multiple cases we want to test:
# (1) Grow an image without having to allocate a new L2 table.
# (2) Grow an image, having to allocate a new L2 table.
# (3) Grow an image, having to grow the L1 table.
# Therefore, we create an image that is 48 kB below 2 MB. Then:
# (1) We resize it to 2 MB - 32 kB. (+ 16 kB)
# (2) We resize it to 2 MB. (+ 48 kB)
# (3) We resize it to 2 MB + 32 kB. (+ 80 kB)
# in B
CREATION_SIZE=$((2 * 1024 * 1024 - 48 * 1024))
# 512 is the actual test -- but it's good to test 64k as well, just to be sure.
for cluster_size in 512 64k; do
# in kB
for GROWTH_SIZE in 16 48 80; do
for create_mode in off metadata falloc full; do
for growth_mode in off metadata falloc full; do
echo "--- cluster_size=$cluster_size growth_size=$GROWTH_SIZE create_mode=$create_mode growth_mode=$growth_mode ---"
_make_test_img -o "preallocation=$create_mode,cluster_size=$cluster_size" ${CREATION_SIZE}
$QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K
host_size_0=$(get_image_size_on_host)
file_length_0=$(stat -c '%s' "$TEST_IMG_FILE")
$QEMU_IO -c "write 0 $CREATION_SIZE" "$TEST_IMG" | _filter_qemu_io
host_size_1=$(get_image_size_on_host)
file_length_1=$(stat -c '%s' "$TEST_IMG_FILE")
$QEMU_IO -c "write $CREATION_SIZE ${GROWTH_SIZE}K" "$TEST_IMG" | _filter_qemu_io
host_size_2=$(get_image_size_on_host)
file_length_2=$(stat -c '%s' "$TEST_IMG_FILE")
# Test creation preallocation: Compare #0 against #1
if [ $create_mode != off ]; then
# The image length should not have grown
if [ $file_length_1 -gt $file_length_0 ]; then
echo "ERROR (create): Image length has grown from $file_length_0 to $file_length_1"
fi
if [ $create_mode != metadata ]; then
# The host size should not have grown either
if [ $host_size_1 -gt $host_size_0 ]; then
echo "ERROR (create): Host size has grown from $host_size_0 to $host_size_1"
fi
fi
fi
# Test resize preallocation: Compare #2 against #1
if [ $growth_mode != off ]; then
# The image length should not have grown
if [ $file_length_2 -gt $file_length_1 ]; then
echo "ERROR (grow): Image length has grown from $file_length_1 to $file_length_2"
fi
if [ $growth_mode != metadata ]; then
# The host size should not have grown either
if [ $host_size_2 -gt $host_size_1 ]; then
echo "ERROR (grow): Host size has grown from $host_size_1 to $host_size_2"
fi
fi
fi
echo
done
done
done
done
# Test image resizing using preallocation and unaligned offsets
$QEMU_IMG create -f raw "$TEST_IMG.base" 128k | _filter_img_create
$QEMU_IO -c 'write -q -P 1 0 128k' -f raw "$TEST_IMG.base"
for orig_size in 31k 33k; do
for dst_size in 96k 128k; do
for prealloc in metadata full; do
echo "--- Resizing image from $orig_size to $dst_size (preallocation=$prealloc) ---"
_make_test_img -F raw -b "$TEST_IMG.base" -o cluster_size=64k "$orig_size"
$QEMU_IMG resize -f "$IMGFMT" --preallocation="$prealloc" "$TEST_IMG" "$dst_size"
# The first part of the image should contain data from the backing file
$QEMU_IO -c "read -q -P 1 0 ${orig_size}" "$TEST_IMG"
# The resized part of the image should contain zeroes
$QEMU_IO -c "read -q -P 0 ${orig_size} 63k" "$TEST_IMG"
# If the image does not have an external data file we can also verify its
# actual size. The resized image should have 7 clusters:
# header, L1 table, L2 table, refcount table, refcount block, 2 data clusters
if ! _get_data_file "$TEST_IMG" > /dev/null; then
expected_file_length=$((65536 * 7))
file_length=$(stat -c '%s' "$TEST_IMG_FILE")
if [ "$file_length" != "$expected_file_length" ]; then
echo "ERROR: file length $file_length (expected $expected_file_length)"
fi
fi
echo
done
done
done
# success, all done
echo '*** done'
rm -f $seq.full
status=0
|