1
The following changes since commit 13d5f87cc3b94bfccc501142df4a7b12fee3a6e7:
1
The following changes since commit 9db3065c62a983286d06c207f4981408cf42184d:
2
2
3
Merge remote-tracking branch 'remotes/rth-gitlab/tags/pull-axp-20210628' into staging (2021-06-29 10:02:42 +0100)
3
Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-6.1-pull-request' into staging (2021-07-08 16:30:18 +0100)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to a527e312b59ac382cb84af4b91f517a846f50705:
9
for you to fetch changes up to e60edf69e2f64e818466019313517a2e6d6b63f4:
10
10
11
vhost-user-blk: Implement reconnection during realize (2021-06-30 13:21:22 +0200)
11
block: Make blockdev-reopen stable API (2021-07-09 13:19:11 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches
14
Block layer patches
15
15
16
- Supporting changing 'file' in x-blockdev-reopen
16
- Make blockdev-reopen stable
17
- ssh: add support for sha256 host key fingerprints
17
- Remove deprecated qemu-img backing file without format
18
- vhost-user-blk: Implement reconnection during realize
18
- rbd: Convert to coroutines and add write zeroes support
19
- introduce QEMU_AUTO_VFREE
19
- rbd: Updated MAINTAINERS
20
- Don't require password of encrypted backing file for image creation
20
- export/fuse: Allow other users access to the export
21
- Code cleanups
21
- vhost-user: Fix backends without multiqueue support
22
- Fix drive-backup transaction endless drained section
22
23
23
----------------------------------------------------------------
24
----------------------------------------------------------------
24
Alberto Garcia (2):
25
Alberto Garcia (4):
25
block: Allow changing bs->file on reopen
26
block: Add bdrv_reopen_queue_free()
26
iotests: Test replacing files with x-blockdev-reopen
27
block: Support multiple reopening with x-blockdev-reopen
28
iotests: Test reopening multiple devices at the same time
29
block: Make blockdev-reopen stable API
27
30
28
Daniel P. Berrangé (1):
31
Eric Blake (3):
29
block/ssh: add support for sha256 host key fingerprints
32
qcow2: Prohibit backing file changes in 'qemu-img amend'
33
qemu-img: Require -F with -b backing image
34
qemu-img: Improve error for rebase without backing format
30
35
31
Eric Blake (1):
36
Heinrich Schuchardt (1):
32
block: Move read-only check during truncation earlier
37
util/uri: do not check argument of uri_free()
33
38
34
Kevin Wolf (7):
39
Ilya Dryomov (1):
35
vhost: Add Error parameter to vhost_dev_init()
40
MAINTAINERS: update block/rbd.c maintainer
36
vhost: Distinguish errors in vhost_backend_init()
37
vhost: Return 0/-errno in vhost_dev_init()
38
vhost-user-blk: Add Error parameter to vhost_user_blk_start()
39
vhost: Distinguish errors in vhost_dev_get_config()
40
vhost-user-blk: Factor out vhost_user_blk_realize_connect()
41
vhost-user-blk: Implement reconnection during realize
42
41
43
Max Reitz (1):
42
Kevin Wolf (3):
44
block: BDRV_O_NO_IO for backing file on creation
43
vhost-user: Fix backends without multiqueue support
44
qcow2: Fix dangling pointer after reopen for 'file'
45
block: Acquire AioContexts during bdrv_reopen_multiple()
45
46
46
Miroslav Rezanina (1):
47
Max Reitz (6):
47
Prevent compiler warning on block.c
48
export/fuse: Pass default_permissions for mount
49
export/fuse: Add allow-other option
50
export/fuse: Give SET_ATTR_SIZE its own branch
51
export/fuse: Let permissions be adjustable
52
iotests/308: Test +w on read-only FUSE exports
53
iotests/fuse-allow-other: Test allow-other
48
54
49
Vladimir Sementsov-Ogievskiy (11):
55
Or Ozeri (1):
50
block: rename bdrv_replace_child to bdrv_replace_child_tran
56
block/rbd: Add support for rbd image encryption
51
block: comment graph-modifying function not updating permissions
52
block: introduce bdrv_remove_file_or_backing_child()
53
block: introduce bdrv_set_file_or_backing_noperm()
54
block: bdrv_reopen_parse_backing(): don't check aio context
55
block: bdrv_reopen_parse_backing(): don't check frozen child
56
block: bdrv_reopen_parse_backing(): simplify handling implicit filters
57
block: move supports_backing check to bdrv_set_file_or_backing_noperm()
58
block: BDRVReopenState: drop replace_backing_bs field
59
introduce QEMU_AUTO_VFREE
60
block/commit: use QEMU_AUTO_VFREE
61
57
62
qapi/block-core.json | 3 +-
58
Peter Lieven (8):
63
include/block/block.h | 2 +-
59
block/rbd: bump librbd requirement to luminous release
64
include/hw/virtio/vhost-backend.h | 5 +-
60
block/rbd: store object_size in BDRVRBDState
65
include/hw/virtio/vhost.h | 6 +-
61
block/rbd: update s->image_size in qemu_rbd_getlength
66
include/qemu/osdep.h | 15 ++
62
block/rbd: migrate from aio to coroutines
67
backends/cryptodev-vhost.c | 5 +-
63
block/rbd: add write zeroes support
68
backends/vhost-user.c | 4 +-
64
block/rbd: drop qemu_rbd_refresh_limits
69
block.c | 314 +++++++++++++++++++++-----------------
65
block/rbd: fix type of task->complete
70
block/commit.c | 25 ++-
66
MAINTAINERS: add block/rbd.c reviewer
71
block/io.c | 10 +-
67
72
block/ssh.c | 3 +
68
Vladimir Sementsov-Ogievskiy (1):
73
hw/block/vhost-user-blk.c | 102 ++++++++-----
69
blockdev: fix drive-backup transaction endless drained section
74
hw/display/vhost-user-gpu.c | 6 +-
70
75
hw/input/vhost-user-input.c | 6 +-
71
qapi/block-core.json | 134 +++-
76
hw/net/vhost_net.c | 8 +-
72
qapi/block-export.json | 33 +-
77
hw/scsi/vhost-scsi.c | 4 +-
73
docs/system/deprecated.rst | 32 -
78
hw/scsi/vhost-user-scsi.c | 4 +-
74
docs/system/removed-features.rst | 31 +
79
hw/virtio/vhost-backend.c | 6 +-
75
include/block/block.h | 3 +
80
hw/virtio/vhost-user-fs.c | 3 +-
76
block.c | 108 +--
81
hw/virtio/vhost-user-vsock.c | 12 +-
77
block/export/fuse.c | 121 +++-
82
hw/virtio/vhost-user.c | 71 +++++----
78
block/nfs.c | 4 +-
83
hw/virtio/vhost-vdpa.c | 8 +-
79
block/qcow2.c | 42 +-
84
hw/virtio/vhost-vsock.c | 3 +-
80
block/rbd.c | 749 +++++++++++++--------
85
hw/virtio/vhost.c | 41 +++--
81
block/replication.c | 7 +
86
tests/unit/test-bdrv-drain.c | 1 +
82
block/ssh.c | 4 +-
87
tests/unit/test-bdrv-graph-mod.c | 1 +
83
blockdev.c | 77 ++-
88
tests/qemu-iotests/189 | 2 +-
84
hw/virtio/vhost-user.c | 3 +
89
tests/qemu-iotests/198 | 2 +-
85
qemu-img.c | 9 +-
90
tests/qemu-iotests/207 | 54 +++++++
86
qemu-io-cmds.c | 7 +-
91
tests/qemu-iotests/207.out | 25 +++
87
util/uri.c | 22 +-
92
tests/qemu-iotests/245 | 140 +++++++++++++++--
88
MAINTAINERS | 3 +-
93
tests/qemu-iotests/245.out | 11 +-
89
meson.build | 7 +-
94
32 files changed, 599 insertions(+), 303 deletions(-)
90
tests/qemu-iotests/040 | 4 +-
91
tests/qemu-iotests/041 | 6 +-
92
tests/qemu-iotests/061 | 3 +
93
tests/qemu-iotests/061.out | 3 +-
94
tests/qemu-iotests/082.out | 6 +-
95
tests/qemu-iotests/114 | 18 +-
96
tests/qemu-iotests/114.out | 11 +-
97
tests/qemu-iotests/155 | 9 +-
98
tests/qemu-iotests/165 | 4 +-
99
tests/qemu-iotests/245 | 78 ++-
100
tests/qemu-iotests/245.out | 4 +-
101
tests/qemu-iotests/248 | 4 +-
102
tests/qemu-iotests/248.out | 2 +-
103
tests/qemu-iotests/296 | 11 +-
104
tests/qemu-iotests/298 | 4 +-
105
tests/qemu-iotests/301 | 4 +-
106
tests/qemu-iotests/301.out | 16 +-
107
tests/qemu-iotests/308 | 20 +-
108
tests/qemu-iotests/308.out | 6 +-
109
tests/qemu-iotests/common.rc | 6 +-
110
tests/qemu-iotests/tests/fuse-allow-other | 168 +++++
111
tests/qemu-iotests/tests/fuse-allow-other.out | 88 +++
112
.../qemu-iotests/tests/remove-bitmap-from-backing | 22 +-
113
42 files changed, 1350 insertions(+), 543 deletions(-)
114
create mode 100755 tests/qemu-iotests/tests/fuse-allow-other
115
create mode 100644 tests/qemu-iotests/tests/fuse-allow-other.out
95
116
96
117
diff view generated by jsdifflib
New patch
1
From: Ilya Dryomov <idryomov@gmail.com>
1
2
3
Jason has moved on from working on RBD and Ceph. I'm taking over
4
his role upstream.
5
6
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
7
Message-Id: <20210519112513.19694-1-idryomov@gmail.com>
8
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
MAINTAINERS | 2 +-
12
1 file changed, 1 insertion(+), 1 deletion(-)
13
14
diff --git a/MAINTAINERS b/MAINTAINERS
15
index XXXXXXX..XXXXXXX 100644
16
--- a/MAINTAINERS
17
+++ b/MAINTAINERS
18
@@ -XXX,XX +XXX,XX @@ S: Supported
19
F: block/vmdk.c
20
21
RBD
22
-M: Jason Dillaman <dillaman@redhat.com>
23
+M: Ilya Dryomov <idryomov@gmail.com>
24
L: qemu-block@nongnu.org
25
S: Supported
26
F: block/rbd.c
27
--
28
2.31.1
29
30
diff view generated by jsdifflib
1
From: Daniel P. Berrangé <berrange@redhat.com>
1
From: Or Ozeri <oro@il.ibm.com>
2
2
3
Currently the SSH block driver supports MD5 and SHA1 for host key
3
Starting from ceph Pacific, RBD has built-in support for image-level encryption.
4
fingerprints. This is a cryptographically sensitive operation and
4
Currently supported formats are LUKS version 1 and 2.
5
so these hash algorithms are inadequate by modern standards. This
5
6
adds support for SHA256 which has been supported in libssh since
6
There are 2 new relevant librbd APIs for controlling encryption, both expect an
7
the 0.8.1 release.
7
open image context:
8
8
9
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
9
rbd_encryption_format: formats an image (i.e. writes the LUKS header)
10
Message-Id: <20210622115156.138458-1-berrange@redhat.com>
10
rbd_encryption_load: loads encryptor/decryptor to the image IO stack
11
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
11
12
Acked-by: Richard W.M. Jones <rjones@redhat.com>
12
This commit extends the qemu rbd driver API to support the above.
13
14
Signed-off-by: Or Ozeri <oro@il.ibm.com>
15
Message-Id: <20210627114635.39326-1-oro@il.ibm.com>
16
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
18
---
15
qapi/block-core.json | 3 ++-
19
qapi/block-core.json | 110 ++++++++++++-
16
block/ssh.c | 3 +++
20
block/rbd.c | 361 ++++++++++++++++++++++++++++++++++++++++++-
17
tests/qemu-iotests/207 | 54 ++++++++++++++++++++++++++++++++++++++
21
2 files changed, 465 insertions(+), 6 deletions(-)
18
tests/qemu-iotests/207.out | 25 ++++++++++++++++++
19
4 files changed, 84 insertions(+), 1 deletion(-)
20
22
21
diff --git a/qapi/block-core.json b/qapi/block-core.json
23
diff --git a/qapi/block-core.json b/qapi/block-core.json
22
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
23
--- a/qapi/block-core.json
25
--- a/qapi/block-core.json
24
+++ b/qapi/block-core.json
26
+++ b/qapi/block-core.json
25
@@ -XXX,XX +XXX,XX @@
27
@@ -XXX,XX +XXX,XX @@
28
'extents': ['ImageInfo']
29
} }
30
31
+##
32
+# @ImageInfoSpecificRbd:
33
+#
34
+# @encryption-format: Image encryption format
35
+#
36
+# Since: 6.1
37
+##
38
+{ 'struct': 'ImageInfoSpecificRbd',
39
+ 'data': {
40
+ '*encryption-format': 'RbdImageEncryptionFormat'
41
+ } }
42
+
43
##
44
# @ImageInfoSpecific:
26
#
45
#
27
# @md5: The given hash is an md5 hash
46
@@ -XXX,XX +XXX,XX @@
28
# @sha1: The given hash is an sha1 hash
47
# If we need to add block driver specific parameters for
29
+# @sha256: The given hash is an sha256 hash
48
# LUKS in future, then we'll subclass QCryptoBlockInfoLUKS
49
# to define a ImageInfoSpecificLUKS
50
- 'luks': 'QCryptoBlockInfoLUKS'
51
+ 'luks': 'QCryptoBlockInfoLUKS',
52
+ 'rbd': 'ImageInfoSpecificRbd'
53
} }
54
55
##
56
@@ -XXX,XX +XXX,XX @@
57
{ 'enum': 'RbdAuthMode',
58
'data': [ 'cephx', 'none' ] }
59
60
+##
61
+# @RbdImageEncryptionFormat:
62
+#
63
+# Since: 6.1
64
+##
65
+{ 'enum': 'RbdImageEncryptionFormat',
66
+ 'data': [ 'luks', 'luks2' ] }
67
+
68
+##
69
+# @RbdEncryptionOptionsLUKSBase:
70
+#
71
+# @key-secret: ID of a QCryptoSecret object providing a passphrase
72
+# for unlocking the encryption
73
+#
74
+# Since: 6.1
75
+##
76
+{ 'struct': 'RbdEncryptionOptionsLUKSBase',
77
+ 'data': { 'key-secret': 'str' } }
78
+
79
+##
80
+# @RbdEncryptionCreateOptionsLUKSBase:
81
+#
82
+# @cipher-alg: The encryption algorithm
83
+#
84
+# Since: 6.1
85
+##
86
+{ 'struct': 'RbdEncryptionCreateOptionsLUKSBase',
87
+ 'base': 'RbdEncryptionOptionsLUKSBase',
88
+ 'data': { '*cipher-alg': 'QCryptoCipherAlgorithm' } }
89
+
90
+##
91
+# @RbdEncryptionOptionsLUKS:
92
+#
93
+# Since: 6.1
94
+##
95
+{ 'struct': 'RbdEncryptionOptionsLUKS',
96
+ 'base': 'RbdEncryptionOptionsLUKSBase',
97
+ 'data': { } }
98
+
99
+##
100
+# @RbdEncryptionOptionsLUKS2:
101
+#
102
+# Since: 6.1
103
+##
104
+{ 'struct': 'RbdEncryptionOptionsLUKS2',
105
+ 'base': 'RbdEncryptionOptionsLUKSBase',
106
+ 'data': { } }
107
+
108
+##
109
+# @RbdEncryptionCreateOptionsLUKS:
110
+#
111
+# Since: 6.1
112
+##
113
+{ 'struct': 'RbdEncryptionCreateOptionsLUKS',
114
+ 'base': 'RbdEncryptionCreateOptionsLUKSBase',
115
+ 'data': { } }
116
+
117
+##
118
+# @RbdEncryptionCreateOptionsLUKS2:
119
+#
120
+# Since: 6.1
121
+##
122
+{ 'struct': 'RbdEncryptionCreateOptionsLUKS2',
123
+ 'base': 'RbdEncryptionCreateOptionsLUKSBase',
124
+ 'data': { } }
125
+
126
+##
127
+# @RbdEncryptionOptions:
128
+#
129
+# Since: 6.1
130
+##
131
+{ 'union': 'RbdEncryptionOptions',
132
+ 'base': { 'format': 'RbdImageEncryptionFormat' },
133
+ 'discriminator': 'format',
134
+ 'data': { 'luks': 'RbdEncryptionOptionsLUKS',
135
+ 'luks2': 'RbdEncryptionOptionsLUKS2' } }
136
+
137
+##
138
+# @RbdEncryptionCreateOptions:
139
+#
140
+# Since: 6.1
141
+##
142
+{ 'union': 'RbdEncryptionCreateOptions',
143
+ 'base': { 'format': 'RbdImageEncryptionFormat' },
144
+ 'discriminator': 'format',
145
+ 'data': { 'luks': 'RbdEncryptionCreateOptionsLUKS',
146
+ 'luks2': 'RbdEncryptionCreateOptionsLUKS2' } }
147
+
148
##
149
# @BlockdevOptionsRbd:
150
#
151
@@ -XXX,XX +XXX,XX @@
152
#
153
# @snapshot: Ceph snapshot name.
154
#
155
+# @encrypt: Image encryption options. (Since 6.1)
156
+#
157
# @user: Ceph id name.
158
#
159
# @auth-client-required: Acceptable authentication modes.
160
@@ -XXX,XX +XXX,XX @@
161
'image': 'str',
162
'*conf': 'str',
163
'*snapshot': 'str',
164
+ '*encrypt': 'RbdEncryptionOptions',
165
'*user': 'str',
166
'*auth-client-required': ['RbdAuthMode'],
167
'*key-secret': 'str',
168
@@ -XXX,XX +XXX,XX @@
169
# point to a snapshot.
170
# @size: Size of the virtual disk in bytes
171
# @cluster-size: RBD object size
172
+# @encrypt: Image encryption options. (Since 6.1)
30
#
173
#
31
# Since: 2.12
174
# Since: 2.12
32
##
175
##
33
{ 'enum': 'SshHostKeyCheckHashType',
176
{ 'struct': 'BlockdevCreateOptionsRbd',
34
- 'data': [ 'md5', 'sha1' ] }
177
'data': { 'location': 'BlockdevOptionsRbd',
35
+ 'data': [ 'md5', 'sha1', 'sha256' ] }
178
'size': 'size',
179
- '*cluster-size' : 'size' } }
180
+ '*cluster-size' : 'size',
181
+ '*encrypt' : 'RbdEncryptionCreateOptions' } }
36
182
37
##
183
##
38
# @SshHostKeyHash:
184
# @BlockdevVmdkSubformat:
39
diff --git a/block/ssh.c b/block/ssh.c
185
diff --git a/block/rbd.c b/block/rbd.c
40
index XXXXXXX..XXXXXXX 100644
186
index XXXXXXX..XXXXXXX 100644
41
--- a/block/ssh.c
187
--- a/block/rbd.c
42
+++ b/block/ssh.c
188
+++ b/block/rbd.c
43
@@ -XXX,XX +XXX,XX @@ static int check_host_key(BDRVSSHState *s, SshHostKeyCheck *hkc, Error **errp)
189
@@ -XXX,XX +XXX,XX @@
44
} else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
190
#define LIBRBD_USE_IOVEC 0
45
return check_host_key_hash(s, hkc->u.hash.hash,
191
#endif
46
SSH_PUBLICKEY_HASH_SHA1, errp);
192
47
+ } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA256) {
193
+#define RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN 8
48
+ return check_host_key_hash(s, hkc->u.hash.hash,
194
+
49
+ SSH_PUBLICKEY_HASH_SHA256, errp);
195
+static const char rbd_luks_header_verification[
196
+ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {
197
+ 'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 1
198
+};
199
+
200
+static const char rbd_luks2_header_verification[
201
+ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {
202
+ 'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 2
203
+};
204
+
205
typedef enum {
206
RBD_AIO_READ,
207
RBD_AIO_WRITE,
208
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs)
209
}
210
}
211
212
+#ifdef LIBRBD_SUPPORTS_ENCRYPTION
213
+static int qemu_rbd_convert_luks_options(
214
+ RbdEncryptionOptionsLUKSBase *luks_opts,
215
+ char **passphrase,
216
+ size_t *passphrase_len,
217
+ Error **errp)
218
+{
219
+ return qcrypto_secret_lookup(luks_opts->key_secret, (uint8_t **)passphrase,
220
+ passphrase_len, errp);
221
+}
222
+
223
+static int qemu_rbd_convert_luks_create_options(
224
+ RbdEncryptionCreateOptionsLUKSBase *luks_opts,
225
+ rbd_encryption_algorithm_t *alg,
226
+ char **passphrase,
227
+ size_t *passphrase_len,
228
+ Error **errp)
229
+{
230
+ int r = 0;
231
+
232
+ r = qemu_rbd_convert_luks_options(
233
+ qapi_RbdEncryptionCreateOptionsLUKSBase_base(luks_opts),
234
+ passphrase, passphrase_len, errp);
235
+ if (r < 0) {
236
+ return r;
237
+ }
238
+
239
+ if (luks_opts->has_cipher_alg) {
240
+ switch (luks_opts->cipher_alg) {
241
+ case QCRYPTO_CIPHER_ALG_AES_128: {
242
+ *alg = RBD_ENCRYPTION_ALGORITHM_AES128;
243
+ break;
244
+ }
245
+ case QCRYPTO_CIPHER_ALG_AES_256: {
246
+ *alg = RBD_ENCRYPTION_ALGORITHM_AES256;
247
+ break;
248
+ }
249
+ default: {
250
+ r = -ENOTSUP;
251
+ error_setg_errno(errp, -r, "unknown encryption algorithm: %u",
252
+ luks_opts->cipher_alg);
253
+ return r;
254
+ }
255
+ }
256
+ } else {
257
+ /* default alg */
258
+ *alg = RBD_ENCRYPTION_ALGORITHM_AES256;
259
+ }
260
+
261
+ return 0;
262
+}
263
+
264
+static int qemu_rbd_encryption_format(rbd_image_t image,
265
+ RbdEncryptionCreateOptions *encrypt,
266
+ Error **errp)
267
+{
268
+ int r = 0;
269
+ g_autofree char *passphrase = NULL;
270
+ size_t passphrase_len;
271
+ rbd_encryption_format_t format;
272
+ rbd_encryption_options_t opts;
273
+ rbd_encryption_luks1_format_options_t luks_opts;
274
+ rbd_encryption_luks2_format_options_t luks2_opts;
275
+ size_t opts_size;
276
+ uint64_t raw_size, effective_size;
277
+
278
+ r = rbd_get_size(image, &raw_size);
279
+ if (r < 0) {
280
+ error_setg_errno(errp, -r, "cannot get raw image size");
281
+ return r;
282
+ }
283
+
284
+ switch (encrypt->format) {
285
+ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: {
286
+ memset(&luks_opts, 0, sizeof(luks_opts));
287
+ format = RBD_ENCRYPTION_FORMAT_LUKS1;
288
+ opts = &luks_opts;
289
+ opts_size = sizeof(luks_opts);
290
+ r = qemu_rbd_convert_luks_create_options(
291
+ qapi_RbdEncryptionCreateOptionsLUKS_base(&encrypt->u.luks),
292
+ &luks_opts.alg, &passphrase, &passphrase_len, errp);
293
+ if (r < 0) {
294
+ return r;
295
+ }
296
+ luks_opts.passphrase = passphrase;
297
+ luks_opts.passphrase_size = passphrase_len;
298
+ break;
299
+ }
300
+ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: {
301
+ memset(&luks2_opts, 0, sizeof(luks2_opts));
302
+ format = RBD_ENCRYPTION_FORMAT_LUKS2;
303
+ opts = &luks2_opts;
304
+ opts_size = sizeof(luks2_opts);
305
+ r = qemu_rbd_convert_luks_create_options(
306
+ qapi_RbdEncryptionCreateOptionsLUKS2_base(
307
+ &encrypt->u.luks2),
308
+ &luks2_opts.alg, &passphrase, &passphrase_len, errp);
309
+ if (r < 0) {
310
+ return r;
311
+ }
312
+ luks2_opts.passphrase = passphrase;
313
+ luks2_opts.passphrase_size = passphrase_len;
314
+ break;
315
+ }
316
+ default: {
317
+ r = -ENOTSUP;
318
+ error_setg_errno(
319
+ errp, -r, "unknown image encryption format: %u",
320
+ encrypt->format);
321
+ return r;
322
+ }
323
+ }
324
+
325
+ r = rbd_encryption_format(image, format, opts, opts_size);
326
+ if (r < 0) {
327
+ error_setg_errno(errp, -r, "encryption format fail");
328
+ return r;
329
+ }
330
+
331
+ r = rbd_get_size(image, &effective_size);
332
+ if (r < 0) {
333
+ error_setg_errno(errp, -r, "cannot get effective image size");
334
+ return r;
335
+ }
336
+
337
+ r = rbd_resize(image, raw_size + (raw_size - effective_size));
338
+ if (r < 0) {
339
+ error_setg_errno(errp, -r, "cannot resize image after format");
340
+ return r;
341
+ }
342
+
343
+ return 0;
344
+}
345
+
346
+static int qemu_rbd_encryption_load(rbd_image_t image,
347
+ RbdEncryptionOptions *encrypt,
348
+ Error **errp)
349
+{
350
+ int r = 0;
351
+ g_autofree char *passphrase = NULL;
352
+ size_t passphrase_len;
353
+ rbd_encryption_luks1_format_options_t luks_opts;
354
+ rbd_encryption_luks2_format_options_t luks2_opts;
355
+ rbd_encryption_format_t format;
356
+ rbd_encryption_options_t opts;
357
+ size_t opts_size;
358
+
359
+ switch (encrypt->format) {
360
+ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: {
361
+ memset(&luks_opts, 0, sizeof(luks_opts));
362
+ format = RBD_ENCRYPTION_FORMAT_LUKS1;
363
+ opts = &luks_opts;
364
+ opts_size = sizeof(luks_opts);
365
+ r = qemu_rbd_convert_luks_options(
366
+ qapi_RbdEncryptionOptionsLUKS_base(&encrypt->u.luks),
367
+ &passphrase, &passphrase_len, errp);
368
+ if (r < 0) {
369
+ return r;
370
+ }
371
+ luks_opts.passphrase = passphrase;
372
+ luks_opts.passphrase_size = passphrase_len;
373
+ break;
374
+ }
375
+ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: {
376
+ memset(&luks2_opts, 0, sizeof(luks2_opts));
377
+ format = RBD_ENCRYPTION_FORMAT_LUKS2;
378
+ opts = &luks2_opts;
379
+ opts_size = sizeof(luks2_opts);
380
+ r = qemu_rbd_convert_luks_options(
381
+ qapi_RbdEncryptionOptionsLUKS2_base(&encrypt->u.luks2),
382
+ &passphrase, &passphrase_len, errp);
383
+ if (r < 0) {
384
+ return r;
385
+ }
386
+ luks2_opts.passphrase = passphrase;
387
+ luks2_opts.passphrase_size = passphrase_len;
388
+ break;
389
+ }
390
+ default: {
391
+ r = -ENOTSUP;
392
+ error_setg_errno(
393
+ errp, -r, "unknown image encryption format: %u",
394
+ encrypt->format);
395
+ return r;
396
+ }
397
+ }
398
+
399
+ r = rbd_encryption_load(image, format, opts, opts_size);
400
+ if (r < 0) {
401
+ error_setg_errno(errp, -r, "encryption load fail");
402
+ return r;
403
+ }
404
+
405
+ return 0;
406
+}
407
+#endif
408
+
409
/* FIXME Deprecate and remove keypairs or make it available in QMP. */
410
static int qemu_rbd_do_create(BlockdevCreateOptions *options,
411
const char *keypairs, const char *password_secret,
412
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_do_create(BlockdevCreateOptions *options,
413
return -EINVAL;
414
}
415
416
+#ifndef LIBRBD_SUPPORTS_ENCRYPTION
417
+ if (opts->has_encrypt) {
418
+ error_setg(errp, "RBD library does not support image encryption");
419
+ return -ENOTSUP;
420
+ }
421
+#endif
422
+
423
if (opts->has_cluster_size) {
424
int64_t objsize = opts->cluster_size;
425
if ((objsize - 1) & objsize) { /* not a power of 2? */
426
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_do_create(BlockdevCreateOptions *options,
427
goto out;
428
}
429
430
+#ifdef LIBRBD_SUPPORTS_ENCRYPTION
431
+ if (opts->has_encrypt) {
432
+ rbd_image_t image;
433
+
434
+ ret = rbd_open(io_ctx, opts->location->image, &image, NULL);
435
+ if (ret < 0) {
436
+ error_setg_errno(errp, -ret,
437
+ "error opening image '%s' for encryption format",
438
+ opts->location->image);
439
+ goto out;
440
+ }
441
+
442
+ ret = qemu_rbd_encryption_format(image, opts->encrypt, errp);
443
+ rbd_close(image);
444
+ if (ret < 0) {
445
+ /* encryption format fail, try removing the image */
446
+ rbd_remove(io_ctx, opts->location->image);
447
+ goto out;
448
+ }
449
+ }
450
+#endif
451
+
452
ret = 0;
453
out:
454
rados_ioctx_destroy(io_ctx);
455
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp)
456
return qemu_rbd_do_create(options, NULL, NULL, errp);
457
}
458
459
+static int qemu_rbd_extract_encryption_create_options(
460
+ QemuOpts *opts,
461
+ RbdEncryptionCreateOptions **spec,
462
+ Error **errp)
463
+{
464
+ QDict *opts_qdict;
465
+ QDict *encrypt_qdict;
466
+ Visitor *v;
467
+ int ret = 0;
468
+
469
+ opts_qdict = qemu_opts_to_qdict(opts, NULL);
470
+ qdict_extract_subqdict(opts_qdict, &encrypt_qdict, "encrypt.");
471
+ qobject_unref(opts_qdict);
472
+ if (!qdict_size(encrypt_qdict)) {
473
+ *spec = NULL;
474
+ goto exit;
475
+ }
476
+
477
+ /* Convert options into a QAPI object */
478
+ v = qobject_input_visitor_new_flat_confused(encrypt_qdict, errp);
479
+ if (!v) {
480
+ ret = -EINVAL;
481
+ goto exit;
482
+ }
483
+
484
+ visit_type_RbdEncryptionCreateOptions(v, NULL, spec, errp);
485
+ visit_free(v);
486
+ if (!*spec) {
487
+ ret = -EINVAL;
488
+ goto exit;
489
+ }
490
+
491
+exit:
492
+ qobject_unref(encrypt_qdict);
493
+ return ret;
494
+}
495
+
496
static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv,
497
const char *filename,
498
QemuOpts *opts,
499
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv,
500
BlockdevCreateOptions *create_options;
501
BlockdevCreateOptionsRbd *rbd_opts;
502
BlockdevOptionsRbd *loc;
503
+ RbdEncryptionCreateOptions *encrypt = NULL;
504
Error *local_err = NULL;
505
const char *keypairs, *password_secret;
506
QDict *options = NULL;
507
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv,
508
goto exit;
509
}
510
511
+ ret = qemu_rbd_extract_encryption_create_options(opts, &encrypt, errp);
512
+ if (ret < 0) {
513
+ goto exit;
514
+ }
515
+ rbd_opts->encrypt = encrypt;
516
+ rbd_opts->has_encrypt = !!encrypt;
517
+
518
/*
519
* Caution: while qdict_get_try_str() is fine, getting non-string
520
* types would require more care. When @options come from -blockdev
521
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
522
goto failed_open;
523
}
524
525
+ if (opts->has_encrypt) {
526
+#ifdef LIBRBD_SUPPORTS_ENCRYPTION
527
+ r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp);
528
+ if (r < 0) {
529
+ goto failed_post_open;
530
+ }
531
+#else
532
+ r = -ENOTSUP;
533
+ error_setg(errp, "RBD library does not support image encryption");
534
+ goto failed_post_open;
535
+#endif
536
+ }
537
+
538
r = rbd_get_size(s->image, &s->image_size);
539
if (r < 0) {
540
error_setg_errno(errp, -r, "error getting image size from %s",
541
s->image_name);
542
- rbd_close(s->image);
543
- goto failed_open;
544
+ goto failed_post_open;
545
}
546
547
/* If we are using an rbd snapshot, we must be r/o, otherwise
548
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
549
if (s->snap != NULL) {
550
r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp);
551
if (r < 0) {
552
- rbd_close(s->image);
553
- goto failed_open;
554
+ goto failed_post_open;
50
}
555
}
51
g_assert_not_reached();
556
}
52
break;
557
53
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
558
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
54
index XXXXXXX..XXXXXXX 100755
559
r = 0;
55
--- a/tests/qemu-iotests/207
560
goto out;
56
+++ b/tests/qemu-iotests/207
561
57
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
562
+failed_post_open:
58
iotests.log("=== Test host-key-check options ===")
563
+ rbd_close(s->image);
59
iotests.log("")
564
failed_open:
60
565
rados_ioctx_destroy(s->io_ctx);
61
+ iotests.log("--- no host key checking --")
566
g_free(s->snap);
62
+ iotests.log("")
567
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi)
63
+
568
return 0;
64
vm.launch()
569
}
65
blockdev_create(vm, { 'driver': 'ssh',
570
66
'location': {
571
+static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs,
67
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
572
+ Error **errp)
68
573
+{
69
iotests.img_info_log(remote_path)
574
+ BDRVRBDState *s = bs->opaque;
70
575
+ ImageInfoSpecific *spec_info;
71
+ iotests.log("--- known_hosts key checking --")
576
+ char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0};
72
+ iotests.log("")
577
+ int r;
73
+
578
+
74
vm.launch()
579
+ if (s->image_size >= RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) {
75
blockdev_create(vm, { 'driver': 'ssh',
580
+ r = rbd_read(s->image, 0,
76
'location': {
581
+ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf);
77
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
582
+ if (r < 0) {
78
# Mappings of base64 representations to digests
583
+ error_setg_errno(errp, -r, "cannot read image start for probe");
79
md5_keys = {}
584
+ return NULL;
80
sha1_keys = {}
585
+ }
81
+ sha256_keys = {}
586
+ }
82
587
+
83
for key in keys:
588
+ spec_info = g_new(ImageInfoSpecific, 1);
84
md5_keys[key] = subprocess.check_output(
589
+ *spec_info = (ImageInfoSpecific){
85
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
590
+ .type = IMAGE_INFO_SPECIFIC_KIND_RBD,
86
'echo %s | base64 -d | sha1sum -b | cut -d" " -f1' % key,
591
+ .u.rbd.data = g_new0(ImageInfoSpecificRbd, 1),
87
shell=True).rstrip().decode('ascii')
592
+ };
88
593
+
89
+ sha256_keys[key] = subprocess.check_output(
594
+ if (memcmp(buf, rbd_luks_header_verification,
90
+ 'echo %s | base64 -d | sha256sum -b | cut -d" " -f1' % key,
595
+ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
91
+ shell=True).rstrip().decode('ascii')
596
+ spec_info->u.rbd.data->encryption_format =
92
+
597
+ RBD_IMAGE_ENCRYPTION_FORMAT_LUKS;
93
vm.launch()
598
+ spec_info->u.rbd.data->has_encryption_format = true;
94
599
+ } else if (memcmp(buf, rbd_luks2_header_verification,
95
# Find correct key first
600
+ RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
96
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
601
+ spec_info->u.rbd.data->encryption_format =
97
vm.shutdown()
602
+ RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2;
98
iotests.notrun('Did not find a key that fits 127.0.0.1')
603
+ spec_info->u.rbd.data->has_encryption_format = true;
99
604
+ } else {
100
+ iotests.log("--- explicit md5 key checking --")
605
+ spec_info->u.rbd.data->has_encryption_format = false;
101
+ iotests.log("")
606
+ }
102
+
607
+
103
blockdev_create(vm, { 'driver': 'ssh',
608
+ return spec_info;
104
'location': {
609
+}
105
'path': disk_path,
610
+
106
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
611
static int64_t qemu_rbd_getlength(BlockDriverState *bs)
107
}
612
{
108
},
613
BDRVRBDState *s = bs->opaque;
109
'size': 2097152 })
614
@@ -XXX,XX +XXX,XX @@ static QemuOptsList qemu_rbd_create_opts = {
110
+
615
.type = QEMU_OPT_STRING,
111
blockdev_create(vm, { 'driver': 'ssh',
616
.help = "ID of secret providing the password",
112
'location': {
617
},
113
'path': disk_path,
618
+ {
114
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
619
+ .name = "encrypt.format",
115
620
+ .type = QEMU_OPT_STRING,
116
iotests.img_info_log(remote_path)
621
+ .help = "Encrypt the image, format choices: 'luks', 'luks2'",
117
622
+ },
118
+ iotests.log("--- explicit sha1 key checking --")
623
+ {
119
+ iotests.log("")
624
+ .name = "encrypt.cipher-alg",
120
+
625
+ .type = QEMU_OPT_STRING,
121
vm.launch()
626
+ .help = "Name of encryption cipher algorithm"
122
blockdev_create(vm, { 'driver': 'ssh',
627
+ " (allowed values: aes-128, aes-256)",
123
'location': {
628
+ },
124
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
629
+ {
125
630
+ .name = "encrypt.key-secret",
126
iotests.img_info_log(remote_path)
631
+ .type = QEMU_OPT_STRING,
127
632
+ .help = "ID of secret providing LUKS passphrase",
128
+ iotests.log("--- explicit sha256 key checking --")
633
+ },
129
+ iotests.log("")
634
{ /* end of list */ }
130
+
635
}
131
+ vm.launch()
636
};
132
+ blockdev_create(vm, { 'driver': 'ssh',
637
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
133
+ 'location': {
638
.bdrv_co_create_opts = qemu_rbd_co_create_opts,
134
+ 'path': disk_path,
639
.bdrv_has_zero_init = bdrv_has_zero_init_1,
135
+ 'server': {
640
.bdrv_get_info = qemu_rbd_getinfo,
136
+ 'host': '127.0.0.1',
641
+ .bdrv_get_specific_info = qemu_rbd_get_specific_info,
137
+ 'port': '22'
642
.create_opts = &qemu_rbd_create_opts,
138
+ },
643
.bdrv_getlength = qemu_rbd_getlength,
139
+ 'host-key-check': {
644
.bdrv_co_truncate = qemu_rbd_co_truncate,
140
+ 'mode': 'hash',
141
+ 'type': 'sha256',
142
+ 'hash': 'wrong',
143
+ }
144
+ },
145
+ 'size': 2097152 })
146
+ blockdev_create(vm, { 'driver': 'ssh',
147
+ 'location': {
148
+ 'path': disk_path,
149
+ 'server': {
150
+ 'host': '127.0.0.1',
151
+ 'port': '22'
152
+ },
153
+ 'host-key-check': {
154
+ 'mode': 'hash',
155
+ 'type': 'sha256',
156
+ 'hash': sha256_keys[matching_key],
157
+ }
158
+ },
159
+ 'size': 4194304 })
160
+ vm.shutdown()
161
+
162
+ iotests.img_info_log(remote_path)
163
+
164
#
165
# Invalid path and user
166
#
167
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
168
index XXXXXXX..XXXXXXX 100644
169
--- a/tests/qemu-iotests/207.out
170
+++ b/tests/qemu-iotests/207.out
171
@@ -XXX,XX +XXX,XX @@ virtual size: 4 MiB (4194304 bytes)
172
173
=== Test host-key-check options ===
174
175
+--- no host key checking --
176
+
177
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
178
{"return": {}}
179
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
180
@@ -XXX,XX +XXX,XX @@ image: TEST_IMG
181
file format: IMGFMT
182
virtual size: 8 MiB (8388608 bytes)
183
184
+--- known_hosts key checking --
185
+
186
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "known_hosts"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
187
{"return": {}}
188
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
189
@@ -XXX,XX +XXX,XX @@ image: TEST_IMG
190
file format: IMGFMT
191
virtual size: 4 MiB (4194304 bytes)
192
193
+--- explicit md5 key checking --
194
+
195
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
196
{"return": {}}
197
Job failed: remote host key does not match host_key_check 'wrong'
198
@@ -XXX,XX +XXX,XX @@ image: TEST_IMG
199
file format: IMGFMT
200
virtual size: 8 MiB (8388608 bytes)
201
202
+--- explicit sha1 key checking --
203
+
204
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
205
{"return": {}}
206
Job failed: remote host key does not match host_key_check 'wrong'
207
@@ -XXX,XX +XXX,XX @@ image: TEST_IMG
208
file format: IMGFMT
209
virtual size: 4 MiB (4194304 bytes)
210
211
+--- explicit sha256 key checking --
212
+
213
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha256"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
214
+{"return": {}}
215
+Job failed: remote host key does not match host_key_check 'wrong'
216
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
217
+{"return": {}}
218
+
219
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "sha256"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
220
+{"return": {}}
221
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
222
+{"return": {}}
223
+
224
+image: TEST_IMG
225
+file format: IMGFMT
226
+virtual size: 4 MiB (4194304 bytes)
227
+
228
=== Invalid path and user ===
229
230
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
231
--
645
--
232
2.31.1
646
2.31.1
233
647
234
648
diff view generated by jsdifflib
New patch
1
1
From: Peter Lieven <pl@kamp.de>
2
3
Ceph Luminous (version 12.2.z) is almost 4 years old at this point.
4
Bump the requirement to get rid of the ifdef'ry in the code.
5
Qemu 6.1 dropped the support for RHEL-7 which was the last supported
6
OS that required an older librbd.
7
8
Signed-off-by: Peter Lieven <pl@kamp.de>
9
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
10
Message-Id: <20210702172356.11574-2-idryomov@gmail.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block/rbd.c | 120 ++++------------------------------------------------
14
meson.build | 7 ++-
15
2 files changed, 13 insertions(+), 114 deletions(-)
16
17
diff --git a/block/rbd.c b/block/rbd.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block/rbd.c
20
+++ b/block/rbd.c
21
@@ -XXX,XX +XXX,XX @@
22
* leading "\".
23
*/
24
25
-/* rbd_aio_discard added in 0.1.2 */
26
-#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2)
27
-#define LIBRBD_SUPPORTS_DISCARD
28
-#else
29
-#undef LIBRBD_SUPPORTS_DISCARD
30
-#endif
31
-
32
#define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER)
33
34
#define RBD_MAX_SNAPS 100
35
36
-/* The LIBRBD_SUPPORTS_IOVEC is defined in librbd.h */
37
-#ifdef LIBRBD_SUPPORTS_IOVEC
38
-#define LIBRBD_USE_IOVEC 1
39
-#else
40
-#define LIBRBD_USE_IOVEC 0
41
-#endif
42
-
43
#define RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN 8
44
45
static const char rbd_luks_header_verification[
46
@@ -XXX,XX +XXX,XX @@ typedef struct RBDAIOCB {
47
BlockAIOCB common;
48
int64_t ret;
49
QEMUIOVector *qiov;
50
- char *bounce;
51
RBDAIOCmd cmd;
52
int error;
53
struct BDRVRBDState *s;
54
@@ -XXX,XX +XXX,XX @@ typedef struct RADOSCB {
55
RBDAIOCB *acb;
56
struct BDRVRBDState *s;
57
int64_t size;
58
- char *buf;
59
int64_t ret;
60
} RADOSCB;
61
62
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
63
64
static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs)
65
{
66
- if (LIBRBD_USE_IOVEC) {
67
- RBDAIOCB *acb = rcb->acb;
68
- iov_memset(acb->qiov->iov, acb->qiov->niov, offs, 0,
69
- acb->qiov->size - offs);
70
- } else {
71
- memset(rcb->buf + offs, 0, rcb->size - offs);
72
- }
73
+ RBDAIOCB *acb = rcb->acb;
74
+ iov_memset(acb->qiov->iov, acb->qiov->niov, offs, 0,
75
+ acb->qiov->size - offs);
76
}
77
78
#ifdef LIBRBD_SUPPORTS_ENCRYPTION
79
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
80
81
g_free(rcb);
82
83
- if (!LIBRBD_USE_IOVEC) {
84
- if (acb->cmd == RBD_AIO_READ) {
85
- qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
86
- }
87
- qemu_vfree(acb->bounce);
88
- }
89
-
90
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
91
92
qemu_aio_unref(acb);
93
@@ -XXX,XX +XXX,XX @@ static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
94
rbd_finish_bh, rcb);
95
}
96
97
-static int rbd_aio_discard_wrapper(rbd_image_t image,
98
- uint64_t off,
99
- uint64_t len,
100
- rbd_completion_t comp)
101
-{
102
-#ifdef LIBRBD_SUPPORTS_DISCARD
103
- return rbd_aio_discard(image, off, len, comp);
104
-#else
105
- return -ENOTSUP;
106
-#endif
107
-}
108
-
109
-static int rbd_aio_flush_wrapper(rbd_image_t image,
110
- rbd_completion_t comp)
111
-{
112
-#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
113
- return rbd_aio_flush(image, comp);
114
-#else
115
- return -ENOTSUP;
116
-#endif
117
-}
118
-
119
static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
120
int64_t off,
121
QEMUIOVector *qiov,
122
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
123
124
rcb = g_new(RADOSCB, 1);
125
126
- if (!LIBRBD_USE_IOVEC) {
127
- if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) {
128
- acb->bounce = NULL;
129
- } else {
130
- acb->bounce = qemu_try_blockalign(bs, qiov->size);
131
- if (acb->bounce == NULL) {
132
- goto failed;
133
- }
134
- }
135
- if (cmd == RBD_AIO_WRITE) {
136
- qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size);
137
- }
138
- rcb->buf = acb->bounce;
139
- }
140
-
141
acb->ret = 0;
142
acb->error = 0;
143
acb->s = s;
144
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
145
}
146
147
switch (cmd) {
148
- case RBD_AIO_WRITE: {
149
+ case RBD_AIO_WRITE:
150
/*
151
* RBD APIs don't allow us to write more than actual size, so in order
152
* to support growing images, we resize the image before write
153
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
154
goto failed_completion;
155
}
156
}
157
-#ifdef LIBRBD_SUPPORTS_IOVEC
158
- r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c);
159
-#else
160
- r = rbd_aio_write(s->image, off, size, rcb->buf, c);
161
-#endif
162
+ r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c);
163
break;
164
- }
165
case RBD_AIO_READ:
166
-#ifdef LIBRBD_SUPPORTS_IOVEC
167
- r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c);
168
-#else
169
- r = rbd_aio_read(s->image, off, size, rcb->buf, c);
170
-#endif
171
+ r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c);
172
break;
173
case RBD_AIO_DISCARD:
174
- r = rbd_aio_discard_wrapper(s->image, off, size, c);
175
+ r = rbd_aio_discard(s->image, off, size, c);
176
break;
177
case RBD_AIO_FLUSH:
178
- r = rbd_aio_flush_wrapper(s->image, c);
179
+ r = rbd_aio_flush(s->image, c);
180
break;
181
default:
182
r = -EINVAL;
183
@@ -XXX,XX +XXX,XX @@ failed_completion:
184
rbd_aio_release(c);
185
failed:
186
g_free(rcb);
187
- if (!LIBRBD_USE_IOVEC) {
188
- qemu_vfree(acb->bounce);
189
- }
190
191
qemu_aio_unref(acb);
192
return NULL;
193
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs,
194
RBD_AIO_WRITE);
195
}
196
197
-#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
198
static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
199
BlockCompletionFunc *cb,
200
void *opaque)
201
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
202
return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH);
203
}
204
205
-#else
206
-
207
-static int qemu_rbd_co_flush(BlockDriverState *bs)
208
-{
209
-#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 1)
210
- /* rbd_flush added in 0.1.1 */
211
- BDRVRBDState *s = bs->opaque;
212
- return rbd_flush(s->image);
213
-#else
214
- return 0;
215
-#endif
216
-}
217
-#endif
218
-
219
static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi)
220
{
221
BDRVRBDState *s = bs->opaque;
222
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
223
return snap_count;
224
}
225
226
-#ifdef LIBRBD_SUPPORTS_DISCARD
227
static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs,
228
int64_t offset,
229
int bytes,
230
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs,
231
return rbd_start_aio(bs, offset, NULL, bytes, cb, opaque,
232
RBD_AIO_DISCARD);
233
}
234
-#endif
235
236
-#ifdef LIBRBD_SUPPORTS_INVALIDATE
237
static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs,
238
Error **errp)
239
{
240
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs,
241
error_setg_errno(errp, -r, "Failed to invalidate the cache");
242
}
243
}
244
-#endif
245
246
static QemuOptsList qemu_rbd_create_opts = {
247
.name = "rbd-create-opts",
248
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
249
.bdrv_aio_preadv = qemu_rbd_aio_preadv,
250
.bdrv_aio_pwritev = qemu_rbd_aio_pwritev,
251
252
-#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
253
.bdrv_aio_flush = qemu_rbd_aio_flush,
254
-#else
255
- .bdrv_co_flush_to_disk = qemu_rbd_co_flush,
256
-#endif
257
-
258
-#ifdef LIBRBD_SUPPORTS_DISCARD
259
.bdrv_aio_pdiscard = qemu_rbd_aio_pdiscard,
260
-#endif
261
262
.bdrv_snapshot_create = qemu_rbd_snap_create,
263
.bdrv_snapshot_delete = qemu_rbd_snap_remove,
264
.bdrv_snapshot_list = qemu_rbd_snap_list,
265
.bdrv_snapshot_goto = qemu_rbd_snap_rollback,
266
-#ifdef LIBRBD_SUPPORTS_INVALIDATE
267
.bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache,
268
-#endif
269
270
.strong_runtime_opts = qemu_rbd_strong_runtime_opts,
271
};
272
diff --git a/meson.build b/meson.build
273
index XXXXXXX..XXXXXXX 100644
274
--- a/meson.build
275
+++ b/meson.build
276
@@ -XXX,XX +XXX,XX @@ if not get_option('rbd').auto() or have_block
277
int main(void) {
278
rados_t cluster;
279
rados_create(&cluster, NULL);
280
+ #if LIBRBD_VERSION_CODE < LIBRBD_VERSION(1, 12, 0)
281
+ #error
282
+ #endif
283
return 0;
284
}''', dependencies: [librbd, librados])
285
rbd = declare_dependency(dependencies: [librbd, librados])
286
elif get_option('rbd').enabled()
287
- error('could not link librados')
288
+ error('librbd >= 1.12.0 required')
289
else
290
- warning('could not link librados, disabling')
291
+ warning('librbd >= 1.12.0 not found, disabling')
292
endif
293
endif
294
endif
295
--
296
2.31.1
297
298
diff view generated by jsdifflib
New patch
1
From: Peter Lieven <pl@kamp.de>
1
2
3
Signed-off-by: Peter Lieven <pl@kamp.de>
4
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
5
Message-Id: <20210702172356.11574-3-idryomov@gmail.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
block/rbd.c | 18 +++++++-----------
9
1 file changed, 7 insertions(+), 11 deletions(-)
10
11
diff --git a/block/rbd.c b/block/rbd.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block/rbd.c
14
+++ b/block/rbd.c
15
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRBDState {
16
char *snap;
17
char *namespace;
18
uint64_t image_size;
19
+ uint64_t object_size;
20
} BDRVRBDState;
21
22
static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
23
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
24
const QDictEntry *e;
25
Error *local_err = NULL;
26
char *keypairs, *secretid;
27
+ rbd_image_info_t info;
28
int r;
29
30
keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
31
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
32
#endif
33
}
34
35
- r = rbd_get_size(s->image, &s->image_size);
36
+ r = rbd_stat(s->image, &info, sizeof(info));
37
if (r < 0) {
38
- error_setg_errno(errp, -r, "error getting image size from %s",
39
+ error_setg_errno(errp, -r, "error getting image info from %s",
40
s->image_name);
41
goto failed_post_open;
42
}
43
+ s->image_size = info.size;
44
+ s->object_size = info.obj_size;
45
46
/* If we are using an rbd snapshot, we must be r/o, otherwise
47
* leave as-is */
48
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
49
static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi)
50
{
51
BDRVRBDState *s = bs->opaque;
52
- rbd_image_info_t info;
53
- int r;
54
-
55
- r = rbd_stat(s->image, &info, sizeof(info));
56
- if (r < 0) {
57
- return r;
58
- }
59
-
60
- bdi->cluster_size = info.obj_size;
61
+ bdi->cluster_size = s->object_size;
62
return 0;
63
}
64
65
--
66
2.31.1
67
68
diff view generated by jsdifflib
1
This allows callers to return better error messages instead of making
1
From: Peter Lieven <pl@kamp.de>
2
one up while the real error ends up on stderr. Most callers can
3
immediately make use of this because they already have an Error
4
parameter themselves. The others just keep printing the error with
5
error_report_err().
6
2
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
While at it just call rbd_get_size and avoid rbd_image_info_t.
8
Message-Id: <20210609154658.350308-2-kwolf@redhat.com>
4
9
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
5
Signed-off-by: Peter Lieven <pl@kamp.de>
10
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
6
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
7
Message-Id: <20210702172356.11574-4-idryomov@gmail.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
9
---
13
include/hw/virtio/vhost.h | 2 +-
10
block/rbd.c | 5 ++---
14
backends/cryptodev-vhost.c | 5 ++++-
11
1 file changed, 2 insertions(+), 3 deletions(-)
15
backends/vhost-user.c | 4 ++--
16
hw/block/vhost-user-blk.c | 4 ++--
17
hw/net/vhost_net.c | 6 +++++-
18
hw/scsi/vhost-scsi.c | 4 +---
19
hw/scsi/vhost-user-scsi.c | 4 +---
20
hw/virtio/vhost-user-fs.c | 3 +--
21
hw/virtio/vhost-user-vsock.c | 3 +--
22
hw/virtio/vhost-vsock.c | 3 +--
23
hw/virtio/vhost.c | 16 ++++++++++------
24
11 files changed, 29 insertions(+), 25 deletions(-)
25
12
26
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
13
diff --git a/block/rbd.c b/block/rbd.c
27
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
28
--- a/include/hw/virtio/vhost.h
15
--- a/block/rbd.c
29
+++ b/include/hw/virtio/vhost.h
16
+++ b/block/rbd.c
30
@@ -XXX,XX +XXX,XX @@ struct vhost_net {
17
@@ -XXX,XX +XXX,XX @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs,
31
18
static int64_t qemu_rbd_getlength(BlockDriverState *bs)
32
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
33
VhostBackendType backend_type,
34
- uint32_t busyloop_timeout);
35
+ uint32_t busyloop_timeout, Error **errp);
36
void vhost_dev_cleanup(struct vhost_dev *hdev);
37
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev);
38
void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev);
39
diff --git a/backends/cryptodev-vhost.c b/backends/cryptodev-vhost.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/backends/cryptodev-vhost.c
42
+++ b/backends/cryptodev-vhost.c
43
@@ -XXX,XX +XXX,XX @@ cryptodev_vhost_init(
44
{
19
{
20
BDRVRBDState *s = bs->opaque;
21
- rbd_image_info_t info;
45
int r;
22
int r;
46
CryptoDevBackendVhost *crypto;
23
47
+ Error *local_err = NULL;
24
- r = rbd_stat(s->image, &info, sizeof(info));
48
25
+ r = rbd_get_size(s->image, &s->image_size);
49
crypto = g_new(CryptoDevBackendVhost, 1);
50
crypto->dev.max_queues = 1;
51
@@ -XXX,XX +XXX,XX @@ cryptodev_vhost_init(
52
/* vhost-user needs vq_index to initiate a specific queue pair */
53
crypto->dev.vq_index = crypto->cc->queue_index * crypto->dev.nvqs;
54
55
- r = vhost_dev_init(&crypto->dev, options->opaque, options->backend_type, 0);
56
+ r = vhost_dev_init(&crypto->dev, options->opaque, options->backend_type, 0,
57
+ &local_err);
58
if (r < 0) {
26
if (r < 0) {
59
+ error_report_err(local_err);
27
return r;
60
goto fail;
61
}
28
}
62
29
63
diff --git a/backends/vhost-user.c b/backends/vhost-user.c
30
- return info.size;
64
index XXXXXXX..XXXXXXX 100644
31
+ return s->image_size;
65
--- a/backends/vhost-user.c
66
+++ b/backends/vhost-user.c
67
@@ -XXX,XX +XXX,XX @@ vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
68
b->dev.nvqs = nvqs;
69
b->dev.vqs = g_new0(struct vhost_virtqueue, nvqs);
70
71
- ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
72
+ ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0,
73
+ errp);
74
if (ret < 0) {
75
- error_setg_errno(errp, -ret, "vhost initialization failed");
76
return -1;
77
}
78
79
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/hw/block/vhost-user-blk.c
82
+++ b/hw/block/vhost-user-blk.c
83
@@ -XXX,XX +XXX,XX @@ static int vhost_user_blk_connect(DeviceState *dev, Error **errp)
84
85
vhost_dev_set_config_notifier(&s->dev, &blk_ops);
86
87
- ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
88
+ ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0,
89
+ errp);
90
if (ret < 0) {
91
- error_setg_errno(errp, -ret, "vhost initialization failed");
92
return ret;
93
}
94
95
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
96
index XXXXXXX..XXXXXXX 100644
97
--- a/hw/net/vhost_net.c
98
+++ b/hw/net/vhost_net.c
99
@@ -XXX,XX +XXX,XX @@
100
#include "standard-headers/linux/vhost_types.h"
101
#include "hw/virtio/virtio-net.h"
102
#include "net/vhost_net.h"
103
+#include "qapi/error.h"
104
#include "qemu/error-report.h"
105
#include "qemu/main-loop.h"
106
107
@@ -XXX,XX +XXX,XX @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
108
bool backend_kernel = options->backend_type == VHOST_BACKEND_TYPE_KERNEL;
109
struct vhost_net *net = g_new0(struct vhost_net, 1);
110
uint64_t features = 0;
111
+ Error *local_err = NULL;
112
113
if (!options->net_backend) {
114
fprintf(stderr, "vhost-net requires net backend to be setup\n");
115
@@ -XXX,XX +XXX,XX @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
116
}
117
118
r = vhost_dev_init(&net->dev, options->opaque,
119
- options->backend_type, options->busyloop_timeout);
120
+ options->backend_type, options->busyloop_timeout,
121
+ &local_err);
122
if (r < 0) {
123
+ error_report_err(local_err);
124
goto fail;
125
}
126
if (backend_kernel) {
127
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
128
index XXXXXXX..XXXXXXX 100644
129
--- a/hw/scsi/vhost-scsi.c
130
+++ b/hw/scsi/vhost-scsi.c
131
@@ -XXX,XX +XXX,XX @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
132
vsc->dev.backend_features = 0;
133
134
ret = vhost_dev_init(&vsc->dev, (void *)(uintptr_t)vhostfd,
135
- VHOST_BACKEND_TYPE_KERNEL, 0);
136
+ VHOST_BACKEND_TYPE_KERNEL, 0, errp);
137
if (ret < 0) {
138
- error_setg(errp, "vhost-scsi: vhost initialization failed: %s",
139
- strerror(-ret));
140
goto free_vqs;
141
}
142
143
diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/hw/scsi/vhost-user-scsi.c
146
+++ b/hw/scsi/vhost-user-scsi.c
147
@@ -XXX,XX +XXX,XX @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
148
vqs = vsc->dev.vqs;
149
150
ret = vhost_dev_init(&vsc->dev, &s->vhost_user,
151
- VHOST_BACKEND_TYPE_USER, 0);
152
+ VHOST_BACKEND_TYPE_USER, 0, errp);
153
if (ret < 0) {
154
- error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s",
155
- strerror(-ret));
156
goto free_vhost;
157
}
158
159
diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c
160
index XXXXXXX..XXXXXXX 100644
161
--- a/hw/virtio/vhost-user-fs.c
162
+++ b/hw/virtio/vhost-user-fs.c
163
@@ -XXX,XX +XXX,XX @@ static void vuf_device_realize(DeviceState *dev, Error **errp)
164
fs->vhost_dev.nvqs = 1 + fs->conf.num_request_queues;
165
fs->vhost_dev.vqs = g_new0(struct vhost_virtqueue, fs->vhost_dev.nvqs);
166
ret = vhost_dev_init(&fs->vhost_dev, &fs->vhost_user,
167
- VHOST_BACKEND_TYPE_USER, 0);
168
+ VHOST_BACKEND_TYPE_USER, 0, errp);
169
if (ret < 0) {
170
- error_setg_errno(errp, -ret, "vhost_dev_init failed");
171
goto err_virtio;
172
}
173
174
diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c
175
index XXXXXXX..XXXXXXX 100644
176
--- a/hw/virtio/vhost-user-vsock.c
177
+++ b/hw/virtio/vhost-user-vsock.c
178
@@ -XXX,XX +XXX,XX @@ static void vuv_device_realize(DeviceState *dev, Error **errp)
179
vhost_dev_set_config_notifier(&vvc->vhost_dev, &vsock_ops);
180
181
ret = vhost_dev_init(&vvc->vhost_dev, &vsock->vhost_user,
182
- VHOST_BACKEND_TYPE_USER, 0);
183
+ VHOST_BACKEND_TYPE_USER, 0, errp);
184
if (ret < 0) {
185
- error_setg_errno(errp, -ret, "vhost_dev_init failed");
186
goto err_virtio;
187
}
188
189
diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c
190
index XXXXXXX..XXXXXXX 100644
191
--- a/hw/virtio/vhost-vsock.c
192
+++ b/hw/virtio/vhost-vsock.c
193
@@ -XXX,XX +XXX,XX @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
194
vhost_vsock_common_realize(vdev, "vhost-vsock");
195
196
ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd,
197
- VHOST_BACKEND_TYPE_KERNEL, 0);
198
+ VHOST_BACKEND_TYPE_KERNEL, 0, errp);
199
if (ret < 0) {
200
- error_setg_errno(errp, -ret, "vhost-vsock: vhost_dev_init failed");
201
goto err_virtio;
202
}
203
204
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
205
index XXXXXXX..XXXXXXX 100644
206
--- a/hw/virtio/vhost.c
207
+++ b/hw/virtio/vhost.c
208
@@ -XXX,XX +XXX,XX @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq)
209
}
32
}
210
33
211
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
34
static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
212
- VhostBackendType backend_type, uint32_t busyloop_timeout)
213
+ VhostBackendType backend_type, uint32_t busyloop_timeout,
214
+ Error **errp)
215
{
216
uint64_t features;
217
int i, r, n_initialized_vqs = 0;
218
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
219
220
r = hdev->vhost_ops->vhost_backend_init(hdev, opaque);
221
if (r < 0) {
222
+ error_setg(errp, "vhost_backend_init failed");
223
goto fail;
224
}
225
226
r = hdev->vhost_ops->vhost_set_owner(hdev);
227
if (r < 0) {
228
- VHOST_OPS_DEBUG("vhost_set_owner failed");
229
+ error_setg(errp, "vhost_set_owner failed");
230
goto fail;
231
}
232
233
r = hdev->vhost_ops->vhost_get_features(hdev, &features);
234
if (r < 0) {
235
- VHOST_OPS_DEBUG("vhost_get_features failed");
236
+ error_setg(errp, "vhost_get_features failed");
237
goto fail;
238
}
239
240
for (i = 0; i < hdev->nvqs; ++i, ++n_initialized_vqs) {
241
r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i);
242
if (r < 0) {
243
+ error_setg_errno(errp, -r, "Failed to initialize virtqueue %d", i);
244
goto fail;
245
}
246
}
247
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
248
r = vhost_virtqueue_set_busyloop_timeout(hdev, hdev->vq_index + i,
249
busyloop_timeout);
250
if (r < 0) {
251
+ error_setg(errp, "Failed to set busyloop timeout");
252
goto fail_busyloop;
253
}
254
}
255
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
256
if (hdev->migration_blocker != NULL) {
257
r = migrate_add_blocker(hdev->migration_blocker, &local_err);
258
if (local_err) {
259
- error_report_err(local_err);
260
+ error_propagate(errp, local_err);
261
error_free(hdev->migration_blocker);
262
goto fail_busyloop;
263
}
264
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
265
QLIST_INSERT_HEAD(&vhost_devices, hdev, entry);
266
267
if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) {
268
- error_report("vhost backend memory slots limit is less"
269
- " than current number of present memory slots");
270
+ error_setg(errp, "vhost backend memory slots limit is less"
271
+ " than current number of present memory slots");
272
r = -1;
273
goto fail_busyloop;
274
}
275
--
35
--
276
2.31.1
36
2.31.1
277
37
278
38
diff view generated by jsdifflib
1
This function is the part that we will want to retry if the connection
1
From: Peter Lieven <pl@kamp.de>
2
is lost during initialisation, so factor it out to keep the following
3
patch simpler.
4
2
5
The error path for vhost_dev_get_config() forgot disconnecting the
3
Signed-off-by: Peter Lieven <pl@kamp.de>
6
chardev, add this while touching the code.
4
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
7
5
Message-Id: <20210702172356.11574-5-idryomov@gmail.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20210609154658.350308-7-kwolf@redhat.com>
10
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
11
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
7
---
14
hw/block/vhost-user-blk.c | 48 ++++++++++++++++++++++++++-------------
8
block/rbd.c | 252 +++++++++++++++++++---------------------------------
15
1 file changed, 32 insertions(+), 16 deletions(-)
9
1 file changed, 90 insertions(+), 162 deletions(-)
16
10
17
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
11
diff --git a/block/rbd.c b/block/rbd.c
18
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
19
--- a/hw/block/vhost-user-blk.c
13
--- a/block/rbd.c
20
+++ b/hw/block/vhost-user-blk.c
14
+++ b/block/rbd.c
21
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent event)
15
@@ -XXX,XX +XXX,XX @@ typedef enum {
16
RBD_AIO_FLUSH
17
} RBDAIOCmd;
18
19
-typedef struct RBDAIOCB {
20
- BlockAIOCB common;
21
- int64_t ret;
22
- QEMUIOVector *qiov;
23
- RBDAIOCmd cmd;
24
- int error;
25
- struct BDRVRBDState *s;
26
-} RBDAIOCB;
27
-
28
-typedef struct RADOSCB {
29
- RBDAIOCB *acb;
30
- struct BDRVRBDState *s;
31
- int64_t size;
32
- int64_t ret;
33
-} RADOSCB;
34
-
35
typedef struct BDRVRBDState {
36
rados_t cluster;
37
rados_ioctx_t io_ctx;
38
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRBDState {
39
uint64_t object_size;
40
} BDRVRBDState;
41
42
+typedef struct RBDTask {
43
+ BlockDriverState *bs;
44
+ Coroutine *co;
45
+ bool complete;
46
+ int64_t ret;
47
+} RBDTask;
48
+
49
static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
50
BlockdevOptionsRbd *opts, bool cache,
51
const char *keypairs, const char *secretid,
52
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
53
return ret;
54
}
55
56
-static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs)
57
-{
58
- RBDAIOCB *acb = rcb->acb;
59
- iov_memset(acb->qiov->iov, acb->qiov->niov, offs, 0,
60
- acb->qiov->size - offs);
61
-}
62
-
63
#ifdef LIBRBD_SUPPORTS_ENCRYPTION
64
static int qemu_rbd_convert_luks_options(
65
RbdEncryptionOptionsLUKSBase *luks_opts,
66
@@ -XXX,XX +XXX,XX @@ exit:
67
return ret;
68
}
69
70
-/*
71
- * This aio completion is being called from rbd_finish_bh() and runs in qemu
72
- * BH context.
73
- */
74
-static void qemu_rbd_complete_aio(RADOSCB *rcb)
75
-{
76
- RBDAIOCB *acb = rcb->acb;
77
- int64_t r;
78
-
79
- r = rcb->ret;
80
-
81
- if (acb->cmd != RBD_AIO_READ) {
82
- if (r < 0) {
83
- acb->ret = r;
84
- acb->error = 1;
85
- } else if (!acb->error) {
86
- acb->ret = rcb->size;
87
- }
88
- } else {
89
- if (r < 0) {
90
- qemu_rbd_memset(rcb, 0);
91
- acb->ret = r;
92
- acb->error = 1;
93
- } else if (r < rcb->size) {
94
- qemu_rbd_memset(rcb, r);
95
- if (!acb->error) {
96
- acb->ret = rcb->size;
97
- }
98
- } else if (!acb->error) {
99
- acb->ret = r;
100
- }
101
- }
102
-
103
- g_free(rcb);
104
-
105
- acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
106
-
107
- qemu_aio_unref(acb);
108
-}
109
-
110
static char *qemu_rbd_mon_host(BlockdevOptionsRbd *opts, Error **errp)
111
{
112
const char **vals;
113
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_resize(BlockDriverState *bs, uint64_t size)
114
return 0;
115
}
116
117
-static const AIOCBInfo rbd_aiocb_info = {
118
- .aiocb_size = sizeof(RBDAIOCB),
119
-};
120
-
121
-static void rbd_finish_bh(void *opaque)
122
+static void qemu_rbd_finish_bh(void *opaque)
123
{
124
- RADOSCB *rcb = opaque;
125
- qemu_rbd_complete_aio(rcb);
126
+ RBDTask *task = opaque;
127
+ task->complete = 1;
128
+ aio_co_wake(task->co);
129
}
130
131
/*
132
- * This is the callback function for rbd_aio_read and _write
133
+ * This is the completion callback function for all rbd aio calls
134
+ * started from qemu_rbd_start_co().
135
*
136
* Note: this function is being called from a non qemu thread so
137
* we need to be careful about what we do here. Generally we only
138
* schedule a BH, and do the rest of the io completion handling
139
- * from rbd_finish_bh() which runs in a qemu context.
140
+ * from qemu_rbd_finish_bh() which runs in a qemu context.
141
*/
142
-static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
143
+static void qemu_rbd_completion_cb(rbd_completion_t c, RBDTask *task)
144
{
145
- RBDAIOCB *acb = rcb->acb;
146
-
147
- rcb->ret = rbd_aio_get_return_value(c);
148
+ task->ret = rbd_aio_get_return_value(c);
149
rbd_aio_release(c);
150
-
151
- replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs),
152
- rbd_finish_bh, rcb);
153
+ aio_bh_schedule_oneshot(bdrv_get_aio_context(task->bs),
154
+ qemu_rbd_finish_bh, task);
155
}
156
157
-static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
158
- int64_t off,
159
- QEMUIOVector *qiov,
160
- int64_t size,
161
- BlockCompletionFunc *cb,
162
- void *opaque,
163
- RBDAIOCmd cmd)
164
+static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs,
165
+ uint64_t offset,
166
+ uint64_t bytes,
167
+ QEMUIOVector *qiov,
168
+ int flags,
169
+ RBDAIOCmd cmd)
170
{
171
- RBDAIOCB *acb;
172
- RADOSCB *rcb = NULL;
173
+ BDRVRBDState *s = bs->opaque;
174
+ RBDTask task = { .bs = bs, .co = qemu_coroutine_self() };
175
rbd_completion_t c;
176
int r;
177
178
- BDRVRBDState *s = bs->opaque;
179
-
180
- acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque);
181
- acb->cmd = cmd;
182
- acb->qiov = qiov;
183
- assert(!qiov || qiov->size == size);
184
-
185
- rcb = g_new(RADOSCB, 1);
186
+ assert(!qiov || qiov->size == bytes);
187
188
- acb->ret = 0;
189
- acb->error = 0;
190
- acb->s = s;
191
-
192
- rcb->acb = acb;
193
- rcb->s = acb->s;
194
- rcb->size = size;
195
- r = rbd_aio_create_completion(rcb, (rbd_callback_t) rbd_finish_aiocb, &c);
196
+ r = rbd_aio_create_completion(&task,
197
+ (rbd_callback_t) qemu_rbd_completion_cb, &c);
198
if (r < 0) {
199
- goto failed;
200
+ return r;
22
}
201
}
23
}
202
24
203
switch (cmd) {
25
+static int vhost_user_blk_realize_connect(VHostUserBlk *s, Error **errp)
204
- case RBD_AIO_WRITE:
26
+{
205
- /*
27
+ DeviceState *dev = &s->parent_obj.parent_obj;
206
- * RBD APIs don't allow us to write more than actual size, so in order
28
+ int ret;
207
- * to support growing images, we resize the image before write
29
+
208
- * operations that exceed the current size.
30
+ s->connected = false;
209
- */
31
+
210
- if (off + size > s->image_size) {
32
+ ret = qemu_chr_fe_wait_connected(&s->chardev, errp);
211
- r = qemu_rbd_resize(bs, off + size);
33
+ if (ret < 0) {
212
- if (r < 0) {
34
+ return ret;
213
- goto failed_completion;
214
- }
215
- }
216
- r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, off, c);
217
- break;
218
case RBD_AIO_READ:
219
- r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, off, c);
220
+ r = rbd_aio_readv(s->image, qiov->iov, qiov->niov, offset, c);
221
+ break;
222
+ case RBD_AIO_WRITE:
223
+ r = rbd_aio_writev(s->image, qiov->iov, qiov->niov, offset, c);
224
break;
225
case RBD_AIO_DISCARD:
226
- r = rbd_aio_discard(s->image, off, size, c);
227
+ r = rbd_aio_discard(s->image, offset, bytes, c);
228
break;
229
case RBD_AIO_FLUSH:
230
r = rbd_aio_flush(s->image, c);
231
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *rbd_start_aio(BlockDriverState *bs,
232
}
233
234
if (r < 0) {
235
- goto failed_completion;
236
+ error_report("rbd request failed early: cmd %d offset %" PRIu64
237
+ " bytes %" PRIu64 " flags %d r %d (%s)", cmd, offset,
238
+ bytes, flags, r, strerror(-r));
239
+ rbd_aio_release(c);
240
+ return r;
241
}
242
- return &acb->common;
243
244
-failed_completion:
245
- rbd_aio_release(c);
246
-failed:
247
- g_free(rcb);
248
+ while (!task.complete) {
249
+ qemu_coroutine_yield();
250
+ }
251
252
- qemu_aio_unref(acb);
253
- return NULL;
254
+ if (task.ret < 0) {
255
+ error_report("rbd request failed: cmd %d offset %" PRIu64 " bytes %"
256
+ PRIu64 " flags %d task.ret %" PRIi64 " (%s)", cmd, offset,
257
+ bytes, flags, task.ret, strerror(-task.ret));
258
+ return task.ret;
35
+ }
259
+ }
36
+
260
+
37
+ ret = vhost_user_blk_connect(dev, errp);
261
+ /* zero pad short reads */
38
+ if (ret < 0) {
262
+ if (cmd == RBD_AIO_READ && task.ret < qiov->size) {
39
+ qemu_chr_fe_disconnect(&s->chardev);
263
+ qemu_iovec_memset(qiov, task.ret, 0, qiov->size - task.ret);
40
+ return ret;
41
+ }
42
+ assert(s->connected);
43
+
44
+ ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
45
+ sizeof(struct virtio_blk_config), errp);
46
+ if (ret < 0) {
47
+ qemu_chr_fe_disconnect(&s->chardev);
48
+ vhost_dev_cleanup(&s->dev);
49
+ return ret;
50
+ }
264
+ }
51
+
265
+
52
+ return 0;
266
+ return 0;
53
+}
267
+}
54
+
268
+
55
static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
269
+static int
56
{
270
+coroutine_fn qemu_rbd_co_preadv(BlockDriverState *bs, uint64_t offset,
57
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
271
+ uint64_t bytes, QEMUIOVector *qiov,
58
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
272
+ int flags)
59
273
+{
60
s->inflight = g_new0(struct vhost_inflight, 1);
274
+ return qemu_rbd_start_co(bs, offset, bytes, qiov, flags, RBD_AIO_READ);
61
s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues);
275
}
62
- s->connected = false;
276
63
-
277
-static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs,
64
- if (qemu_chr_fe_wait_connected(&s->chardev, errp) < 0) {
278
- uint64_t offset, uint64_t bytes,
65
- goto virtio_err;
279
- QEMUIOVector *qiov, int flags,
66
- }
280
- BlockCompletionFunc *cb,
67
281
- void *opaque)
68
- if (vhost_user_blk_connect(dev, errp) < 0) {
282
+static int
69
- qemu_chr_fe_disconnect(&s->chardev);
283
+coroutine_fn qemu_rbd_co_pwritev(BlockDriverState *bs, uint64_t offset,
70
- goto virtio_err;
284
+ uint64_t bytes, QEMUIOVector *qiov,
71
- }
285
+ int flags)
72
- assert(s->connected);
286
{
73
-
287
- return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque,
74
- ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
288
- RBD_AIO_READ);
75
- sizeof(struct virtio_blk_config), errp);
289
+ BDRVRBDState *s = bs->opaque;
76
+ ret = vhost_user_blk_realize_connect(s, errp);
290
+ /*
77
if (ret < 0) {
291
+ * RBD APIs don't allow us to write more than actual size, so in order
78
- goto vhost_err;
292
+ * to support growing images, we resize the image before write
79
+ goto virtio_err;
293
+ * operations that exceed the current size.
80
}
294
+ */
81
295
+ if (offset + bytes > s->image_size) {
82
/* we're fully initialized, now we can operate, so add the handler */
296
+ int r = qemu_rbd_resize(bs, offset + bytes);
83
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
297
+ if (r < 0) {
84
NULL, true);
298
+ return r;
85
return;
299
+ }
86
300
+ }
87
-vhost_err:
301
+ return qemu_rbd_start_co(bs, offset, bytes, qiov, flags, RBD_AIO_WRITE);
88
- vhost_dev_cleanup(&s->dev);
302
}
89
virtio_err:
303
90
g_free(s->vhost_vqs);
304
-static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs,
91
s->vhost_vqs = NULL;
305
- uint64_t offset, uint64_t bytes,
306
- QEMUIOVector *qiov, int flags,
307
- BlockCompletionFunc *cb,
308
- void *opaque)
309
+static int coroutine_fn qemu_rbd_co_flush(BlockDriverState *bs)
310
{
311
- return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque,
312
- RBD_AIO_WRITE);
313
+ return qemu_rbd_start_co(bs, 0, 0, NULL, 0, RBD_AIO_FLUSH);
314
}
315
316
-static BlockAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs,
317
- BlockCompletionFunc *cb,
318
- void *opaque)
319
+static int coroutine_fn qemu_rbd_co_pdiscard(BlockDriverState *bs,
320
+ int64_t offset, int count)
321
{
322
- return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH);
323
+ return qemu_rbd_start_co(bs, offset, count, NULL, 0, RBD_AIO_DISCARD);
324
}
325
326
static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi)
327
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
328
return snap_count;
329
}
330
331
-static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs,
332
- int64_t offset,
333
- int bytes,
334
- BlockCompletionFunc *cb,
335
- void *opaque)
336
-{
337
- return rbd_start_aio(bs, offset, NULL, bytes, cb, opaque,
338
- RBD_AIO_DISCARD);
339
-}
340
-
341
static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs,
342
Error **errp)
343
{
344
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
345
.bdrv_co_truncate = qemu_rbd_co_truncate,
346
.protocol_name = "rbd",
347
348
- .bdrv_aio_preadv = qemu_rbd_aio_preadv,
349
- .bdrv_aio_pwritev = qemu_rbd_aio_pwritev,
350
-
351
- .bdrv_aio_flush = qemu_rbd_aio_flush,
352
- .bdrv_aio_pdiscard = qemu_rbd_aio_pdiscard,
353
+ .bdrv_co_preadv = qemu_rbd_co_preadv,
354
+ .bdrv_co_pwritev = qemu_rbd_co_pwritev,
355
+ .bdrv_co_flush_to_disk = qemu_rbd_co_flush,
356
+ .bdrv_co_pdiscard = qemu_rbd_co_pdiscard,
357
358
.bdrv_snapshot_create = qemu_rbd_snap_create,
359
.bdrv_snapshot_delete = qemu_rbd_snap_remove,
92
--
360
--
93
2.31.1
361
2.31.1
94
362
95
363
diff view generated by jsdifflib
1
Instead of letting the caller make up a meaningless error message, add
1
From: Peter Lieven <pl@kamp.de>
2
an Error parameter to allow reporting the real error.
3
2
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
This patch wittingly sets BDRV_REQ_NO_FALLBACK and silently ignores
5
Message-Id: <20210609154658.350308-5-kwolf@redhat.com>
4
BDRV_REQ_MAY_UNMAP for older librbd versions.
6
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
5
7
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
6
The rationale for this is as follows (citing Ilya Dryomov current RBD
7
maintainer):
8
9
---8<---
10
a) remove the BDRV_REQ_MAY_UNMAP check in qemu_rbd_co_pwrite_zeroes()
11
and as a consequence always unmap if librbd is too old
12
13
It's not clear what qemu's expectation is but in general Write
14
Zeroes is allowed to unmap. The only guarantee is that subsequent
15
reads return zeroes, everything else is a hint. This is how it is
16
specified in the kernel and in the NVMe spec.
17
18
In particular, block/nvme.c implements it as follows:
19
20
if (flags & BDRV_REQ_MAY_UNMAP) {
21
cdw12 |= (1 << 25);
22
}
23
24
This sets the Deallocate bit. But if it's not set, the device may
25
still deallocate:
26
27
"""
28
If the Deallocate bit (CDW12.DEAC) is set to '1' in a Write Zeroes
29
command, and the namespace supports clearing all bytes to 0h in the
30
values read (e.g., bits 2:0 in the DLFEAT field are set to 001b)
31
from a deallocated logical block and its metadata (excluding
32
protection information), then for each specified logical block, the
33
controller:
34
- should deallocate that logical block;
35
36
...
37
38
If the Deallocate bit is cleared to '0' in a Write Zeroes command,
39
and the namespace supports clearing all bytes to 0h in the values
40
read (e.g., bits 2:0 in the DLFEAT field are set to 001b) from
41
a deallocated logical block and its metadata (excluding protection
42
information), then, for each specified logical block, the
43
controller:
44
- may deallocate that logical block;
45
"""
46
47
https://nvmexpress.org/wp-content/uploads/NVM-Express-NVM-Command-Set-Specification-2021.06.02-Ratified-1.pdf
48
49
b) set BDRV_REQ_NO_FALLBACK in supported_zero_flags
50
51
Again, it's not clear what qemu expects here, but without it we end
52
up in a ridiculous situation where specifying the "don't allow slow
53
fallback" switch immediately fails all efficient zeroing requests on
54
a device where Write Zeroes is always efficient:
55
56
$ qemu-io -c 'help write' | grep -- '-[zun]'
57
-n, -- with -z, don't allow slow fallback
58
-u, -- with -z, allow unmapping
59
-z, -- write zeroes using blk_co_pwrite_zeroes
60
61
$ qemu-io -f rbd -c 'write -z -u -n 0 1M' rbd:foo/bar
62
write failed: Operation not supported
63
--->8---
64
65
Signed-off-by: Peter Lieven <pl@kamp.de>
66
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
67
Message-Id: <20210702172356.11574-6-idryomov@gmail.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
68
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
69
---
10
hw/block/vhost-user-blk.c | 31 +++++++++++++++----------------
70
block/rbd.c | 32 +++++++++++++++++++++++++++++++-
11
1 file changed, 15 insertions(+), 16 deletions(-)
71
1 file changed, 31 insertions(+), 1 deletion(-)
12
72
13
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
73
diff --git a/block/rbd.c b/block/rbd.c
14
index XXXXXXX..XXXXXXX 100644
74
index XXXXXXX..XXXXXXX 100644
15
--- a/hw/block/vhost-user-blk.c
75
--- a/block/rbd.c
16
+++ b/hw/block/vhost-user-blk.c
76
+++ b/block/rbd.c
17
@@ -XXX,XX +XXX,XX @@ const VhostDevConfigOps blk_ops = {
77
@@ -XXX,XX +XXX,XX @@ typedef enum {
18
.vhost_dev_config_notifier = vhost_user_blk_handle_config_change,
78
RBD_AIO_READ,
19
};
79
RBD_AIO_WRITE,
20
80
RBD_AIO_DISCARD,
21
-static int vhost_user_blk_start(VirtIODevice *vdev)
81
- RBD_AIO_FLUSH
22
+static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp)
82
+ RBD_AIO_FLUSH,
23
{
83
+ RBD_AIO_WRITE_ZEROES
24
VHostUserBlk *s = VHOST_USER_BLK(vdev);
84
} RBDAIOCmd;
25
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
85
26
@@ -XXX,XX +XXX,XX @@ static int vhost_user_blk_start(VirtIODevice *vdev)
86
typedef struct BDRVRBDState {
27
int i, ret;
87
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
28
29
if (!k->set_guest_notifiers) {
30
- error_report("binding does not support guest notifiers");
31
+ error_setg(errp, "binding does not support guest notifiers");
32
return -ENOSYS;
33
}
34
35
ret = vhost_dev_enable_notifiers(&s->dev, vdev);
36
if (ret < 0) {
37
- error_report("Error enabling host notifiers: %d", -ret);
38
+ error_setg_errno(errp, -ret, "Error enabling host notifiers");
39
return ret;
40
}
41
42
ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true);
43
if (ret < 0) {
44
- error_report("Error binding guest notifier: %d", -ret);
45
+ error_setg_errno(errp, -ret, "Error binding guest notifier");
46
goto err_host_notifiers;
47
}
48
49
@@ -XXX,XX +XXX,XX @@ static int vhost_user_blk_start(VirtIODevice *vdev)
50
51
ret = vhost_dev_prepare_inflight(&s->dev, vdev);
52
if (ret < 0) {
53
- error_report("Error set inflight format: %d", -ret);
54
+ error_setg_errno(errp, -ret, "Error setting inflight format");
55
goto err_guest_notifiers;
56
}
57
58
if (!s->inflight->addr) {
59
ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
60
if (ret < 0) {
61
- error_report("Error get inflight: %d", -ret);
62
+ error_setg_errno(errp, -ret, "Error getting inflight");
63
goto err_guest_notifiers;
64
}
88
}
65
}
89
}
66
90
67
ret = vhost_dev_set_inflight(&s->dev, s->inflight);
91
+#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES
68
if (ret < 0) {
92
+ bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
69
- error_report("Error set inflight: %d", -ret);
93
+#endif
70
+ error_setg_errno(errp, -ret, "Error setting inflight");
94
+
71
goto err_guest_notifiers;
95
/* When extending regular files, we get zeros from the OS */
96
bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
97
98
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_start_co(BlockDriverState *bs,
99
case RBD_AIO_FLUSH:
100
r = rbd_aio_flush(s->image, c);
101
break;
102
+#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES
103
+ case RBD_AIO_WRITE_ZEROES: {
104
+ int zero_flags = 0;
105
+#ifdef RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
106
+ if (!(flags & BDRV_REQ_MAY_UNMAP)) {
107
+ zero_flags = RBD_WRITE_ZEROES_FLAG_THICK_PROVISION;
108
+ }
109
+#endif
110
+ r = rbd_aio_write_zeroes(s->image, offset, bytes, c, zero_flags, 0);
111
+ break;
112
+ }
113
+#endif
114
default:
115
r = -EINVAL;
72
}
116
}
73
117
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_pdiscard(BlockDriverState *bs,
74
ret = vhost_dev_start(&s->dev, vdev);
118
return qemu_rbd_start_co(bs, offset, count, NULL, 0, RBD_AIO_DISCARD);
75
if (ret < 0) {
119
}
76
- error_report("Error starting vhost: %d", -ret);
120
77
+ error_setg_errno(errp, -ret, "Error starting vhost");
121
+#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES
78
goto err_guest_notifiers;
122
+static int
79
}
123
+coroutine_fn qemu_rbd_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
80
s->started_vu = true;
124
+ int count, BdrvRequestFlags flags)
81
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
125
+{
126
+ return qemu_rbd_start_co(bs, offset, count, NULL, flags,
127
+ RBD_AIO_WRITE_ZEROES);
128
+}
129
+#endif
130
+
131
static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi)
82
{
132
{
83
VHostUserBlk *s = VHOST_USER_BLK(vdev);
133
BDRVRBDState *s = bs->opaque;
84
bool should_start = virtio_device_started(vdev, status);
134
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
85
+ Error *local_err = NULL;
135
.bdrv_co_pwritev = qemu_rbd_co_pwritev,
86
int ret;
136
.bdrv_co_flush_to_disk = qemu_rbd_co_flush,
87
137
.bdrv_co_pdiscard = qemu_rbd_co_pdiscard,
88
if (!vdev->vm_running) {
138
+#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES
89
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
139
+ .bdrv_co_pwrite_zeroes = qemu_rbd_co_pwrite_zeroes,
90
}
140
+#endif
91
141
92
if (should_start) {
142
.bdrv_snapshot_create = qemu_rbd_snap_create,
93
- ret = vhost_user_blk_start(vdev);
143
.bdrv_snapshot_delete = qemu_rbd_snap_remove,
94
+ ret = vhost_user_blk_start(vdev, &local_err);
95
if (ret < 0) {
96
- error_report("vhost-user-blk: vhost start failed: %s",
97
- strerror(-ret));
98
+ error_reportf_err(local_err, "vhost-user-blk: vhost start failed: ");
99
qemu_chr_fe_disconnect(&s->chardev);
100
}
101
} else {
102
@@ -XXX,XX +XXX,XX @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
103
static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
104
{
105
VHostUserBlk *s = VHOST_USER_BLK(vdev);
106
+ Error *local_err = NULL;
107
int i, ret;
108
109
if (!vdev->start_on_kick) {
110
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
111
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
112
* vhost here instead of waiting for .set_status().
113
*/
114
- ret = vhost_user_blk_start(vdev);
115
+ ret = vhost_user_blk_start(vdev, &local_err);
116
if (ret < 0) {
117
- error_report("vhost-user-blk: vhost start failed: %s",
118
- strerror(-ret));
119
+ error_reportf_err(local_err, "vhost-user-blk: vhost start failed: ");
120
qemu_chr_fe_disconnect(&s->chardev);
121
return;
122
}
123
@@ -XXX,XX +XXX,XX @@ static int vhost_user_blk_connect(DeviceState *dev, Error **errp)
124
125
/* restore vhost state */
126
if (virtio_device_started(vdev, vdev->status)) {
127
- ret = vhost_user_blk_start(vdev);
128
+ ret = vhost_user_blk_start(vdev, errp);
129
if (ret < 0) {
130
- error_setg_errno(errp, -ret, "vhost start failed");
131
return ret;
132
}
133
}
134
--
144
--
135
2.31.1
145
2.31.1
136
146
137
147
diff view generated by jsdifflib
New patch
1
From: Peter Lieven <pl@kamp.de>
1
2
3
librbd supports 1 byte alignment for all aio operations.
4
5
Currently, there is no API call to query limits from the Ceph
6
ObjectStore backend. So drop the bdrv_refresh_limits completely
7
until there is such an API call.
8
9
Signed-off-by: Peter Lieven <pl@kamp.de>
10
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
11
Message-Id: <20210702172356.11574-7-idryomov@gmail.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
block/rbd.c | 9 ---------
15
1 file changed, 9 deletions(-)
16
17
diff --git a/block/rbd.c b/block/rbd.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block/rbd.c
20
+++ b/block/rbd.c
21
@@ -XXX,XX +XXX,XX @@ done:
22
return;
23
}
24
25
-
26
-static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp)
27
-{
28
- /* XXX Does RBD support AIO on less than 512-byte alignment? */
29
- bs->bl.request_alignment = 512;
30
-}
31
-
32
-
33
static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts,
34
Error **errp)
35
{
36
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
37
.format_name = "rbd",
38
.instance_size = sizeof(BDRVRBDState),
39
.bdrv_parse_filename = qemu_rbd_parse_filename,
40
- .bdrv_refresh_limits = qemu_rbd_refresh_limits,
41
.bdrv_file_open = qemu_rbd_open,
42
.bdrv_close = qemu_rbd_close,
43
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
44
--
45
2.31.1
46
47
diff view generated by jsdifflib
1
Instead of just returning 0/-1 and letting the caller make up a
1
From: Heinrich Schuchardt <xypron.glpk@gmx.de>
2
meaningless error message, add an Error parameter to allow reporting the
3
real error and switch to 0/-errno so that different kind of errors can
4
be distinguished in the caller.
5
2
6
Specifically, in vhost-user, EPROTO is used for all errors that relate
3
uri_free() checks if its argument is NULL in uri_clean() and g_free().
7
to the connection itself, whereas other error codes are used for errors
4
There is no need to check the argument before the call.
8
relating to the content of the connection. This will allow us later to
9
automatically reconnect when the connection goes away, without ending up
10
in an endless loop if it's a permanent error in the configuration.
11
5
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
13
Message-Id: <20210609154658.350308-3-kwolf@redhat.com>
7
Message-Id: <20210629063602.4239-1-xypron.glpk@gmx.de>
14
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
8
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
15
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
9
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
11
---
18
include/hw/virtio/vhost-backend.h | 3 ++-
12
block/nfs.c | 4 +---
19
hw/virtio/vhost-backend.c | 2 +-
13
block/ssh.c | 4 +---
20
hw/virtio/vhost-user.c | 41 ++++++++++++++++---------------
14
util/uri.c | 22 ++++++----------------
21
hw/virtio/vhost-vdpa.c | 2 +-
15
3 files changed, 8 insertions(+), 22 deletions(-)
22
hw/virtio/vhost.c | 13 +++++-----
23
5 files changed, 32 insertions(+), 29 deletions(-)
24
16
25
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
17
diff --git a/block/nfs.c b/block/nfs.c
26
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
27
--- a/include/hw/virtio/vhost-backend.h
19
--- a/block/nfs.c
28
+++ b/include/hw/virtio/vhost-backend.h
20
+++ b/block/nfs.c
29
@@ -XXX,XX +XXX,XX @@ struct vhost_scsi_target;
21
@@ -XXX,XX +XXX,XX @@ out:
30
struct vhost_iotlb_msg;
22
if (qp) {
31
struct vhost_virtqueue;
23
query_params_free(qp);
32
24
}
33
-typedef int (*vhost_backend_init)(struct vhost_dev *dev, void *opaque);
25
- if (uri) {
34
+typedef int (*vhost_backend_init)(struct vhost_dev *dev, void *opaque,
26
- uri_free(uri);
35
+ Error **errp);
27
- }
36
typedef int (*vhost_backend_cleanup)(struct vhost_dev *dev);
28
+ uri_free(uri);
37
typedef int (*vhost_backend_memslots_limit)(struct vhost_dev *dev);
29
return ret;
38
30
}
39
diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c
31
32
diff --git a/block/ssh.c b/block/ssh.c
40
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
41
--- a/hw/virtio/vhost-backend.c
34
--- a/block/ssh.c
42
+++ b/hw/virtio/vhost-backend.c
35
+++ b/block/ssh.c
43
@@ -XXX,XX +XXX,XX @@ static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request,
36
@@ -XXX,XX +XXX,XX @@ static int parse_uri(const char *filename, QDict *options, Error **errp)
44
return ioctl(fd, request, arg);
37
return 0;
38
39
err:
40
- if (uri) {
41
- uri_free(uri);
42
- }
43
+ uri_free(uri);
44
return -EINVAL;
45
}
45
}
46
46
47
-static int vhost_kernel_init(struct vhost_dev *dev, void *opaque)
47
diff --git a/util/uri.c b/util/uri.c
48
+static int vhost_kernel_init(struct vhost_dev *dev, void *opaque, Error **errp)
49
{
50
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL);
51
52
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
53
index XXXXXXX..XXXXXXX 100644
48
index XXXXXXX..XXXXXXX 100644
54
--- a/hw/virtio/vhost-user.c
49
--- a/util/uri.c
55
+++ b/hw/virtio/vhost-user.c
50
+++ b/util/uri.c
56
@@ -XXX,XX +XXX,XX @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier,
51
@@ -XXX,XX +XXX,XX @@ static void uri_clean(URI *uri)
57
return 0;
52
53
/**
54
* uri_free:
55
- * @uri: pointer to an URI
56
+ * @uri: pointer to an URI, NULL is ignored
57
*
58
* Free up the URI struct
59
*/
60
@@ -XXX,XX +XXX,XX @@ step_7:
61
val = uri_to_string(res);
62
63
done:
64
- if (ref != NULL) {
65
- uri_free(ref);
66
- }
67
- if (bas != NULL) {
68
- uri_free(bas);
69
- }
70
- if (res != NULL) {
71
- uri_free(res);
72
- }
73
+ uri_free(ref);
74
+ uri_free(bas);
75
+ uri_free(res);
76
return val;
58
}
77
}
59
78
60
-static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
79
@@ -XXX,XX +XXX,XX @@ done:
61
+static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque,
80
if (remove_path != 0) {
62
+ Error **errp)
81
ref->path = NULL;
63
{
64
uint64_t features, protocol_features, ram_slots;
65
struct vhost_user *u;
66
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
67
68
err = vhost_user_get_features(dev, &features);
69
if (err < 0) {
70
- return err;
71
+ return -EPROTO;
72
}
82
}
73
83
- if (ref != NULL) {
74
if (virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES)) {
84
- uri_free(ref);
75
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
85
- }
76
err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES,
86
- if (bas != NULL) {
77
&protocol_features);
87
- uri_free(bas);
78
if (err < 0) {
88
- }
79
- return err;
89
+ uri_free(ref);
80
+ return -EPROTO;
90
+ uri_free(bas);
81
}
91
82
92
return val;
83
dev->protocol_features =
84
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
85
dev->protocol_features &= ~(1ULL << VHOST_USER_PROTOCOL_F_CONFIG);
86
} else if (!(protocol_features &
87
(1ULL << VHOST_USER_PROTOCOL_F_CONFIG))) {
88
- error_report("Device expects VHOST_USER_PROTOCOL_F_CONFIG "
89
- "but backend does not support it.");
90
- return -1;
91
+ error_setg(errp, "Device expects VHOST_USER_PROTOCOL_F_CONFIG "
92
+ "but backend does not support it.");
93
+ return -EINVAL;
94
}
95
96
err = vhost_user_set_protocol_features(dev, dev->protocol_features);
97
if (err < 0) {
98
- return err;
99
+ return -EPROTO;
100
}
101
102
/* query the max queues we support if backend supports Multiple Queue */
103
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
104
err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM,
105
&dev->max_queues);
106
if (err < 0) {
107
- return err;
108
+ return -EPROTO;
109
}
110
}
111
if (dev->num_queues && dev->max_queues < dev->num_queues) {
112
- error_report("The maximum number of queues supported by the "
113
- "backend is %" PRIu64, dev->max_queues);
114
+ error_setg(errp, "The maximum number of queues supported by the "
115
+ "backend is %" PRIu64, dev->max_queues);
116
return -EINVAL;
117
}
118
119
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
120
VHOST_USER_PROTOCOL_F_SLAVE_REQ) &&
121
virtio_has_feature(dev->protocol_features,
122
VHOST_USER_PROTOCOL_F_REPLY_ACK))) {
123
- error_report("IOMMU support requires reply-ack and "
124
- "slave-req protocol features.");
125
- return -1;
126
+ error_setg(errp, "IOMMU support requires reply-ack and "
127
+ "slave-req protocol features.");
128
+ return -EINVAL;
129
}
130
131
/* get max memory regions if backend supports configurable RAM slots */
132
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
133
} else {
134
err = vhost_user_get_max_memslots(dev, &ram_slots);
135
if (err < 0) {
136
- return err;
137
+ return -EPROTO;
138
}
139
140
if (ram_slots < u->user->memory_slots) {
141
- error_report("The backend specified a max ram slots limit "
142
- "of %" PRIu64", when the prior validated limit was %d. "
143
- "This limit should never decrease.", ram_slots,
144
- u->user->memory_slots);
145
- return -1;
146
+ error_setg(errp, "The backend specified a max ram slots limit "
147
+ "of %" PRIu64", when the prior validated limit was "
148
+ "%d. This limit should never decrease.", ram_slots,
149
+ u->user->memory_slots);
150
+ return -EINVAL;
151
}
152
153
u->user->memory_slots = MIN(ram_slots, VHOST_USER_MAX_RAM_SLOTS);
154
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque)
155
if (dev->vq_index == 0) {
156
err = vhost_setup_slave_channel(dev);
157
if (err < 0) {
158
- return err;
159
+ return -EPROTO;
160
}
161
}
162
163
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
164
index XXXXXXX..XXXXXXX 100644
165
--- a/hw/virtio/vhost-vdpa.c
166
+++ b/hw/virtio/vhost-vdpa.c
167
@@ -XXX,XX +XXX,XX @@ static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status)
168
vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &s);
169
}
93
}
170
171
-static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque)
172
+static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
173
{
174
struct vhost_vdpa *v;
175
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA);
176
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/hw/virtio/vhost.c
179
+++ b/hw/virtio/vhost.c
180
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
181
VhostBackendType backend_type, uint32_t busyloop_timeout,
182
Error **errp)
183
{
184
+ ERRP_GUARD();
185
uint64_t features;
186
int i, r, n_initialized_vqs = 0;
187
- Error *local_err = NULL;
188
189
hdev->vdev = NULL;
190
hdev->migration_blocker = NULL;
191
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
192
r = vhost_set_backend_type(hdev, backend_type);
193
assert(r >= 0);
194
195
- r = hdev->vhost_ops->vhost_backend_init(hdev, opaque);
196
+ r = hdev->vhost_ops->vhost_backend_init(hdev, opaque, errp);
197
if (r < 0) {
198
- error_setg(errp, "vhost_backend_init failed");
199
+ if (!*errp) {
200
+ error_setg_errno(errp, -r, "vhost_backend_init failed");
201
+ }
202
goto fail;
203
}
204
205
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
206
}
207
208
if (hdev->migration_blocker != NULL) {
209
- r = migrate_add_blocker(hdev->migration_blocker, &local_err);
210
- if (local_err) {
211
- error_propagate(errp, local_err);
212
+ r = migrate_add_blocker(hdev->migration_blocker, errp);
213
+ if (*errp) {
214
error_free(hdev->migration_blocker);
215
goto fail_busyloop;
216
}
217
--
94
--
218
2.31.1
95
2.31.1
219
96
220
97
diff view generated by jsdifflib
1
From: Miroslav Rezanina <mrezanin@redhat.com>
1
From: Max Reitz <mreitz@redhat.com>
2
2
3
Commit 3108a15cf (block: introduce bdrv_drop_filter()) introduced
3
We do not do any permission checks in fuse_open(), so let the kernel do
4
uninitialized variable to_cow_parent in bdrv_replace_node_common
4
them. We already let fuse_getattr() report the proper UNIX permissions,
5
function that is used only when detach_subchain is true. It is used in
5
so this should work the way we want.
6
two places. First if block properly initialize the variable and second
7
block use it.
8
6
9
However, compiler may treat these two blocks as two independent cases so
7
This causes a change in 308's reference output, because now opening a
10
it thinks first block can fail test and second one pass (although both
8
non-writable export with O_RDWR fails already, instead of only actually
11
use same condition). This cause warning that variable can be
9
attempting to write to it. (That is an improvement.)
12
uninitialized in second block.
13
10
14
The warning was observed with GCC 8.4.1 and 11.0.1.
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
12
Message-Id: <20210625142317.271673-2-mreitz@redhat.com>
16
To prevent this warning, initialize the variable with NULL.
17
18
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
19
Message-Id: <1162368493.17178530.1620201543649.JavaMail.zimbra@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
---
14
---
22
block.c | 2 +-
15
block/export/fuse.c | 8 ++++++--
23
1 file changed, 1 insertion(+), 1 deletion(-)
16
tests/qemu-iotests/308 | 3 ++-
17
tests/qemu-iotests/308.out | 2 +-
18
3 files changed, 9 insertions(+), 4 deletions(-)
24
19
25
diff --git a/block.c b/block.c
20
diff --git a/block/export/fuse.c b/block/export/fuse.c
26
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
27
--- a/block.c
22
--- a/block/export/fuse.c
28
+++ b/block.c
23
+++ b/block/export/fuse.c
29
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
24
@@ -XXX,XX +XXX,XX @@ static int setup_fuse_export(FuseExport *exp, const char *mountpoint,
30
Transaction *tran = tran_new();
25
struct fuse_args fuse_args;
31
g_autoptr(GHashTable) found = NULL;
32
g_autoptr(GSList) refresh_list = NULL;
33
- BlockDriverState *to_cow_parent;
34
+ BlockDriverState *to_cow_parent = NULL;
35
int ret;
26
int ret;
36
27
37
if (detach_subchain) {
28
- /* Needs to match what fuse_init() sets. Only max_read must be supplied. */
29
- mount_opts = g_strdup_printf("max_read=%zu", FUSE_MAX_BOUNCE_BYTES);
30
+ /*
31
+ * max_read needs to match what fuse_init() sets.
32
+ * max_write need not be supplied.
33
+ */
34
+ mount_opts = g_strdup_printf("max_read=%zu,default_permissions",
35
+ FUSE_MAX_BOUNCE_BYTES);
36
37
fuse_argv[0] = ""; /* Dummy program name */
38
fuse_argv[1] = "-o";
39
diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308
40
index XXXXXXX..XXXXXXX 100755
41
--- a/tests/qemu-iotests/308
42
+++ b/tests/qemu-iotests/308
43
@@ -XXX,XX +XXX,XX @@ echo '=== Writable export ==='
44
fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP', 'writable': true"
45
46
# Check that writing to the read-only export fails
47
-$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" | _filter_qemu_io
48
+$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \
49
+ | _filter_qemu_io | _filter_testdir | _filter_imgfmt
50
51
# But here it should work
52
$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$EXT_MP" | _filter_qemu_io
53
diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out
54
index XXXXXXX..XXXXXXX 100644
55
--- a/tests/qemu-iotests/308.out
56
+++ b/tests/qemu-iotests/308.out
57
@@ -XXX,XX +XXX,XX @@ virtual size: 0 B (0 bytes)
58
'mountpoint': 'TEST_DIR/t.IMGFMT.fuse', 'writable': true
59
} }
60
{"return": {}}
61
-write failed: Permission denied
62
+qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
63
wrote 65536/65536 bytes at offset 1048576
64
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
65
wrote 65536/65536 bytes at offset 1048576
38
--
66
--
39
2.31.1
67
2.31.1
40
68
41
69
diff view generated by jsdifflib
1
Instead of just returning 0/-1 and letting the caller make up a
1
From: Max Reitz <mreitz@redhat.com>
2
meaningless error message, switch to 0/-errno so that different kinds of
2
3
errors can be distinguished in the caller.
3
Without the allow_other mount option, no user (not even root) but the
4
4
one who started qemu/the storage daemon can access the export. Allow
5
This involves changing a few more callbacks in VhostOps to return
5
users to configure the export such that such accesses are possible.
6
0/-errno: .vhost_set_owner(), .vhost_get_features() and
6
7
.vhost_virtqueue_set_busyloop_timeout(). The implementations of these
7
While allow_other is probably what users want, we cannot make it an
8
functions are trivial as they generally just send a message to the
8
unconditional default, because passing it is only possible (for non-root
9
backend.
9
users) if the global fuse.conf configuration file allows it. Thus, the
10
10
default is an 'auto' mode, in which we first try with allow_other, and
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
then fall back to without.
12
Message-Id: <20210609154658.350308-4-kwolf@redhat.com>
12
13
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
13
FuseExport.allow_other reports whether allow_other was actually used as
14
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
14
a mount option or not. Currently, this information is not used, but a
15
future patch will let this field decide whether e.g. an export's UID and
16
GID can be changed through chmod.
17
18
One notable thing about 'auto' mode is that libfuse may print error
19
messages directly to stderr, and so may fusermount (which it executes).
20
Our export code cannot really filter or hide them. Therefore, if 'auto'
21
fails its first attempt and has to fall back, fusermount will print an
22
error message that mounting with allow_other failed.
23
24
This behavior necessitates a change to iotest 308, namely we need to
25
filter out this error message (because if the first attempt at mounting
26
with allow_other succeeds, there will be no such message).
27
28
Furthermore, common.rc's _make_test_img should use allow-other=off for
29
FUSE exports, because iotests generally do not need to access images
30
from other users, so allow-other=on or allow-other=auto have no
31
advantage. OTOH, allow-other=on will not work on systems where
32
user_allow_other is disabled, and with allow-other=auto, we get said
33
error message that we would need to filter out again. Just disabling
34
allow-other is simplest.
35
36
Signed-off-by: Max Reitz <mreitz@redhat.com>
37
Message-Id: <20210625142317.271673-3-mreitz@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
38
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
39
---
17
hw/virtio/vhost-backend.c | 4 +++-
40
qapi/block-export.json | 33 ++++++++++++++++++++++++++++++++-
18
hw/virtio/vhost-user.c | 10 +++++++---
41
block/export/fuse.c | 28 +++++++++++++++++++++++-----
19
hw/virtio/vhost-vdpa.c | 4 +++-
42
tests/qemu-iotests/308 | 6 +++++-
20
hw/virtio/vhost.c | 8 ++++----
43
tests/qemu-iotests/common.rc | 6 +++++-
21
4 files changed, 17 insertions(+), 9 deletions(-)
44
4 files changed, 65 insertions(+), 8 deletions(-)
22
45
23
diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c
46
diff --git a/qapi/block-export.json b/qapi/block-export.json
24
index XXXXXXX..XXXXXXX 100644
47
index XXXXXXX..XXXXXXX 100644
25
--- a/hw/virtio/vhost-backend.c
48
--- a/qapi/block-export.json
26
+++ b/hw/virtio/vhost-backend.c
49
+++ b/qapi/block-export.json
27
@@ -XXX,XX +XXX,XX @@ static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request,
50
@@ -XXX,XX +XXX,XX @@
28
void *arg)
51
     '*logical-block-size': 'size',
29
{
52
'*num-queues': 'uint16'} }
30
int fd = (uintptr_t) dev->opaque;
53
31
+ int ret;
54
+##
32
55
+# @FuseExportAllowOther:
33
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL);
56
+#
34
57
+# Possible allow_other modes for FUSE exports.
35
- return ioctl(fd, request, arg);
58
+#
36
+ ret = ioctl(fd, request, arg);
59
+# @off: Do not pass allow_other as a mount option.
37
+ return ret < 0 ? -errno : ret;
60
+#
38
}
61
+# @on: Pass allow_other as a mount option.
39
62
+#
40
static int vhost_kernel_init(struct vhost_dev *dev, void *opaque, Error **errp)
63
+# @auto: Try mounting with allow_other first, and if that fails, retry
41
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
64
+# without allow_other.
65
+#
66
+# Since: 6.1
67
+##
68
+{ 'enum': 'FuseExportAllowOther',
69
+ 'data': ['off', 'on', 'auto'] }
70
+
71
##
72
# @BlockExportOptionsFuse:
73
#
74
@@ -XXX,XX +XXX,XX @@
75
# @growable: Whether writes beyond the EOF should grow the block node
76
# accordingly. (default: false)
77
#
78
+# @allow-other: If this is off, only qemu's user is allowed access to
79
+# this export. That cannot be changed even with chmod or
80
+# chown.
81
+# Enabling this option will allow other users access to
82
+# the export with the FUSE mount option "allow_other".
83
+# Note that using allow_other as a non-root user requires
84
+# user_allow_other to be enabled in the global fuse.conf
85
+# configuration file.
86
+# In auto mode (the default), the FUSE export driver will
87
+# first attempt to mount the export with allow_other, and
88
+# if that fails, try again without.
89
+# (since 6.1; default: auto)
90
+#
91
# Since: 6.0
92
##
93
{ 'struct': 'BlockExportOptionsFuse',
94
'data': { 'mountpoint': 'str',
95
- '*growable': 'bool' },
96
+ '*growable': 'bool',
97
+ '*allow-other': 'FuseExportAllowOther' },
98
'if': 'defined(CONFIG_FUSE)' }
99
100
##
101
diff --git a/block/export/fuse.c b/block/export/fuse.c
42
index XXXXXXX..XXXXXXX 100644
102
index XXXXXXX..XXXXXXX 100644
43
--- a/hw/virtio/vhost-user.c
103
--- a/block/export/fuse.c
44
+++ b/hw/virtio/vhost-user.c
104
+++ b/block/export/fuse.c
45
@@ -XXX,XX +XXX,XX @@ static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64)
105
@@ -XXX,XX +XXX,XX @@ typedef struct FuseExport {
46
106
char *mountpoint;
47
static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features)
107
bool writable;
48
{
108
bool growable;
49
- return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features);
109
+ /* Whether allow_other was used as a mount option or not */
50
+ if (vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features) < 0) {
110
+ bool allow_other;
51
+ return -EPROTO;
111
} FuseExport;
112
113
static GHashTable *exports;
114
@@ -XXX,XX +XXX,XX @@ static void fuse_export_delete(BlockExport *exp);
115
static void init_exports_table(void);
116
117
static int setup_fuse_export(FuseExport *exp, const char *mountpoint,
118
- Error **errp);
119
+ bool allow_other, Error **errp);
120
static void read_from_fuse_export(void *opaque);
121
122
static bool is_regular_file(const char *path, Error **errp);
123
@@ -XXX,XX +XXX,XX @@ static int fuse_export_create(BlockExport *blk_exp,
124
exp->writable = blk_exp_args->writable;
125
exp->growable = args->growable;
126
127
- ret = setup_fuse_export(exp, args->mountpoint, errp);
128
+ /* set default */
129
+ if (!args->has_allow_other) {
130
+ args->allow_other = FUSE_EXPORT_ALLOW_OTHER_AUTO;
52
+ }
131
+ }
53
+
132
+
54
+ return 0;
133
+ if (args->allow_other == FUSE_EXPORT_ALLOW_OTHER_AUTO) {
55
}
134
+ /* Ignore errors on our first attempt */
56
135
+ ret = setup_fuse_export(exp, args->mountpoint, true, NULL);
57
static int vhost_user_set_owner(struct vhost_dev *dev)
136
+ exp->allow_other = ret == 0;
58
@@ -XXX,XX +XXX,XX @@ static int vhost_user_set_owner(struct vhost_dev *dev)
137
+ if (ret < 0) {
59
};
138
+ ret = setup_fuse_export(exp, args->mountpoint, false, errp);
60
139
+ }
61
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
140
+ } else {
62
- return -1;
141
+ exp->allow_other = args->allow_other == FUSE_EXPORT_ALLOW_OTHER_ON;
63
+ return -EPROTO;
142
+ ret = setup_fuse_export(exp, args->mountpoint, exp->allow_other, errp);
64
}
143
+ }
65
144
if (ret < 0) {
66
return 0;
67
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque,
68
69
err = vhost_user_get_features(dev, &features);
70
if (err < 0) {
71
- return -EPROTO;
72
+ return err;
73
}
74
75
if (virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES)) {
76
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
77
index XXXXXXX..XXXXXXX 100644
78
--- a/hw/virtio/vhost-vdpa.c
79
+++ b/hw/virtio/vhost-vdpa.c
80
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_call(struct vhost_dev *dev, unsigned long int request,
81
{
82
struct vhost_vdpa *v = dev->opaque;
83
int fd = v->device_fd;
84
+ int ret;
85
86
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA);
87
88
- return ioctl(fd, request, arg);
89
+ ret = ioctl(fd, request, arg);
90
+ return ret < 0 ? -errno : ret;
91
}
92
93
static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status)
94
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
95
index XXXXXXX..XXXXXXX 100644
96
--- a/hw/virtio/vhost.c
97
+++ b/hw/virtio/vhost.c
98
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
99
100
r = hdev->vhost_ops->vhost_set_owner(hdev);
101
if (r < 0) {
102
- error_setg(errp, "vhost_set_owner failed");
103
+ error_setg_errno(errp, -r, "vhost_set_owner failed");
104
goto fail;
145
goto fail;
105
}
146
}
106
147
@@ -XXX,XX +XXX,XX @@ static void init_exports_table(void)
107
r = hdev->vhost_ops->vhost_get_features(hdev, &features);
148
* Create exp->fuse_session and mount it.
108
if (r < 0) {
149
*/
109
- error_setg(errp, "vhost_get_features failed");
150
static int setup_fuse_export(FuseExport *exp, const char *mountpoint,
110
+ error_setg_errno(errp, -r, "vhost_get_features failed");
151
- Error **errp)
111
goto fail;
152
+ bool allow_other, Error **errp)
112
}
153
{
113
154
const char *fuse_argv[4];
114
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
155
char *mount_opts;
115
r = vhost_virtqueue_set_busyloop_timeout(hdev, hdev->vq_index + i,
156
@@ -XXX,XX +XXX,XX @@ static int setup_fuse_export(FuseExport *exp, const char *mountpoint,
116
busyloop_timeout);
157
* max_read needs to match what fuse_init() sets.
117
if (r < 0) {
158
* max_write need not be supplied.
118
- error_setg(errp, "Failed to set busyloop timeout");
159
*/
119
+ error_setg_errno(errp, -r, "Failed to set busyloop timeout");
160
- mount_opts = g_strdup_printf("max_read=%zu,default_permissions",
120
goto fail_busyloop;
161
- FUSE_MAX_BOUNCE_BYTES);
121
}
162
+ mount_opts = g_strdup_printf("max_read=%zu,default_permissions%s",
122
}
163
+ FUSE_MAX_BOUNCE_BYTES,
123
@@ -XXX,XX +XXX,XX @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
164
+ allow_other ? ",allow_other" : "");
124
if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) {
165
125
error_setg(errp, "vhost backend memory slots limit is less"
166
fuse_argv[0] = ""; /* Dummy program name */
126
" than current number of present memory slots");
167
fuse_argv[1] = "-o";
127
- r = -1;
168
diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308
128
+ r = -EINVAL;
169
index XXXXXXX..XXXXXXX 100755
129
goto fail_busyloop;
170
--- a/tests/qemu-iotests/308
130
}
171
+++ b/tests/qemu-iotests/308
131
172
@@ -XXX,XX +XXX,XX @@ _supported_os Linux # We need /dev/urandom
173
# $4: Node to export (defaults to 'node-format')
174
fuse_export_add()
175
{
176
+ # The grep -v is a filter for errors when /etc/fuse.conf does not contain
177
+ # user_allow_other. (The error is benign, but it is printed by fusermount
178
+ # on the first mount attempt, so our export code cannot hide it.)
179
_send_qemu_cmd $QEMU_HANDLE \
180
"{'execute': 'block-export-add',
181
'arguments': {
182
@@ -XXX,XX +XXX,XX @@ fuse_export_add()
183
$2
184
} }" \
185
"${3:-return}" \
186
- | _filter_imgfmt
187
+ | _filter_imgfmt \
188
+ | grep -v 'option allow_other only allowed if'
189
}
190
191
# $1: Export ID
192
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
193
index XXXXXXX..XXXXXXX 100644
194
--- a/tests/qemu-iotests/common.rc
195
+++ b/tests/qemu-iotests/common.rc
196
@@ -XXX,XX +XXX,XX @@ _make_test_img()
197
# Usually, users would export formatted nodes. But we present fuse as a
198
# protocol-level driver here, so we have to leave the format to the
199
# client.
200
+ # Switch off allow-other, because in general we do not need it for
201
+ # iotests. The default allow-other=auto has the downside of printing a
202
+ # fusermount error on its first attempt if allow_other is not
203
+ # permissible, which we would need to filter.
204
QSD_NEED_PID=y $QSD \
205
--blockdev file,node-name=export-node,filename=$img_name,discard=unmap \
206
- --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=on \
207
+ --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=on,allow-other=off \
208
&
209
210
pidfile="$QEMU_TEST_DIR/qemu-storage-daemon.pid"
132
--
211
--
133
2.31.1
212
2.31.1
134
213
135
214
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Max Reitz <mreitz@redhat.com>
2
2
3
The logic around finding overlay here is not obvious. Actually it does
3
In order to support changing other attributes than the file size in
4
two simple things:
4
fuse_setattr(), we have to give each its own independent branch. This
5
also applies to the only attribute we do support right now.
5
6
6
1. If new bs is already in backing chain, split from parent bs by
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
several implicit filters we are done, do nothing.
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
9
Message-Id: <20210625142317.271673-4-mreitz@redhat.com>
9
2. Otherwise, don't try to replace implicit filter.
10
11
Let's rewrite this in more obvious way.
12
13
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
Message-Id: <20210610120537.196183-6-vsementsov@virtuozzo.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
11
---
17
block.c | 53 ++++++++++++++++-------------------------------------
12
block/export/fuse.c | 20 +++++++++++---------
18
1 file changed, 16 insertions(+), 37 deletions(-)
13
1 file changed, 11 insertions(+), 9 deletions(-)
19
14
20
diff --git a/block.c b/block.c
15
diff --git a/block/export/fuse.c b/block/export/fuse.c
21
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
22
--- a/block.c
17
--- a/block/export/fuse.c
23
+++ b/block.c
18
+++ b/block/export/fuse.c
24
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
19
@@ -XXX,XX +XXX,XX @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf,
25
Error **errp)
20
FuseExport *exp = fuse_req_userdata(req);
26
{
21
int ret;
27
BlockDriverState *bs = reopen_state->bs;
22
28
- BlockDriverState *overlay_bs, *below_bs, *new_backing_bs;
23
- if (!exp->writable) {
29
+ BlockDriverState *new_backing_bs;
24
- fuse_reply_err(req, EACCES);
30
QObject *value;
25
- return;
31
const char *str;
26
- }
32
27
-
33
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
28
if (to_set & ~FUSE_SET_ATTR_SIZE) {
34
g_assert_not_reached();
29
fuse_reply_err(req, ENOTSUP);
30
return;
35
}
31
}
36
32
37
+ if (bs->backing) {
33
- ret = fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MODE_OFF);
38
+ if (bdrv_skip_implicit_filters(bs->backing->bs) == new_backing_bs) {
34
- if (ret < 0) {
39
+ return 0;
35
- fuse_reply_err(req, -ret);
36
- return;
37
+ if (to_set & FUSE_SET_ATTR_SIZE) {
38
+ if (!exp->writable) {
39
+ fuse_reply_err(req, EACCES);
40
+ return;
40
+ }
41
+ }
41
+
42
+
42
+ if (bs->backing->bs->implicit) {
43
+ ret = fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MODE_OFF);
43
+ error_setg(errp, "Cannot change backing link if '%s' has "
44
+ if (ret < 0) {
44
+ "an implicit backing file", bs->node_name);
45
+ fuse_reply_err(req, -ret);
45
+ return -EPERM;
46
+ return;
46
+ }
47
+ }
47
+ }
48
+
49
/*
50
* Ensure that @bs can really handle backing files, because we are
51
* about to give it one (or swap the existing one)
52
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
53
return -EINVAL;
54
}
48
}
55
49
56
- /*
50
fuse_getattr(req, inode, fi);
57
- * Find the "actual" backing file by skipping all links that point
58
- * to an implicit node, if any (e.g. a commit filter node).
59
- * We cannot use any of the bdrv_skip_*() functions here because
60
- * those return the first explicit node, while we are looking for
61
- * its overlay here.
62
- */
63
- overlay_bs = bs;
64
- for (below_bs = bdrv_filter_or_cow_bs(overlay_bs);
65
- below_bs && below_bs->implicit;
66
- below_bs = bdrv_filter_or_cow_bs(overlay_bs))
67
- {
68
- overlay_bs = below_bs;
69
- }
70
-
71
- /* If we want to replace the backing file we need some extra checks */
72
- if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) {
73
- int ret;
74
-
75
- /* Check for implicit nodes between bs and its backing file */
76
- if (bs != overlay_bs) {
77
- error_setg(errp, "Cannot change backing link if '%s' has "
78
- "an implicit backing file", bs->node_name);
79
- return -EPERM;
80
- }
81
-
82
- reopen_state->replace_backing_bs = true;
83
- reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
84
- ret = bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran,
85
- errp);
86
- if (ret < 0) {
87
- return ret;
88
- }
89
- }
90
-
91
- return 0;
92
+ reopen_state->replace_backing_bs = true;
93
+ reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
94
+ return bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran, errp);
95
}
96
97
/*
98
--
51
--
99
2.31.1
52
2.31.1
100
53
101
54
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Max Reitz <mreitz@redhat.com>
2
2
3
To be used for reopen in future commit.
3
Allow changing the file mode, UID, and GID through SETATTR.
4
4
5
Notes:
5
Without allow_other, UID and GID are not allowed to be changed, because
6
- It seems OK to update inherits_from if new bs is recursively inherits
6
it would not make sense. Also, changing group or others' permissions
7
from parent bs. Let's just not check for backing_chain_contains, to
7
is not allowed either.
8
support file child of non-filters.
9
8
10
- Simply check child->frozen instead of
9
For read-only exports, +w cannot be set.
11
bdrv_is_backing_chain_frozen(), as we really interested only in this
12
one child.
13
10
14
- Role determination of new child is a bit more complex: it remains
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
the same for backing child, it's obvious for filter driver. But for
12
Message-Id: <20210625142317.271673-5-mreitz@redhat.com>
16
non-filter file child let's for now restrict to only replacing
17
existing child (and keeping its role).
18
19
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
20
Message-Id: <20210610120537.196183-3-vsementsov@virtuozzo.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
14
---
23
block.c | 83 +++++++++++++++++++++++++++++++++++++++++++--------------
15
block/export/fuse.c | 73 ++++++++++++++++++++++++++++++++++++++-------
24
1 file changed, 63 insertions(+), 20 deletions(-)
16
1 file changed, 62 insertions(+), 11 deletions(-)
25
17
26
diff --git a/block.c b/block.c
18
diff --git a/block/export/fuse.c b/block/export/fuse.c
27
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
28
--- a/block.c
20
--- a/block/export/fuse.c
29
+++ b/block.c
21
+++ b/block/export/fuse.c
30
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
22
@@ -XXX,XX +XXX,XX @@ typedef struct FuseExport {
31
23
bool growable;
32
static void bdrv_replace_child_noperm(BdrvChild *child,
24
/* Whether allow_other was used as a mount option or not */
33
BlockDriverState *new_bs);
25
bool allow_other;
34
+static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
26
+
35
+ BdrvChild *child,
27
+ mode_t st_mode;
36
+ Transaction *tran);
28
+ uid_t st_uid;
37
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
29
+ gid_t st_gid;
38
Transaction *tran);
30
} FuseExport;
39
31
40
@@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
32
static GHashTable *exports;
33
@@ -XXX,XX +XXX,XX @@ static int fuse_export_create(BlockExport *blk_exp,
34
args->allow_other = FUSE_EXPORT_ALLOW_OTHER_AUTO;
35
}
36
37
+ exp->st_mode = S_IFREG | S_IRUSR;
38
+ if (exp->writable) {
39
+ exp->st_mode |= S_IWUSR;
40
+ }
41
+ exp->st_uid = getuid();
42
+ exp->st_gid = getgid();
43
+
44
if (args->allow_other == FUSE_EXPORT_ALLOW_OTHER_AUTO) {
45
/* Ignore errors on our first attempt */
46
ret = setup_fuse_export(exp, args->mountpoint, true, NULL);
47
@@ -XXX,XX +XXX,XX @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t inode,
48
int64_t length, allocated_blocks;
49
time_t now = time(NULL);
50
FuseExport *exp = fuse_req_userdata(req);
51
- mode_t mode;
52
53
length = blk_getlength(exp->common.blk);
54
if (length < 0) {
55
@@ -XXX,XX +XXX,XX @@ static void fuse_getattr(fuse_req_t req, fuse_ino_t inode,
56
allocated_blocks = DIV_ROUND_UP(allocated_blocks, 512);
57
}
58
59
- mode = S_IFREG | S_IRUSR;
60
- if (exp->writable) {
61
- mode |= S_IWUSR;
62
- }
63
-
64
statbuf = (struct stat) {
65
.st_ino = inode,
66
- .st_mode = mode,
67
+ .st_mode = exp->st_mode,
68
.st_nlink = 1,
69
- .st_uid = getuid(),
70
- .st_gid = getgid(),
71
+ .st_uid = exp->st_uid,
72
+ .st_gid = exp->st_gid,
73
.st_size = length,
74
.st_blksize = blk_bs(exp->common.blk)->bl.request_alignment,
75
.st_blocks = allocated_blocks,
76
@@ -XXX,XX +XXX,XX @@ static int fuse_do_truncate(const FuseExport *exp, int64_t size,
41
}
77
}
42
78
43
/*
79
/**
44
- * Sets the bs->backing link of a BDS. A new reference is created; callers
80
- * Let clients set file attributes. Only resizing is supported.
45
- * which don't need their own reference any more must call bdrv_unref().
81
+ * Let clients set file attributes. Only resizing and changing
46
+ * Sets the bs->backing or bs->file link of a BDS. A new reference is created;
82
+ * permissions (st_mode, st_uid, st_gid) is allowed.
47
+ * callers which don't need their own reference any more must call bdrv_unref().
83
+ * Changing permissions is only allowed as far as it will actually
48
*
84
+ * permit access: Read-only exports cannot be given +w, and exports
49
* Function doesn't update permissions, caller is responsible for this.
85
+ * without allow_other cannot be given a different UID or GID, and
86
+ * they cannot be given non-owner access.
50
*/
87
*/
51
-static int bdrv_set_backing_noperm(BlockDriverState *bs,
88
static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf,
52
- BlockDriverState *backing_hd,
89
int to_set, struct fuse_file_info *fi)
53
- Transaction *tran, Error **errp)
54
+static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs,
55
+ BlockDriverState *child_bs,
56
+ bool is_backing,
57
+ Transaction *tran, Error **errp)
58
{
90
{
59
int ret = 0;
91
FuseExport *exp = fuse_req_userdata(req);
60
- bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
92
+ int supported_attrs;
61
- bdrv_inherits_from_recursive(backing_hd, bs);
93
int ret;
62
+ bool update_inherits_from =
94
63
+ bdrv_inherits_from_recursive(child_bs, parent_bs);
95
- if (to_set & ~FUSE_SET_ATTR_SIZE) {
64
+ BdrvChild *child = is_backing ? parent_bs->backing : parent_bs->file;
96
+ supported_attrs = FUSE_SET_ATTR_SIZE | FUSE_SET_ATTR_MODE;
65
+ BdrvChildRole role;
97
+ if (exp->allow_other) {
66
98
+ supported_attrs |= FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID;
67
- if (bdrv_is_backing_chain_frozen(bs, child_bs(bs->backing), errp)) {
68
+ if (!parent_bs->drv) {
69
+ /*
70
+ * Node without drv is an object without a class :/. TODO: finally fix
71
+ * qcow2 driver to never clear bs->drv and implement format corruption
72
+ * handling in other way.
73
+ */
74
+ error_setg(errp, "Node corrupted");
75
+ return -EINVAL;
76
+ }
99
+ }
77
+
100
+
78
+ if (child && child->frozen) {
101
+ if (to_set & ~supported_attrs) {
79
+ error_setg(errp, "Cannot change frozen '%s' link from '%s' to '%s'",
102
fuse_reply_err(req, ENOTSUP);
80
+ child->name, parent_bs->node_name, child->bs->node_name);
103
return;
81
return -EPERM;
82
}
104
}
83
105
84
- if (bs->backing) {
106
+ /* Do some argument checks first before committing to anything */
85
- /* Cannot be frozen, we checked that above */
107
+ if (to_set & FUSE_SET_ATTR_MODE) {
86
- bdrv_unset_inherits_from(bs, bs->backing, tran);
87
- bdrv_remove_filter_or_cow_child(bs, tran);
88
+ if (parent_bs->drv->is_filter) {
89
+ role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY;
90
+ } else if (is_backing) {
91
+ role = BDRV_CHILD_COW;
92
+ } else {
93
+ /*
108
+ /*
94
+ * We only can use same role as it is in existing child. We don't have
109
+ * Without allow_other, non-owners can never access the export, so do
95
+ * infrastructure to determine role of file child in generic way
110
+ * not allow setting permissions for them
96
+ */
111
+ */
97
+ if (!child) {
112
+ if (!exp->allow_other &&
98
+ error_setg(errp, "Cannot set file child to format node without "
113
+ (statbuf->st_mode & (S_IRWXG | S_IRWXO)) != 0)
99
+ "file child");
114
+ {
100
+ return -EINVAL;
115
+ fuse_reply_err(req, EPERM);
116
+ return;
101
+ }
117
+ }
102
+ role = child->role;
118
+
103
}
119
+ /* +w for read-only exports makes no sense, disallow it */
104
120
+ if (!exp->writable &&
105
- if (!backing_hd) {
121
+ (statbuf->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
106
+ if (child) {
122
+ {
107
+ bdrv_unset_inherits_from(parent_bs, child, tran);
123
+ fuse_reply_err(req, EROFS);
108
+ bdrv_remove_file_or_backing_child(parent_bs, child, tran);
124
+ return;
125
+ }
109
+ }
126
+ }
110
+
127
+
111
+ if (!child_bs) {
128
if (to_set & FUSE_SET_ATTR_SIZE) {
112
goto out;
129
if (!exp->writable) {
130
fuse_reply_err(req, EACCES);
131
@@ -XXX,XX +XXX,XX @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf,
132
}
113
}
133
}
114
134
115
- ret = bdrv_attach_child_noperm(bs, backing_hd, "backing",
135
+ if (to_set & FUSE_SET_ATTR_MODE) {
116
- &child_of_bds, bdrv_backing_role(bs),
136
+ /* Ignore FUSE-supplied file type, only change the mode */
117
- &bs->backing, tran, errp);
137
+ exp->st_mode = (statbuf->st_mode & 07777) | S_IFREG;
118
+ ret = bdrv_attach_child_noperm(parent_bs, child_bs,
138
+ }
119
+ is_backing ? "backing" : "file",
139
+
120
+ &child_of_bds, role,
140
+ if (to_set & FUSE_SET_ATTR_UID) {
121
+ is_backing ? &parent_bs->backing :
141
+ exp->st_uid = statbuf->st_uid;
122
+ &parent_bs->file,
142
+ }
123
+ tran, errp);
143
+
124
if (ret < 0) {
144
+ if (to_set & FUSE_SET_ATTR_GID) {
125
return ret;
145
+ exp->st_gid = statbuf->st_gid;
126
}
146
+ }
127
147
+
128
148
fuse_getattr(req, inode, fi);
129
/*
130
- * If backing_hd was already part of bs's backing chain, and
131
- * inherits_from pointed recursively to bs then let's update it to
132
+ * If inherits_from pointed recursively to bs then let's update it to
133
* point directly to bs (else it will become NULL).
134
*/
135
if (update_inherits_from) {
136
- bdrv_set_inherits_from(backing_hd, bs, tran);
137
+ bdrv_set_inherits_from(child_bs, parent_bs, tran);
138
}
139
140
out:
141
- bdrv_refresh_limits(bs, tran, NULL);
142
+ bdrv_refresh_limits(parent_bs, tran, NULL);
143
144
return 0;
145
}
149
}
146
150
147
+static int bdrv_set_backing_noperm(BlockDriverState *bs,
148
+ BlockDriverState *backing_hd,
149
+ Transaction *tran, Error **errp)
150
+{
151
+ return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp);
152
+}
153
+
154
int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
155
Error **errp)
156
{
157
--
151
--
158
2.31.1
152
2.31.1
159
153
160
154
diff view generated by jsdifflib
1
Commit dabefdd6 removed code that was supposed to try reconnecting
1
From: Max Reitz <mreitz@redhat.com>
2
during .realize(), but actually just crashed and had several design
3
problems.
4
2
5
This adds the feature back without the crash in simple cases while also
3
Test that +w on read-only FUSE exports returns an EROFS error. u+x on
6
fixing some design problems: Reconnection is now only tried if there was
4
the other hand should work. (There is no special reason to choose u+x
7
a problem with the connection and not an error related to the content
5
here, it simply is like +w another flag that is not set by default.)
8
(which would fail again the same way in the next attempt). Reconnection
9
is limited to three attempts (four with the initial attempt) so that we
10
won't end up in an infinite loop if a problem is permanent. If the
11
backend restarts three times in the very short time window of device
12
initialisation, we have bigger problems and erroring out is the right
13
course of action.
14
6
15
In the case that a connection error occurs and we reconnect, the error
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
message is printed using error_report_err(), but otherwise ignored.
8
Message-Id: <20210625142317.271673-6-mreitz@redhat.com>
17
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Message-Id: <20210609154658.350308-8-kwolf@redhat.com>
20
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
10
---
23
hw/block/vhost-user-blk.c | 16 +++++++++++++++-
11
tests/qemu-iotests/308 | 11 +++++++++++
24
1 file changed, 15 insertions(+), 1 deletion(-)
12
tests/qemu-iotests/308.out | 4 ++++
13
2 files changed, 15 insertions(+)
25
14
26
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
15
diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308
16
index XXXXXXX..XXXXXXX 100755
17
--- a/tests/qemu-iotests/308
18
+++ b/tests/qemu-iotests/308
19
@@ -XXX,XX +XXX,XX @@ fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP'"
20
# Check that the export presents the same data as the original image
21
$QEMU_IMG compare -f raw -F $IMGFMT -U "$EXT_MP" "$TEST_IMG"
22
23
+# Some quick chmod tests
24
+stat -c 'Permissions pre-chmod: %a' "$EXT_MP"
25
+
26
+# Verify that we cannot set +w
27
+chmod u+w "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
28
+stat -c 'Permissions post-+w: %a' "$EXT_MP"
29
+
30
+# But that we can set, say, +x (if we are so inclined)
31
+chmod u+x "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
32
+stat -c 'Permissions post-+x: %a' "$EXT_MP"
33
+
34
echo
35
echo '=== Mount over existing file ==='
36
37
diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out
27
index XXXXXXX..XXXXXXX 100644
38
index XXXXXXX..XXXXXXX 100644
28
--- a/hw/block/vhost-user-blk.c
39
--- a/tests/qemu-iotests/308.out
29
+++ b/hw/block/vhost-user-blk.c
40
+++ b/tests/qemu-iotests/308.out
30
@@ -XXX,XX +XXX,XX @@
41
@@ -XXX,XX +XXX,XX @@ wrote 67108864/67108864 bytes at offset 0
31
#include "sysemu/sysemu.h"
42
} }
32
#include "sysemu/runstate.h"
43
{"return": {}}
33
44
Images are identical.
34
+#define REALIZE_CONNECTION_RETRIES 3
45
+Permissions pre-chmod: 400
35
+
46
+chmod: changing permissions of 'TEST_DIR/t.IMGFMT.fuse': Read-only file system
36
static const int user_feature_bits[] = {
47
+Permissions post-+w: 400
37
VIRTIO_BLK_F_SIZE_MAX,
48
+Permissions post-+x: 500
38
VIRTIO_BLK_F_SEG_MAX,
49
39
@@ -XXX,XX +XXX,XX @@ static int vhost_user_blk_realize_connect(VHostUserBlk *s, Error **errp)
50
=== Mount over existing file ===
40
51
{'execute': 'block-export-add',
41
static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
42
{
43
+ ERRP_GUARD();
44
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
45
VHostUserBlk *s = VHOST_USER_BLK(vdev);
46
+ int retries;
47
int i, ret;
48
49
if (!s->chardev.chr) {
50
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
51
s->inflight = g_new0(struct vhost_inflight, 1);
52
s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues);
53
54
- ret = vhost_user_blk_realize_connect(s, errp);
55
+ retries = REALIZE_CONNECTION_RETRIES;
56
+ assert(!*errp);
57
+ do {
58
+ if (*errp) {
59
+ error_prepend(errp, "Reconnecting after error: ");
60
+ error_report_err(*errp);
61
+ *errp = NULL;
62
+ }
63
+ ret = vhost_user_blk_realize_connect(s, errp);
64
+ } while (ret == -EPROTO && retries--);
65
+
66
if (ret < 0) {
67
goto virtio_err;
68
}
69
--
52
--
70
2.31.1
53
2.31.1
71
54
72
55
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Max Reitz <mreitz@redhat.com>
2
2
3
When creating an image file with a backing file, we generally try to
4
open the backing file (unless -u was specified), mostly to verify that
5
it is there, but also to get the file size if none was specified for the
6
new image.
7
8
For neither of these things do we need data I/O, and so we can pass
9
BDRV_O_NO_IO when opening the backing file. This allows us to open even
10
encrypted backing images without requiring the user to provide a secret.
11
12
This makes the -u switch in iotests 189 and 198 unnecessary (and the
13
$size parameter), so drop it, because this way we get regression tests
14
for this patch here.
15
16
Fixes: https://gitlab.com/qemu-project/qemu/-/issues/441
17
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
18
Message-Id: <20210622140030.212487-1-mreitz@redhat.com>
4
Message-Id: <20210625142317.271673-7-mreitz@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
6
---
21
block.c | 6 +++++-
7
tests/qemu-iotests/tests/fuse-allow-other | 168 ++++++++++++++++++
22
tests/qemu-iotests/189 | 2 +-
8
tests/qemu-iotests/tests/fuse-allow-other.out | 88 +++++++++
23
tests/qemu-iotests/198 | 2 +-
9
2 files changed, 256 insertions(+)
24
3 files changed, 7 insertions(+), 3 deletions(-)
10
create mode 100755 tests/qemu-iotests/tests/fuse-allow-other
25
11
create mode 100644 tests/qemu-iotests/tests/fuse-allow-other.out
26
diff --git a/block.c b/block.c
12
27
index XXXXXXX..XXXXXXX 100644
13
diff --git a/tests/qemu-iotests/tests/fuse-allow-other b/tests/qemu-iotests/tests/fuse-allow-other
28
--- a/block.c
14
new file mode 100755
29
+++ b/block.c
15
index XXXXXXX..XXXXXXX
30
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
16
--- /dev/null
31
}
17
+++ b/tests/qemu-iotests/tests/fuse-allow-other
32
assert(full_backing);
18
@@ -XXX,XX +XXX,XX @@
33
19
+#!/usr/bin/env bash
34
- /* backing files always opened read-only */
20
+# group: rw
35
+ /*
21
+#
36
+ * No need to do I/O here, which allows us to open encrypted
22
+# Test FUSE exports' allow-other option
37
+ * backing images without needing the secret
23
+#
38
+ */
24
+# Copyright (C) 2021 Red Hat, Inc.
39
back_flags = flags;
25
+#
40
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
26
+# This program is free software; you can redistribute it and/or modify
41
+ back_flags |= BDRV_O_NO_IO;
27
+# it under the terms of the GNU General Public License as published by
42
28
+# the Free Software Foundation; either version 2 of the License, or
43
backing_options = qdict_new();
29
+# (at your option) any later version.
44
if (backing_fmt) {
30
+#
45
diff --git a/tests/qemu-iotests/189 b/tests/qemu-iotests/189
31
+# This program is distributed in the hope that it will be useful,
46
index XXXXXXX..XXXXXXX 100755
32
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
47
--- a/tests/qemu-iotests/189
33
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48
+++ b/tests/qemu-iotests/189
34
+# GNU General Public License for more details.
49
@@ -XXX,XX +XXX,XX @@ echo "== verify pattern =="
35
+#
50
$QEMU_IO --object $SECRET0 -c "read -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
36
+# You should have received a copy of the GNU General Public License
51
37
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
52
echo "== create overlay =="
38
+#
53
-_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" -F $IMGFMT $size
39
+
54
+_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -b "$TEST_IMG_BASE" -F $IMGFMT
40
+seq=$(basename "$0")
55
41
+echo "QA output created by $seq"
56
echo
42
+
57
echo "== writing part of a cluster =="
43
+status=1    # failure is the default!
58
diff --git a/tests/qemu-iotests/198 b/tests/qemu-iotests/198
44
+
59
index XXXXXXX..XXXXXXX 100755
45
+_cleanup()
60
--- a/tests/qemu-iotests/198
46
+{
61
+++ b/tests/qemu-iotests/198
47
+ _cleanup_qemu
62
@@ -XXX,XX +XXX,XX @@ echo "== writing whole image base =="
48
+ _cleanup_test_img
63
$QEMU_IO --object $SECRET0 -c "write -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
49
+ rm -f "$EXT_MP"
64
50
+}
65
echo "== create overlay =="
51
+trap "_cleanup; exit \$status" 0 1 2 3 15
66
-_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" -F $IMGFMT $size
52
+
67
+_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -b "$TEST_IMG_BASE" -F $IMGFMT
53
+# get standard environment, filters and checks
68
54
+. ../common.rc
69
echo
55
+. ../common.filter
70
echo "== writing whole image layer =="
56
+. ../common.qemu
57
+
58
+_supported_fmt generic
59
+
60
+_supported_proto file # We create the FUSE export manually
61
+
62
+sudo -n -u nobody true || \
63
+ _notrun 'Password-less sudo as nobody required to test allow_other'
64
+
65
+# $1: Export ID
66
+# $2: Options (beyond the node-name and ID)
67
+# $3: Expected return value (defaults to 'return')
68
+# $4: Node to export (defaults to 'node-format')
69
+fuse_export_add()
70
+{
71
+ allow_other_not_supported='option allow_other only allowed if'
72
+
73
+ output=$(
74
+ success_or_failure=yes _send_qemu_cmd $QEMU_HANDLE \
75
+ "{'execute': 'block-export-add',
76
+ 'arguments': {
77
+ 'type': 'fuse',
78
+ 'id': '$1',
79
+ 'node-name': '${4:-node-format}',
80
+ $2
81
+ } }" \
82
+ "${3:-return}" \
83
+ "$allow_other_not_supported" \
84
+ | _filter_imgfmt
85
+ )
86
+
87
+ if echo "$output" | grep -q "$allow_other_not_supported"; then
88
+ # Shut down qemu gracefully so it can unmount the export
89
+ _send_qemu_cmd $QEMU_HANDLE \
90
+ "{'execute': 'quit'}" \
91
+ 'return'
92
+
93
+ wait=yes _cleanup_qemu
94
+
95
+ _notrun "allow_other not supported"
96
+ fi
97
+
98
+ echo "$output"
99
+}
100
+
101
+EXT_MP="$TEST_DIR/fuse-export"
102
+
103
+_make_test_img 64k
104
+touch "$EXT_MP"
105
+
106
+echo
107
+echo '=== Test permissions ==='
108
+
109
+# $1: allow-other value ('on'/'off'/'auto')
110
+run_permission_test()
111
+{
112
+ _launch_qemu \
113
+ -blockdev \
114
+ "$IMGFMT,node-name=node-format,file.driver=file,file.filename=$TEST_IMG"
115
+
116
+ _send_qemu_cmd $QEMU_HANDLE \
117
+ "{'execute': 'qmp_capabilities'}" \
118
+ 'return'
119
+
120
+ fuse_export_add 'export' \
121
+ "'mountpoint': '$EXT_MP',
122
+ 'allow-other': '$1'"
123
+
124
+ # Should always work
125
+ echo '(Removing all permissions)'
126
+ chmod 000 "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
127
+ stat -c 'Permissions post-chmod: %a' "$EXT_MP"
128
+
129
+ # Should always work
130
+ echo '(Granting u+r)'
131
+ chmod u+r "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
132
+ stat -c 'Permissions post-chmod: %a' "$EXT_MP"
133
+
134
+ # Should only work with allow-other: Otherwise, no permissions can be
135
+ # granted to the group or others
136
+ echo '(Granting read permissions for everyone)'
137
+ chmod 444 "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
138
+ stat -c 'Permissions post-chmod: %a' "$EXT_MP"
139
+
140
+ echo 'Doing operations as nobody:'
141
+ # Change to TEST_DIR, so nobody will not have to attempt a lookup
142
+ pushd "$TEST_DIR" >/dev/null
143
+
144
+ # This is already prevented by the permissions (without allow-other, FUSE
145
+ # exports always have o-r), but test it anyway
146
+ sudo -n -u nobody cat fuse-export >/dev/null
147
+
148
+ # If the only problem were the lack of permissions, we should still be able
149
+ # to stat the export as nobody; it should not work without allow-other,
150
+ # though
151
+ sudo -n -u nobody \
152
+ stat -c 'Permissions seen by nobody: %a' fuse-export 2>&1 \
153
+ | _filter_imgfmt
154
+
155
+ # To prove the point, revoke read permissions for others and try again
156
+ chmod o-r fuse-export 2>&1 | _filter_testdir | _filter_imgfmt
157
+
158
+ # Should fail
159
+ sudo -n -u nobody cat fuse-export >/dev/null
160
+ # Should work with allow_other
161
+ sudo -n -u nobody \
162
+ stat -c 'Permissions seen by nobody: %a' fuse-export 2>&1 \
163
+ | _filter_imgfmt
164
+
165
+ popd >/dev/null
166
+
167
+ _send_qemu_cmd $QEMU_HANDLE \
168
+ "{'execute': 'quit'}" \
169
+ 'return'
170
+
171
+ wait=yes _cleanup_qemu
172
+}
173
+
174
+# 'auto' should behave exactly like 'on', because 'on' tests that
175
+# allow_other works (otherwise, this test is skipped)
176
+for ao in off on auto; do
177
+ echo
178
+ echo "--- allow-other=$ao ---"
179
+
180
+ run_permission_test "$ao"
181
+done
182
+
183
+# success, all done
184
+echo "*** done"
185
+rm -f $seq.full
186
+status=0
187
diff --git a/tests/qemu-iotests/tests/fuse-allow-other.out b/tests/qemu-iotests/tests/fuse-allow-other.out
188
new file mode 100644
189
index XXXXXXX..XXXXXXX
190
--- /dev/null
191
+++ b/tests/qemu-iotests/tests/fuse-allow-other.out
192
@@ -XXX,XX +XXX,XX @@
193
+QA output created by fuse-allow-other
194
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
195
+
196
+=== Test permissions ===
197
+
198
+--- allow-other=off ---
199
+{'execute': 'qmp_capabilities'}
200
+{"return": {}}
201
+{'execute': 'block-export-add',
202
+ 'arguments': {
203
+ 'type': 'fuse',
204
+ 'id': 'export',
205
+ 'node-name': 'node-format',
206
+ 'mountpoint': 'TEST_DIR/fuse-export',
207
+ 'allow-other': 'off'
208
+ } }
209
+{"return": {}}
210
+(Removing all permissions)
211
+Permissions post-chmod: 0
212
+(Granting u+r)
213
+Permissions post-chmod: 400
214
+(Granting read permissions for everyone)
215
+chmod: changing permissions of 'TEST_DIR/fuse-export': Operation not permitted
216
+Permissions post-chmod: 400
217
+Doing operations as nobody:
218
+cat: fuse-export: Permission denied
219
+stat: cannot statx 'fuse-export': Permission denied
220
+cat: fuse-export: Permission denied
221
+stat: cannot statx 'fuse-export': Permission denied
222
+{'execute': 'quit'}
223
+{"return": {}}
224
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
225
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}}
226
+
227
+--- allow-other=on ---
228
+{'execute': 'qmp_capabilities'}
229
+{"return": {}}
230
+{'execute': 'block-export-add',
231
+ 'arguments': {
232
+ 'type': 'fuse',
233
+ 'id': 'export',
234
+ 'node-name': 'node-format',
235
+ 'mountpoint': 'TEST_DIR/fuse-export',
236
+ 'allow-other': 'on'
237
+ } }
238
+{"return": {}}
239
+(Removing all permissions)
240
+Permissions post-chmod: 0
241
+(Granting u+r)
242
+Permissions post-chmod: 400
243
+(Granting read permissions for everyone)
244
+Permissions post-chmod: 444
245
+Doing operations as nobody:
246
+Permissions seen by nobody: 444
247
+cat: fuse-export: Permission denied
248
+Permissions seen by nobody: 440
249
+{'execute': 'quit'}
250
+{"return": {}}
251
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
252
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}}
253
+
254
+--- allow-other=auto ---
255
+{'execute': 'qmp_capabilities'}
256
+{"return": {}}
257
+{'execute': 'block-export-add',
258
+ 'arguments': {
259
+ 'type': 'fuse',
260
+ 'id': 'export',
261
+ 'node-name': 'node-format',
262
+ 'mountpoint': 'TEST_DIR/fuse-export',
263
+ 'allow-other': 'auto'
264
+ } }
265
+{"return": {}}
266
+(Removing all permissions)
267
+Permissions post-chmod: 0
268
+(Granting u+r)
269
+Permissions post-chmod: 400
270
+(Granting read permissions for everyone)
271
+Permissions post-chmod: 444
272
+Doing operations as nobody:
273
+Permissions seen by nobody: 444
274
+cat: fuse-export: Permission denied
275
+Permissions seen by nobody: 440
276
+{'execute': 'quit'}
277
+{"return": {}}
278
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
279
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}}
280
+*** done
71
--
281
--
72
2.31.1
282
2.31.1
73
283
74
284
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Peter Lieven <pl@kamp.de>
2
2
3
To be used for reopen in future commit.
3
task->complete is a bool not an integer.
4
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Signed-off-by: Peter Lieven <pl@kamp.de>
6
Message-Id: <20210610120537.196183-2-vsementsov@virtuozzo.com>
6
Message-Id: <20210707180449.32665-1-pl@kamp.de>
7
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
---
9
block.c | 24 +++++++++++++++++-------
10
block/rbd.c | 2 +-
10
1 file changed, 17 insertions(+), 7 deletions(-)
11
1 file changed, 1 insertion(+), 1 deletion(-)
11
12
12
diff --git a/block.c b/block.c
13
diff --git a/block/rbd.c b/block/rbd.c
13
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
14
--- a/block.c
15
--- a/block/rbd.c
15
+++ b/block.c
16
+++ b/block/rbd.c
16
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
17
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_resize(BlockDriverState *bs, uint64_t size)
17
};
18
static void qemu_rbd_finish_bh(void *opaque)
18
19
/*
20
- * A function to remove backing-chain child of @bs if exists: cow child for
21
- * format nodes (always .backing) and filter child for filters (may be .file or
22
- * .backing)
23
- *
24
+ * A function to remove backing or file child of @bs.
25
* Function doesn't update permissions, caller is responsible for this.
26
*/
27
-static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
28
- Transaction *tran)
29
+static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
30
+ BdrvChild *child,
31
+ Transaction *tran)
32
{
19
{
33
BdrvRemoveFilterOrCowChild *s;
20
RBDTask *task = opaque;
34
- BdrvChild *child = bdrv_filter_or_cow_child(bs);
21
- task->complete = 1;
35
+
22
+ task->complete = true;
36
+ assert(child == bs->backing || child == bs->file);
23
aio_co_wake(task->co);
37
38
if (!child) {
39
return;
40
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
41
}
42
}
24
}
43
25
44
+/*
45
+ * A function to remove backing-chain child of @bs if exists: cow child for
46
+ * format nodes (always .backing) and filter child for filters (may be .file or
47
+ * .backing)
48
+ */
49
+static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
50
+ Transaction *tran)
51
+{
52
+ bdrv_remove_file_or_backing_child(bs, bdrv_filter_or_cow_child(bs), tran);
53
+}
54
+
55
static int bdrv_replace_node_noperm(BlockDriverState *from,
56
BlockDriverState *to,
57
bool auto_skip, Transaction *tran,
58
--
26
--
59
2.31.1
27
2.31.1
60
28
61
29
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Peter Lieven <pl@kamp.de>
2
2
3
Introduce a convenient macro, that works for qemu_memalign() like
3
adding myself as a designated reviewer.
4
g_autofree works with g_malloc.
5
4
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Signed-off-by: Peter Lieven <pl@kamp.de>
7
Message-Id: <20210628121133.193984-2-vsementsov@virtuozzo.com>
6
Message-Id: <20210707180449.32665-2-pl@kamp.de>
7
Acked-by: Ilya Dryomov <idryomov@gmail.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
9
---
10
include/qemu/osdep.h | 15 +++++++++++++++
10
MAINTAINERS | 1 +
11
1 file changed, 15 insertions(+)
11
1 file changed, 1 insertion(+)
12
12
13
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
13
diff --git a/MAINTAINERS b/MAINTAINERS
14
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
15
--- a/include/qemu/osdep.h
15
--- a/MAINTAINERS
16
+++ b/include/qemu/osdep.h
16
+++ b/MAINTAINERS
17
@@ -XXX,XX +XXX,XX @@ void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared,
17
@@ -XXX,XX +XXX,XX @@ F: block/vmdk.c
18
void qemu_vfree(void *ptr);
18
19
void qemu_anon_ram_free(void *ptr, size_t size);
19
RBD
20
20
M: Ilya Dryomov <idryomov@gmail.com>
21
+/*
21
+R: Peter Lieven <pl@kamp.de>
22
+ * It's an analog of GLIB's g_autoptr_cleanup_generic_gfree(), used to define
22
L: qemu-block@nongnu.org
23
+ * g_autofree macro.
23
S: Supported
24
+ */
24
F: block/rbd.c
25
+static inline void qemu_cleanup_generic_vfree(void *p)
26
+{
27
+ void **pp = (void **)p;
28
+ qemu_vfree(*pp);
29
+}
30
+
31
+/*
32
+ * Analog of g_autofree, but qemu_vfree is called on cleanup instead of g_free.
33
+ */
34
+#define QEMU_AUTO_VFREE __attribute__((cleanup(qemu_cleanup_generic_vfree)))
35
+
36
/*
37
* Abstraction of PROT_ and MAP_ flags as passed to mmap(), for example,
38
* consumed by qemu_ram_mmap().
39
--
25
--
40
2.31.1
26
2.31.1
41
27
42
28
diff view generated by jsdifflib
1
Instead of just returning 0/-1 and letting the caller make up a
1
dev->max_queues was never initialised for backends that don't support
2
meaningless error message, add an Error parameter to allow reporting the
2
VHOST_USER_PROTOCOL_F_MQ, so it would use 0 as the maximum number of
3
real error and switch to 0/-errno so that different kind of errors can
3
queues to check against and consequently fail for any such backend.
4
be distinguished in the caller.
5
4
6
config_len in vhost_user_get_config() is defined by the device, so if
5
Set it to 1 if the backend doesn't have multiqueue support.
7
it's larger than VHOST_USER_MAX_CONFIG_SIZE, this is a programming
8
error. Turn the corresponding check into an assertion.
9
6
7
Fixes: c90bd505a3e8210c23d69fecab9ee6f56ec4a161
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20210609154658.350308-6-kwolf@redhat.com>
9
Message-Id: <20210705171429.29286-1-kwolf@redhat.com>
12
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
10
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
13
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
11
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
13
---
16
include/hw/virtio/vhost-backend.h | 2 +-
14
hw/virtio/vhost-user.c | 3 +++
17
include/hw/virtio/vhost.h | 4 ++--
15
1 file changed, 3 insertions(+)
18
hw/block/vhost-user-blk.c | 9 +++++----
19
hw/display/vhost-user-gpu.c | 6 ++++--
20
hw/input/vhost-user-input.c | 6 ++++--
21
hw/net/vhost_net.c | 2 +-
22
hw/virtio/vhost-user-vsock.c | 9 +++++----
23
hw/virtio/vhost-user.c | 24 ++++++++++++------------
24
hw/virtio/vhost-vdpa.c | 2 +-
25
hw/virtio/vhost.c | 14 +++++++++++---
26
10 files changed, 46 insertions(+), 32 deletions(-)
27
16
28
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
29
index XXXXXXX..XXXXXXX 100644
30
--- a/include/hw/virtio/vhost-backend.h
31
+++ b/include/hw/virtio/vhost-backend.h
32
@@ -XXX,XX +XXX,XX @@ typedef int (*vhost_set_config_op)(struct vhost_dev *dev, const uint8_t *data,
33
uint32_t offset, uint32_t size,
34
uint32_t flags);
35
typedef int (*vhost_get_config_op)(struct vhost_dev *dev, uint8_t *config,
36
- uint32_t config_len);
37
+ uint32_t config_len, Error **errp);
38
39
typedef int (*vhost_crypto_create_session_op)(struct vhost_dev *dev,
40
void *session_info,
41
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
42
index XXXXXXX..XXXXXXX 100644
43
--- a/include/hw/virtio/vhost.h
44
+++ b/include/hw/virtio/vhost.h
45
@@ -XXX,XX +XXX,XX @@ int vhost_net_set_backend(struct vhost_dev *hdev,
46
struct vhost_vring_file *file);
47
48
int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write);
49
-int vhost_dev_get_config(struct vhost_dev *dev, uint8_t *config,
50
- uint32_t config_len);
51
+int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config,
52
+ uint32_t config_len, Error **errp);
53
int vhost_dev_set_config(struct vhost_dev *dev, const uint8_t *data,
54
uint32_t offset, uint32_t size, uint32_t flags);
55
/* notifier callback in case vhost device config space changed
56
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
57
index XXXXXXX..XXXXXXX 100644
58
--- a/hw/block/vhost-user-blk.c
59
+++ b/hw/block/vhost-user-blk.c
60
@@ -XXX,XX +XXX,XX @@ static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
61
int ret;
62
struct virtio_blk_config blkcfg;
63
VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
64
+ Error *local_err = NULL;
65
66
ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg,
67
- sizeof(struct virtio_blk_config));
68
+ sizeof(struct virtio_blk_config),
69
+ &local_err);
70
if (ret < 0) {
71
- error_report("get config space failed");
72
+ error_report_err(local_err);
73
return -1;
74
}
75
76
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
77
assert(s->connected);
78
79
ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
80
- sizeof(struct virtio_blk_config));
81
+ sizeof(struct virtio_blk_config), errp);
82
if (ret < 0) {
83
- error_setg(errp, "vhost-user-blk: get block config failed");
84
goto vhost_err;
85
}
86
87
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/hw/display/vhost-user-gpu.c
90
+++ b/hw/display/vhost-user-gpu.c
91
@@ -XXX,XX +XXX,XX @@ vhost_user_gpu_get_config(VirtIODevice *vdev, uint8_t *config_data)
92
VirtIOGPUBase *b = VIRTIO_GPU_BASE(vdev);
93
struct virtio_gpu_config *vgconfig =
94
(struct virtio_gpu_config *)config_data;
95
+ Error *local_err = NULL;
96
int ret;
97
98
memset(config_data, 0, sizeof(struct virtio_gpu_config));
99
100
ret = vhost_dev_get_config(&g->vhost->dev,
101
- config_data, sizeof(struct virtio_gpu_config));
102
+ config_data, sizeof(struct virtio_gpu_config),
103
+ &local_err);
104
if (ret) {
105
- error_report("vhost-user-gpu: get device config space failed");
106
+ error_report_err(local_err);
107
return;
108
}
109
110
diff --git a/hw/input/vhost-user-input.c b/hw/input/vhost-user-input.c
111
index XXXXXXX..XXXXXXX 100644
112
--- a/hw/input/vhost-user-input.c
113
+++ b/hw/input/vhost-user-input.c
114
@@ -XXX,XX +XXX,XX @@ static void vhost_input_get_config(VirtIODevice *vdev, uint8_t *config_data)
115
{
116
VirtIOInput *vinput = VIRTIO_INPUT(vdev);
117
VHostUserInput *vhi = VHOST_USER_INPUT(vdev);
118
+ Error *local_err = NULL;
119
int ret;
120
121
memset(config_data, 0, vinput->cfg_size);
122
123
- ret = vhost_dev_get_config(&vhi->vhost->dev, config_data, vinput->cfg_size);
124
+ ret = vhost_dev_get_config(&vhi->vhost->dev, config_data, vinput->cfg_size,
125
+ &local_err);
126
if (ret) {
127
- error_report("vhost-user-input: get device config space failed");
128
+ error_report_err(local_err);
129
return;
130
}
131
}
132
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
133
index XXXXXXX..XXXXXXX 100644
134
--- a/hw/net/vhost_net.c
135
+++ b/hw/net/vhost_net.c
136
@@ -XXX,XX +XXX,XX @@ uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
137
int vhost_net_get_config(struct vhost_net *net, uint8_t *config,
138
uint32_t config_len)
139
{
140
- return vhost_dev_get_config(&net->dev, config, config_len);
141
+ return vhost_dev_get_config(&net->dev, config, config_len, NULL);
142
}
143
int vhost_net_set_config(struct vhost_net *net, const uint8_t *data,
144
uint32_t offset, uint32_t size, uint32_t flags)
145
diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/hw/virtio/vhost-user-vsock.c
148
+++ b/hw/virtio/vhost-user-vsock.c
149
@@ -XXX,XX +XXX,XX @@ static void vuv_get_config(VirtIODevice *vdev, uint8_t *config)
150
static int vuv_handle_config_change(struct vhost_dev *dev)
151
{
152
VHostUserVSock *vsock = VHOST_USER_VSOCK(dev->vdev);
153
+ Error *local_err = NULL;
154
int ret = vhost_dev_get_config(dev, (uint8_t *)&vsock->vsockcfg,
155
- sizeof(struct virtio_vsock_config));
156
+ sizeof(struct virtio_vsock_config),
157
+ &local_err);
158
if (ret < 0) {
159
- error_report("get config space failed");
160
+ error_report_err(local_err);
161
return -1;
162
}
163
164
@@ -XXX,XX +XXX,XX @@ static void vuv_device_realize(DeviceState *dev, Error **errp)
165
}
166
167
ret = vhost_dev_get_config(&vvc->vhost_dev, (uint8_t *)&vsock->vsockcfg,
168
- sizeof(struct virtio_vsock_config));
169
+ sizeof(struct virtio_vsock_config), errp);
170
if (ret < 0) {
171
- error_setg_errno(errp, -ret, "get config space failed");
172
goto err_vhost_dev;
173
}
174
175
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
17
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
176
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
177
--- a/hw/virtio/vhost-user.c
19
--- a/hw/virtio/vhost-user.c
178
+++ b/hw/virtio/vhost-user.c
20
+++ b/hw/virtio/vhost-user.c
179
@@ -XXX,XX +XXX,XX @@ static void vhost_user_set_iotlb_callback(struct vhost_dev *dev, int enabled)
21
@@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque,
180
}
22
if (err < 0) {
181
23
return -EPROTO;
182
static int vhost_user_get_config(struct vhost_dev *dev, uint8_t *config,
24
}
183
- uint32_t config_len)
25
+ } else {
184
+ uint32_t config_len, Error **errp)
26
+ dev->max_queues = 1;
185
{
27
}
186
VhostUserMsg msg = {
187
.hdr.request = VHOST_USER_GET_CONFIG,
188
@@ -XXX,XX +XXX,XX @@ static int vhost_user_get_config(struct vhost_dev *dev, uint8_t *config,
189
190
if (!virtio_has_feature(dev->protocol_features,
191
VHOST_USER_PROTOCOL_F_CONFIG)) {
192
- return -1;
193
+ error_setg(errp, "VHOST_USER_PROTOCOL_F_CONFIG not supported");
194
+ return -EINVAL;
195
}
196
197
- if (config_len > VHOST_USER_MAX_CONFIG_SIZE) {
198
- return -1;
199
- }
200
+ assert(config_len <= VHOST_USER_MAX_CONFIG_SIZE);
201
202
msg.payload.config.offset = 0;
203
msg.payload.config.size = config_len;
204
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
205
- return -1;
206
+ return -EPROTO;
207
}
208
209
if (vhost_user_read(dev, &msg) < 0) {
210
- return -1;
211
+ return -EPROTO;
212
}
213
214
if (msg.hdr.request != VHOST_USER_GET_CONFIG) {
215
- error_report("Received unexpected msg type. Expected %d received %d",
216
- VHOST_USER_GET_CONFIG, msg.hdr.request);
217
- return -1;
218
+ error_setg(errp,
219
+ "Received unexpected msg type. Expected %d received %d",
220
+ VHOST_USER_GET_CONFIG, msg.hdr.request);
221
+ return -EINVAL;
222
}
223
224
if (msg.hdr.size != VHOST_USER_CONFIG_HDR_SIZE + config_len) {
225
- error_report("Received bad msg size.");
226
- return -1;
227
+ error_setg(errp, "Received bad msg size.");
228
+ return -EINVAL;
229
}
230
231
memcpy(config, msg.payload.config.region, config_len);
232
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
233
index XXXXXXX..XXXXXXX 100644
234
--- a/hw/virtio/vhost-vdpa.c
235
+++ b/hw/virtio/vhost-vdpa.c
236
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_config(struct vhost_dev *dev, const uint8_t *data,
237
}
238
239
static int vhost_vdpa_get_config(struct vhost_dev *dev, uint8_t *config,
240
- uint32_t config_len)
241
+ uint32_t config_len, Error **errp)
242
{
243
struct vhost_vdpa_config *v_config;
244
unsigned long config_size = offsetof(struct vhost_vdpa_config, buf);
245
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
246
index XXXXXXX..XXXXXXX 100644
247
--- a/hw/virtio/vhost.c
248
+++ b/hw/virtio/vhost.c
249
@@ -XXX,XX +XXX,XX @@ void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
250
}
251
252
int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config,
253
- uint32_t config_len)
254
+ uint32_t config_len, Error **errp)
255
{
256
+ ERRP_GUARD();
257
+ int ret;
258
+
28
+
259
assert(hdev->vhost_ops);
29
if (dev->num_queues && dev->max_queues < dev->num_queues) {
260
30
error_setg(errp, "The maximum number of queues supported by the "
261
if (hdev->vhost_ops->vhost_get_config) {
31
"backend is %" PRIu64, dev->max_queues);
262
- return hdev->vhost_ops->vhost_get_config(hdev, config, config_len);
263
+ ret = hdev->vhost_ops->vhost_get_config(hdev, config, config_len, errp);
264
+ if (ret < 0 && !*errp) {
265
+ error_setg_errno(errp, -ret, "vhost_get_config failed");
266
+ }
267
+ return ret;
268
}
269
270
- return -1;
271
+ error_setg(errp, "vhost_get_config not implemented");
272
+ return -ENOTSUP;
273
}
274
275
int vhost_dev_set_config(struct vhost_dev *hdev, const uint8_t *data,
276
--
32
--
277
2.31.1
33
2.31.1
278
34
279
35
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Move supports_backing check of bdrv_reopen_parse_backing to called
3
drive_backup_prepare() does bdrv_drained_begin() in hope that
4
(through bdrv_set_backing_noperm()) bdrv_set_file_or_backing_noperm()
4
bdrv_drained_end() will be called in drive_backup_clean(). Still we
5
function. The check applies to general case, so it's appropriate for
5
need to set state->bs for this to work. That's done too late: a lot of
6
bdrv_set_file_or_backing_noperm().
6
failure paths in drive_backup_prepare() miss setting state->bs. Fix
7
that.
7
8
8
We have to declare backing support for two test drivers, otherwise new
9
Fixes: 2288ccfac96281c316db942d10e3f921c1373064
9
check fails.
10
Fixes: https://gitlab.com/qemu-project/qemu/-/issues/399
10
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Message-Id: <20210610120537.196183-7-vsementsov@virtuozzo.com>
12
Message-Id: <20210608171852.250775-1-vsementsov@virtuozzo.com>
13
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
---
15
block.c | 29 +++++++++++++++--------------
16
blockdev.c | 3 +--
16
tests/unit/test-bdrv-drain.c | 1 +
17
1 file changed, 1 insertion(+), 2 deletions(-)
17
tests/unit/test-bdrv-graph-mod.c | 1 +
18
3 files changed, 17 insertions(+), 14 deletions(-)
19
18
20
diff --git a/block.c b/block.c
19
diff --git a/blockdev.c b/blockdev.c
21
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
22
--- a/block.c
21
--- a/blockdev.c
23
+++ b/block.c
22
+++ b/blockdev.c
24
@@ -XXX,XX +XXX,XX @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs,
23
@@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
25
return -EPERM;
24
aio_context = bdrv_get_aio_context(bs);
26
}
25
aio_context_acquire(aio_context);
27
26
28
+ if (is_backing && !parent_bs->drv->is_filter &&
27
+ state->bs = bs;
29
+ !parent_bs->drv->supports_backing)
28
/* Paired with .clean() */
30
+ {
29
bdrv_drained_begin(bs);
31
+ error_setg(errp, "Driver '%s' of node '%s' does not support backing "
30
32
+ "files", parent_bs->drv->format_name, parent_bs->node_name);
31
@@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
33
+ return -EINVAL;
34
+ }
35
+
36
if (parent_bs->drv->is_filter) {
37
role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY;
38
} else if (is_backing) {
39
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
40
}
32
}
41
}
33
}
42
34
43
- /*
35
- state->bs = bs;
44
- * Ensure that @bs can really handle backing files, because we are
36
-
45
- * about to give it one (or swap the existing one)
37
state->job = do_backup_common(qapi_DriveBackup_base(backup),
46
- */
38
bs, target_bs, aio_context,
47
- if (bs->drv->is_filter) {
39
common->block_job_txn, errp);
48
- /* Filters always have a file or a backing child */
49
- if (!bs->backing) {
50
- error_setg(errp, "'%s' is a %s filter node that does not support a "
51
- "backing child", bs->node_name, bs->drv->format_name);
52
- return -EINVAL;
53
- }
54
- } else if (!bs->drv->supports_backing) {
55
- error_setg(errp, "Driver '%s' of node '%s' does not support backing "
56
- "files", bs->drv->format_name, bs->node_name);
57
+ if (bs->drv->is_filter && !bs->backing) {
58
+ /*
59
+ * Filters always have a file or a backing child, so we are trying to
60
+ * change wrong child
61
+ */
62
+ error_setg(errp, "'%s' is a %s filter node that does not support a "
63
+ "backing child", bs->node_name, bs->drv->format_name);
64
return -EINVAL;
65
}
66
67
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
68
index XXXXXXX..XXXXXXX 100644
69
--- a/tests/unit/test-bdrv-drain.c
70
+++ b/tests/unit/test-bdrv-drain.c
71
@@ -XXX,XX +XXX,XX @@ static int bdrv_test_change_backing_file(BlockDriverState *bs,
72
static BlockDriver bdrv_test = {
73
.format_name = "test",
74
.instance_size = sizeof(BDRVTestState),
75
+ .supports_backing = true,
76
77
.bdrv_close = bdrv_test_close,
78
.bdrv_co_preadv = bdrv_test_co_preadv,
79
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/tests/unit/test-bdrv-graph-mod.c
82
+++ b/tests/unit/test-bdrv-graph-mod.c
83
@@ -XXX,XX +XXX,XX @@ static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c,
84
85
static BlockDriver bdrv_no_perm = {
86
.format_name = "no-perm",
87
+ .supports_backing = true,
88
.bdrv_child_perm = no_perm_default_perms,
89
};
90
91
--
40
--
92
2.31.1
41
2.31.1
93
42
94
43
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Eric Blake <eblake@redhat.com>
2
2
3
No need to start a tracked request that will always fail. The choice
3
This was deprecated back in bc5ee6da7 (qcow2: Deprecate use of
4
to check read-only after bdrv_inc_in_flight() predates 1bc5f09f2e
4
qemu-img amend to change backing file), and no one in the meantime has
5
(block: Use tracked request for truncate), but waiting for serializing
5
given any reasons why it should be supported. Time to make change
6
requests can make the effect more noticeable.
6
attempts a hard error (but for convenience, specifying the _same_
7
backing chain is not forbidden). Update a couple of iotests to match.
7
8
8
Signed-off-by: Eric Blake <eblake@redhat.com>
9
Signed-off-by: Eric Blake <eblake@redhat.com>
9
Message-Id: <20210609163034.997943-1-eblake@redhat.com>
10
Message-Id: <20210503213600.569128-2-eblake@redhat.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Reviewed-by: Connor Kuehl <ckuehl@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
---
13
block/io.c | 10 +++++-----
14
docs/system/deprecated.rst | 12 ------------
14
1 file changed, 5 insertions(+), 5 deletions(-)
15
docs/system/removed-features.rst | 12 ++++++++++++
16
block/qcow2.c | 13 ++++---------
17
tests/qemu-iotests/061 | 3 +++
18
tests/qemu-iotests/061.out | 3 ++-
19
tests/qemu-iotests/082.out | 6 ++++--
20
6 files changed, 25 insertions(+), 24 deletions(-)
15
21
16
diff --git a/block/io.c b/block/io.c
22
diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
17
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
18
--- a/block/io.c
24
--- a/docs/system/deprecated.rst
19
+++ b/block/io.c
25
+++ b/docs/system/deprecated.rst
20
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
26
@@ -XXX,XX +XXX,XX @@ this CPU is also deprecated.
21
return old_size;
27
Related binaries
28
----------------
29
30
-qemu-img amend to adjust backing file (since 5.1)
31
-'''''''''''''''''''''''''''''''''''''''''''''''''
32
-
33
-The use of ``qemu-img amend`` to modify the name or format of a qcow2
34
-backing image is deprecated; this functionality was never fully
35
-documented or tested, and interferes with other amend operations that
36
-need access to the original backing image (such as deciding whether a
37
-v3 zero cluster may be left unallocated when converting to a v2
38
-image). Rather, any changes to the backing chain should be performed
39
-with ``qemu-img rebase -u`` either before or after the remaining
40
-changes being performed by amend, as appropriate.
41
-
42
qemu-img backing file without format (since 5.1)
43
''''''''''''''''''''''''''''''''''''''''''''''''
44
45
diff --git a/docs/system/removed-features.rst b/docs/system/removed-features.rst
46
index XXXXXXX..XXXXXXX 100644
47
--- a/docs/system/removed-features.rst
48
+++ b/docs/system/removed-features.rst
49
@@ -XXX,XX +XXX,XX @@ topologies described with -smp include all possible cpus, i.e.
50
The ``enforce-config-section`` property was replaced by the
51
``-global migration.send-configuration={on|off}`` option.
52
53
+qemu-img amend to adjust backing file (removed in 6.1)
54
+''''''''''''''''''''''''''''''''''''''''''''''''''''''
55
+
56
+The use of ``qemu-img amend`` to modify the name or format of a qcow2
57
+backing image was never fully documented or tested, and interferes
58
+with other amend operations that need access to the original backing
59
+image (such as deciding whether a v3 zero cluster may be left
60
+unallocated when converting to a v2 image). Any changes to the
61
+backing chain should be performed with ``qemu-img rebase -u`` either
62
+before or after the remaining changes being performed by amend, as
63
+appropriate.
64
+
65
Block devices
66
-------------
67
68
diff --git a/block/qcow2.c b/block/qcow2.c
69
index XXXXXXX..XXXXXXX 100644
70
--- a/block/qcow2.c
71
+++ b/block/qcow2.c
72
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
73
if (backing_file || backing_format) {
74
if (g_strcmp0(backing_file, s->image_backing_file) ||
75
g_strcmp0(backing_format, s->image_backing_format)) {
76
- warn_report("Deprecated use of amend to alter the backing file; "
77
- "use qemu-img rebase instead");
78
- }
79
- ret = qcow2_change_backing_file(bs,
80
- backing_file ?: s->image_backing_file,
81
- backing_format ?: s->image_backing_format);
82
- if (ret < 0) {
83
- error_setg_errno(errp, -ret, "Failed to change the backing file");
84
- return ret;
85
+ error_setg(errp, "Cannot amend the backing file");
86
+ error_append_hint(errp,
87
+ "You can use 'qemu-img rebase' instead.\n");
88
+ return -EINVAL;
89
}
22
}
90
}
23
91
24
+ if (bdrv_is_read_only(bs)) {
92
diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061
25
+ error_setg(errp, "Image is read-only");
93
index XXXXXXX..XXXXXXX 100755
26
+ return -EACCES;
94
--- a/tests/qemu-iotests/061
27
+ }
95
+++ b/tests/qemu-iotests/061
28
+
96
@@ -XXX,XX +XXX,XX @@ _make_test_img -o "compat=1.1" 64M
29
if (offset > old_size) {
97
TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M
30
new_bytes = offset - old_size;
98
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
31
} else {
99
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
32
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
100
+$QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" \
33
if (new_bytes) {
101
+     "$TEST_IMG" && echo "unexpected pass"
34
bdrv_make_request_serialising(&req, 1);
102
+$QEMU_IMG rebase -u -b "$TEST_IMG.base" -F qcow2 "$TEST_IMG"
35
}
103
$QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" "$TEST_IMG"
36
- if (bdrv_is_read_only(bs)) {
104
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
37
- error_setg(errp, "Image is read-only");
105
_check_test_img
38
- ret = -EACCES;
106
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
39
- goto out;
107
index XXXXXXX..XXXXXXX 100644
40
- }
108
--- a/tests/qemu-iotests/061.out
41
ret = bdrv_co_write_req_prepare(child, offset - new_bytes, new_bytes, &req,
109
+++ b/tests/qemu-iotests/061.out
42
0);
110
@@ -XXX,XX +XXX,XX @@ wrote 131072/131072 bytes at offset 0
43
if (ret < 0) {
111
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
112
read 131072/131072 bytes at offset 0
113
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
114
-qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead
115
+qemu-img: Cannot amend the backing file
116
+You can use 'qemu-img rebase' instead.
117
read 131072/131072 bytes at offset 0
118
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
119
No errors were found on the image.
120
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
121
index XXXXXXX..XXXXXXX 100644
122
--- a/tests/qemu-iotests/082.out
123
+++ b/tests/qemu-iotests/082.out
124
@@ -XXX,XX +XXX,XX @@ Amend options for 'qcow2':
125
size=<size> - Virtual disk size
126
127
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2
128
-qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead
129
+qemu-img: Cannot amend the backing file
130
+You can use 'qemu-img rebase' instead.
131
132
Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2
133
134
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2
135
-qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead
136
+qemu-img: Cannot amend the backing file
137
+You can use 'qemu-img rebase' instead.
138
139
Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2
140
44
--
141
--
45
2.31.1
142
2.31.1
46
143
47
144
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Eric Blake <eblake@redhat.com>
2
2
3
We have bdrv_replace_child() wrapper on bdrv_replace_child_noperm().
3
Back in commit d9f059aa6c (qemu-img: Deprecate use of -b without -F),
4
But bdrv_replace_child() doesn't update permissions. It's rather
4
we deprecated the ability to create a file with a backing image that
5
strange, as normally it's expected that foo() should call foo_noperm()
5
requires qemu to perform format probing. Qemu can still probe older
6
and update permissions.
6
files for backwards compatibility, but it is time to finish off the
7
7
ability to create such images, due to the potential security risk they
8
Let's rename and add comment.
8
present. Update a couple of iotests affected by the change.
9
9
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Signed-off-by: Eric Blake <eblake@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
Message-Id: <20210503213600.569128-3-eblake@redhat.com>
12
Message-Id: <20210610112618.127378-2-vsementsov@virtuozzo.com>
12
Reviewed-by: Connor Kuehl <ckuehl@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
14
---
15
block.c | 14 ++++++++------
15
docs/system/deprecated.rst | 20 -----------------
16
1 file changed, 8 insertions(+), 6 deletions(-)
16
docs/system/removed-features.rst | 19 ++++++++++++++++
17
17
block.c | 37 ++++++++++----------------------
18
qemu-img.c | 6 ++++--
19
tests/qemu-iotests/040 | 4 ++--
20
tests/qemu-iotests/041 | 6 ++++--
21
tests/qemu-iotests/114 | 18 ++++++++--------
22
tests/qemu-iotests/114.out | 11 ++++------
23
tests/qemu-iotests/301 | 4 +---
24
tests/qemu-iotests/301.out | 16 ++------------
25
10 files changed, 56 insertions(+), 85 deletions(-)
26
27
diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
28
index XXXXXXX..XXXXXXX 100644
29
--- a/docs/system/deprecated.rst
30
+++ b/docs/system/deprecated.rst
31
@@ -XXX,XX +XXX,XX @@ this CPU is also deprecated.
32
Related binaries
33
----------------
34
35
-qemu-img backing file without format (since 5.1)
36
-''''''''''''''''''''''''''''''''''''''''''''''''
37
-
38
-The use of ``qemu-img create``, ``qemu-img rebase``, or ``qemu-img
39
-convert`` to create or modify an image that depends on a backing file
40
-now recommends that an explicit backing format be provided. This is
41
-for safety: if QEMU probes a different format than what you thought,
42
-the data presented to the guest will be corrupt; similarly, presenting
43
-a raw image to a guest allows a potential security exploit if a future
44
-probe sees a non-raw image based on guest writes.
45
-
46
-To avoid the warning message, or even future refusal to create an
47
-unsafe image, you must pass ``-o backing_fmt=`` (or the shorthand
48
-``-F`` during create) to specify the intended backing format. You may
49
-use ``qemu-img rebase -u`` to retroactively add a backing format to an
50
-existing image. However, be aware that there are already potential
51
-security risks to blindly using ``qemu-img info`` to probe the format
52
-of an untrusted backing image, when deciding what format to add into
53
-an existing image.
54
-
55
Backwards compatibility
56
-----------------------
57
58
diff --git a/docs/system/removed-features.rst b/docs/system/removed-features.rst
59
index XXXXXXX..XXXXXXX 100644
60
--- a/docs/system/removed-features.rst
61
+++ b/docs/system/removed-features.rst
62
@@ -XXX,XX +XXX,XX @@ backing chain should be performed with ``qemu-img rebase -u`` either
63
before or after the remaining changes being performed by amend, as
64
appropriate.
65
66
+qemu-img backing file without format (removed in 6.1)
67
+'''''''''''''''''''''''''''''''''''''''''''''''''''''
68
+
69
+The use of ``qemu-img create``, ``qemu-img rebase``, or ``qemu-img
70
+convert`` to create or modify an image that depends on a backing file
71
+now requires that an explicit backing format be provided. This is
72
+for safety: if QEMU probes a different format than what you thought,
73
+the data presented to the guest will be corrupt; similarly, presenting
74
+a raw image to a guest allows a potential security exploit if a future
75
+probe sees a non-raw image based on guest writes.
76
+
77
+To avoid creating unsafe backing chains, you must pass ``-o
78
+backing_fmt=`` (or the shorthand ``-F`` during create) to specify the
79
+intended backing format. You may use ``qemu-img rebase -u`` to
80
+retroactively add a backing format to an existing image. However, be
81
+aware that there are already potential security risks to blindly using
82
+``qemu-img info`` to probe the format of an untrusted backing image,
83
+when deciding what format to add into an existing image.
84
+
85
Block devices
86
-------------
87
18
diff --git a/block.c b/block.c
88
diff --git a/block.c b/block.c
19
index XXXXXXX..XXXXXXX 100644
89
index XXXXXXX..XXXXXXX 100644
20
--- a/block.c
90
--- a/block.c
21
+++ b/block.c
91
+++ b/block.c
22
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = {
92
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_check(BlockDriverState *bs,
23
};
93
* -ENOTSUP - format driver doesn't support changing the backing file
24
25
/*
26
- * bdrv_replace_child
27
+ * bdrv_replace_child_tran
28
*
29
* Note: real unref of old_bs is done only on commit.
30
+ *
31
+ * The function doesn't update permissions, caller is responsible for this.
32
*/
94
*/
33
-static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
95
int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
34
- Transaction *tran)
96
- const char *backing_fmt, bool warn)
35
+static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
97
+ const char *backing_fmt, bool require)
36
+ Transaction *tran)
37
{
98
{
38
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
99
BlockDriver *drv = bs->drv;
39
*s = (BdrvReplaceChildState) {
100
int ret;
40
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
101
@@ -XXX,XX +XXX,XX @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
102
return -EINVAL;
41
}
103
}
42
104
43
/*
105
- if (warn && backing_file && !backing_fmt) {
44
- * We don't have to restore child->bs here to undo bdrv_replace_child()
106
- warn_report("Deprecated use of backing file without explicit "
45
+ * We don't have to restore child->bs here to undo bdrv_replace_child_tran()
107
- "backing format, use of this image requires "
46
* because that function is transactionable and it registered own completion
108
- "potentially unsafe format probing");
47
* entries in @tran, so .abort() for bdrv_replace_child_safe() will be
109
+ if (require && backing_file && !backing_fmt) {
48
* called automatically.
110
+ return -EINVAL;
49
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
50
}
111
}
51
112
52
if (child->bs) {
113
if (drv->bdrv_change_backing_file != NULL) {
53
- bdrv_replace_child(child, NULL, tran);
114
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
54
+ bdrv_replace_child_tran(child, NULL, tran);
115
goto out;
116
} else {
117
if (!backing_fmt) {
118
- warn_report("Deprecated use of backing file without explicit "
119
- "backing format (detected format of %s)",
120
- bs->drv->format_name);
121
- if (bs->drv != &bdrv_raw) {
122
- /*
123
- * A probe of raw deserves the most attention:
124
- * leaving the backing format out of the image
125
- * will ensure bs->probed is set (ensuring we
126
- * don't accidentally commit into the backing
127
- * file), and allow more spots to warn the users
128
- * to fix their toolchain when opening this image
129
- * later. For other images, we can safely record
130
- * the format that we probed.
131
- */
132
- backing_fmt = bs->drv->format_name;
133
- qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, backing_fmt,
134
- NULL);
135
- }
136
+ error_setg(&local_err,
137
+ "Backing file specified without backing format");
138
+ error_append_hint(&local_err, "Detected format of %s.",
139
+ bs->drv->format_name);
140
+ goto out;
141
}
142
if (size == -1) {
143
/* Opened BS, have no size */
144
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
145
}
146
/* (backing_file && !(flags & BDRV_O_NO_BACKING)) */
147
} else if (backing_file && !backing_fmt) {
148
- warn_report("Deprecated use of unopened backing file without "
149
- "explicit backing format, use of this image requires "
150
- "potentially unsafe format probing");
151
+ error_setg(&local_err,
152
+ "Backing file specified without backing format");
153
+ goto out;
55
}
154
}
56
155
57
s = g_new(BdrvRemoveFilterOrCowChild, 1);
156
if (size == -1) {
58
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
157
diff --git a/qemu-img.c b/qemu-img.c
59
c->name, from->node_name);
158
index XXXXXXX..XXXXXXX 100644
60
return -EPERM;
159
--- a/qemu-img.c
160
+++ b/qemu-img.c
161
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
162
163
if (out_baseimg_param) {
164
if (!qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT)) {
165
- warn_report("Deprecated use of backing file without explicit "
166
- "backing format");
167
+ error_report("Use of backing file requires explicit "
168
+ "backing format");
169
+ ret = -1;
170
+ goto out;
61
}
171
}
62
- bdrv_replace_child(c, to, tran);
63
+ bdrv_replace_child_tran(c, to, tran);
64
}
172
}
65
173
66
return 0;
174
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
175
index XXXXXXX..XXXXXXX 100755
176
--- a/tests/qemu-iotests/040
177
+++ b/tests/qemu-iotests/040
178
@@ -XXX,XX +XXX,XX @@ class TestCommitWithOverriddenBacking(iotests.QMPTestCase):
179
def setUp(self):
180
qemu_img('create', '-f', iotests.imgfmt, self.img_base_a, '1M')
181
qemu_img('create', '-f', iotests.imgfmt, self.img_base_b, '1M')
182
- qemu_img('create', '-f', iotests.imgfmt, '-b', self.img_base_a, \
183
- self.img_top)
184
+ qemu_img('create', '-f', iotests.imgfmt, '-b', self.img_base_a,
185
+ '-F', iotests.imgfmt, self.img_top)
186
187
self.vm = iotests.VM()
188
self.vm.launch()
189
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
190
index XXXXXXX..XXXXXXX 100755
191
--- a/tests/qemu-iotests/041
192
+++ b/tests/qemu-iotests/041
193
@@ -XXX,XX +XXX,XX @@ class TestReplaces(iotests.QMPTestCase):
194
class TestFilters(iotests.QMPTestCase):
195
def setUp(self):
196
qemu_img('create', '-f', iotests.imgfmt, backing_img, '1M')
197
- qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img, test_img)
198
- qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img, target_img)
199
+ qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img,
200
+ '-F', iotests.imgfmt, test_img)
201
+ qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img,
202
+ '-F', iotests.imgfmt, target_img)
203
204
qemu_io('-c', 'write -P 1 0 512k', backing_img)
205
qemu_io('-c', 'write -P 2 512k 512k', test_img)
206
diff --git a/tests/qemu-iotests/114 b/tests/qemu-iotests/114
207
index XXXXXXX..XXXXXXX 100755
208
--- a/tests/qemu-iotests/114
209
+++ b/tests/qemu-iotests/114
210
@@ -XXX,XX +XXX,XX @@ _supported_os Linux
211
# qcow2.py does not work too well with external data files
212
_unsupported_imgopts data_file
213
214
-# Intentionally specify backing file without backing format; demonstrate
215
-# the difference in warning messages when backing file could be probed.
216
-# Note that only a non-raw probe result will affect the resulting image.
217
+# Older qemu-img could set up backing file without backing format; modern
218
+# qemu can't but we can use qcow2.py to simulate older files.
219
truncate -s $((64 * 1024 * 1024)) "$TEST_IMG.orig"
220
-_make_test_img -b "$TEST_IMG.orig" 64M
221
+_make_test_img -b "$TEST_IMG.orig" -F raw 64M
222
+$PYTHON qcow2.py "$TEST_IMG" del-header-ext 0xE2792ACA
223
224
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
225
$QEMU_IMG convert -O qcow2 -B "$TEST_IMG.orig" "$TEST_IMG.orig" "$TEST_IMG"
226
-_make_test_img -b "$TEST_IMG.base" 64M
227
-_make_test_img -u -b "$TEST_IMG.base" 64M
228
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 64M
229
+_make_test_img -u -b "$TEST_IMG.base" -F $IMGFMT 64M
230
231
# Set an invalid backing file format
232
$PYTHON qcow2.py "$TEST_IMG" add-header-ext 0xE2792ACA "foo"
233
@@ -XXX,XX +XXX,XX @@ _img_info
234
$QEMU_IO -c "open $TEST_IMG" -c "read 0 4k" 2>&1 | _filter_qemu_io | _filter_testdir
235
$QEMU_IO -c "open -o backing.driver=$IMGFMT $TEST_IMG" -c "read 0 4k" | _filter_qemu_io
236
237
-# Rebase the image, to show that omitting backing format triggers a warning,
238
-# but probing now lets us use the backing file.
239
-$QEMU_IMG rebase -u -b "$TEST_IMG.base" "$TEST_IMG"
240
+# Rebase the image, to show that backing format is required.
241
+($QEMU_IMG rebase -u -b "$TEST_IMG.base" "$TEST_IMG" 2>&1 && echo "unexpected pass") | _filter_testdir
242
+$QEMU_IMG rebase -u -b "$TEST_IMG.base" -F $IMGFMT "$TEST_IMG"
243
$QEMU_IO -c "open $TEST_IMG" -c "read 0 4k" 2>&1 | _filter_qemu_io | _filter_testdir
244
245
# success, all done
246
diff --git a/tests/qemu-iotests/114.out b/tests/qemu-iotests/114.out
247
index XXXXXXX..XXXXXXX 100644
248
--- a/tests/qemu-iotests/114.out
249
+++ b/tests/qemu-iotests/114.out
250
@@ -XXX,XX +XXX,XX @@
251
QA output created by 114
252
-qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of raw)
253
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.orig
254
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.orig backing_fmt=raw
255
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
256
-qemu-img: warning: Deprecated use of backing file without explicit backing format
257
-qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of IMGFMT)
258
+qemu-img: Use of backing file requires explicit backing format
259
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
260
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
261
-qemu-img: warning: Deprecated use of unopened backing file without explicit backing format, use of this image requires potentially unsafe format probing
262
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
263
image: TEST_DIR/t.IMGFMT
264
file format: IMGFMT
265
virtual size: 64 MiB (67108864 bytes)
266
@@ -XXX,XX +XXX,XX @@ qemu-io: can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknow
267
no file open, try 'help open'
268
read 4096/4096 bytes at offset 0
269
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
270
-qemu-img: warning: Deprecated use of backing file without explicit backing format, use of this image requires potentially unsafe format probing
271
+qemu-img: Could not change the backing file to 'TEST_DIR/t.qcow2.base': Invalid argument
272
read 4096/4096 bytes at offset 0
273
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
274
*** done
275
diff --git a/tests/qemu-iotests/301 b/tests/qemu-iotests/301
276
index XXXXXXX..XXXXXXX 100755
277
--- a/tests/qemu-iotests/301
278
+++ b/tests/qemu-iotests/301
279
@@ -XXX,XX +XXX,XX @@
280
#
281
# Test qcow backing file warnings
282
#
283
-# Copyright (C) 2020 Red Hat, Inc.
284
+# Copyright (C) 2020-2021 Red Hat, Inc.
285
#
286
# This program is free software; you can redistribute it and/or modify
287
# it under the terms of the GNU General Public License as published by
288
@@ -XXX,XX +XXX,XX @@ echo "== qcow backed by qcow =="
289
290
TEST_IMG="$TEST_IMG.base" _make_test_img $size
291
_make_test_img -b "$TEST_IMG.base" $size
292
-_img_info
293
_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size
294
_img_info
295
296
@@ -XXX,XX +XXX,XX @@ echo "== qcow backed by raw =="
297
rm "$TEST_IMG.base"
298
truncate --size=$size "$TEST_IMG.base"
299
_make_test_img -b "$TEST_IMG.base" $size
300
-_img_info
301
_make_test_img -b "$TEST_IMG.base" -F raw $size
302
_img_info
303
304
diff --git a/tests/qemu-iotests/301.out b/tests/qemu-iotests/301.out
305
index XXXXXXX..XXXXXXX 100644
306
--- a/tests/qemu-iotests/301.out
307
+++ b/tests/qemu-iotests/301.out
308
@@ -XXX,XX +XXX,XX @@ QA output created by 301
309
310
== qcow backed by qcow ==
311
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
312
-qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of IMGFMT)
313
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
314
-image: TEST_DIR/t.IMGFMT
315
-file format: IMGFMT
316
-virtual size: 32 MiB (33554432 bytes)
317
-cluster_size: 512
318
-backing file: TEST_DIR/t.IMGFMT.base
319
+qemu-img: TEST_DIR/t.IMGFMT: Backing file specified without backing format
320
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
321
image: TEST_DIR/t.IMGFMT
322
file format: IMGFMT
323
@@ -XXX,XX +XXX,XX @@ cluster_size: 512
324
backing file: TEST_DIR/t.IMGFMT.base
325
326
== qcow backed by raw ==
327
-qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of raw)
328
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
329
-image: TEST_DIR/t.IMGFMT
330
-file format: IMGFMT
331
-virtual size: 32 MiB (33554432 bytes)
332
-cluster_size: 512
333
-backing file: TEST_DIR/t.IMGFMT.base
334
+qemu-img: TEST_DIR/t.IMGFMT: Backing file specified without backing format
335
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw
336
image: TEST_DIR/t.IMGFMT
337
file format: IMGFMT
67
--
338
--
68
2.31.1
339
2.31.1
69
340
70
341
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Eric Blake <eblake@redhat.com>
2
2
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
3
When removeing support for qemu-img being able to create backing
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
4
chains without embedded backing formats, we caused a poor error
5
Message-Id: <20210610112618.127378-3-vsementsov@virtuozzo.com>
5
message as caught by iotest 114. Improve the situation to inform the
6
user what went wrong.
7
8
Suggested-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Eric Blake <eblake@redhat.com>
10
Message-Id: <20210708155228.2666172-1-eblake@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
12
---
8
block.c | 8 ++++++++
13
qemu-img.c | 3 +++
9
1 file changed, 8 insertions(+)
14
tests/qemu-iotests/114.out | 2 +-
15
2 files changed, 4 insertions(+), 1 deletion(-)
10
16
11
diff --git a/block.c b/block.c
17
diff --git a/qemu-img.c b/qemu-img.c
12
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
13
--- a/block.c
19
--- a/qemu-img.c
14
+++ b/block.c
20
+++ b/qemu-img.c
15
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_attach_child_common_drv = {
21
@@ -XXX,XX +XXX,XX @@ static int img_rebase(int argc, char **argv)
16
* @child is saved to a new entry of @tran, so that *@child could be reverted to
22
if (ret == -ENOSPC) {
17
* NULL on abort(). So referenced variable must live at least until transaction
23
error_report("Could not change the backing file to '%s': No "
18
* end.
24
"space left in the file header", out_baseimg);
19
+ *
25
+ } else if (ret == -EINVAL && out_baseimg && !out_basefmt) {
20
+ * Function doesn't update permissions, caller is responsible for this.
26
+ error_report("Could not change the backing file to '%s': backing "
21
*/
27
+ "format must be specified", out_baseimg);
22
static int bdrv_attach_child_common(BlockDriverState *child_bs,
28
} else if (ret < 0) {
23
const char *child_name,
29
error_report("Could not change the backing file to '%s': %s",
24
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
30
out_baseimg, strerror(-ret));
25
/*
31
diff --git a/tests/qemu-iotests/114.out b/tests/qemu-iotests/114.out
26
* Variable referenced by @child must live at least until transaction end.
32
index XXXXXXX..XXXXXXX 100644
27
* (see bdrv_attach_child_common() doc for details)
33
--- a/tests/qemu-iotests/114.out
28
+ *
34
+++ b/tests/qemu-iotests/114.out
29
+ * Function doesn't update permissions, caller is responsible for this.
35
@@ -XXX,XX +XXX,XX @@ qemu-io: can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknow
30
*/
36
no file open, try 'help open'
31
static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
37
read 4096/4096 bytes at offset 0
32
BlockDriverState *child_bs,
38
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
33
@@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
39
-qemu-img: Could not change the backing file to 'TEST_DIR/t.qcow2.base': Invalid argument
34
/*
40
+qemu-img: Could not change the backing file to 'TEST_DIR/t.qcow2.base': backing format must be specified
35
* Sets the bs->backing link of a BDS. A new reference is created; callers
41
read 4096/4096 bytes at offset 0
36
* which don't need their own reference any more must call bdrv_unref().
42
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
37
+ *
43
*** done
38
+ * Function doesn't update permissions, caller is responsible for this.
39
*/
40
static int bdrv_set_backing_noperm(BlockDriverState *bs,
41
BlockDriverState *backing_hd,
42
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
43
* A function to remove backing-chain child of @bs if exists: cow child for
44
* format nodes (always .backing) and filter child for filters (may be .file or
45
* .backing)
46
+ *
47
+ * Function doesn't update permissions, caller is responsible for this.
48
*/
49
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
50
Transaction *tran)
51
--
44
--
52
2.31.1
45
2.31.1
53
46
54
47
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
Without an external data file, s->data_file is a second pointer with the
2
same value as bs->file. When changing bs->file to a different BdrvChild
3
and freeing the old BdrvChild, s->data_file must also be updated,
4
otherwise it points to freed memory and causes crashes.
2
5
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
This problem was caught by iotests case 245.
4
Message-Id: <20210628121133.193984-3-vsementsov@virtuozzo.com>
7
8
Fixes: df2b7086f169239ebad5d150efa29c9bb6d4f820
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20210708114709.206487-2-kwolf@redhat.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
14
---
7
block/commit.c | 25 +++++++++----------------
15
block/qcow2.c | 29 +++++++++++++++++++++++++++++
8
1 file changed, 9 insertions(+), 16 deletions(-)
16
1 file changed, 29 insertions(+)
9
17
10
diff --git a/block/commit.c b/block/commit.c
18
diff --git a/block/qcow2.c b/block/qcow2.c
11
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
12
--- a/block/commit.c
20
--- a/block/qcow2.c
13
+++ b/block/commit.c
21
+++ b/block/qcow2.c
14
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
22
@@ -XXX,XX +XXX,XX @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
15
uint64_t delay_ns = 0;
23
static int qcow2_reopen_prepare(BDRVReopenState *state,
16
int ret = 0;
24
BlockReopenQueue *queue, Error **errp)
17
int64_t n = 0; /* bytes */
25
{
18
- void *buf = NULL;
26
+ BDRVQcow2State *s = state->bs->opaque;
19
+ QEMU_AUTO_VFREE void *buf = NULL;
27
Qcow2ReopenState *r;
20
int64_t len, base_len;
28
int ret;
21
29
22
- ret = len = blk_getlength(s->top);
30
@@ -XXX,XX +XXX,XX @@ static int qcow2_reopen_prepare(BDRVReopenState *state,
23
+ len = blk_getlength(s->top);
24
if (len < 0) {
25
- goto out;
26
+ return len;
27
}
28
job_progress_set_remaining(&s->common.job, len);
29
30
- ret = base_len = blk_getlength(s->base);
31
+ base_len = blk_getlength(s->base);
32
if (base_len < 0) {
33
- goto out;
34
+ return base_len;
35
}
36
37
if (base_len < len) {
38
ret = blk_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL);
39
if (ret) {
40
- goto out;
41
+ return ret;
42
}
31
}
43
}
32
}
44
33
45
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
34
+ /*
46
block_job_error_action(&s->common, s->on_error,
35
+ * Without an external data file, s->data_file points to the same BdrvChild
47
error_in_source, -ret);
36
+ * as bs->file. It needs to be resynced after reopen because bs->file may
48
if (action == BLOCK_ERROR_ACTION_REPORT) {
37
+ * be changed. We can't use it in the meantime.
49
- goto out;
38
+ */
50
+ return ret;
39
+ if (!has_data_file(state->bs)) {
51
} else {
40
+ assert(s->data_file == state->bs->file);
52
n = 0;
41
+ s->data_file = NULL;
53
continue;
42
+ }
54
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
43
+
55
}
44
return 0;
56
}
45
57
46
fail:
58
- ret = 0;
47
@@ -XXX,XX +XXX,XX @@ fail:
59
-
48
60
-out:
49
static void qcow2_reopen_commit(BDRVReopenState *state)
61
- qemu_vfree(buf);
50
{
62
-
51
+ BDRVQcow2State *s = state->bs->opaque;
63
- return ret;
52
+
64
+ return 0;
53
qcow2_update_options_commit(state->bs, state->opaque);
54
+ if (!s->data_file) {
55
+ /*
56
+ * If we don't have an external data file, s->data_file was cleared by
57
+ * qcow2_reopen_prepare() and needs to be updated.
58
+ */
59
+ s->data_file = state->bs->file;
60
+ }
61
g_free(state->opaque);
65
}
62
}
66
63
67
static const BlockJobDriver commit_job_driver = {
64
@@ -XXX,XX +XXX,XX @@ static void qcow2_reopen_commit_post(BDRVReopenState *state)
68
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
65
69
int ro;
66
static void qcow2_reopen_abort(BDRVReopenState *state)
70
int64_t n;
67
{
71
int ret = 0;
68
+ BDRVQcow2State *s = state->bs->opaque;
72
- uint8_t *buf = NULL;
69
+
73
+ QEMU_AUTO_VFREE uint8_t *buf = NULL;
70
+ if (!s->data_file) {
74
Error *local_err = NULL;
71
+ /*
75
72
+ * If we don't have an external data file, s->data_file was cleared by
76
if (!drv)
73
+ * qcow2_reopen_prepare() and needs to be restored.
77
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
74
+ */
78
75
+ s->data_file = state->bs->file;
79
ret = 0;
76
+ }
80
ro_cleanup:
77
qcow2_update_options_abort(state->bs, state->opaque);
81
- qemu_vfree(buf);
78
g_free(state->opaque);
82
-
79
}
83
blk_unref(backing);
84
if (bdrv_cow_bs(bs) != backing_file_bs) {
85
bdrv_set_backing_hd(bs, backing_file_bs, &error_abort);
86
--
80
--
87
2.31.1
81
2.31.1
88
82
89
83
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
It's used only in bdrv_reopen_commit(). "backing" is covered by the
3
Move the code to free a BlockReopenQueue to a separate function.
4
loop through all children except for case when we removed backing child
4
It will be used in a subsequent patch.
5
during reopen.
6
5
7
Make it more obvious and drop extra boolean field: qdict_del will not
6
[ kwolf: Also free explicit_options and options, and explicitly
8
fail if there is no such entry.
7
qobject_ref() the value when it continues to be used. This makes
8
future memory leaks less likely. ]
9
9
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Signed-off-by: Alberto Garcia <berto@igalia.com>
11
Message-Id: <20210610120537.196183-8-vsementsov@virtuozzo.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Message-Id: <20210708114709.206487-3-kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
15
---
14
include/block/block.h | 1 -
16
include/block/block.h | 1 +
15
block.c | 10 ++++------
17
block.c | 22 ++++++++++++++++------
16
2 files changed, 4 insertions(+), 7 deletions(-)
18
2 files changed, 17 insertions(+), 6 deletions(-)
17
19
18
diff --git a/include/block/block.h b/include/block/block.h
20
diff --git a/include/block/block.h b/include/block/block.h
19
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/block.h
22
--- a/include/block/block.h
21
+++ b/include/block/block.h
23
+++ b/include/block/block.h
22
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState {
24
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
23
int flags;
25
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
24
BlockdevDetectZeroesOptions detect_zeroes;
26
BlockDriverState *bs, QDict *options,
25
bool backing_missing;
27
bool keep_old_opts);
26
- bool replace_backing_bs; /* new_backing_bs is ignored if this is false */
28
+void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue);
27
BlockDriverState *old_backing_bs; /* keep pointer for permissions update */
29
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
28
QDict *options;
30
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
29
QDict *explicit_options;
31
Error **errp);
30
diff --git a/block.c b/block.c
32
diff --git a/block.c b/block.c
31
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
32
--- a/block.c
34
--- a/block.c
33
+++ b/block.c
35
+++ b/block.c
34
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
36
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
35
return -EINVAL;
37
NULL, 0, keep_old_opts);
38
}
39
40
+void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue)
41
+{
42
+ if (bs_queue) {
43
+ BlockReopenQueueEntry *bs_entry, *next;
44
+ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
45
+ qobject_unref(bs_entry->state.explicit_options);
46
+ qobject_unref(bs_entry->state.options);
47
+ g_free(bs_entry);
48
+ }
49
+ g_free(bs_queue);
50
+ }
51
+}
52
+
53
/*
54
* Reopen multiple BlockDriverStates atomically & transactionally.
55
*
56
@@ -XXX,XX +XXX,XX @@ abort:
57
if (bs_entry->prepared) {
58
bdrv_reopen_abort(&bs_entry->state);
59
}
60
- qobject_unref(bs_entry->state.explicit_options);
61
- qobject_unref(bs_entry->state.options);
36
}
62
}
37
63
38
- reopen_state->replace_backing_bs = true;
64
cleanup:
39
reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
65
- QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
40
return bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran, errp);
66
- g_free(bs_entry);
67
- }
68
- g_free(bs_queue);
69
+ bdrv_reopen_queue_free(bs_queue);
70
71
return ret;
41
}
72
}
42
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
73
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
43
bs->open_flags = reopen_state->flags;
74
/* set BDS specific flags now */
44
bs->detect_zeroes = reopen_state->detect_zeroes;
75
qobject_unref(bs->explicit_options);
45
76
qobject_unref(bs->options);
46
- if (reopen_state->replace_backing_bs) {
77
+ qobject_ref(reopen_state->explicit_options);
47
- qdict_del(bs->explicit_options, "backing");
78
+ qobject_ref(reopen_state->options);
48
- qdict_del(bs->options, "backing");
79
49
- }
80
bs->explicit_options = reopen_state->explicit_options;
50
-
81
bs->options = reopen_state->options;
51
/* Remove child references from bs->options and bs->explicit_options.
52
* Child options were already removed in bdrv_reopen_queue_child() */
53
QLIST_FOREACH(child, &bs->children, next) {
54
qdict_del(bs->explicit_options, child->name);
55
qdict_del(bs->options, child->name);
56
}
57
+ /* backing is probably removed, so it's not handled by previous loop */
58
+ qdict_del(bs->explicit_options, "backing");
59
+ qdict_del(bs->options, "backing");
60
+
61
bdrv_refresh_limits(bs, NULL, NULL);
62
}
63
64
--
82
--
65
2.31.1
83
2.31.1
66
84
67
85
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
As the BlockReopenQueue can contain nodes in multiple AioContexts, only
2
2
one of which may be locked when AIO_WAIT_WHILE() can be called, we can't
3
We don't need this check: bdrv_set_backing_noperm() will do it anyway
3
let the caller lock the right contexts. Instead, individually lock the
4
(actually in bdrv_attach_child_common()).
4
AioContext of a single node when iterating the queue.
5
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reintroduce bdrv_reopen() as a wrapper for reopening a single node that
7
Message-Id: <20210610120537.196183-4-vsementsov@virtuozzo.com>
7
drains the node and temporarily drops the AioContext lock for
8
bdrv_reopen_multiple().
9
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Message-Id: <20210708114709.206487-4-kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
14
---
10
block.c | 33 ---------------------------------
15
include/block/block.h | 2 ++
11
1 file changed, 33 deletions(-)
16
block.c | 49 ++++++++++++++++++++++++++++++++++++-------
12
17
block/replication.c | 7 +++++++
18
blockdev.c | 5 +++++
19
qemu-io-cmds.c | 7 +------
20
5 files changed, 57 insertions(+), 13 deletions(-)
21
22
diff --git a/include/block/block.h b/include/block/block.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/block.h
25
+++ b/include/block/block.h
26
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
27
bool keep_old_opts);
28
void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue);
29
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
30
+int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
31
+ Error **errp);
32
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
33
Error **errp);
34
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
13
diff --git a/block.c b/block.c
35
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
36
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
37
--- a/block.c
16
+++ b/block.c
38
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
39
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue)
40
*
41
* All affected nodes must be drained between bdrv_reopen_queue() and
42
* bdrv_reopen_multiple().
43
+ *
44
+ * To be called from the main thread, with all other AioContexts unlocked.
45
*/
46
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
47
{
48
int ret = -1;
49
BlockReopenQueueEntry *bs_entry, *next;
50
+ AioContext *ctx;
51
Transaction *tran = tran_new();
52
g_autoptr(GHashTable) found = NULL;
53
g_autoptr(GSList) refresh_list = NULL;
54
55
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
56
assert(bs_queue != NULL);
57
58
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
59
+ ctx = bdrv_get_aio_context(bs_entry->state.bs);
60
+ aio_context_acquire(ctx);
61
ret = bdrv_flush(bs_entry->state.bs);
62
+ aio_context_release(ctx);
63
if (ret < 0) {
64
error_setg_errno(errp, -ret, "Error flushing drive");
65
goto abort;
66
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
67
68
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
69
assert(bs_entry->state.bs->quiesce_counter > 0);
70
+ ctx = bdrv_get_aio_context(bs_entry->state.bs);
71
+ aio_context_acquire(ctx);
72
ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp);
73
+ aio_context_release(ctx);
74
if (ret < 0) {
75
goto abort;
76
}
77
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
78
* to first element.
79
*/
80
QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
81
+ ctx = bdrv_get_aio_context(bs_entry->state.bs);
82
+ aio_context_acquire(ctx);
83
bdrv_reopen_commit(&bs_entry->state);
84
+ aio_context_release(ctx);
85
}
86
87
tran_commit(tran);
88
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
89
BlockDriverState *bs = bs_entry->state.bs;
90
91
if (bs->drv->bdrv_reopen_commit_post) {
92
+ ctx = bdrv_get_aio_context(bs);
93
+ aio_context_acquire(ctx);
94
bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
95
+ aio_context_release(ctx);
96
}
97
}
98
99
@@ -XXX,XX +XXX,XX @@ abort:
100
tran_abort(tran);
101
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
102
if (bs_entry->prepared) {
103
+ ctx = bdrv_get_aio_context(bs_entry->state.bs);
104
+ aio_context_acquire(ctx);
105
bdrv_reopen_abort(&bs_entry->state);
106
+ aio_context_release(ctx);
107
}
108
}
109
110
@@ -XXX,XX +XXX,XX @@ cleanup:
18
return ret;
111
return ret;
19
}
112
}
20
113
21
-static bool bdrv_reopen_can_attach(BlockDriverState *parent,
114
-int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
22
- BdrvChild *child,
115
- Error **errp)
23
- BlockDriverState *new_child,
116
+int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
24
- Error **errp)
117
+ Error **errp)
25
-{
118
{
26
- AioContext *parent_ctx = bdrv_get_aio_context(parent);
119
- int ret;
27
- AioContext *child_ctx = bdrv_get_aio_context(new_child);
120
+ AioContext *ctx = bdrv_get_aio_context(bs);
28
- GSList *ignore;
121
BlockReopenQueue *queue;
29
- bool ret;
122
- QDict *opts = qdict_new();
30
-
123
-
31
- ignore = g_slist_prepend(NULL, child);
124
- qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only);
32
- ret = bdrv_can_set_aio_context(new_child, parent_ctx, &ignore, NULL);
125
+ int ret;
33
- g_slist_free(ignore);
126
34
- if (ret) {
127
bdrv_subtree_drained_begin(bs);
35
- return ret;
128
- queue = bdrv_reopen_queue(NULL, bs, opts, true);
36
- }
129
+ if (ctx != qemu_get_aio_context()) {
37
-
130
+ aio_context_release(ctx);
38
- ignore = g_slist_prepend(NULL, child);
131
+ }
39
- ret = bdrv_can_set_aio_context(parent, child_ctx, &ignore, errp);
132
+
40
- g_slist_free(ignore);
133
+ queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts);
41
- return ret;
134
ret = bdrv_reopen_multiple(queue, errp);
42
-}
135
+
43
-
136
+ if (ctx != qemu_get_aio_context()) {
137
+ aio_context_acquire(ctx);
138
+ }
139
bdrv_subtree_drained_end(bs);
140
141
return ret;
142
}
143
144
+int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
145
+ Error **errp)
146
+{
147
+ QDict *opts = qdict_new();
148
+
149
+ qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only);
150
+
151
+ return bdrv_reopen(bs, opts, true, errp);
152
+}
153
+
44
/*
154
/*
45
* Take a BDRVReopenState and check if the value of 'backing' in the
155
* Take a BDRVReopenState and check if the value of 'backing' in the
46
* reopen_state->options QDict is valid or not.
156
* reopen_state->options QDict is valid or not.
47
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
157
diff --git a/block/replication.c b/block/replication.c
48
g_assert_not_reached();
158
index XXXXXXX..XXXXXXX 100644
49
}
159
--- a/block/replication.c
50
160
+++ b/block/replication.c
51
- /*
161
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
52
- * Check AioContext compatibility so that the bdrv_set_backing_hd() call in
162
}
53
- * bdrv_reopen_commit() won't fail.
163
54
- */
164
if (reopen_queue) {
55
- if (new_backing_bs) {
165
+ AioContext *ctx = bdrv_get_aio_context(bs);
56
- if (!bdrv_reopen_can_attach(bs, bs->backing, new_backing_bs, errp)) {
166
+ if (ctx != qemu_get_aio_context()) {
57
- return -EINVAL;
167
+ aio_context_release(ctx);
58
- }
168
+ }
59
- }
169
bdrv_reopen_multiple(reopen_queue, errp);
170
+ if (ctx != qemu_get_aio_context()) {
171
+ aio_context_acquire(ctx);
172
+ }
173
}
174
175
bdrv_subtree_drained_end(s->hidden_disk->bs);
176
diff --git a/blockdev.c b/blockdev.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/blockdev.c
179
+++ b/blockdev.c
180
@@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
181
ctx = bdrv_get_aio_context(bs);
182
aio_context_acquire(ctx);
183
bdrv_subtree_drained_begin(bs);
184
+ aio_context_release(ctx);
185
+
186
queue = bdrv_reopen_queue(NULL, bs, qdict, false);
187
bdrv_reopen_multiple(queue, errp);
188
+
189
+ ctx = bdrv_get_aio_context(bs);
190
+ aio_context_acquire(ctx);
191
bdrv_subtree_drained_end(bs);
192
aio_context_release(ctx);
193
194
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
195
index XXXXXXX..XXXXXXX 100644
196
--- a/qemu-io-cmds.c
197
+++ b/qemu-io-cmds.c
198
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
199
bool writethrough = !blk_enable_write_cache(blk);
200
bool has_rw_option = false;
201
bool has_cache_option = false;
60
-
202
-
61
/*
203
- BlockReopenQueue *brq;
62
* Ensure that @bs can really handle backing files, because we are
204
Error *local_err = NULL;
63
* about to give it one (or swap the existing one)
205
206
while ((c = getopt(argc, argv, "c:o:rw")) != -1) {
207
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
208
qdict_put_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, flags & BDRV_O_NO_FLUSH);
209
}
210
211
- bdrv_subtree_drained_begin(bs);
212
- brq = bdrv_reopen_queue(NULL, bs, opts, true);
213
- bdrv_reopen_multiple(brq, &local_err);
214
- bdrv_subtree_drained_end(bs);
215
+ bdrv_reopen(bs, opts, true, &local_err);
216
217
if (local_err) {
218
error_report_err(local_err);
64
--
219
--
65
2.31.1
220
2.31.1
66
221
67
222
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
When the x-blockdev-reopen was added it allowed reconfiguring the
3
[ kwolf: Fixed AioContext locking ]
4
graph by replacing backing files, but changing the 'file' option was
5
forbidden. Because of this restriction some operations are not
6
possible, notably inserting and removing block filters.
7
8
This patch adds support for replacing the 'file' option. This is
9
similar to replacing the backing file and the user is likewise
10
responsible for the correctness of the resulting graph, otherwise this
11
can lead to data corruption.
12
4
13
Signed-off-by: Alberto Garcia <berto@igalia.com>
5
Signed-off-by: Alberto Garcia <berto@igalia.com>
14
[vsementsov: bdrv_reopen_parse_file_or_backing() is modified a lot]
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
16
Message-Id: <20210610120537.196183-9-vsementsov@virtuozzo.com>
8
Message-Id: <20210708114709.206487-5-kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
10
---
19
include/block/block.h | 1 +
11
qapi/block-core.json | 18 +++--
20
block.c | 78 +++++++++++++++++++++++++++++-------------
12
blockdev.c | 81 ++++++++++---------
21
tests/qemu-iotests/245 | 23 +++++++------
13
tests/qemu-iotests/155 | 9 ++-
22
3 files changed, 67 insertions(+), 35 deletions(-)
14
tests/qemu-iotests/165 | 4 +-
23
15
tests/qemu-iotests/245 | 27 ++++---
24
diff --git a/include/block/block.h b/include/block/block.h
16
tests/qemu-iotests/248 | 2 +-
17
tests/qemu-iotests/248.out | 2 +-
18
tests/qemu-iotests/296 | 9 ++-
19
tests/qemu-iotests/298 | 4 +-
20
.../tests/remove-bitmap-from-backing | 18 +++--
21
10 files changed, 99 insertions(+), 75 deletions(-)
22
23
diff --git a/qapi/block-core.json b/qapi/block-core.json
25
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/block.h
25
--- a/qapi/block-core.json
27
+++ b/include/block/block.h
26
+++ b/qapi/block-core.json
28
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState {
27
@@ -XXX,XX +XXX,XX @@
29
BlockdevDetectZeroesOptions detect_zeroes;
28
##
30
bool backing_missing;
29
# @x-blockdev-reopen:
31
BlockDriverState *old_backing_bs; /* keep pointer for permissions update */
30
#
32
+ BlockDriverState *old_file_bs; /* keep pointer for permissions update */
31
-# Reopens a block device using the given set of options. Any option
33
QDict *options;
32
-# not specified will be reset to its default value regardless of its
34
QDict *explicit_options;
33
-# previous status. If an option cannot be changed or a particular
35
void *opaque;
34
+# Reopens one or more block devices using the given set of options.
36
diff --git a/block.c b/block.c
35
+# Any option not specified will be reset to its default value regardless
36
+# of its previous status. If an option cannot be changed or a particular
37
# driver does not support reopening then the command will return an
38
-# error.
39
+# error. All devices in the list are reopened in one transaction, so
40
+# if one of them fails then the whole transaction is cancelled.
41
#
42
-# The top-level @node-name option (from BlockdevOptions) must be
43
+# The command receives a list of block devices to reopen. For each one
44
+# of them, the top-level @node-name option (from BlockdevOptions) must be
45
# specified and is used to select the block device to be reopened.
46
# Other @node-name options must be either omitted or set to the
47
# current name of the appropriate node. This command won't change any
48
@@ -XXX,XX +XXX,XX @@
49
#
50
# 4) NULL: the current child (if any) is detached.
51
#
52
-# Options (1) and (2) are supported in all cases, but at the moment
53
-# only @backing allows replacing or detaching an existing child.
54
+# Options (1) and (2) are supported in all cases. Option (3) is
55
+# supported for @file and @backing, and option (4) for @backing only.
56
#
57
# Unlike with blockdev-add, the @backing option must always be present
58
# unless the node being reopened does not have a backing file and its
59
@@ -XXX,XX +XXX,XX @@
60
# Since: 4.0
61
##
62
{ 'command': 'x-blockdev-reopen',
63
- 'data': 'BlockdevOptions', 'boxed': true }
64
+ 'data': { 'options': ['BlockdevOptions'] } }
65
66
##
67
# @blockdev-del:
68
diff --git a/blockdev.c b/blockdev.c
37
index XXXXXXX..XXXXXXX 100644
69
index XXXXXXX..XXXXXXX 100644
38
--- a/block.c
70
--- a/blockdev.c
39
+++ b/block.c
71
+++ b/blockdev.c
40
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
72
@@ -XXX,XX +XXX,XX @@ fail:
41
73
visit_free(v);
42
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
74
}
43
BlockReopenQueue *queue,
75
44
- Transaction *set_backings_tran, Error **errp);
76
-void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
45
+ Transaction *change_child_tran, Error **errp);
77
-{
46
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
78
- BlockDriverState *bs;
47
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
79
- AioContext *ctx;
48
80
- QObject *obj;
49
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
81
- Visitor *v = qobject_output_visitor_new(&obj);
50
refresh_list = bdrv_topological_dfs(refresh_list, found,
82
- BlockReopenQueue *queue;
51
state->old_backing_bs);
83
- QDict *qdict;
52
}
84
+void qmp_x_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
53
+ if (state->old_file_bs) {
85
+{
54
+ refresh_list = bdrv_topological_dfs(refresh_list, found,
86
+ BlockReopenQueue *queue = NULL;
55
+ state->old_file_bs);
87
+ GSList *drained = NULL;
88
+
89
+ /* Add each one of the BDS that we want to reopen to the queue */
90
+ for (; reopen_list != NULL; reopen_list = reopen_list->next) {
91
+ BlockdevOptions *options = reopen_list->value;
92
+ BlockDriverState *bs;
93
+ AioContext *ctx;
94
+ QObject *obj;
95
+ Visitor *v;
96
+ QDict *qdict;
97
+
98
+ /* Check for the selected node name */
99
+ if (!options->has_node_name) {
100
+ error_setg(errp, "node-name not specified");
101
+ goto fail;
56
+ }
102
+ }
57
}
103
58
104
- /* Check for the selected node name */
59
/*
105
- if (!options->has_node_name) {
60
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
106
- error_setg(errp, "node-name not specified");
61
*
107
- goto fail;
62
* Return 0 on success, otherwise return < 0 and set @errp.
108
- }
63
*/
109
+ bs = bdrv_find_node(options->node_name);
64
-static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
110
+ if (!bs) {
65
- Transaction *set_backings_tran,
111
+ error_setg(errp, "Failed to find node with node-name='%s'",
66
- Error **errp)
112
+ options->node_name);
67
+static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
113
+ goto fail;
68
+ bool is_backing, Transaction *tran,
114
+ }
69
+ Error **errp)
115
70
{
116
- bs = bdrv_find_node(options->node_name);
71
BlockDriverState *bs = reopen_state->bs;
117
- if (!bs) {
72
- BlockDriverState *new_backing_bs;
118
- error_setg(errp, "Failed to find node with node-name='%s'",
73
+ BlockDriverState *new_child_bs;
119
- options->node_name);
74
+ BlockDriverState *old_child_bs = is_backing ? child_bs(bs->backing) :
120
- goto fail;
75
+ child_bs(bs->file);
121
- }
76
+ const char *child_name = is_backing ? "backing" : "file";
122
+ /* Put all options in a QDict and flatten it */
77
QObject *value;
123
+ v = qobject_output_visitor_new(&obj);
78
const char *str;
124
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
79
125
+ visit_complete(v, &obj);
80
- value = qdict_get(reopen_state->options, "backing");
126
+ visit_free(v);
81
+ value = qdict_get(reopen_state->options, child_name);
127
82
if (value == NULL) {
128
- /* Put all options in a QDict and flatten it */
83
return 0;
129
- visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
84
}
130
- visit_complete(v, &obj);
85
131
- qdict = qobject_to(QDict, obj);
86
switch (qobject_type(value)) {
132
+ qdict = qobject_to(QDict, obj);
87
case QTYPE_QNULL:
133
88
- new_backing_bs = NULL;
134
- qdict_flatten(qdict);
89
+ assert(is_backing); /* The 'file' option does not allow a null value */
135
+ qdict_flatten(qdict);
90
+ new_child_bs = NULL;
136
91
break;
137
- /* Perform the reopen operation */
92
case QTYPE_QSTRING:
138
- ctx = bdrv_get_aio_context(bs);
93
str = qstring_get_str(qobject_to(QString, value));
139
- aio_context_acquire(ctx);
94
- new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
140
- bdrv_subtree_drained_begin(bs);
95
- if (new_backing_bs == NULL) {
141
- aio_context_release(ctx);
96
+ new_child_bs = bdrv_lookup_bs(NULL, str, errp);
142
+ ctx = bdrv_get_aio_context(bs);
97
+ if (new_child_bs == NULL) {
143
+ aio_context_acquire(ctx);
98
return -EINVAL;
144
99
- } else if (bdrv_recurse_has_child(new_backing_bs, bs)) {
145
- queue = bdrv_reopen_queue(NULL, bs, qdict, false);
100
- error_setg(errp, "Making '%s' a backing file of '%s' "
146
- bdrv_reopen_multiple(queue, errp);
101
- "would create a cycle", str, bs->node_name);
147
+ bdrv_subtree_drained_begin(bs);
102
+ } else if (bdrv_recurse_has_child(new_child_bs, bs)) {
148
+ queue = bdrv_reopen_queue(queue, bs, qdict, false);
103
+ error_setg(errp, "Making '%s' a %s child of '%s' would create a "
149
+ drained = g_slist_prepend(drained, bs);
104
+ "cycle", str, child_name, bs->node_name);
150
105
return -EINVAL;
151
- ctx = bdrv_get_aio_context(bs);
106
}
152
- aio_context_acquire(ctx);
107
break;
153
- bdrv_subtree_drained_end(bs);
108
default:
154
- aio_context_release(ctx);
109
- /* 'backing' does not allow any other data type */
155
+ aio_context_release(ctx);
110
+ /*
111
+ * The options QDict has been flattened, so 'backing' and 'file'
112
+ * do not allow any other data type here.
113
+ */
114
g_assert_not_reached();
115
}
116
117
- if (bs->backing) {
118
- if (bdrv_skip_implicit_filters(bs->backing->bs) == new_backing_bs) {
119
+ if (old_child_bs == new_child_bs) {
120
+ return 0;
121
+ }
156
+ }
122
+
157
+
123
+ if (old_child_bs) {
158
+ /* Perform the reopen operation */
124
+ if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) {
159
+ bdrv_reopen_multiple(queue, errp);
125
return 0;
160
+ queue = NULL;
126
}
161
127
162
fail:
128
- if (bs->backing->bs->implicit) {
163
- visit_free(v);
129
- error_setg(errp, "Cannot change backing link if '%s' has "
164
+ bdrv_reopen_queue_free(queue);
130
- "an implicit backing file", bs->node_name);
165
+ g_slist_free_full(drained, (GDestroyNotify) bdrv_subtree_drained_end);
131
+ if (old_child_bs->implicit) {
132
+ error_setg(errp, "Cannot replace implicit %s child of %s",
133
+ child_name, bs->node_name);
134
return -EPERM;
135
}
136
}
137
138
- if (bs->drv->is_filter && !bs->backing) {
139
+ if (bs->drv->is_filter && !old_child_bs) {
140
/*
141
* Filters always have a file or a backing child, so we are trying to
142
* change wrong child
143
*/
144
error_setg(errp, "'%s' is a %s filter node that does not support a "
145
- "backing child", bs->node_name, bs->drv->format_name);
146
+ "%s child", bs->node_name, bs->drv->format_name, child_name);
147
return -EINVAL;
148
}
149
150
- reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
151
- return bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran, errp);
152
+ if (is_backing) {
153
+ reopen_state->old_backing_bs = old_child_bs;
154
+ } else {
155
+ reopen_state->old_file_bs = old_child_bs;
156
+ }
157
+
158
+ return bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing,
159
+ tran, errp);
160
}
166
}
161
167
162
/*
168
void qmp_blockdev_del(const char *node_name, Error **errp)
163
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
169
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
164
*/
170
index XXXXXXX..XXXXXXX 100755
165
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
171
--- a/tests/qemu-iotests/155
166
BlockReopenQueue *queue,
172
+++ b/tests/qemu-iotests/155
167
- Transaction *set_backings_tran, Error **errp)
173
@@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorReopen(MirrorBaseClass):
168
+ Transaction *change_child_tran, Error **errp)
174
result = self.vm.qmp('blockdev-add', node_name="backing",
169
{
175
driver="null-co")
170
int ret = -1;
176
self.assert_qmp(result, 'return', {})
171
int old_flags;
177
- result = self.vm.qmp('x-blockdev-reopen', node_name="target",
172
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
178
- driver=iotests.imgfmt, file="target-file",
173
* either a reference to an existing node (using its node name)
179
- backing="backing")
174
* or NULL to simply detach the current backing file.
180
+ result = self.vm.qmp('x-blockdev-reopen', options=[{
175
*/
181
+ 'node-name': "target",
176
- ret = bdrv_reopen_parse_backing(reopen_state, set_backings_tran, errp);
182
+ 'driver': iotests.imgfmt,
177
+ ret = bdrv_reopen_parse_file_or_backing(reopen_state, true,
183
+ 'file': "target-file",
178
+ change_child_tran, errp);
184
+ 'backing': "backing"
179
if (ret < 0) {
185
+ }])
180
goto error;
186
self.assert_qmp(result, 'return', {})
181
}
187
182
qdict_del(reopen_state->options, "backing");
188
class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen):
183
189
diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165
184
+ /* Allow changing the 'file' option. In this case NULL is not allowed */
190
index XXXXXXX..XXXXXXX 100755
185
+ ret = bdrv_reopen_parse_file_or_backing(reopen_state, false,
191
--- a/tests/qemu-iotests/165
186
+ change_child_tran, errp);
192
+++ b/tests/qemu-iotests/165
187
+ if (ret < 0) {
193
@@ -XXX,XX +XXX,XX @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
188
+ goto error;
194
assert sha256_1 == self.getSha256()
189
+ }
195
190
+ qdict_del(reopen_state->options, "file");
196
# Reopen to RW
191
+
197
- result = self.vm.qmp('x-blockdev-reopen', **{
192
/* Options that are not handled are only okay if they are unchanged
198
+ result = self.vm.qmp('x-blockdev-reopen', options=[{
193
* compared to the old state. It is expected that some options are only
199
'node-name': 'node0',
194
* used for the initial open, but not reopen (e.g. filename) */
200
'driver': iotests.imgfmt,
201
'file': {
202
@@ -XXX,XX +XXX,XX @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
203
'filename': disk
204
},
205
'read-only': False
206
- })
207
+ }])
208
self.assert_qmp(result, 'return', {})
209
210
# Check that bitmap is reopened to RW and we can write to it.
195
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
211
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
196
index XXXXXXX..XXXXXXX 100755
212
index XXXXXXX..XXXXXXX 100755
197
--- a/tests/qemu-iotests/245
213
--- a/tests/qemu-iotests/245
198
+++ b/tests/qemu-iotests/245
214
+++ b/tests/qemu-iotests/245
199
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
215
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
216
"Expected output of %d qemu-io commands, found %d" %
217
(found, self.total_io_cmds))
218
219
- # Run x-blockdev-reopen with 'opts' but applying 'newopts'
220
- # on top of it. The original 'opts' dict is unmodified
221
+ # Run x-blockdev-reopen on a list of block devices
222
+ def reopenMultiple(self, opts, errmsg = None):
223
+ result = self.vm.qmp('x-blockdev-reopen', conv_keys=False, options=opts)
224
+ if errmsg:
225
+ self.assert_qmp(result, 'error/class', 'GenericError')
226
+ self.assert_qmp(result, 'error/desc', errmsg)
227
+ else:
228
+ self.assert_qmp(result, 'return', {})
229
+
230
+ # Run x-blockdev-reopen on a single block device (specified by
231
+ # 'opts') but applying 'newopts' on top of it. The original 'opts'
232
+ # dict is unmodified
233
def reopen(self, opts, newopts = {}, errmsg = None):
234
opts = copy.deepcopy(opts)
235
236
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
237
subdict = opts[prefix]
238
subdict[key] = value
239
240
- result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts)
241
- if errmsg:
242
- self.assert_qmp(result, 'error/class', 'GenericError')
243
- self.assert_qmp(result, 'error/desc', errmsg)
244
- else:
245
- self.assert_qmp(result, 'return', {})
246
+ self.reopenMultiple([ opts ], errmsg)
247
248
249
# Run query-named-block-nodes and return the specified entry
250
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
251
# We cannot change any of these
252
self.reopen(opts, {'node-name': 'not-found'}, "Failed to find node with node-name='not-found'")
253
self.reopen(opts, {'node-name': ''}, "Failed to find node with node-name=''")
254
- self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string")
255
+ self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'options[0].node-name', expected: string")
200
self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'")
256
self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'")
201
self.reopen(opts, {'driver': ''}, "Invalid parameter ''")
257
self.reopen(opts, {'driver': ''}, "Invalid parameter ''")
202
self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string")
258
- self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string")
203
- self.reopen(opts, {'file': 'not-found'}, "Cannot change the option 'file'")
259
+ self.reopen(opts, {'driver': None}, "Invalid parameter type for 'options[0].driver', expected: string")
204
- self.reopen(opts, {'file': ''}, "Cannot change the option 'file'")
260
self.reopen(opts, {'file': 'not-found'}, "Cannot find device='' nor node-name='not-found'")
205
+ self.reopen(opts, {'file': 'not-found'}, "Cannot find device='' nor node-name='not-found'")
261
self.reopen(opts, {'file': ''}, "Cannot find device='' nor node-name=''")
206
+ self.reopen(opts, {'file': ''}, "Cannot find device='' nor node-name=''")
207
self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef")
262
self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef")
208
self.reopen(opts, {'file.node-name': 'newname'}, "Cannot change the option 'node-name'")
209
self.reopen(opts, {'file.driver': 'host_device'}, "Cannot change the option 'driver'")
210
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
263
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
211
264
self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'")
212
# Illegal operation: hd2 is a child of hd1
265
self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'")
213
self.reopen(opts[2], {'backing': 'hd1'},
266
self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
214
- "Making 'hd1' a backing file of 'hd2' would create a cycle")
267
- self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string")
215
+ "Making 'hd1' a backing child of 'hd2' would create a cycle")
268
+ self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'options[0].file.filename', expected: string")
216
269
217
# hd2 <- hd0, hd2 <- hd1
270
# node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it
218
self.reopen(opts[0], {'backing': 'hd2'})
271
del opts['node-name']
219
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
272
diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248
220
273
index XXXXXXX..XXXXXXX 100755
221
# More illegal operations
274
--- a/tests/qemu-iotests/248
222
self.reopen(opts[2], {'backing': 'hd1'},
275
+++ b/tests/qemu-iotests/248
223
- "Making 'hd1' a backing file of 'hd2' would create a cycle")
276
@@ -XXX,XX +XXX,XX @@ vm.get_qmp_events()
224
- self.reopen(opts[2], {'file': 'hd0-file'}, "Cannot change the option 'file'")
277
225
+ "Making 'hd1' a backing child of 'hd2' would create a cycle")
278
del blockdev_opts['file']['size']
226
+ self.reopen(opts[2], {'file': 'hd0-file'},
279
vm.qmp_log('x-blockdev-reopen', filters=[filter_qmp_testfiles],
227
+ "Permission conflict on node 'hd0-file': permissions 'write, resize' are both required by node 'hd0' (uses node 'hd0-file' as 'file' child) and unshared by node 'hd2' (uses node 'hd0-file' as 'file' child).")
280
- **blockdev_opts)
228
281
+ options = [ blockdev_opts ])
229
result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
282
230
self.assert_qmp(result, 'error/class', 'GenericError')
283
vm.qmp_log('block-job-resume', device='drive0')
231
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
284
vm.event_wait('JOB_STATUS_CHANGE', timeout=1.0,
232
285
diff --git a/tests/qemu-iotests/248.out b/tests/qemu-iotests/248.out
233
# Illegal: hd2 is backed by hd1
286
index XXXXXXX..XXXXXXX 100644
234
self.reopen(opts[1], {'backing': 'hd2'},
287
--- a/tests/qemu-iotests/248.out
235
- "Making 'hd2' a backing file of 'hd1' would create a cycle")
288
+++ b/tests/qemu-iotests/248.out
236
+ "Making 'hd2' a backing child of 'hd1' would create a cycle")
289
@@ -XXX,XX +XXX,XX @@
237
290
{"return": {}}
238
# hd1 <- hd0 <- hd2
291
{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "on-target-error": "enospc", "sync": "full", "target": "target"}}
239
self.reopen(opts[2], {'backing': 'hd0'})
292
{"return": {}}
240
293
-{"execute": "x-blockdev-reopen", "arguments": {"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}}
241
# Illegal: hd2 is backed by hd0, which is backed by hd1
294
+{"execute": "x-blockdev-reopen", "arguments": {"options": [{"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}]}}
242
self.reopen(opts[1], {'backing': 'hd2'},
295
{"return": {}}
243
- "Making 'hd2' a backing file of 'hd1' would create a cycle")
296
{"execute": "block-job-resume", "arguments": {"device": "drive0"}}
244
+ "Making 'hd2' a backing child of 'hd1' would create a cycle")
297
{"return": {}}
245
298
diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296
246
# Illegal: hd1 cannot point to itself
299
index XXXXXXX..XXXXXXX 100755
247
self.reopen(opts[1], {'backing': 'hd1'},
300
--- a/tests/qemu-iotests/296
248
- "Making 'hd1' a backing file of 'hd1' would create a cycle")
301
+++ b/tests/qemu-iotests/296
249
+ "Making 'hd1' a backing child of 'hd1' would create a cycle")
302
@@ -XXX,XX +XXX,XX @@ class EncryptionSetupTestCase(iotests.QMPTestCase):
250
303
251
# Remove all backing files
304
command = 'x-blockdev-reopen' if reOpen else 'blockdev-add'
252
self.reopen(opts[0])
305
253
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
306
- result = vm.qmp(command, **
254
307
- {
255
# Illegal: hd0 is a child of the blkverify node
308
+ opts = {
256
self.reopen(opts[0], {'backing': 'bv'},
309
'driver': iotests.imgfmt,
257
- "Making 'bv' a backing file of 'hd0' would create a cycle")
310
'node-name': id,
258
+ "Making 'bv' a backing child of 'hd0' would create a cycle")
311
'read-only': readOnly,
259
312
@@ -XXX,XX +XXX,XX @@ class EncryptionSetupTestCase(iotests.QMPTestCase):
260
# Delete the blkverify node
313
'filename': test_img,
261
result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv')
314
}
262
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
315
}
263
# You can't make quorum0 a backing file of hd0:
316
- )
264
# hd0 is already a child of quorum0.
317
+
265
self.reopen(hd_opts(0), {'backing': 'quorum0'},
318
+ if reOpen:
266
- "Making 'quorum0' a backing file of 'hd0' would create a cycle")
319
+ result = vm.qmp(command, options=[opts])
267
+ "Making 'quorum0' a backing child of 'hd0' would create a cycle")
320
+ else:
268
321
+ result = vm.qmp(command, **opts)
269
# Delete quorum0
322
self.assert_qmp(result, 'return', {})
270
result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'quorum0')
323
271
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
324
272
325
diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298
273
# We can't remove hd1 while the commit job is ongoing
326
index XXXXXXX..XXXXXXX 100755
274
opts['backing'] = None
327
--- a/tests/qemu-iotests/298
275
- self.reopen(opts, {}, "Cannot change backing link if 'hd0' has an implicit backing file")
328
+++ b/tests/qemu-iotests/298
276
+ self.reopen(opts, {}, "Cannot replace implicit backing child of hd0")
329
@@ -XXX,XX +XXX,XX @@ class TestPreallocateFilter(TestPreallocateBase):
277
330
self.check_big()
278
# hd2 <- hd0
331
279
self.vm.run_job('commit0', auto_finalize = False, auto_dismiss = True)
332
def test_reopen_opts(self):
333
- result = self.vm.qmp('x-blockdev-reopen', **{
334
+ result = self.vm.qmp('x-blockdev-reopen', options=[{
335
'node-name': 'disk',
336
'driver': iotests.imgfmt,
337
'file': {
338
@@ -XXX,XX +XXX,XX @@ class TestPreallocateFilter(TestPreallocateBase):
339
'filename': disk
340
}
341
}
342
- })
343
+ }])
344
self.assert_qmp(result, 'return', {})
345
346
self.vm.hmp_qemu_io('drive0', 'write 0 1M')
347
diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qemu-iotests/tests/remove-bitmap-from-backing
348
index XXXXXXX..XXXXXXX 100755
349
--- a/tests/qemu-iotests/tests/remove-bitmap-from-backing
350
+++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing
351
@@ -XXX,XX +XXX,XX @@ log('Trying to remove persistent bitmap from r-o base node, should fail:')
352
vm.qmp_log('block-dirty-bitmap-remove', node='base', name='bitmap0')
353
354
new_base_opts = {
355
- 'node-name': 'base',
356
- 'driver': 'qcow2',
357
- 'file': {
358
- 'driver': 'file',
359
- 'filename': base
360
- },
361
- 'read-only': False
362
+ 'options': [{
363
+ 'node-name': 'base',
364
+ 'driver': 'qcow2',
365
+ 'file': {
366
+ 'driver': 'file',
367
+ 'filename': base
368
+ },
369
+ 'read-only': False
370
+ }]
371
}
372
373
# Don't want to bother with filtering qmp_log for reopen command
374
@@ -XXX,XX +XXX,XX @@ if result != {'return': {}}:
375
log('Remove persistent bitmap from base node reopened to RW:')
376
vm.qmp_log('block-dirty-bitmap-remove', node='base', name='bitmap0')
377
378
-new_base_opts['read-only'] = True
379
+new_base_opts['options'][0]['read-only'] = True
380
result = vm.qmp('x-blockdev-reopen', **new_base_opts)
381
if result != {'return': {}}:
382
log('Failed to reopen: ' + str(result))
280
--
383
--
281
2.31.1
384
2.31.1
282
385
283
386
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
This patch adds new tests in which we use x-blockdev-reopen to change
3
This test swaps the images used by two active block devices.
4
bs->file
4
5
This is now possible thanks to the new ability to run
6
x-blockdev-reopen on multiple devices at the same time.
5
7
6
Signed-off-by: Alberto Garcia <berto@igalia.com>
8
Signed-off-by: Alberto Garcia <berto@igalia.com>
7
Message-Id: <20210610120537.196183-10-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Message-Id: <20210708114709.206487-6-kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
13
---
10
tests/qemu-iotests/245 | 109 ++++++++++++++++++++++++++++++++++++-
14
tests/qemu-iotests/245 | 47 ++++++++++++++++++++++++++++++++++++++
11
tests/qemu-iotests/245.out | 11 +++-
15
tests/qemu-iotests/245.out | 4 ++--
12
2 files changed, 117 insertions(+), 3 deletions(-)
16
2 files changed, 49 insertions(+), 2 deletions(-)
13
17
14
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
18
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
15
index XXXXXXX..XXXXXXX 100755
19
index XXXXXXX..XXXXXXX 100755
16
--- a/tests/qemu-iotests/245
20
--- a/tests/qemu-iotests/245
17
+++ b/tests/qemu-iotests/245
21
+++ b/tests/qemu-iotests/245
18
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
22
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
19
for line in log.split("\n"):
23
'-c', 'read -P 0x40 0x40008 1',
20
if line.startswith("Pattern verification failed"):
24
'-c', 'read -P 0x80 0x40010 1', hd_path[0])
21
raise Exception("%s (command #%d)" % (line, found))
25
22
- if re.match("read .*/.* bytes at offset", line):
26
+ # Swap the disk images of two active block devices
23
+ if re.match("(read|wrote) .*/.* bytes at offset", line):
27
+ def test_swap_files(self):
24
found += 1
28
+ # Add hd0 and hd2 (none of them with backing files)
25
self.assertEqual(found, self.total_io_cmds,
29
+ opts0 = hd_opts(0)
26
"Expected output of %d qemu-io commands, found %d" %
30
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts0)
27
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
28
result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv')
29
self.assert_qmp(result, 'return', {})
30
31
+ # Replace the protocol layer ('file' parameter) of a disk image
32
+ def test_replace_file(self):
33
+ # Create two small raw images and add them to a running VM
34
+ qemu_img('create', '-f', 'raw', hd_path[0], '10k')
35
+ qemu_img('create', '-f', 'raw', hd_path[1], '10k')
36
+
37
+ hd0_opts = {'driver': 'file', 'node-name': 'hd0-file', 'filename': hd_path[0] }
38
+ hd1_opts = {'driver': 'file', 'node-name': 'hd1-file', 'filename': hd_path[1] }
39
+
40
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **hd0_opts)
41
+ self.assert_qmp(result, 'return', {})
42
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **hd1_opts)
43
+ self.assert_qmp(result, 'return', {})
31
+ self.assert_qmp(result, 'return', {})
44
+
32
+
45
+ # Add a raw format layer that uses hd0-file as its protocol layer
33
+ opts2 = hd_opts(2)
46
+ opts = {'driver': 'raw', 'node-name': 'hd', 'file': 'hd0-file'}
34
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2)
47
+
48
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
49
+ self.assert_qmp(result, 'return', {})
35
+ self.assert_qmp(result, 'return', {})
50
+
36
+
51
+ # Fill the image with data
37
+ # Write different data to both block devices
52
+ self.run_qemu_io("hd", "read -P 0 0 10k")
38
+ self.run_qemu_io("hd0", "write -P 0xa0 0 1k")
53
+ self.run_qemu_io("hd", "write -P 0xa0 0 10k")
39
+ self.run_qemu_io("hd2", "write -P 0xa2 0 1k")
54
+
40
+
55
+ # Replace hd0-file with hd1-file and fill it with (different) data
41
+ # Check that the data reads correctly
56
+ self.reopen(opts, {'file': 'hd1-file'})
42
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1k")
57
+ self.run_qemu_io("hd", "read -P 0 0 10k")
43
+ self.run_qemu_io("hd2", "read -P 0xa2 0 1k")
58
+ self.run_qemu_io("hd", "write -P 0xa1 0 10k")
59
+
44
+
60
+ # Use hd0-file again and check that it contains the expected data
45
+ # It's not possible to make a block device use an image that
61
+ self.reopen(opts, {'file': 'hd0-file'})
46
+ # is already being used by the other device.
62
+ self.run_qemu_io("hd", "read -P 0xa0 0 10k")
47
+ self.reopen(opts0, {'file': 'hd2-file'},
48
+ "Permission conflict on node 'hd2-file': permissions "
49
+ "'write, resize' are both required by node 'hd2' (uses "
50
+ "node 'hd2-file' as 'file' child) and unshared by node "
51
+ "'hd0' (uses node 'hd2-file' as 'file' child).")
52
+ self.reopen(opts2, {'file': 'hd0-file'},
53
+ "Permission conflict on node 'hd0-file': permissions "
54
+ "'write, resize' are both required by node 'hd0' (uses "
55
+ "node 'hd0-file' as 'file' child) and unshared by node "
56
+ "'hd2' (uses node 'hd0-file' as 'file' child).")
63
+
57
+
64
+ # And finally do the same with hd1-file
58
+ # But we can swap the images if we reopen both devices at the
65
+ self.reopen(opts, {'file': 'hd1-file'})
59
+ # same time
66
+ self.run_qemu_io("hd", "read -P 0xa1 0 10k")
60
+ opts0['file'] = 'hd2-file'
61
+ opts2['file'] = 'hd0-file'
62
+ self.reopenMultiple([opts0, opts2])
63
+ self.run_qemu_io("hd0", "read -P 0xa2 0 1k")
64
+ self.run_qemu_io("hd2", "read -P 0xa0 0 1k")
67
+
65
+
68
+ # Insert (and remove) a throttle filter
66
+ # And we can of course come back to the original state
69
+ def test_insert_throttle_filter(self):
67
+ opts0['file'] = 'hd0-file'
70
+ # Add an image to the VM
68
+ opts2['file'] = 'hd2-file'
71
+ hd0_opts = hd_opts(0)
69
+ self.reopenMultiple([opts0, opts2])
72
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **hd0_opts)
70
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1k")
73
+ self.assert_qmp(result, 'return', {})
71
+ self.run_qemu_io("hd2", "read -P 0xa2 0 1k")
74
+
75
+ # Create a throttle-group object
76
+ opts = { 'qom-type': 'throttle-group', 'id': 'group0',
77
+ 'limits': { 'iops-total': 1000 } }
78
+ result = self.vm.qmp('object-add', conv_keys = False, **opts)
79
+ self.assert_qmp(result, 'return', {})
80
+
81
+ # Add a throttle filter with the group that we just created.
82
+ # The filter is not used by anyone yet
83
+ opts = { 'driver': 'throttle', 'node-name': 'throttle0',
84
+ 'throttle-group': 'group0', 'file': 'hd0-file' }
85
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
86
+ self.assert_qmp(result, 'return', {})
87
+
88
+ # Insert the throttle filter between hd0 and hd0-file
89
+ self.reopen(hd0_opts, {'file': 'throttle0'})
90
+
91
+ # Remove the throttle filter from hd0
92
+ self.reopen(hd0_opts, {'file': 'hd0-file'})
93
+
94
+ # Insert (and remove) a compress filter
95
+ def test_insert_compress_filter(self):
96
+ # Add an image to the VM: hd (raw) -> hd0 (qcow2) -> hd0-file (file)
97
+ opts = {'driver': 'raw', 'node-name': 'hd', 'file': hd_opts(0)}
98
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
99
+ self.assert_qmp(result, 'return', {})
100
+
101
+ # Add a 'compress' filter
102
+ filter_opts = {'driver': 'compress',
103
+ 'node-name': 'compress0',
104
+ 'file': 'hd0'}
105
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **filter_opts)
106
+ self.assert_qmp(result, 'return', {})
107
+
108
+ # Unmap the beginning of the image (we cannot write compressed
109
+ # data to an allocated cluster)
110
+ self.run_qemu_io("hd", "write -z -u 0 128k")
111
+
112
+ # Write data to the first cluster
113
+ self.run_qemu_io("hd", "write -P 0xa0 0 64k")
114
+
115
+ # Insert the filter then write to the second cluster
116
+ # hd -> compress0 -> hd0 -> hd0-file
117
+ self.reopen(opts, {'file': 'compress0'})
118
+ self.run_qemu_io("hd", "write -P 0xa1 64k 64k")
119
+
120
+ # Remove the filter then write to the third cluster
121
+ # hd -> hd0 -> hd0-file
122
+ self.reopen(opts, {'file': 'hd0'})
123
+ self.run_qemu_io("hd", "write -P 0xa2 128k 64k")
124
+
125
+ # Verify the data that we just wrote
126
+ self.run_qemu_io("hd", "read -P 0xa0 0 64k")
127
+ self.run_qemu_io("hd", "read -P 0xa1 64k 64k")
128
+ self.run_qemu_io("hd", "read -P 0xa2 128k 64k")
129
+
130
+ self.vm.shutdown()
131
+
132
+ # Check the first byte of the first three L2 entries and verify that
133
+ # the second one is compressed (0x40) while the others are not (0x80)
134
+ iotests.qemu_io_log('-f', 'raw', '-c', 'read -P 0x80 0x40000 1',
135
+ '-c', 'read -P 0x40 0x40008 1',
136
+ '-c', 'read -P 0x80 0x40010 1', hd_path[0])
137
+
72
+
138
# Misc reopen tests with different block drivers
73
# Misc reopen tests with different block drivers
139
@iotests.skip_if_unsupported(['quorum', 'throttle'])
74
@iotests.skip_if_unsupported(['quorum', 'throttle'])
140
def test_misc_drivers(self):
75
def test_misc_drivers(self):
141
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
76
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
142
index XXXXXXX..XXXXXXX 100644
77
index XXXXXXX..XXXXXXX 100644
143
--- a/tests/qemu-iotests/245.out
78
--- a/tests/qemu-iotests/245.out
144
+++ b/tests/qemu-iotests/245.out
79
+++ b/tests/qemu-iotests/245.out
145
@@ -XXX,XX +XXX,XX @@
80
@@ -XXX,XX +XXX,XX @@ read 1/1 bytes at offset 262152
146
{"return": {}}
81
read 1/1 bytes at offset 262160
147
{"data": {"id": "stream0", "type": "stream"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
82
1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
148
{"data": {"device": "stream0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "stream"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
83
149
-...............
84
-..............
150
+....read 1/1 bytes at offset 262144
85
+...............
151
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
152
+read 1/1 bytes at offset 262152
153
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
154
+read 1/1 bytes at offset 262160
155
+1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
156
+
157
+..............
158
----------------------------------------------------------------------
86
----------------------------------------------------------------------
159
-Ran 21 tests
87
-Ran 24 tests
160
+Ran 24 tests
88
+Ran 25 tests
161
89
162
OK
90
OK
163
--
91
--
164
2.31.1
92
2.31.1
165
93
166
94
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
bdrv_set_backing_noperm() takes care of it (actual check is in
3
This patch drops the 'x-' prefix from x-blockdev-reopen.
4
bdrv_set_file_or_backing_noperm()), so we don't need to check it here.
4
5
5
Signed-off-by: Alberto Garcia <berto@igalia.com>
6
While being here, improve error message a bit.
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-Id: <20210708114709.206487-7-kwolf@redhat.com>
9
Message-Id: <20210610120537.196183-5-vsementsov@virtuozzo.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
10
---
12
block.c | 14 +-------------
11
qapi/block-core.json | 6 +++---
13
tests/qemu-iotests/245 | 8 ++++----
12
blockdev.c | 2 +-
14
2 files changed, 5 insertions(+), 17 deletions(-)
13
tests/qemu-iotests/155 | 2 +-
15
14
tests/qemu-iotests/165 | 2 +-
16
diff --git a/block.c b/block.c
15
tests/qemu-iotests/245 | 10 +++++-----
16
tests/qemu-iotests/248 | 2 +-
17
tests/qemu-iotests/248.out | 2 +-
18
tests/qemu-iotests/296 | 2 +-
19
tests/qemu-iotests/298 | 2 +-
20
tests/qemu-iotests/tests/remove-bitmap-from-backing | 4 ++--
21
10 files changed, 17 insertions(+), 17 deletions(-)
22
23
diff --git a/qapi/block-core.json b/qapi/block-core.json
17
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
25
--- a/qapi/block-core.json
19
+++ b/block.c
26
+++ b/qapi/block-core.json
20
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
27
@@ -XXX,XX +XXX,XX @@
21
"an implicit backing file", bs->node_name);
28
{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true }
22
return -EPERM;
29
23
}
30
##
24
- /*
31
-# @x-blockdev-reopen:
25
- * Check if the backing link that we want to replace is frozen.
32
+# @blockdev-reopen:
26
- * Note that
33
#
27
- * bdrv_filter_or_cow_child(overlay_bs) == overlay_bs->backing,
34
# Reopens one or more block devices using the given set of options.
28
- * because we know that overlay_bs == bs, and that @bs
35
# Any option not specified will be reset to its default value regardless
29
- * either is a filter that uses ->backing or a COW format BDS
36
@@ -XXX,XX +XXX,XX @@
30
- * with bs->drv->supports_backing == true.
37
# image does not have a default backing file name as part of its
31
- */
38
# metadata.
32
- if (bdrv_is_backing_chain_frozen(overlay_bs,
39
#
33
- child_bs(overlay_bs->backing), errp))
40
-# Since: 4.0
34
- {
41
+# Since: 6.1
35
- return -EPERM;
42
##
36
- }
43
-{ 'command': 'x-blockdev-reopen',
37
+
44
+{ 'command': 'blockdev-reopen',
38
reopen_state->replace_backing_bs = true;
45
'data': { 'options': ['BlockdevOptions'] } }
39
reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
46
40
ret = bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran,
47
##
48
diff --git a/blockdev.c b/blockdev.c
49
index XXXXXXX..XXXXXXX 100644
50
--- a/blockdev.c
51
+++ b/blockdev.c
52
@@ -XXX,XX +XXX,XX @@ fail:
53
visit_free(v);
54
}
55
56
-void qmp_x_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
57
+void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
58
{
59
BlockReopenQueue *queue = NULL;
60
GSList *drained = NULL;
61
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
62
index XXXXXXX..XXXXXXX 100755
63
--- a/tests/qemu-iotests/155
64
+++ b/tests/qemu-iotests/155
65
@@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorReopen(MirrorBaseClass):
66
result = self.vm.qmp('blockdev-add', node_name="backing",
67
driver="null-co")
68
self.assert_qmp(result, 'return', {})
69
- result = self.vm.qmp('x-blockdev-reopen', options=[{
70
+ result = self.vm.qmp('blockdev-reopen', options=[{
71
'node-name': "target",
72
'driver': iotests.imgfmt,
73
'file': "target-file",
74
diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165
75
index XXXXXXX..XXXXXXX 100755
76
--- a/tests/qemu-iotests/165
77
+++ b/tests/qemu-iotests/165
78
@@ -XXX,XX +XXX,XX @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
79
assert sha256_1 == self.getSha256()
80
81
# Reopen to RW
82
- result = self.vm.qmp('x-blockdev-reopen', options=[{
83
+ result = self.vm.qmp('blockdev-reopen', options=[{
84
'node-name': 'node0',
85
'driver': iotests.imgfmt,
86
'file': {
41
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
87
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
42
index XXXXXXX..XXXXXXX 100755
88
index XXXXXXX..XXXXXXX 100755
43
--- a/tests/qemu-iotests/245
89
--- a/tests/qemu-iotests/245
44
+++ b/tests/qemu-iotests/245
90
+++ b/tests/qemu-iotests/245
91
@@ -XXX,XX +XXX,XX @@
92
#!/usr/bin/env python3
93
# group: rw
94
#
95
-# Test cases for the QMP 'x-blockdev-reopen' command
96
+# Test cases for the QMP 'blockdev-reopen' command
97
#
98
# Copyright (C) 2018-2019 Igalia, S.L.
99
# Author: Alberto Garcia <berto@igalia.com>
45
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
100
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
46
101
"Expected output of %d qemu-io commands, found %d" %
47
# We can't remove hd1 while the stream job is ongoing
102
(found, self.total_io_cmds))
48
opts['backing'] = None
103
49
- self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'")
104
- # Run x-blockdev-reopen on a list of block devices
50
+ self.reopen(opts, {}, "Cannot change frozen 'backing' link from 'hd0' to 'hd1'")
105
+ # Run blockdev-reopen on a list of block devices
51
106
def reopenMultiple(self, opts, errmsg = None):
52
self.vm.run_job('stream0', auto_finalize = False, auto_dismiss = True)
107
- result = self.vm.qmp('x-blockdev-reopen', conv_keys=False, options=opts)
53
108
+ result = self.vm.qmp('blockdev-reopen', conv_keys=False, options=opts)
109
if errmsg:
110
self.assert_qmp(result, 'error/class', 'GenericError')
111
self.assert_qmp(result, 'error/desc', errmsg)
112
else:
113
self.assert_qmp(result, 'return', {})
114
115
- # Run x-blockdev-reopen on a single block device (specified by
116
+ # Run blockdev-reopen on a single block device (specified by
117
# 'opts') but applying 'newopts' on top of it. The original 'opts'
118
# dict is unmodified
119
def reopen(self, opts, newopts = {}, errmsg = None):
54
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
120
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
55
# We can't remove hd2 while the stream job is ongoing
121
self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
56
opts['backing']['backing'] = None
122
self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'options[0].file.filename', expected: string")
57
self.reopen(opts['backing'], {'read-only': False},
123
58
- "Cannot change 'backing' link from 'hd1' to 'hd2'")
124
- # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it
59
+ "Cannot change frozen 'backing' link from 'hd1' to 'hd2'")
125
+ # node-name is optional in BlockdevOptions, but blockdev-reopen needs it
60
126
del opts['node-name']
61
# We can detach hd1 from hd0 because it doesn't affect the stream job
127
self.reopen(opts, {}, "node-name not specified")
62
opts['backing'] = None
128
63
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
129
diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248
64
130
index XXXXXXX..XXXXXXX 100755
65
# We can't remove hd2 while the commit job is ongoing
131
--- a/tests/qemu-iotests/248
66
opts['backing']['backing'] = None
132
+++ b/tests/qemu-iotests/248
67
- self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
133
@@ -XXX,XX +XXX,XX @@ vm.event_wait('JOB_STATUS_CHANGE', timeout=3.0,
68
+ self.reopen(opts, {}, "Cannot change frozen 'backing' link from 'hd1' to 'hd2'")
134
vm.get_qmp_events()
69
135
70
# We can't remove hd1 while the commit job is ongoing
136
del blockdev_opts['file']['size']
71
opts['backing'] = None
137
-vm.qmp_log('x-blockdev-reopen', filters=[filter_qmp_testfiles],
72
- self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'")
138
+vm.qmp_log('blockdev-reopen', filters=[filter_qmp_testfiles],
73
+ self.reopen(opts, {}, "Cannot change frozen 'backing' link from 'hd0' to 'hd1'")
139
options = [ blockdev_opts ])
74
140
75
event = self.vm.event_wait(name='BLOCK_JOB_READY')
141
vm.qmp_log('block-job-resume', device='drive0')
76
self.assert_qmp(event, 'data/device', 'commit0')
142
diff --git a/tests/qemu-iotests/248.out b/tests/qemu-iotests/248.out
143
index XXXXXXX..XXXXXXX 100644
144
--- a/tests/qemu-iotests/248.out
145
+++ b/tests/qemu-iotests/248.out
146
@@ -XXX,XX +XXX,XX @@
147
{"return": {}}
148
{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "on-target-error": "enospc", "sync": "full", "target": "target"}}
149
{"return": {}}
150
-{"execute": "x-blockdev-reopen", "arguments": {"options": [{"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}]}}
151
+{"execute": "blockdev-reopen", "arguments": {"options": [{"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}]}}
152
{"return": {}}
153
{"execute": "block-job-resume", "arguments": {"device": "drive0"}}
154
{"return": {}}
155
diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296
156
index XXXXXXX..XXXXXXX 100755
157
--- a/tests/qemu-iotests/296
158
+++ b/tests/qemu-iotests/296
159
@@ -XXX,XX +XXX,XX @@ class EncryptionSetupTestCase(iotests.QMPTestCase):
160
def openImageQmp(self, vm, id, file, secret,
161
readOnly = False, reOpen = False):
162
163
- command = 'x-blockdev-reopen' if reOpen else 'blockdev-add'
164
+ command = 'blockdev-reopen' if reOpen else 'blockdev-add'
165
166
opts = {
167
'driver': iotests.imgfmt,
168
diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298
169
index XXXXXXX..XXXXXXX 100755
170
--- a/tests/qemu-iotests/298
171
+++ b/tests/qemu-iotests/298
172
@@ -XXX,XX +XXX,XX @@ class TestPreallocateFilter(TestPreallocateBase):
173
self.check_big()
174
175
def test_reopen_opts(self):
176
- result = self.vm.qmp('x-blockdev-reopen', options=[{
177
+ result = self.vm.qmp('blockdev-reopen', options=[{
178
'node-name': 'disk',
179
'driver': iotests.imgfmt,
180
'file': {
181
diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qemu-iotests/tests/remove-bitmap-from-backing
182
index XXXXXXX..XXXXXXX 100755
183
--- a/tests/qemu-iotests/tests/remove-bitmap-from-backing
184
+++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing
185
@@ -XXX,XX +XXX,XX @@ new_base_opts = {
186
}
187
188
# Don't want to bother with filtering qmp_log for reopen command
189
-result = vm.qmp('x-blockdev-reopen', **new_base_opts)
190
+result = vm.qmp('blockdev-reopen', **new_base_opts)
191
if result != {'return': {}}:
192
log('Failed to reopen: ' + str(result))
193
194
@@ -XXX,XX +XXX,XX @@ log('Remove persistent bitmap from base node reopened to RW:')
195
vm.qmp_log('block-dirty-bitmap-remove', node='base', name='bitmap0')
196
197
new_base_opts['options'][0]['read-only'] = True
198
-result = vm.qmp('x-blockdev-reopen', **new_base_opts)
199
+result = vm.qmp('blockdev-reopen', **new_base_opts)
200
if result != {'return': {}}:
201
log('Failed to reopen: ' + str(result))
202
77
--
203
--
78
2.31.1
204
2.31.1
79
205
80
206
diff view generated by jsdifflib