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 |