1 | The following changes since commit 22dbfdecc3c52228d3489da3fe81da92b21197bf: | 1 | The following changes since commit 9db3065c62a983286d06c207f4981408cf42184d: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/awilliam/tags/vfio-update-20191010.0' into staging (2019-10-14 15:09:08 +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 a1406a9262a087d9ec9627b88da13c4590b61dae: | 9 | for you to fetch changes up to e60edf69e2f64e818466019313517a2e6d6b63f4: |
10 | 10 | ||
11 | iotests: Test large write request to qcow2 file (2019-10-14 17:12:48 +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 | - block: Fix crash with qcow2 partial cluster COW with small cluster | 16 | - Make blockdev-reopen stable |
17 | sizes (misaligned write requests with BDRV_REQ_NO_FALLBACK) | 17 | - Remove deprecated qemu-img backing file without format |
18 | - qcow2: Fix integer overflow potentially causing corruption with huge | 18 | - rbd: Convert to coroutines and add write zeroes support |
19 | requests | 19 | - rbd: Updated MAINTAINERS |
20 | - vhdx: Detect truncated image files | 20 | - export/fuse: Allow other users access to the export |
21 | - tools: Support help options for --object | 21 | - vhost-user: Fix backends without multiqueue support |
22 | - Various block-related replay improvements | 22 | - Fix drive-backup transaction endless drained section |
23 | - iotests/028: Fix for long $TEST_DIRs | ||
24 | 23 | ||
25 | ---------------------------------------------------------------- | 24 | ---------------------------------------------------------------- |
26 | Alberto Garcia (1): | 25 | Alberto Garcia (4): |
27 | block: Reject misaligned write requests with BDRV_REQ_NO_FALLBACK | 26 | block: Add bdrv_reopen_queue_free() |
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 | ||
28 | 30 | ||
29 | Kevin Wolf (4): | 31 | Eric Blake (3): |
30 | vl: Split off user_creatable_print_help() | 32 | qcow2: Prohibit backing file changes in 'qemu-img amend' |
31 | qemu-io: Support help options for --object | 33 | qemu-img: Require -F with -b backing image |
32 | qemu-img: Support help options for --object | 34 | qemu-img: Improve error for rebase without backing format |
33 | qemu-nbd: Support help options for --object | ||
34 | 35 | ||
35 | Max Reitz (3): | 36 | Heinrich Schuchardt (1): |
36 | iotests/028: Fix for long $TEST_DIRs | 37 | util/uri: do not check argument of uri_free() |
37 | qcow2: Limit total allocation range to INT_MAX | ||
38 | iotests: Test large write request to qcow2 file | ||
39 | 38 | ||
40 | Pavel Dovgaluk (6): | 39 | Ilya Dryomov (1): |
41 | block: implement bdrv_snapshot_goto for blkreplay | 40 | MAINTAINERS: update block/rbd.c maintainer |
42 | replay: disable default snapshot for record/replay | ||
43 | replay: update docs for record/replay with block devices | ||
44 | replay: don't drain/flush bdrv queue while RR is working | ||
45 | replay: finish record/replay before closing the disks | ||
46 | replay: add BH oneshot event for block layer | ||
47 | 41 | ||
48 | Peter Lieven (1): | 42 | Kevin Wolf (3): |
49 | block/vhdx: add check for truncated image files | 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() | ||
50 | 46 | ||
51 | docs/replay.txt | 12 +++- | 47 | Max Reitz (6): |
52 | include/qom/object_interfaces.h | 12 ++++ | 48 | export/fuse: Pass default_permissions for mount |
53 | include/sysemu/replay.h | 4 ++ | 49 | export/fuse: Add allow-other option |
54 | replay/replay-internal.h | 1 + | 50 | export/fuse: Give SET_ATTR_SIZE its own branch |
55 | block/blkreplay.c | 8 +++ | 51 | export/fuse: Let permissions be adjustable |
56 | block/block-backend.c | 9 ++- | 52 | iotests/308: Test +w on read-only FUSE exports |
57 | block/io.c | 39 ++++++++++++- | 53 | iotests/fuse-allow-other: Test allow-other |
58 | block/iscsi.c | 5 +- | ||
59 | block/nfs.c | 6 +- | ||
60 | block/null.c | 4 +- | ||
61 | block/nvme.c | 6 +- | ||
62 | block/qcow2-cluster.c | 5 +- | ||
63 | block/rbd.c | 5 +- | ||
64 | block/vhdx.c | 120 ++++++++++++++++++++++++++++++++++------ | ||
65 | block/vxhs.c | 5 +- | ||
66 | cpus.c | 2 - | ||
67 | qemu-img.c | 34 +++++++----- | ||
68 | qemu-io.c | 9 ++- | ||
69 | qemu-nbd.c | 9 ++- | ||
70 | qom/object_interfaces.c | 61 ++++++++++++++++++++ | ||
71 | replay/replay-events.c | 16 ++++++ | ||
72 | replay/replay.c | 2 + | ||
73 | stubs/replay-user.c | 9 +++ | ||
74 | vl.c | 63 ++++----------------- | ||
75 | stubs/Makefile.objs | 1 + | ||
76 | tests/qemu-iotests/028 | 11 +++- | ||
77 | tests/qemu-iotests/028.out | 1 - | ||
78 | tests/qemu-iotests/268 | 55 ++++++++++++++++++ | ||
79 | tests/qemu-iotests/268.out | 7 +++ | ||
80 | tests/qemu-iotests/270 | 83 +++++++++++++++++++++++++++ | ||
81 | tests/qemu-iotests/270.out | 9 +++ | ||
82 | tests/qemu-iotests/group | 2 + | ||
83 | 32 files changed, 504 insertions(+), 111 deletions(-) | ||
84 | create mode 100644 stubs/replay-user.c | ||
85 | create mode 100755 tests/qemu-iotests/268 | ||
86 | create mode 100644 tests/qemu-iotests/268.out | ||
87 | create mode 100755 tests/qemu-iotests/270 | ||
88 | create mode 100644 tests/qemu-iotests/270.out | ||
89 | 54 | ||
55 | Or Ozeri (1): | ||
56 | block/rbd: Add support for rbd image encryption | ||
57 | |||
58 | Peter Lieven (8): | ||
59 | block/rbd: bump librbd requirement to luminous release | ||
60 | block/rbd: store object_size in BDRVRBDState | ||
61 | block/rbd: update s->image_size in qemu_rbd_getlength | ||
62 | block/rbd: migrate from aio to coroutines | ||
63 | block/rbd: add write zeroes support | ||
64 | block/rbd: drop qemu_rbd_refresh_limits | ||
65 | block/rbd: fix type of task->complete | ||
66 | MAINTAINERS: add block/rbd.c reviewer | ||
67 | |||
68 | Vladimir Sementsov-Ogievskiy (1): | ||
69 | blockdev: fix drive-backup transaction endless drained section | ||
70 | |||
71 | qapi/block-core.json | 134 +++- | ||
72 | qapi/block-export.json | 33 +- | ||
73 | docs/system/deprecated.rst | 32 - | ||
74 | docs/system/removed-features.rst | 31 + | ||
75 | include/block/block.h | 3 + | ||
76 | block.c | 108 +-- | ||
77 | block/export/fuse.c | 121 +++- | ||
78 | block/nfs.c | 4 +- | ||
79 | block/qcow2.c | 42 +- | ||
80 | block/rbd.c | 749 +++++++++++++-------- | ||
81 | block/replication.c | 7 + | ||
82 | block/ssh.c | 4 +- | ||
83 | blockdev.c | 77 ++- | ||
84 | hw/virtio/vhost-user.c | 3 + | ||
85 | qemu-img.c | 9 +- | ||
86 | qemu-io-cmds.c | 7 +- | ||
87 | util/uri.c | 22 +- | ||
88 | MAINTAINERS | 3 +- | ||
89 | meson.build | 7 +- | ||
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 | ||
116 | |||
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: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> | 1 | From: Or Ozeri <oro@il.ibm.com> |
---|---|---|---|
2 | 2 | ||
3 | Replay is capable of recording normal BH events, but sometimes | 3 | Starting from ceph Pacific, RBD has built-in support for image-level encryption. |
4 | there are single use callbacks scheduled with aio_bh_schedule_oneshot | 4 | Currently supported formats are LUKS version 1 and 2. |
5 | function. This patch enables recording and replaying such callbacks. | 5 | |
6 | Block layer uses these events for calling the completion function. | 6 | There are 2 new relevant librbd APIs for controlling encryption, both expect an |
7 | Replaying these calls makes the execution deterministic. | 7 | open image context: |
8 | 8 | ||
9 | Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> | 9 | rbd_encryption_format: formats an image (i.e. writes the LUKS header) |
10 | Acked-by: Kevin Wolf <kwolf@redhat.com> | 10 | rbd_encryption_load: loads encryptor/decryptor to the image IO stack |
11 | |||
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> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 18 | --- |
13 | include/sysemu/replay.h | 4 ++++ | 19 | qapi/block-core.json | 110 ++++++++++++- |
14 | replay/replay-internal.h | 1 + | 20 | block/rbd.c | 361 ++++++++++++++++++++++++++++++++++++++++++- |
15 | block/block-backend.c | 9 ++++++--- | 21 | 2 files changed, 465 insertions(+), 6 deletions(-) |
16 | block/io.c | 4 ++-- | 22 | |
17 | block/iscsi.c | 5 +++-- | 23 | diff --git a/qapi/block-core.json b/qapi/block-core.json |
18 | block/nfs.c | 6 ++++-- | ||
19 | block/null.c | 4 +++- | ||
20 | block/nvme.c | 6 ++++-- | ||
21 | block/rbd.c | 5 +++-- | ||
22 | block/vxhs.c | 5 +++-- | ||
23 | replay/replay-events.c | 16 ++++++++++++++++ | ||
24 | stubs/replay-user.c | 9 +++++++++ | ||
25 | stubs/Makefile.objs | 1 + | ||
26 | 13 files changed, 59 insertions(+), 16 deletions(-) | ||
27 | create mode 100644 stubs/replay-user.c | ||
28 | |||
29 | diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h | ||
30 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/include/sysemu/replay.h | 25 | --- a/qapi/block-core.json |
32 | +++ b/include/sysemu/replay.h | 26 | +++ b/qapi/block-core.json |
33 | @@ -XXX,XX +XXX,XX @@ | 27 | @@ -XXX,XX +XXX,XX @@ |
34 | #include "qapi/qapi-types-misc.h" | 28 | 'extents': ['ImageInfo'] |
35 | #include "qapi/qapi-types-run-state.h" | 29 | } } |
36 | #include "qapi/qapi-types-ui.h" | 30 | |
37 | +#include "block/aio.h" | 31 | +## |
38 | 32 | +# @ImageInfoSpecificRbd: | |
39 | /* replay clock kinds */ | 33 | +# |
40 | enum ReplayClockKind { | 34 | +# @encryption-format: Image encryption format |
41 | @@ -XXX,XX +XXX,XX @@ void replay_enable_events(void); | 35 | +# |
42 | bool replay_events_enabled(void); | 36 | +# Since: 6.1 |
43 | /*! Adds bottom half event to the queue */ | 37 | +## |
44 | void replay_bh_schedule_event(QEMUBH *bh); | 38 | +{ 'struct': 'ImageInfoSpecificRbd', |
45 | +/* Adds oneshot bottom half event to the queue */ | 39 | + 'data': { |
46 | +void replay_bh_schedule_oneshot_event(AioContext *ctx, | 40 | + '*encryption-format': 'RbdImageEncryptionFormat' |
47 | + QEMUBHFunc *cb, void *opaque); | 41 | + } } |
48 | /*! Adds input event to the queue */ | 42 | + |
49 | void replay_input_event(QemuConsole *src, InputEvent *evt); | 43 | ## |
50 | /*! Adds input sync event to the queue */ | 44 | # @ImageInfoSpecific: |
51 | diff --git a/replay/replay-internal.h b/replay/replay-internal.h | 45 | # |
52 | index XXXXXXX..XXXXXXX 100644 | ||
53 | --- a/replay/replay-internal.h | ||
54 | +++ b/replay/replay-internal.h | ||
55 | @@ -XXX,XX +XXX,XX @@ enum ReplayEvents { | ||
56 | |||
57 | enum ReplayAsyncEventKind { | ||
58 | REPLAY_ASYNC_EVENT_BH, | ||
59 | + REPLAY_ASYNC_EVENT_BH_ONESHOT, | ||
60 | REPLAY_ASYNC_EVENT_INPUT, | ||
61 | REPLAY_ASYNC_EVENT_INPUT_SYNC, | ||
62 | REPLAY_ASYNC_EVENT_CHAR_READ, | ||
63 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
64 | index XXXXXXX..XXXXXXX 100644 | ||
65 | --- a/block/block-backend.c | ||
66 | +++ b/block/block-backend.c | ||
67 | @@ -XXX,XX +XXX,XX @@ | 46 | @@ -XXX,XX +XXX,XX @@ |
68 | #include "hw/qdev-core.h" | 47 | # If we need to add block driver specific parameters for |
69 | #include "sysemu/blockdev.h" | 48 | # LUKS in future, then we'll subclass QCryptoBlockInfoLUKS |
70 | #include "sysemu/runstate.h" | 49 | # to define a ImageInfoSpecificLUKS |
71 | +#include "sysemu/sysemu.h" | 50 | - 'luks': 'QCryptoBlockInfoLUKS' |
72 | +#include "sysemu/replay.h" | 51 | + 'luks': 'QCryptoBlockInfoLUKS', |
73 | #include "qapi/error.h" | 52 | + 'rbd': 'ImageInfoSpecificRbd' |
74 | #include "qapi/qapi-events-block.h" | 53 | } } |
75 | #include "qemu/id.h" | 54 | |
76 | @@ -XXX,XX +XXX,XX @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, | 55 | ## |
77 | acb->blk = blk; | ||
78 | acb->ret = ret; | ||
79 | |||
80 | - aio_bh_schedule_oneshot(blk_get_aio_context(blk), error_callback_bh, acb); | ||
81 | + replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), | ||
82 | + error_callback_bh, acb); | ||
83 | return &acb->common; | ||
84 | } | ||
85 | |||
86 | @@ -XXX,XX +XXX,XX @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes, | ||
87 | |||
88 | acb->has_returned = true; | ||
89 | if (acb->rwco.ret != NOT_DONE) { | ||
90 | - aio_bh_schedule_oneshot(blk_get_aio_context(blk), | ||
91 | - blk_aio_complete_bh, acb); | ||
92 | + replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), | ||
93 | + blk_aio_complete_bh, acb); | ||
94 | } | ||
95 | |||
96 | return &acb->common; | ||
97 | diff --git a/block/io.c b/block/io.c | ||
98 | index XXXXXXX..XXXXXXX 100644 | ||
99 | --- a/block/io.c | ||
100 | +++ b/block/io.c | ||
101 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
102 | if (bs) { | ||
103 | bdrv_inc_in_flight(bs); | ||
104 | } | ||
105 | - aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), | ||
106 | - bdrv_co_drain_bh_cb, &data); | ||
107 | + replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs), | ||
108 | + bdrv_co_drain_bh_cb, &data); | ||
109 | |||
110 | qemu_coroutine_yield(); | ||
111 | /* If we are resumed from some other event (such as an aio completion or a | ||
112 | diff --git a/block/iscsi.c b/block/iscsi.c | ||
113 | index XXXXXXX..XXXXXXX 100644 | ||
114 | --- a/block/iscsi.c | ||
115 | +++ b/block/iscsi.c | ||
116 | @@ -XXX,XX +XXX,XX @@ | 56 | @@ -XXX,XX +XXX,XX @@ |
117 | #include "qemu/module.h" | 57 | { 'enum': 'RbdAuthMode', |
118 | #include "qemu/option.h" | 58 | 'data': [ 'cephx', 'none' ] } |
119 | #include "qemu/uuid.h" | 59 | |
120 | +#include "sysemu/replay.h" | 60 | +## |
121 | #include "qapi/error.h" | 61 | +# @RbdImageEncryptionFormat: |
122 | #include "qapi/qapi-commands-misc.h" | 62 | +# |
123 | #include "qapi/qmp/qdict.h" | 63 | +# Since: 6.1 |
124 | @@ -XXX,XX +XXX,XX @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, | 64 | +## |
125 | } | 65 | +{ 'enum': 'RbdImageEncryptionFormat', |
126 | 66 | + 'data': [ 'luks', 'luks2' ] } | |
127 | if (iTask->co) { | 67 | + |
128 | - aio_bh_schedule_oneshot(iTask->iscsilun->aio_context, | 68 | +## |
129 | - iscsi_co_generic_bh_cb, iTask); | 69 | +# @RbdEncryptionOptionsLUKSBase: |
130 | + replay_bh_schedule_oneshot_event(iTask->iscsilun->aio_context, | 70 | +# |
131 | + iscsi_co_generic_bh_cb, iTask); | 71 | +# @key-secret: ID of a QCryptoSecret object providing a passphrase |
132 | } else { | 72 | +# for unlocking the encryption |
133 | iTask->complete = 1; | 73 | +# |
134 | } | 74 | +# Since: 6.1 |
135 | diff --git a/block/nfs.c b/block/nfs.c | 75 | +## |
136 | index XXXXXXX..XXXXXXX 100644 | 76 | +{ 'struct': 'RbdEncryptionOptionsLUKSBase', |
137 | --- a/block/nfs.c | 77 | + 'data': { 'key-secret': 'str' } } |
138 | +++ b/block/nfs.c | 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 | # | ||
139 | @@ -XXX,XX +XXX,XX @@ | 151 | @@ -XXX,XX +XXX,XX @@ |
140 | #include "qemu/option.h" | 152 | # |
141 | #include "qemu/uri.h" | 153 | # @snapshot: Ceph snapshot name. |
142 | #include "qemu/cutils.h" | 154 | # |
143 | +#include "sysemu/sysemu.h" | 155 | +# @encrypt: Image encryption options. (Since 6.1) |
144 | +#include "sysemu/replay.h" | 156 | +# |
145 | #include "qapi/qapi-visit-block-core.h" | 157 | # @user: Ceph id name. |
146 | #include "qapi/qmp/qdict.h" | 158 | # |
147 | #include "qapi/qmp/qstring.h" | 159 | # @auth-client-required: Acceptable authentication modes. |
148 | @@ -XXX,XX +XXX,XX @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, | ||
149 | if (task->ret < 0) { | ||
150 | error_report("NFS Error: %s", nfs_get_error(nfs)); | ||
151 | } | ||
152 | - aio_bh_schedule_oneshot(task->client->aio_context, | ||
153 | - nfs_co_generic_bh_cb, task); | ||
154 | + replay_bh_schedule_oneshot_event(task->client->aio_context, | ||
155 | + nfs_co_generic_bh_cb, task); | ||
156 | } | ||
157 | |||
158 | static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset, | ||
159 | diff --git a/block/null.c b/block/null.c | ||
160 | index XXXXXXX..XXXXXXX 100644 | ||
161 | --- a/block/null.c | ||
162 | +++ b/block/null.c | ||
163 | @@ -XXX,XX +XXX,XX @@ | 160 | @@ -XXX,XX +XXX,XX @@ |
164 | #include "qemu/module.h" | 161 | 'image': 'str', |
165 | #include "qemu/option.h" | 162 | '*conf': 'str', |
166 | #include "block/block_int.h" | 163 | '*snapshot': 'str', |
167 | +#include "sysemu/replay.h" | 164 | + '*encrypt': 'RbdEncryptionOptions', |
168 | 165 | '*user': 'str', | |
169 | #define NULL_OPT_LATENCY "latency-ns" | 166 | '*auth-client-required': ['RbdAuthMode'], |
170 | #define NULL_OPT_ZEROES "read-zeroes" | 167 | '*key-secret': 'str', |
171 | @@ -XXX,XX +XXX,XX @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, | ||
172 | timer_mod_ns(&acb->timer, | ||
173 | qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns); | ||
174 | } else { | ||
175 | - aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), null_bh_cb, acb); | ||
176 | + replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs), | ||
177 | + null_bh_cb, acb); | ||
178 | } | ||
179 | return &acb->common; | ||
180 | } | ||
181 | diff --git a/block/nvme.c b/block/nvme.c | ||
182 | index XXXXXXX..XXXXXXX 100644 | ||
183 | --- a/block/nvme.c | ||
184 | +++ b/block/nvme.c | ||
185 | @@ -XXX,XX +XXX,XX @@ | 168 | @@ -XXX,XX +XXX,XX @@ |
186 | #include "qemu/option.h" | 169 | # point to a snapshot. |
187 | #include "qemu/vfio-helpers.h" | 170 | # @size: Size of the virtual disk in bytes |
188 | #include "block/block_int.h" | 171 | # @cluster-size: RBD object size |
189 | +#include "sysemu/replay.h" | 172 | +# @encrypt: Image encryption options. (Since 6.1) |
190 | #include "trace.h" | 173 | # |
191 | 174 | # Since: 2.12 | |
192 | #include "block/nvme.h" | 175 | ## |
193 | @@ -XXX,XX +XXX,XX @@ static bool nvme_process_completion(BDRVNVMeState *s, NVMeQueuePair *q) | 176 | { 'struct': 'BlockdevCreateOptionsRbd', |
194 | smp_mb_release(); | 177 | 'data': { 'location': 'BlockdevOptionsRbd', |
195 | *q->cq.doorbell = cpu_to_le32(q->cq.head); | 178 | 'size': 'size', |
196 | if (!qemu_co_queue_empty(&q->free_req_queue)) { | 179 | - '*cluster-size' : 'size' } } |
197 | - aio_bh_schedule_oneshot(s->aio_context, nvme_free_req_queue_cb, q); | 180 | + '*cluster-size' : 'size', |
198 | + replay_bh_schedule_oneshot_event(s->aio_context, | 181 | + '*encrypt' : 'RbdEncryptionCreateOptions' } } |
199 | + nvme_free_req_queue_cb, q); | 182 | |
200 | } | 183 | ## |
201 | } | 184 | # @BlockdevVmdkSubformat: |
202 | q->busy = false; | ||
203 | @@ -XXX,XX +XXX,XX @@ static void nvme_rw_cb(void *opaque, int ret) | ||
204 | /* The rw coroutine hasn't yielded, don't try to enter. */ | ||
205 | return; | ||
206 | } | ||
207 | - aio_bh_schedule_oneshot(data->ctx, nvme_rw_cb_bh, data); | ||
208 | + replay_bh_schedule_oneshot_event(data->ctx, nvme_rw_cb_bh, data); | ||
209 | } | ||
210 | |||
211 | static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs, | ||
212 | diff --git a/block/rbd.c b/block/rbd.c | 185 | diff --git a/block/rbd.c b/block/rbd.c |
213 | index XXXXXXX..XXXXXXX 100644 | 186 | index XXXXXXX..XXXXXXX 100644 |
214 | --- a/block/rbd.c | 187 | --- a/block/rbd.c |
215 | +++ b/block/rbd.c | 188 | +++ b/block/rbd.c |
216 | @@ -XXX,XX +XXX,XX @@ | 189 | @@ -XXX,XX +XXX,XX @@ |
217 | #include "block/qdict.h" | 190 | #define LIBRBD_USE_IOVEC 0 |
218 | #include "crypto/secret.h" | 191 | #endif |
219 | #include "qemu/cutils.h" | 192 | |
220 | +#include "sysemu/replay.h" | 193 | +#define RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN 8 |
221 | #include "qapi/qmp/qstring.h" | 194 | + |
222 | #include "qapi/qmp/qdict.h" | 195 | +static const char rbd_luks_header_verification[ |
223 | #include "qapi/qmp/qjson.h" | 196 | + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = { |
224 | @@ -XXX,XX +XXX,XX @@ static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb) | 197 | + 'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 1 |
225 | rcb->ret = rbd_aio_get_return_value(c); | 198 | +}; |
226 | rbd_aio_release(c); | 199 | + |
227 | 200 | +static const char rbd_luks2_header_verification[ | |
228 | - aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs), | 201 | + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = { |
229 | - rbd_finish_bh, rcb); | 202 | + 'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 2 |
230 | + replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs), | 203 | +}; |
231 | + rbd_finish_bh, rcb); | 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 | } | ||
232 | } | 210 | } |
233 | 211 | ||
234 | static int rbd_aio_discard_wrapper(rbd_image_t image, | 212 | +#ifdef LIBRBD_SUPPORTS_ENCRYPTION |
235 | diff --git a/block/vxhs.c b/block/vxhs.c | 213 | +static int qemu_rbd_convert_luks_options( |
236 | index XXXXXXX..XXXXXXX 100644 | 214 | + RbdEncryptionOptionsLUKSBase *luks_opts, |
237 | --- a/block/vxhs.c | 215 | + char **passphrase, |
238 | +++ b/block/vxhs.c | 216 | + size_t *passphrase_len, |
239 | @@ -XXX,XX +XXX,XX @@ | 217 | + Error **errp) |
240 | #include "qapi/error.h" | 218 | +{ |
241 | #include "qemu/uuid.h" | 219 | + return qcrypto_secret_lookup(luks_opts->key_secret, (uint8_t **)passphrase, |
242 | #include "crypto/tlscredsx509.h" | 220 | + passphrase_len, errp); |
243 | +#include "sysemu/replay.h" | 221 | +} |
244 | 222 | + | |
245 | #define VXHS_OPT_FILENAME "filename" | 223 | +static int qemu_rbd_convert_luks_create_options( |
246 | #define VXHS_OPT_VDISK_ID "vdisk-id" | 224 | + RbdEncryptionCreateOptionsLUKSBase *luks_opts, |
247 | @@ -XXX,XX +XXX,XX @@ static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error) | 225 | + rbd_encryption_algorithm_t *alg, |
248 | trace_vxhs_iio_callback(error); | 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; | ||
249 | } | 555 | } |
250 | 556 | } | |
251 | - aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs), | 557 | |
252 | - vxhs_complete_aio_bh, acb); | 558 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, |
253 | + replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs), | 559 | r = 0; |
254 | + vxhs_complete_aio_bh, acb); | 560 | goto out; |
255 | break; | 561 | |
256 | 562 | +failed_post_open: | |
257 | default: | 563 | + rbd_close(s->image); |
258 | diff --git a/replay/replay-events.c b/replay/replay-events.c | 564 | failed_open: |
259 | index XXXXXXX..XXXXXXX 100644 | 565 | rados_ioctx_destroy(s->io_ctx); |
260 | --- a/replay/replay-events.c | 566 | g_free(s->snap); |
261 | +++ b/replay/replay-events.c | 567 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) |
262 | @@ -XXX,XX +XXX,XX @@ static void replay_run_event(Event *event) | 568 | return 0; |
263 | case REPLAY_ASYNC_EVENT_BH: | ||
264 | aio_bh_call(event->opaque); | ||
265 | break; | ||
266 | + case REPLAY_ASYNC_EVENT_BH_ONESHOT: | ||
267 | + ((QEMUBHFunc *)event->opaque)(event->opaque2); | ||
268 | + break; | ||
269 | case REPLAY_ASYNC_EVENT_INPUT: | ||
270 | qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque); | ||
271 | qapi_free_InputEvent((InputEvent *)event->opaque); | ||
272 | @@ -XXX,XX +XXX,XX @@ void replay_bh_schedule_event(QEMUBH *bh) | ||
273 | } | ||
274 | } | 569 | } |
275 | 570 | ||
276 | +void replay_bh_schedule_oneshot_event(AioContext *ctx, | 571 | +static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, |
277 | + QEMUBHFunc *cb, void *opaque) | 572 | + Error **errp) |
278 | +{ | 573 | +{ |
279 | + if (events_enabled) { | 574 | + BDRVRBDState *s = bs->opaque; |
280 | + uint64_t id = replay_get_current_icount(); | 575 | + ImageInfoSpecific *spec_info; |
281 | + replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id); | 576 | + char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0}; |
577 | + int r; | ||
578 | + | ||
579 | + if (s->image_size >= RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) { | ||
580 | + r = rbd_read(s->image, 0, | ||
581 | + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf); | ||
582 | + if (r < 0) { | ||
583 | + error_setg_errno(errp, -r, "cannot read image start for probe"); | ||
584 | + return NULL; | ||
585 | + } | ||
586 | + } | ||
587 | + | ||
588 | + spec_info = g_new(ImageInfoSpecific, 1); | ||
589 | + *spec_info = (ImageInfoSpecific){ | ||
590 | + .type = IMAGE_INFO_SPECIFIC_KIND_RBD, | ||
591 | + .u.rbd.data = g_new0(ImageInfoSpecificRbd, 1), | ||
592 | + }; | ||
593 | + | ||
594 | + if (memcmp(buf, rbd_luks_header_verification, | ||
595 | + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { | ||
596 | + spec_info->u.rbd.data->encryption_format = | ||
597 | + RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; | ||
598 | + spec_info->u.rbd.data->has_encryption_format = true; | ||
599 | + } else if (memcmp(buf, rbd_luks2_header_verification, | ||
600 | + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { | ||
601 | + spec_info->u.rbd.data->encryption_format = | ||
602 | + RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; | ||
603 | + spec_info->u.rbd.data->has_encryption_format = true; | ||
282 | + } else { | 604 | + } else { |
283 | + aio_bh_schedule_oneshot(ctx, cb, opaque); | 605 | + spec_info->u.rbd.data->has_encryption_format = false; |
284 | + } | 606 | + } |
607 | + | ||
608 | + return spec_info; | ||
285 | +} | 609 | +} |
286 | + | 610 | + |
287 | void replay_add_input_event(struct InputEvent *event) | 611 | static int64_t qemu_rbd_getlength(BlockDriverState *bs) |
288 | { | 612 | { |
289 | replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0); | 613 | BDRVRBDState *s = bs->opaque; |
290 | @@ -XXX,XX +XXX,XX @@ static void replay_save_event(Event *event, int checkpoint) | 614 | @@ -XXX,XX +XXX,XX @@ static QemuOptsList qemu_rbd_create_opts = { |
291 | /* save event-specific data */ | 615 | .type = QEMU_OPT_STRING, |
292 | switch (event->event_kind) { | 616 | .help = "ID of secret providing the password", |
293 | case REPLAY_ASYNC_EVENT_BH: | 617 | }, |
294 | + case REPLAY_ASYNC_EVENT_BH_ONESHOT: | 618 | + { |
295 | replay_put_qword(event->id); | 619 | + .name = "encrypt.format", |
296 | break; | 620 | + .type = QEMU_OPT_STRING, |
297 | case REPLAY_ASYNC_EVENT_INPUT: | 621 | + .help = "Encrypt the image, format choices: 'luks', 'luks2'", |
298 | @@ -XXX,XX +XXX,XX @@ static Event *replay_read_event(int checkpoint) | 622 | + }, |
299 | /* Events that has not to be in the queue */ | 623 | + { |
300 | switch (replay_state.read_event_kind) { | 624 | + .name = "encrypt.cipher-alg", |
301 | case REPLAY_ASYNC_EVENT_BH: | 625 | + .type = QEMU_OPT_STRING, |
302 | + case REPLAY_ASYNC_EVENT_BH_ONESHOT: | 626 | + .help = "Name of encryption cipher algorithm" |
303 | if (replay_state.read_event_id == -1) { | 627 | + " (allowed values: aes-128, aes-256)", |
304 | replay_state.read_event_id = replay_get_qword(); | 628 | + }, |
305 | } | 629 | + { |
306 | diff --git a/stubs/replay-user.c b/stubs/replay-user.c | 630 | + .name = "encrypt.key-secret", |
307 | new file mode 100644 | 631 | + .type = QEMU_OPT_STRING, |
308 | index XXXXXXX..XXXXXXX | 632 | + .help = "ID of secret providing LUKS passphrase", |
309 | --- /dev/null | 633 | + }, |
310 | +++ b/stubs/replay-user.c | 634 | { /* end of list */ } |
311 | @@ -XXX,XX +XXX,XX @@ | 635 | } |
312 | +#include "qemu/osdep.h" | 636 | }; |
313 | +#include "sysemu/replay.h" | 637 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = { |
314 | +#include "sysemu/sysemu.h" | 638 | .bdrv_co_create_opts = qemu_rbd_co_create_opts, |
315 | + | 639 | .bdrv_has_zero_init = bdrv_has_zero_init_1, |
316 | +void replay_bh_schedule_oneshot_event(AioContext *ctx, | 640 | .bdrv_get_info = qemu_rbd_getinfo, |
317 | + QEMUBHFunc *cb, void *opaque) | 641 | + .bdrv_get_specific_info = qemu_rbd_get_specific_info, |
318 | +{ | 642 | .create_opts = &qemu_rbd_create_opts, |
319 | + aio_bh_schedule_oneshot(ctx, cb, opaque); | 643 | .bdrv_getlength = qemu_rbd_getlength, |
320 | +} | 644 | .bdrv_co_truncate = qemu_rbd_co_truncate, |
321 | diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs | ||
322 | index XXXXXXX..XXXXXXX 100644 | ||
323 | --- a/stubs/Makefile.objs | ||
324 | +++ b/stubs/Makefile.objs | ||
325 | @@ -XXX,XX +XXX,XX @@ stub-obj-y += monitor.o | ||
326 | stub-obj-y += notify-event.o | ||
327 | stub-obj-y += qtest.o | ||
328 | stub-obj-y += replay.o | ||
329 | +stub-obj-y += replay-user.o | ||
330 | stub-obj-y += runstate-check.o | ||
331 | stub-obj-y += set-fd-handler.o | ||
332 | stub-obj-y += sysbus.o | ||
333 | -- | 645 | -- |
334 | 2.20.1 | 646 | 2.31.1 |
335 | 647 | ||
336 | 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 |
New patch | |||
---|---|---|---|
1 | From: Peter Lieven <pl@kamp.de> | ||
1 | 2 | ||
3 | While at it just call rbd_get_size and avoid rbd_image_info_t. | ||
4 | |||
5 | Signed-off-by: Peter Lieven <pl@kamp.de> | ||
6 | Reviewed-by: Ilya Dryomov <idryomov@gmail.com> | ||
7 | Message-Id: <20210702172356.11574-4-idryomov@gmail.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | block/rbd.c | 5 ++--- | ||
11 | 1 file changed, 2 insertions(+), 3 deletions(-) | ||
12 | |||
13 | diff --git a/block/rbd.c b/block/rbd.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block/rbd.c | ||
16 | +++ b/block/rbd.c | ||
17 | @@ -XXX,XX +XXX,XX @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, | ||
18 | static int64_t qemu_rbd_getlength(BlockDriverState *bs) | ||
19 | { | ||
20 | BDRVRBDState *s = bs->opaque; | ||
21 | - rbd_image_info_t info; | ||
22 | int r; | ||
23 | |||
24 | - r = rbd_stat(s->image, &info, sizeof(info)); | ||
25 | + r = rbd_get_size(s->image, &s->image_size); | ||
26 | if (r < 0) { | ||
27 | return r; | ||
28 | } | ||
29 | |||
30 | - return info.size; | ||
31 | + return s->image_size; | ||
32 | } | ||
33 | |||
34 | static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs, | ||
35 | -- | ||
36 | 2.31.1 | ||
37 | |||
38 | diff view generated by jsdifflib |
1 | From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> | 1 | From: Peter Lieven <pl@kamp.de> |
---|---|---|---|
2 | 2 | ||
3 | This patch enables making snapshots with blkreplay used in | 3 | Signed-off-by: Peter Lieven <pl@kamp.de> |
4 | block devices. | 4 | Reviewed-by: Ilya Dryomov <idryomov@gmail.com> |
5 | This function is required to make bdrv_snapshot_goto without | 5 | Message-Id: <20210702172356.11574-5-idryomov@gmail.com> |
6 | calling .bdrv_open which is not implemented. | ||
7 | |||
8 | Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> | ||
9 | Acked-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | --- | 7 | --- |
12 | block/blkreplay.c | 8 ++++++++ | 8 | block/rbd.c | 252 +++++++++++++++++++--------------------------------- |
13 | 1 file changed, 8 insertions(+) | 9 | 1 file changed, 90 insertions(+), 162 deletions(-) |
14 | 10 | ||
15 | diff --git a/block/blkreplay.c b/block/blkreplay.c | 11 | diff --git a/block/rbd.c b/block/rbd.c |
16 | index XXXXXXX..XXXXXXX 100644 | 12 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/block/blkreplay.c | 13 | --- a/block/rbd.c |
18 | +++ b/block/blkreplay.c | 14 | +++ b/block/rbd.c |
19 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs) | 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, | ||
20 | return ret; | 53 | return ret; |
21 | } | 54 | } |
22 | 55 | ||
23 | +static int blkreplay_snapshot_goto(BlockDriverState *bs, | 56 | -static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs) |
24 | + const char *snapshot_id) | 57 | -{ |
25 | +{ | 58 | - RBDAIOCB *acb = rcb->acb; |
26 | + return bdrv_snapshot_goto(bs->file->bs, snapshot_id, NULL); | 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; | ||
201 | } | ||
202 | |||
203 | switch (cmd) { | ||
204 | - case RBD_AIO_WRITE: | ||
205 | - /* | ||
206 | - * RBD APIs don't allow us to write more than actual size, so in order | ||
207 | - * to support growing images, we resize the image before write | ||
208 | - * operations that exceed the current size. | ||
209 | - */ | ||
210 | - if (off + size > s->image_size) { | ||
211 | - r = qemu_rbd_resize(bs, off + size); | ||
212 | - if (r < 0) { | ||
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; | ||
259 | + } | ||
260 | + | ||
261 | + /* zero pad short reads */ | ||
262 | + if (cmd == RBD_AIO_READ && task.ret < qiov->size) { | ||
263 | + qemu_iovec_memset(qiov, task.ret, 0, qiov->size - task.ret); | ||
264 | + } | ||
265 | + | ||
266 | + return 0; | ||
27 | +} | 267 | +} |
28 | + | 268 | + |
29 | static BlockDriver bdrv_blkreplay = { | 269 | +static int |
30 | .format_name = "blkreplay", | 270 | +coroutine_fn qemu_rbd_co_preadv(BlockDriverState *bs, uint64_t offset, |
31 | .instance_size = 0, | 271 | + uint64_t bytes, QEMUIOVector *qiov, |
32 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkreplay = { | 272 | + int flags) |
33 | .bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes, | 273 | +{ |
34 | .bdrv_co_pdiscard = blkreplay_co_pdiscard, | 274 | + return qemu_rbd_start_co(bs, offset, bytes, qiov, flags, RBD_AIO_READ); |
35 | .bdrv_co_flush = blkreplay_co_flush, | 275 | } |
36 | + | 276 | |
37 | + .bdrv_snapshot_goto = blkreplay_snapshot_goto, | 277 | -static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs, |
38 | }; | 278 | - uint64_t offset, uint64_t bytes, |
39 | 279 | - QEMUIOVector *qiov, int flags, | |
40 | static void bdrv_blkreplay_init(void) | 280 | - BlockCompletionFunc *cb, |
281 | - void *opaque) | ||
282 | +static int | ||
283 | +coroutine_fn qemu_rbd_co_pwritev(BlockDriverState *bs, uint64_t offset, | ||
284 | + uint64_t bytes, QEMUIOVector *qiov, | ||
285 | + int flags) | ||
286 | { | ||
287 | - return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, | ||
288 | - RBD_AIO_READ); | ||
289 | + BDRVRBDState *s = bs->opaque; | ||
290 | + /* | ||
291 | + * RBD APIs don't allow us to write more than actual size, so in order | ||
292 | + * to support growing images, we resize the image before write | ||
293 | + * operations that exceed the current size. | ||
294 | + */ | ||
295 | + if (offset + bytes > s->image_size) { | ||
296 | + int r = qemu_rbd_resize(bs, offset + bytes); | ||
297 | + if (r < 0) { | ||
298 | + return r; | ||
299 | + } | ||
300 | + } | ||
301 | + return qemu_rbd_start_co(bs, offset, bytes, qiov, flags, RBD_AIO_WRITE); | ||
302 | } | ||
303 | |||
304 | -static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs, | ||
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, | ||
41 | -- | 360 | -- |
42 | 2.20.1 | 361 | 2.31.1 |
43 | 362 | ||
44 | 363 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Peter Lieven <pl@kamp.de> | ||
1 | 2 | ||
3 | This patch wittingly sets BDRV_REQ_NO_FALLBACK and silently ignores | ||
4 | BDRV_REQ_MAY_UNMAP for older librbd versions. | ||
5 | |||
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> | ||
68 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
69 | --- | ||
70 | block/rbd.c | 32 +++++++++++++++++++++++++++++++- | ||
71 | 1 file changed, 31 insertions(+), 1 deletion(-) | ||
72 | |||
73 | diff --git a/block/rbd.c b/block/rbd.c | ||
74 | index XXXXXXX..XXXXXXX 100644 | ||
75 | --- a/block/rbd.c | ||
76 | +++ b/block/rbd.c | ||
77 | @@ -XXX,XX +XXX,XX @@ typedef enum { | ||
78 | RBD_AIO_READ, | ||
79 | RBD_AIO_WRITE, | ||
80 | RBD_AIO_DISCARD, | ||
81 | - RBD_AIO_FLUSH | ||
82 | + RBD_AIO_FLUSH, | ||
83 | + RBD_AIO_WRITE_ZEROES | ||
84 | } RBDAIOCmd; | ||
85 | |||
86 | typedef struct BDRVRBDState { | ||
87 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||
88 | } | ||
89 | } | ||
90 | |||
91 | +#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES | ||
92 | + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; | ||
93 | +#endif | ||
94 | + | ||
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; | ||
116 | } | ||
117 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_pdiscard(BlockDriverState *bs, | ||
118 | return qemu_rbd_start_co(bs, offset, count, NULL, 0, RBD_AIO_DISCARD); | ||
119 | } | ||
120 | |||
121 | +#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES | ||
122 | +static int | ||
123 | +coroutine_fn qemu_rbd_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, | ||
124 | + int count, BdrvRequestFlags flags) | ||
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) | ||
132 | { | ||
133 | BDRVRBDState *s = bs->opaque; | ||
134 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = { | ||
135 | .bdrv_co_pwritev = qemu_rbd_co_pwritev, | ||
136 | .bdrv_co_flush_to_disk = qemu_rbd_co_flush, | ||
137 | .bdrv_co_pdiscard = qemu_rbd_co_pdiscard, | ||
138 | +#ifdef LIBRBD_SUPPORTS_WRITE_ZEROES | ||
139 | + .bdrv_co_pwrite_zeroes = qemu_rbd_co_pwrite_zeroes, | ||
140 | +#endif | ||
141 | |||
142 | .bdrv_snapshot_create = qemu_rbd_snap_create, | ||
143 | .bdrv_snapshot_delete = qemu_rbd_snap_remove, | ||
144 | -- | ||
145 | 2.31.1 | ||
146 | |||
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 |
New patch | |||
---|---|---|---|
1 | From: Heinrich Schuchardt <xypron.glpk@gmx.de> | ||
1 | 2 | ||
3 | uri_free() checks if its argument is NULL in uri_clean() and g_free(). | ||
4 | There is no need to check the argument before the call. | ||
5 | |||
6 | Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de> | ||
7 | Message-Id: <20210629063602.4239-1-xypron.glpk@gmx.de> | ||
8 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
9 | Reviewed-by: Richard W.M. Jones <rjones@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | block/nfs.c | 4 +--- | ||
13 | block/ssh.c | 4 +--- | ||
14 | util/uri.c | 22 ++++++---------------- | ||
15 | 3 files changed, 8 insertions(+), 22 deletions(-) | ||
16 | |||
17 | diff --git a/block/nfs.c b/block/nfs.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/block/nfs.c | ||
20 | +++ b/block/nfs.c | ||
21 | @@ -XXX,XX +XXX,XX @@ out: | ||
22 | if (qp) { | ||
23 | query_params_free(qp); | ||
24 | } | ||
25 | - if (uri) { | ||
26 | - uri_free(uri); | ||
27 | - } | ||
28 | + uri_free(uri); | ||
29 | return ret; | ||
30 | } | ||
31 | |||
32 | diff --git a/block/ssh.c b/block/ssh.c | ||
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/block/ssh.c | ||
35 | +++ b/block/ssh.c | ||
36 | @@ -XXX,XX +XXX,XX @@ static int parse_uri(const char *filename, QDict *options, Error **errp) | ||
37 | return 0; | ||
38 | |||
39 | err: | ||
40 | - if (uri) { | ||
41 | - uri_free(uri); | ||
42 | - } | ||
43 | + uri_free(uri); | ||
44 | return -EINVAL; | ||
45 | } | ||
46 | |||
47 | diff --git a/util/uri.c b/util/uri.c | ||
48 | index XXXXXXX..XXXXXXX 100644 | ||
49 | --- a/util/uri.c | ||
50 | +++ b/util/uri.c | ||
51 | @@ -XXX,XX +XXX,XX @@ static void uri_clean(URI *uri) | ||
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; | ||
77 | } | ||
78 | |||
79 | @@ -XXX,XX +XXX,XX @@ done: | ||
80 | if (remove_path != 0) { | ||
81 | ref->path = NULL; | ||
82 | } | ||
83 | - if (ref != NULL) { | ||
84 | - uri_free(ref); | ||
85 | - } | ||
86 | - if (bas != NULL) { | ||
87 | - uri_free(bas); | ||
88 | - } | ||
89 | + uri_free(ref); | ||
90 | + uri_free(bas); | ||
91 | |||
92 | return val; | ||
93 | } | ||
94 | -- | ||
95 | 2.31.1 | ||
96 | |||
97 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Max Reitz <mreitz@redhat.com> | ||
1 | 2 | ||
3 | We do not do any permission checks in fuse_open(), so let the kernel do | ||
4 | them. We already let fuse_getattr() report the proper UNIX permissions, | ||
5 | so this should work the way we want. | ||
6 | |||
7 | This causes a change in 308's reference output, because now opening a | ||
8 | non-writable export with O_RDWR fails already, instead of only actually | ||
9 | attempting to write to it. (That is an improvement.) | ||
10 | |||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | Message-Id: <20210625142317.271673-2-mreitz@redhat.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | --- | ||
15 | block/export/fuse.c | 8 ++++++-- | ||
16 | tests/qemu-iotests/308 | 3 ++- | ||
17 | tests/qemu-iotests/308.out | 2 +- | ||
18 | 3 files changed, 9 insertions(+), 4 deletions(-) | ||
19 | |||
20 | diff --git a/block/export/fuse.c b/block/export/fuse.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/block/export/fuse.c | ||
23 | +++ b/block/export/fuse.c | ||
24 | @@ -XXX,XX +XXX,XX @@ static int setup_fuse_export(FuseExport *exp, const char *mountpoint, | ||
25 | struct fuse_args fuse_args; | ||
26 | int ret; | ||
27 | |||
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 | ||
66 | -- | ||
67 | 2.31.1 | ||
68 | |||
69 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | From: Max Reitz <mreitz@redhat.com> | |
2 | |||
3 | Without the allow_other mount option, no user (not even root) but the | ||
4 | one who started qemu/the storage daemon can access the export. Allow | ||
5 | users to configure the export such that such accesses are possible. | ||
6 | |||
7 | While allow_other is probably what users want, we cannot make it an | ||
8 | unconditional default, because passing it is only possible (for non-root | ||
9 | users) if the global fuse.conf configuration file allows it. Thus, the | ||
10 | default is an 'auto' mode, in which we first try with allow_other, and | ||
11 | then fall back to without. | ||
12 | |||
13 | FuseExport.allow_other reports whether allow_other was actually used as | ||
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> | ||
38 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
39 | --- | ||
40 | qapi/block-export.json | 33 ++++++++++++++++++++++++++++++++- | ||
41 | block/export/fuse.c | 28 +++++++++++++++++++++++----- | ||
42 | tests/qemu-iotests/308 | 6 +++++- | ||
43 | tests/qemu-iotests/common.rc | 6 +++++- | ||
44 | 4 files changed, 65 insertions(+), 8 deletions(-) | ||
45 | |||
46 | diff --git a/qapi/block-export.json b/qapi/block-export.json | ||
47 | index XXXXXXX..XXXXXXX 100644 | ||
48 | --- a/qapi/block-export.json | ||
49 | +++ b/qapi/block-export.json | ||
50 | @@ -XXX,XX +XXX,XX @@ | ||
51 | '*logical-block-size': 'size', | ||
52 | '*num-queues': 'uint16'} } | ||
53 | |||
54 | +## | ||
55 | +# @FuseExportAllowOther: | ||
56 | +# | ||
57 | +# Possible allow_other modes for FUSE exports. | ||
58 | +# | ||
59 | +# @off: Do not pass allow_other as a mount option. | ||
60 | +# | ||
61 | +# @on: Pass allow_other as a mount option. | ||
62 | +# | ||
63 | +# @auto: Try mounting with allow_other first, and if that fails, retry | ||
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 | ||
102 | index XXXXXXX..XXXXXXX 100644 | ||
103 | --- a/block/export/fuse.c | ||
104 | +++ b/block/export/fuse.c | ||
105 | @@ -XXX,XX +XXX,XX @@ typedef struct FuseExport { | ||
106 | char *mountpoint; | ||
107 | bool writable; | ||
108 | bool growable; | ||
109 | + /* Whether allow_other was used as a mount option or not */ | ||
110 | + bool allow_other; | ||
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; | ||
131 | + } | ||
132 | + | ||
133 | + if (args->allow_other == FUSE_EXPORT_ALLOW_OTHER_AUTO) { | ||
134 | + /* Ignore errors on our first attempt */ | ||
135 | + ret = setup_fuse_export(exp, args->mountpoint, true, NULL); | ||
136 | + exp->allow_other = ret == 0; | ||
137 | + if (ret < 0) { | ||
138 | + ret = setup_fuse_export(exp, args->mountpoint, false, errp); | ||
139 | + } | ||
140 | + } else { | ||
141 | + exp->allow_other = args->allow_other == FUSE_EXPORT_ALLOW_OTHER_ON; | ||
142 | + ret = setup_fuse_export(exp, args->mountpoint, exp->allow_other, errp); | ||
143 | + } | ||
144 | if (ret < 0) { | ||
145 | goto fail; | ||
146 | } | ||
147 | @@ -XXX,XX +XXX,XX @@ static void init_exports_table(void) | ||
148 | * Create exp->fuse_session and mount it. | ||
149 | */ | ||
150 | static int setup_fuse_export(FuseExport *exp, const char *mountpoint, | ||
151 | - Error **errp) | ||
152 | + bool allow_other, Error **errp) | ||
153 | { | ||
154 | const char *fuse_argv[4]; | ||
155 | char *mount_opts; | ||
156 | @@ -XXX,XX +XXX,XX @@ static int setup_fuse_export(FuseExport *exp, const char *mountpoint, | ||
157 | * max_read needs to match what fuse_init() sets. | ||
158 | * max_write need not be supplied. | ||
159 | */ | ||
160 | - mount_opts = g_strdup_printf("max_read=%zu,default_permissions", | ||
161 | - FUSE_MAX_BOUNCE_BYTES); | ||
162 | + mount_opts = g_strdup_printf("max_read=%zu,default_permissions%s", | ||
163 | + FUSE_MAX_BOUNCE_BYTES, | ||
164 | + allow_other ? ",allow_other" : ""); | ||
165 | |||
166 | fuse_argv[0] = ""; /* Dummy program name */ | ||
167 | fuse_argv[1] = "-o"; | ||
168 | diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 | ||
169 | index XXXXXXX..XXXXXXX 100755 | ||
170 | --- a/tests/qemu-iotests/308 | ||
171 | +++ b/tests/qemu-iotests/308 | ||
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" | ||
211 | -- | ||
212 | 2.31.1 | ||
213 | |||
214 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Max Reitz <mreitz@redhat.com> | ||
1 | 2 | ||
3 | In order to support changing other attributes than the file size in | ||
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. | ||
6 | |||
7 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | Message-Id: <20210625142317.271673-4-mreitz@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | block/export/fuse.c | 20 +++++++++++--------- | ||
13 | 1 file changed, 11 insertions(+), 9 deletions(-) | ||
14 | |||
15 | diff --git a/block/export/fuse.c b/block/export/fuse.c | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/block/export/fuse.c | ||
18 | +++ b/block/export/fuse.c | ||
19 | @@ -XXX,XX +XXX,XX @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf, | ||
20 | FuseExport *exp = fuse_req_userdata(req); | ||
21 | int ret; | ||
22 | |||
23 | - if (!exp->writable) { | ||
24 | - fuse_reply_err(req, EACCES); | ||
25 | - return; | ||
26 | - } | ||
27 | - | ||
28 | if (to_set & ~FUSE_SET_ATTR_SIZE) { | ||
29 | fuse_reply_err(req, ENOTSUP); | ||
30 | return; | ||
31 | } | ||
32 | |||
33 | - ret = fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MODE_OFF); | ||
34 | - if (ret < 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; | ||
41 | + } | ||
42 | + | ||
43 | + ret = fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MODE_OFF); | ||
44 | + if (ret < 0) { | ||
45 | + fuse_reply_err(req, -ret); | ||
46 | + return; | ||
47 | + } | ||
48 | } | ||
49 | |||
50 | fuse_getattr(req, inode, fi); | ||
51 | -- | ||
52 | 2.31.1 | ||
53 | |||
54 | diff view generated by jsdifflib |
1 | From: Peter Lieven <pl@kamp.de> | 1 | From: Max Reitz <mreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | qemu is currently not able to detect truncated vhdx image files. | 3 | Allow changing the file mode, UID, and GID through SETATTR. |
4 | Add a basic check if all allocated blocks are reachable at open and | ||
5 | report all errors during bdrv_co_check. | ||
6 | 4 | ||
7 | Signed-off-by: Peter Lieven <pl@kamp.de> | 5 | Without allow_other, UID and GID are not allowed to be changed, because |
6 | it would not make sense. Also, changing group or others' permissions | ||
7 | is not allowed either. | ||
8 | |||
9 | For read-only exports, +w cannot be set. | ||
10 | |||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | Message-Id: <20210625142317.271673-5-mreitz@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 14 | --- |
10 | block/vhdx.c | 120 +++++++++++++++++++++++++++++++++++++++++++-------- | 15 | block/export/fuse.c | 73 ++++++++++++++++++++++++++++++++++++++------- |
11 | 1 file changed, 103 insertions(+), 17 deletions(-) | 16 | 1 file changed, 62 insertions(+), 11 deletions(-) |
12 | 17 | ||
13 | diff --git a/block/vhdx.c b/block/vhdx.c | 18 | diff --git a/block/export/fuse.c b/block/export/fuse.c |
14 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block/vhdx.c | 20 | --- a/block/export/fuse.c |
16 | +++ b/block/vhdx.c | 21 | +++ b/block/export/fuse.c |
17 | @@ -XXX,XX +XXX,XX @@ | 22 | @@ -XXX,XX +XXX,XX @@ typedef struct FuseExport { |
18 | #include "qemu/option.h" | 23 | bool growable; |
19 | #include "qemu/crc32c.h" | 24 | /* Whether allow_other was used as a mount option or not */ |
20 | #include "qemu/bswap.h" | 25 | bool allow_other; |
21 | +#include "qemu/error-report.h" | 26 | + |
22 | #include "vhdx.h" | 27 | + mode_t st_mode; |
23 | #include "migration/blocker.h" | 28 | + uid_t st_uid; |
24 | #include "qemu/uuid.h" | 29 | + gid_t st_gid; |
25 | @@ -XXX,XX +XXX,XX @@ static int vhdx_region_check(BDRVVHDXState *s, uint64_t start, uint64_t length) | 30 | } FuseExport; |
26 | end = start + length; | 31 | |
27 | QLIST_FOREACH(r, &s->regions, entries) { | 32 | static GHashTable *exports; |
28 | if (!((start >= r->end) || (end <= r->start))) { | 33 | @@ -XXX,XX +XXX,XX @@ static int fuse_export_create(BlockExport *blk_exp, |
29 | + error_report("VHDX region %" PRIu64 "-%" PRIu64 " overlaps with " | 34 | args->allow_other = FUSE_EXPORT_ALLOW_OTHER_AUTO; |
30 | + "region %" PRIu64 "-%." PRIu64, start, end, r->start, | 35 | } |
31 | + r->end); | 36 | |
32 | ret = -EINVAL; | 37 | + exp->st_mode = S_IFREG | S_IRUSR; |
33 | goto exit; | 38 | + if (exp->writable) { |
34 | } | 39 | + exp->st_mode |= S_IWUSR; |
35 | @@ -XXX,XX +XXX,XX @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s) | 40 | + } |
36 | 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, | ||
37 | } | 77 | } |
38 | 78 | ||
39 | +static int vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt) | 79 | /** |
40 | +{ | 80 | - * Let clients set file attributes. Only resizing is supported. |
41 | + BDRVVHDXState *s = bs->opaque; | 81 | + * Let clients set file attributes. Only resizing and changing |
42 | + int64_t image_file_size = bdrv_getlength(bs->file->bs); | 82 | + * permissions (st_mode, st_uid, st_gid) is allowed. |
43 | + uint64_t payblocks = s->chunk_ratio; | 83 | + * Changing permissions is only allowed as far as it will actually |
44 | + uint64_t i; | 84 | + * permit access: Read-only exports cannot be given +w, and exports |
45 | + int ret = 0; | 85 | + * without allow_other cannot be given a different UID or GID, and |
46 | + | 86 | + * they cannot be given non-owner access. |
47 | + if (image_file_size < 0) { | 87 | */ |
48 | + error_report("Could not determinate VHDX image file size."); | 88 | static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf, |
49 | + return image_file_size; | 89 | int to_set, struct fuse_file_info *fi) |
90 | { | ||
91 | FuseExport *exp = fuse_req_userdata(req); | ||
92 | + int supported_attrs; | ||
93 | int ret; | ||
94 | |||
95 | - if (to_set & ~FUSE_SET_ATTR_SIZE) { | ||
96 | + supported_attrs = FUSE_SET_ATTR_SIZE | FUSE_SET_ATTR_MODE; | ||
97 | + if (exp->allow_other) { | ||
98 | + supported_attrs |= FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID; | ||
50 | + } | 99 | + } |
51 | + | 100 | + |
52 | + for (i = 0; i < s->bat_entries; i++) { | 101 | + if (to_set & ~supported_attrs) { |
53 | + if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) == | 102 | fuse_reply_err(req, ENOTSUP); |
54 | + PAYLOAD_BLOCK_FULLY_PRESENT) { | 103 | return; |
55 | + uint64_t offset = s->bat[i] & VHDX_BAT_FILE_OFF_MASK; | 104 | } |
56 | + /* | 105 | |
57 | + * Allow that the last block exists only partially. The VHDX spec | 106 | + /* Do some argument checks first before committing to anything */ |
58 | + * states that the image file can only grow in blocksize increments, | 107 | + if (to_set & FUSE_SET_ATTR_MODE) { |
59 | + * but QEMU created images with partial last blocks in the past. | 108 | + /* |
60 | + */ | 109 | + * Without allow_other, non-owners can never access the export, so do |
61 | + uint32_t block_length = MIN(s->block_size, | 110 | + * not allow setting permissions for them |
62 | + bs->total_sectors * BDRV_SECTOR_SIZE - i * s->block_size); | 111 | + */ |
63 | + /* | 112 | + if (!exp->allow_other && |
64 | + * Check for BAT entry overflow. | 113 | + (statbuf->st_mode & (S_IRWXG | S_IRWXO)) != 0) |
65 | + */ | 114 | + { |
66 | + if (offset > INT64_MAX - s->block_size) { | 115 | + fuse_reply_err(req, EPERM); |
67 | + error_report("VHDX BAT entry %" PRIu64 " offset overflow.", i); | 116 | + return; |
68 | + ret = -EINVAL; | 117 | + } |
69 | + if (!errcnt) { | ||
70 | + break; | ||
71 | + } | ||
72 | + (*errcnt)++; | ||
73 | + } | ||
74 | + /* | ||
75 | + * Check if fully allocated BAT entries do not reside after | ||
76 | + * end of the image file. | ||
77 | + */ | ||
78 | + if (offset >= image_file_size) { | ||
79 | + error_report("VHDX BAT entry %" PRIu64 " start offset %" PRIu64 | ||
80 | + " points after end of file (%" PRIi64 "). Image" | ||
81 | + " has probably been truncated.", | ||
82 | + i, offset, image_file_size); | ||
83 | + ret = -EINVAL; | ||
84 | + if (!errcnt) { | ||
85 | + break; | ||
86 | + } | ||
87 | + (*errcnt)++; | ||
88 | + } else if (offset + block_length > image_file_size) { | ||
89 | + error_report("VHDX BAT entry %" PRIu64 " end offset %" PRIu64 | ||
90 | + " points after end of file (%" PRIi64 "). Image" | ||
91 | + " has probably been truncated.", | ||
92 | + i, offset + block_length - 1, image_file_size); | ||
93 | + ret = -EINVAL; | ||
94 | + if (!errcnt) { | ||
95 | + break; | ||
96 | + } | ||
97 | + (*errcnt)++; | ||
98 | + } | ||
99 | + | 118 | + |
100 | + /* | 119 | + /* +w for read-only exports makes no sense, disallow it */ |
101 | + * verify populated BAT field file offsets against | 120 | + if (!exp->writable && |
102 | + * region table and log entries | 121 | + (statbuf->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) |
103 | + */ | 122 | + { |
104 | + if (payblocks--) { | 123 | + fuse_reply_err(req, EROFS); |
105 | + /* payload bat entries */ | 124 | + return; |
106 | + int ret2; | ||
107 | + ret2 = vhdx_region_check(s, offset, s->block_size); | ||
108 | + if (ret2 < 0) { | ||
109 | + ret = -EINVAL; | ||
110 | + if (!errcnt) { | ||
111 | + break; | ||
112 | + } | ||
113 | + (*errcnt)++; | ||
114 | + } | ||
115 | + } else { | ||
116 | + payblocks = s->chunk_ratio; | ||
117 | + /* | ||
118 | + * Once differencing files are supported, verify sector bitmap | ||
119 | + * blocks here | ||
120 | + */ | ||
121 | + } | ||
122 | + } | 125 | + } |
123 | + } | 126 | + } |
124 | + | 127 | + |
125 | + return ret; | 128 | if (to_set & FUSE_SET_ATTR_SIZE) { |
126 | +} | 129 | if (!exp->writable) { |
127 | + | 130 | fuse_reply_err(req, EACCES); |
128 | static void vhdx_close(BlockDriverState *bs) | 131 | @@ -XXX,XX +XXX,XX @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf, |
129 | { | 132 | } |
130 | BDRVVHDXState *s = bs->opaque; | ||
131 | @@ -XXX,XX +XXX,XX @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, | ||
132 | goto fail; | ||
133 | } | 133 | } |
134 | 134 | ||
135 | - uint64_t payblocks = s->chunk_ratio; | 135 | + if (to_set & FUSE_SET_ATTR_MODE) { |
136 | - /* endian convert, and verify populated BAT field file offsets against | 136 | + /* Ignore FUSE-supplied file type, only change the mode */ |
137 | - * region table and log entries */ | 137 | + exp->st_mode = (statbuf->st_mode & 07777) | S_IFREG; |
138 | + /* endian convert populated BAT field entires */ | ||
139 | for (i = 0; i < s->bat_entries; i++) { | ||
140 | s->bat[i] = le64_to_cpu(s->bat[i]); | ||
141 | - if (payblocks--) { | ||
142 | - /* payload bat entries */ | ||
143 | - if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) == | ||
144 | - PAYLOAD_BLOCK_FULLY_PRESENT) { | ||
145 | - ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK, | ||
146 | - s->block_size); | ||
147 | - if (ret < 0) { | ||
148 | - goto fail; | ||
149 | - } | ||
150 | - } | ||
151 | - } else { | ||
152 | - payblocks = s->chunk_ratio; | ||
153 | - /* Once differencing files are supported, verify sector bitmap | ||
154 | - * blocks here */ | ||
155 | + } | 138 | + } |
156 | + | 139 | + |
157 | + if (!(flags & BDRV_O_CHECK)) { | 140 | + if (to_set & FUSE_SET_ATTR_UID) { |
158 | + ret = vhdx_check_bat_entries(bs, NULL); | 141 | + exp->st_uid = statbuf->st_uid; |
159 | + if (ret < 0) { | 142 | + } |
160 | + goto fail; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_check(BlockDriverState *bs, | ||
165 | if (s->log_replayed_on_open) { | ||
166 | result->corruptions_fixed++; | ||
167 | } | ||
168 | + | 143 | + |
169 | + vhdx_check_bat_entries(bs, &result->corruptions); | 144 | + if (to_set & FUSE_SET_ATTR_GID) { |
145 | + exp->st_gid = statbuf->st_gid; | ||
146 | + } | ||
170 | + | 147 | + |
171 | return 0; | 148 | fuse_getattr(req, inode, fi); |
172 | } | 149 | } |
173 | 150 | ||
174 | -- | 151 | -- |
175 | 2.20.1 | 152 | 2.31.1 |
176 | 153 | ||
177 | 154 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | From: Max Reitz <mreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | For long test image paths, the order of the "Formatting" line and the | 3 | Test that +w on read-only FUSE exports returns an EROFS error. u+x on |
4 | "(qemu)" prompt after a drive_backup HMP command may be reversed. In | 4 | the other hand should work. (There is no special reason to choose u+x |
5 | fact, the interaction between the prompt and the line may lead to the | 5 | here, it simply is like +w another flag that is not set by default.) |
6 | "Formatting" to being greppable at all after "read"-ing it (if the | ||
7 | prompt injects an IFS character into the "Formatting" string). | ||
8 | 6 | ||
9 | So just wait until we get a prompt. At that point, the block job must | ||
10 | have been started, so "info block-jobs" will only return "No active | ||
11 | jobs" once it is done. | ||
12 | |||
13 | Reported-by: Thomas Huth <thuth@redhat.com> | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 7 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
15 | Reviewed-by: John Snow <jsnow@redhat.com> | 8 | Message-Id: <20210625142317.271673-6-mreitz@redhat.com> |
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
17 | --- | 10 | --- |
18 | tests/qemu-iotests/028 | 11 ++++++++--- | 11 | tests/qemu-iotests/308 | 11 +++++++++++ |
19 | tests/qemu-iotests/028.out | 1 - | 12 | tests/qemu-iotests/308.out | 4 ++++ |
20 | 2 files changed, 8 insertions(+), 4 deletions(-) | 13 | 2 files changed, 15 insertions(+) |
21 | 14 | ||
22 | diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 | 15 | diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 |
23 | index XXXXXXX..XXXXXXX 100755 | 16 | index XXXXXXX..XXXXXXX 100755 |
24 | --- a/tests/qemu-iotests/028 | 17 | --- a/tests/qemu-iotests/308 |
25 | +++ b/tests/qemu-iotests/028 | 18 | +++ b/tests/qemu-iotests/308 |
26 | @@ -XXX,XX +XXX,XX @@ fi | 19 | @@ -XXX,XX +XXX,XX @@ fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP'" |
27 | # Silence output since it contains the disk image path and QEMU's readline | 20 | # Check that the export presents the same data as the original image |
28 | # character echoing makes it very hard to filter the output. Plus, there | 21 | $QEMU_IMG compare -f raw -F $IMGFMT -U "$EXT_MP" "$TEST_IMG" |
29 | # is no telling how many times the command will repeat before succeeding. | 22 | |
30 | -_send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)" >/dev/null | 23 | +# Some quick chmod tests |
31 | -_send_qemu_cmd $h "" "Formatting" | _filter_img_create | 24 | +stat -c 'Permissions pre-chmod: %a' "$EXT_MP" |
32 | -qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs" >/dev/null | 25 | + |
33 | +# (Note that creating the image results in a "Formatting..." message over | 26 | +# Verify that we cannot set +w |
34 | +# stdout, which is the same channel the monitor uses. We cannot reliably | 27 | +chmod u+w "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt |
35 | +# wait for it because the monitor output may interact with it in such a | 28 | +stat -c 'Permissions post-+w: %a' "$EXT_MP" |
36 | +# way that _timed_wait_for cannot read it. However, once the block job is | 29 | + |
37 | +# done, we know that the "Formatting..." message must have appeared | 30 | +# But that we can set, say, +x (if we are so inclined) |
38 | +# already, so the output is still deterministic.) | 31 | +chmod u+x "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt |
39 | +silent=y _send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)" | 32 | +stat -c 'Permissions post-+x: %a' "$EXT_MP" |
40 | +silent=y qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs" | 33 | + |
41 | _send_qemu_cmd $h "info block-jobs" "No active jobs" | 34 | echo |
42 | _send_qemu_cmd $h 'quit' "" | 35 | echo '=== Mount over existing file ===' |
43 | 36 | ||
44 | diff --git a/tests/qemu-iotests/028.out b/tests/qemu-iotests/028.out | 37 | diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out |
45 | index XXXXXXX..XXXXXXX 100644 | 38 | index XXXXXXX..XXXXXXX 100644 |
46 | --- a/tests/qemu-iotests/028.out | 39 | --- a/tests/qemu-iotests/308.out |
47 | +++ b/tests/qemu-iotests/028.out | 40 | +++ b/tests/qemu-iotests/308.out |
48 | @@ -XXX,XX +XXX,XX @@ No errors were found on the image. | 41 | @@ -XXX,XX +XXX,XX @@ wrote 67108864/67108864 bytes at offset 0 |
49 | 42 | } } | |
50 | block-backup | 43 | {"return": {}} |
51 | 44 | Images are identical. | |
52 | -Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | 45 | +Permissions pre-chmod: 400 |
53 | (qemu) info block-jobs | 46 | +chmod: changing permissions of 'TEST_DIR/t.IMGFMT.fuse': Read-only file system |
54 | No active jobs | 47 | +Permissions post-+w: 400 |
55 | === IO: pattern 195 | 48 | +Permissions post-+x: 500 |
49 | |||
50 | === Mount over existing file === | ||
51 | {'execute': 'block-export-add', | ||
56 | -- | 52 | -- |
57 | 2.20.1 | 53 | 2.31.1 |
58 | 54 | ||
59 | 55 | diff view generated by jsdifflib |
1 | From: Alberto Garcia <berto@igalia.com> | 1 | From: Max Reitz <mreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The BDRV_REQ_NO_FALLBACK flag means that an operation should only be | 3 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
4 | performed if it can be offloaded or otherwise performed efficiently. | 4 | Message-Id: <20210625142317.271673-7-mreitz@redhat.com> |
5 | |||
6 | However a misaligned write request requires a RMW so we should return | ||
7 | an error and let the caller decide how to proceed. | ||
8 | |||
9 | This hits an assertion since commit c8bb23cbdb if the required | ||
10 | alignment is larger than the cluster size: | ||
11 | |||
12 | qemu-img create -f qcow2 -o cluster_size=2k img.qcow2 4G | ||
13 | qemu-io -c "open -o driver=qcow2,file.align=4k blkdebug::img.qcow2" \ | ||
14 | -c 'write 0 512' | ||
15 | qemu-io: block/io.c:1127: bdrv_driver_pwritev: Assertion `!(flags & BDRV_REQ_NO_FALLBACK)' failed. | ||
16 | Aborted | ||
17 | |||
18 | The reason is that when writing to an unallocated cluster we try to | ||
19 | skip the copy-on-write part and zeroize it using BDRV_REQ_NO_FALLBACK | ||
20 | instead, resulting in a write request that is too small (2KB cluster | ||
21 | size vs 4KB required alignment). | ||
22 | |||
23 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
25 | --- | 6 | --- |
26 | block/io.c | 7 +++++ | 7 | tests/qemu-iotests/tests/fuse-allow-other | 168 ++++++++++++++++++ |
27 | tests/qemu-iotests/268 | 55 ++++++++++++++++++++++++++++++++++++++ | 8 | tests/qemu-iotests/tests/fuse-allow-other.out | 88 +++++++++ |
28 | tests/qemu-iotests/268.out | 7 +++++ | 9 | 2 files changed, 256 insertions(+) |
29 | tests/qemu-iotests/group | 1 + | 10 | create mode 100755 tests/qemu-iotests/tests/fuse-allow-other |
30 | 4 files changed, 70 insertions(+) | 11 | create mode 100644 tests/qemu-iotests/tests/fuse-allow-other.out |
31 | create mode 100755 tests/qemu-iotests/268 | 12 | |
32 | create mode 100644 tests/qemu-iotests/268.out | 13 | diff --git a/tests/qemu-iotests/tests/fuse-allow-other b/tests/qemu-iotests/tests/fuse-allow-other |
33 | |||
34 | diff --git a/block/io.c b/block/io.c | ||
35 | index XXXXXXX..XXXXXXX 100644 | ||
36 | --- a/block/io.c | ||
37 | +++ b/block/io.c | ||
38 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, | ||
39 | return ret; | ||
40 | } | ||
41 | |||
42 | + /* If the request is misaligned then we can't make it efficient */ | ||
43 | + if ((flags & BDRV_REQ_NO_FALLBACK) && | ||
44 | + !QEMU_IS_ALIGNED(offset | bytes, align)) | ||
45 | + { | ||
46 | + return -ENOTSUP; | ||
47 | + } | ||
48 | + | ||
49 | bdrv_inc_in_flight(bs); | ||
50 | /* | ||
51 | * Align write if necessary by performing a read-modify-write cycle. | ||
52 | diff --git a/tests/qemu-iotests/268 b/tests/qemu-iotests/268 | ||
53 | new file mode 100755 | 14 | new file mode 100755 |
54 | index XXXXXXX..XXXXXXX | 15 | index XXXXXXX..XXXXXXX |
55 | --- /dev/null | 16 | --- /dev/null |
56 | +++ b/tests/qemu-iotests/268 | 17 | +++ b/tests/qemu-iotests/tests/fuse-allow-other |
57 | @@ -XXX,XX +XXX,XX @@ | 18 | @@ -XXX,XX +XXX,XX @@ |
58 | +#!/usr/bin/env bash | 19 | +#!/usr/bin/env bash |
59 | +# | 20 | +# group: rw |
60 | +# Test write request with required alignment larger than the cluster size | 21 | +# |
61 | +# | 22 | +# Test FUSE exports' allow-other option |
62 | +# Copyright (C) 2019 Igalia, S.L. | 23 | +# |
63 | +# Author: Alberto Garcia <berto@igalia.com> | 24 | +# Copyright (C) 2021 Red Hat, Inc. |
64 | +# | 25 | +# |
65 | +# This program is free software; you can redistribute it and/or modify | 26 | +# This program is free software; you can redistribute it and/or modify |
66 | +# it under the terms of the GNU General Public License as published by | 27 | +# it under the terms of the GNU General Public License as published by |
67 | +# the Free Software Foundation; either version 2 of the License, or | 28 | +# the Free Software Foundation; either version 2 of the License, or |
68 | +# (at your option) any later version. | 29 | +# (at your option) any later version. |
... | ... | ||
74 | +# | 35 | +# |
75 | +# You should have received a copy of the GNU General Public License | 36 | +# You should have received a copy of the GNU General Public License |
76 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | 37 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
77 | +# | 38 | +# |
78 | + | 39 | + |
79 | +# creator | 40 | +seq=$(basename "$0") |
80 | +owner=berto@igalia.com | ||
81 | + | ||
82 | +seq=`basename $0` | ||
83 | +echo "QA output created by $seq" | 41 | +echo "QA output created by $seq" |
84 | + | 42 | + |
85 | +status=1 # failure is the default! | 43 | +status=1 # failure is the default! |
86 | + | 44 | + |
87 | +_cleanup() | 45 | +_cleanup() |
88 | +{ | 46 | +{ |
47 | + _cleanup_qemu | ||
89 | + _cleanup_test_img | 48 | + _cleanup_test_img |
49 | + rm -f "$EXT_MP" | ||
90 | +} | 50 | +} |
91 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | 51 | +trap "_cleanup; exit \$status" 0 1 2 3 15 |
92 | + | 52 | + |
93 | +# get standard environment, filters and checks | 53 | +# get standard environment, filters and checks |
94 | +. ./common.rc | 54 | +. ../common.rc |
95 | +. ./common.filter | 55 | +. ../common.filter |
96 | + | 56 | +. ../common.qemu |
97 | +_supported_fmt qcow2 | 57 | + |
98 | +_supported_proto file | 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" | ||
99 | + | 105 | + |
100 | +echo | 106 | +echo |
101 | +echo "== Required alignment larger than cluster size ==" | 107 | +echo '=== Test permissions ===' |
102 | + | 108 | + |
103 | +CLUSTER_SIZE=2k _make_test_img 1M | 109 | +# $1: allow-other value ('on'/'off'/'auto') |
104 | +# Since commit c8bb23cbdb writing to an unallocated cluster fills the | 110 | +run_permission_test() |
105 | +# empty COW areas with bdrv_write_zeroes(flags=BDRV_REQ_NO_FALLBACK) | 111 | +{ |
106 | +$QEMU_IO -c "open -o driver=$IMGFMT,file.align=4k blkdebug::$TEST_IMG" \ | 112 | + _launch_qemu \ |
107 | + -c "write 0 512" | _filter_qemu_io | 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 | ||
108 | + | 182 | + |
109 | +# success, all done | 183 | +# success, all done |
110 | +echo "*** done" | 184 | +echo "*** done" |
111 | +rm -f $seq.full | 185 | +rm -f $seq.full |
112 | +status=0 | 186 | +status=0 |
113 | diff --git a/tests/qemu-iotests/268.out b/tests/qemu-iotests/268.out | 187 | diff --git a/tests/qemu-iotests/tests/fuse-allow-other.out b/tests/qemu-iotests/tests/fuse-allow-other.out |
114 | new file mode 100644 | 188 | new file mode 100644 |
115 | index XXXXXXX..XXXXXXX | 189 | index XXXXXXX..XXXXXXX |
116 | --- /dev/null | 190 | --- /dev/null |
117 | +++ b/tests/qemu-iotests/268.out | 191 | +++ b/tests/qemu-iotests/tests/fuse-allow-other.out |
118 | @@ -XXX,XX +XXX,XX @@ | 192 | @@ -XXX,XX +XXX,XX @@ |
119 | +QA output created by 268 | 193 | +QA output created by fuse-allow-other |
120 | + | 194 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 |
121 | +== Required alignment larger than cluster size == | 195 | + |
122 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 | 196 | +=== Test permissions === |
123 | +wrote 512/512 bytes at offset 0 | 197 | + |
124 | +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 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"}} | ||
125 | +*** done | 280 | +*** done |
126 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
127 | index XXXXXXX..XXXXXXX 100644 | ||
128 | --- a/tests/qemu-iotests/group | ||
129 | +++ b/tests/qemu-iotests/group | ||
130 | @@ -XXX,XX +XXX,XX @@ | ||
131 | 265 rw auto quick | ||
132 | 266 rw quick | ||
133 | 267 rw auto quick snapshot | ||
134 | +268 rw auto quick | ||
135 | -- | 281 | -- |
136 | 2.20.1 | 282 | 2.31.1 |
137 | 283 | ||
138 | 284 | diff view generated by jsdifflib |
1 | From: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> | 1 | From: Peter Lieven <pl@kamp.de> |
---|---|---|---|
2 | 2 | ||
3 | After recent updates block devices cannot be closed on qemu exit. | 3 | task->complete is a bool not an integer. |
4 | This happens due to the block request polling when replay is not finished. | ||
5 | Therefore now we stop execution recording before closing the block devices. | ||
6 | 4 | ||
7 | Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> | 5 | Signed-off-by: Peter Lieven <pl@kamp.de> |
6 | Message-Id: <20210707180449.32665-1-pl@kamp.de> | ||
7 | Reviewed-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 | replay/replay.c | 2 ++ | 10 | block/rbd.c | 2 +- |
11 | vl.c | 1 + | 11 | 1 file changed, 1 insertion(+), 1 deletion(-) |
12 | 2 files changed, 3 insertions(+) | ||
13 | 12 | ||
14 | diff --git a/replay/replay.c b/replay/replay.c | 13 | diff --git a/block/rbd.c b/block/rbd.c |
15 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/replay/replay.c | 15 | --- a/block/rbd.c |
17 | +++ b/replay/replay.c | 16 | +++ b/block/rbd.c |
18 | @@ -XXX,XX +XXX,XX @@ void replay_finish(void) | 17 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_resize(BlockDriverState *bs, uint64_t size) |
19 | g_free(replay_snapshot); | 18 | static void qemu_rbd_finish_bh(void *opaque) |
20 | replay_snapshot = NULL; | 19 | { |
21 | 20 | RBDTask *task = opaque; | |
22 | + replay_mode = REPLAY_MODE_NONE; | 21 | - task->complete = 1; |
23 | + | 22 | + task->complete = true; |
24 | replay_finish_events(); | 23 | aio_co_wake(task->co); |
25 | } | 24 | } |
26 | 25 | ||
27 | diff --git a/vl.c b/vl.c | ||
28 | index XXXXXXX..XXXXXXX 100644 | ||
29 | --- a/vl.c | ||
30 | +++ b/vl.c | ||
31 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) | ||
32 | |||
33 | /* No more vcpu or device emulation activity beyond this point */ | ||
34 | vm_shutdown(); | ||
35 | + replay_finish(); | ||
36 | |||
37 | job_cancel_sync_all(); | ||
38 | bdrv_close_all(); | ||
39 | -- | 26 | -- |
40 | 2.20.1 | 27 | 2.31.1 |
41 | 28 | ||
42 | 29 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Peter Lieven <pl@kamp.de> | ||
1 | 2 | ||
3 | adding myself as a designated reviewer. | ||
4 | |||
5 | Signed-off-by: Peter Lieven <pl@kamp.de> | ||
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> | ||
9 | --- | ||
10 | MAINTAINERS | 1 + | ||
11 | 1 file changed, 1 insertion(+) | ||
12 | |||
13 | diff --git a/MAINTAINERS b/MAINTAINERS | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/MAINTAINERS | ||
16 | +++ b/MAINTAINERS | ||
17 | @@ -XXX,XX +XXX,XX @@ F: block/vmdk.c | ||
18 | |||
19 | RBD | ||
20 | M: Ilya Dryomov <idryomov@gmail.com> | ||
21 | +R: Peter Lieven <pl@kamp.de> | ||
22 | L: qemu-block@nongnu.org | ||
23 | S: Supported | ||
24 | F: block/rbd.c | ||
25 | -- | ||
26 | 2.31.1 | ||
27 | |||
28 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | dev->max_queues was never initialised for backends that don't support | ||
2 | VHOST_USER_PROTOCOL_F_MQ, so it would use 0 as the maximum number of | ||
3 | queues to check against and consequently fail for any such backend. | ||
1 | 4 | ||
5 | Set it to 1 if the backend doesn't have multiqueue support. | ||
6 | |||
7 | Fixes: c90bd505a3e8210c23d69fecab9ee6f56ec4a161 | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | Message-Id: <20210705171429.29286-1-kwolf@redhat.com> | ||
10 | Reviewed-by: Cornelia Huck <cohuck@redhat.com> | ||
11 | Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | hw/virtio/vhost-user.c | 3 +++ | ||
15 | 1 file changed, 3 insertions(+) | ||
16 | |||
17 | diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/hw/virtio/vhost-user.c | ||
20 | +++ b/hw/virtio/vhost-user.c | ||
21 | @@ -XXX,XX +XXX,XX @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque, | ||
22 | if (err < 0) { | ||
23 | return -EPROTO; | ||
24 | } | ||
25 | + } else { | ||
26 | + dev->max_queues = 1; | ||
27 | } | ||
28 | + | ||
29 | if (dev->num_queues && dev->max_queues < dev->num_queues) { | ||
30 | error_setg(errp, "The maximum number of queues supported by the " | ||
31 | "backend is %" PRIu64, dev->max_queues); | ||
32 | -- | ||
33 | 2.31.1 | ||
34 | |||
35 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | drive_backup_prepare() does bdrv_drained_begin() in hope that | ||
4 | bdrv_drained_end() will be called in drive_backup_clean(). Still we | ||
5 | need to set state->bs for this to work. That's done too late: a lot of | ||
6 | failure paths in drive_backup_prepare() miss setting state->bs. Fix | ||
7 | that. | ||
8 | |||
9 | Fixes: 2288ccfac96281c316db942d10e3f921c1373064 | ||
10 | Fixes: https://gitlab.com/qemu-project/qemu/-/issues/399 | ||
11 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
12 | Message-Id: <20210608171852.250775-1-vsementsov@virtuozzo.com> | ||
13 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
15 | --- | ||
16 | blockdev.c | 3 +-- | ||
17 | 1 file changed, 1 insertion(+), 2 deletions(-) | ||
18 | |||
19 | diff --git a/blockdev.c b/blockdev.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/blockdev.c | ||
22 | +++ b/blockdev.c | ||
23 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) | ||
24 | aio_context = bdrv_get_aio_context(bs); | ||
25 | aio_context_acquire(aio_context); | ||
26 | |||
27 | + state->bs = bs; | ||
28 | /* Paired with .clean() */ | ||
29 | bdrv_drained_begin(bs); | ||
30 | |||
31 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | - state->bs = bs; | ||
36 | - | ||
37 | state->job = do_backup_common(qapi_DriveBackup_base(backup), | ||
38 | bs, target_bs, aio_context, | ||
39 | common->block_job_txn, errp); | ||
40 | -- | ||
41 | 2.31.1 | ||
42 | |||
43 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Eric Blake <eblake@redhat.com> | ||
1 | 2 | ||
3 | This was deprecated back in bc5ee6da7 (qcow2: Deprecate use of | ||
4 | qemu-img amend to change backing file), and no one in the meantime has | ||
5 | given any reasons why it should be supported. Time to make change | ||
6 | attempts a hard error (but for convenience, specifying the _same_ | ||
7 | backing chain is not forbidden). Update a couple of iotests to match. | ||
8 | |||
9 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
10 | Message-Id: <20210503213600.569128-2-eblake@redhat.com> | ||
11 | Reviewed-by: Connor Kuehl <ckuehl@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | docs/system/deprecated.rst | 12 ------------ | ||
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(-) | ||
21 | |||
22 | diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst | ||
23 | index XXXXXXX..XXXXXXX 100644 | ||
24 | --- a/docs/system/deprecated.rst | ||
25 | +++ b/docs/system/deprecated.rst | ||
26 | @@ -XXX,XX +XXX,XX @@ this CPU is also deprecated. | ||
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 | } | ||
90 | } | ||
91 | |||
92 | diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 | ||
93 | index XXXXXXX..XXXXXXX 100755 | ||
94 | --- a/tests/qemu-iotests/061 | ||
95 | +++ b/tests/qemu-iotests/061 | ||
96 | @@ -XXX,XX +XXX,XX @@ _make_test_img -o "compat=1.1" 64M | ||
97 | TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M | ||
98 | $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io | ||
99 | $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io | ||
100 | +$QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" \ | ||
101 | + "$TEST_IMG" && echo "unexpected pass" | ||
102 | +$QEMU_IMG rebase -u -b "$TEST_IMG.base" -F qcow2 "$TEST_IMG" | ||
103 | $QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" "$TEST_IMG" | ||
104 | $QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io | ||
105 | _check_test_img | ||
106 | diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out | ||
107 | index XXXXXXX..XXXXXXX 100644 | ||
108 | --- a/tests/qemu-iotests/061.out | ||
109 | +++ b/tests/qemu-iotests/061.out | ||
110 | @@ -XXX,XX +XXX,XX @@ wrote 131072/131072 bytes at offset 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 | |||
141 | -- | ||
142 | 2.31.1 | ||
143 | |||
144 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | From: Eric Blake <eblake@redhat.com> | |
2 | |||
3 | Back in commit d9f059aa6c (qemu-img: Deprecate use of -b without -F), | ||
4 | we deprecated the ability to create a file with a backing image that | ||
5 | requires qemu to perform format probing. Qemu can still probe older | ||
6 | files for backwards compatibility, but it is time to finish off the | ||
7 | ability to create such images, due to the potential security risk they | ||
8 | present. Update a couple of iotests affected by the change. | ||
9 | |||
10 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
11 | Message-Id: <20210503213600.569128-3-eblake@redhat.com> | ||
12 | Reviewed-by: Connor Kuehl <ckuehl@redhat.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | --- | ||
15 | docs/system/deprecated.rst | 20 ----------------- | ||
16 | docs/system/removed-features.rst | 19 ++++++++++++++++ | ||
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 | |||
88 | diff --git a/block.c b/block.c | ||
89 | index XXXXXXX..XXXXXXX 100644 | ||
90 | --- a/block.c | ||
91 | +++ b/block.c | ||
92 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_check(BlockDriverState *bs, | ||
93 | * -ENOTSUP - format driver doesn't support changing the backing file | ||
94 | */ | ||
95 | int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, | ||
96 | - const char *backing_fmt, bool warn) | ||
97 | + const char *backing_fmt, bool require) | ||
98 | { | ||
99 | BlockDriver *drv = bs->drv; | ||
100 | int ret; | ||
101 | @@ -XXX,XX +XXX,XX @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, | ||
102 | return -EINVAL; | ||
103 | } | ||
104 | |||
105 | - if (warn && backing_file && !backing_fmt) { | ||
106 | - warn_report("Deprecated use of backing file without explicit " | ||
107 | - "backing format, use of this image requires " | ||
108 | - "potentially unsafe format probing"); | ||
109 | + if (require && backing_file && !backing_fmt) { | ||
110 | + return -EINVAL; | ||
111 | } | ||
112 | |||
113 | if (drv->bdrv_change_backing_file != NULL) { | ||
114 | @@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt, | ||
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; | ||
154 | } | ||
155 | |||
156 | if (size == -1) { | ||
157 | diff --git a/qemu-img.c b/qemu-img.c | ||
158 | index XXXXXXX..XXXXXXX 100644 | ||
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; | ||
171 | } | ||
172 | } | ||
173 | |||
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 | ||
338 | -- | ||
339 | 2.31.1 | ||
340 | |||
341 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Eric Blake <eblake@redhat.com> | ||
1 | 2 | ||
3 | When removeing support for qemu-img being able to create backing | ||
4 | chains without embedded backing formats, we caused a poor error | ||
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> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | --- | ||
13 | qemu-img.c | 3 +++ | ||
14 | tests/qemu-iotests/114.out | 2 +- | ||
15 | 2 files changed, 4 insertions(+), 1 deletion(-) | ||
16 | |||
17 | diff --git a/qemu-img.c b/qemu-img.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/qemu-img.c | ||
20 | +++ b/qemu-img.c | ||
21 | @@ -XXX,XX +XXX,XX @@ static int img_rebase(int argc, char **argv) | ||
22 | if (ret == -ENOSPC) { | ||
23 | error_report("Could not change the backing file to '%s': No " | ||
24 | "space left in the file header", out_baseimg); | ||
25 | + } else if (ret == -EINVAL && out_baseimg && !out_basefmt) { | ||
26 | + error_report("Could not change the backing file to '%s': backing " | ||
27 | + "format must be specified", out_baseimg); | ||
28 | } else if (ret < 0) { | ||
29 | error_report("Could not change the backing file to '%s': %s", | ||
30 | out_baseimg, strerror(-ret)); | ||
31 | diff --git a/tests/qemu-iotests/114.out b/tests/qemu-iotests/114.out | ||
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/tests/qemu-iotests/114.out | ||
34 | +++ b/tests/qemu-iotests/114.out | ||
35 | @@ -XXX,XX +XXX,XX @@ qemu-io: can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknow | ||
36 | no file open, try 'help open' | ||
37 | read 4096/4096 bytes at offset 0 | ||
38 | 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
39 | -qemu-img: Could not change the backing file to 'TEST_DIR/t.qcow2.base': Invalid argument | ||
40 | +qemu-img: Could not change the backing file to 'TEST_DIR/t.qcow2.base': backing format must be specified | ||
41 | read 4096/4096 bytes at offset 0 | ||
42 | 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
43 | *** done | ||
44 | -- | ||
45 | 2.31.1 | ||
46 | |||
47 | diff view generated by jsdifflib |
1 | From: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> | 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 | In record/replay mode bdrv queue is controlled by replay mechanism. | 6 | This problem was caught by iotests case 245. |
4 | It does not allow saving or loading the snapshots | ||
5 | when bdrv queue is not empty. Stopping the VM is not blocked by nonempty | ||
6 | queue, but flushing the queue is still impossible there, | ||
7 | because it may cause deadlocks in replay mode. | ||
8 | This patch disables bdrv_drain_all and bdrv_flush_all in | ||
9 | record/replay mode. | ||
10 | 7 | ||
11 | Stopping the machine when the IO requests are not finished is needed | 8 | Fixes: df2b7086f169239ebad5d150efa29c9bb6d4f820 |
12 | for the debugging. E.g., breakpoint may be set at the specified step, | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | and forcing the IO requests to finish may break the determinism | 10 | Message-Id: <20210708114709.206487-2-kwolf@redhat.com> |
14 | of the execution. | 11 | Reviewed-by: Eric Blake <eblake@redhat.com> |
15 | 12 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | |
16 | Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> | ||
17 | Acked-by: Kevin Wolf <kwolf@redhat.com> | ||
18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
19 | --- | 14 | --- |
20 | block/io.c | 28 ++++++++++++++++++++++++++++ | 15 | block/qcow2.c | 29 +++++++++++++++++++++++++++++ |
21 | cpus.c | 2 -- | 16 | 1 file changed, 29 insertions(+) |
22 | 2 files changed, 28 insertions(+), 2 deletions(-) | ||
23 | 17 | ||
24 | diff --git a/block/io.c b/block/io.c | 18 | diff --git a/block/qcow2.c b/block/qcow2.c |
25 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
26 | --- a/block/io.c | 20 | --- a/block/qcow2.c |
27 | +++ b/block/io.c | 21 | +++ b/block/qcow2.c |
28 | @@ -XXX,XX +XXX,XX @@ | 22 | @@ -XXX,XX +XXX,XX @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) |
29 | #include "qapi/error.h" | 23 | static int qcow2_reopen_prepare(BDRVReopenState *state, |
30 | #include "qemu/error-report.h" | 24 | BlockReopenQueue *queue, Error **errp) |
31 | #include "qemu/main-loop.h" | 25 | { |
32 | +#include "sysemu/replay.h" | 26 | + BDRVQcow2State *s = state->bs->opaque; |
33 | 27 | Qcow2ReopenState *r; | |
34 | #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ | 28 | int ret; |
35 | 29 | ||
36 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | 30 | @@ -XXX,XX +XXX,XX @@ static int qcow2_reopen_prepare(BDRVReopenState *state, |
37 | return; | 31 | } |
38 | } | 32 | } |
39 | 33 | ||
40 | + /* | 34 | + /* |
41 | + * bdrv queue is managed by record/replay, | 35 | + * Without an external data file, s->data_file points to the same BdrvChild |
42 | + * waiting for finishing the I/O requests may | 36 | + * as bs->file. It needs to be resynced after reopen because bs->file may |
43 | + * be infinite | 37 | + * be changed. We can't use it in the meantime. |
44 | + */ | 38 | + */ |
45 | + if (replay_events_enabled()) { | 39 | + if (!has_data_file(state->bs)) { |
46 | + return; | 40 | + assert(s->data_file == state->bs->file); |
41 | + s->data_file = NULL; | ||
47 | + } | 42 | + } |
48 | + | 43 | + |
49 | /* AIO_WAIT_WHILE() with a NULL context can only be called from the main | 44 | return 0; |
50 | * loop AioContext, so make sure we're in the main context. */ | 45 | |
51 | assert(qemu_get_current_aio_context() == qemu_get_aio_context()); | 46 | fail: |
52 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | 47 | @@ -XXX,XX +XXX,XX @@ fail: |
53 | BlockDriverState *bs = NULL; | 48 | |
54 | int drained_end_counter = 0; | 49 | static void qcow2_reopen_commit(BDRVReopenState *state) |
55 | 50 | { | |
56 | + /* | 51 | + BDRVQcow2State *s = state->bs->opaque; |
57 | + * bdrv queue is managed by record/replay, | 52 | + |
58 | + * waiting for finishing the I/O requests may | 53 | qcow2_update_options_commit(state->bs, state->opaque); |
59 | + * be endless | 54 | + if (!s->data_file) { |
60 | + */ | 55 | + /* |
61 | + if (replay_events_enabled()) { | 56 | + * If we don't have an external data file, s->data_file was cleared by |
62 | + return; | 57 | + * qcow2_reopen_prepare() and needs to be updated. |
58 | + */ | ||
59 | + s->data_file = state->bs->file; | ||
63 | + } | 60 | + } |
61 | g_free(state->opaque); | ||
62 | } | ||
63 | |||
64 | @@ -XXX,XX +XXX,XX @@ static void qcow2_reopen_commit_post(BDRVReopenState *state) | ||
65 | |||
66 | static void qcow2_reopen_abort(BDRVReopenState *state) | ||
67 | { | ||
68 | + BDRVQcow2State *s = state->bs->opaque; | ||
64 | + | 69 | + |
65 | while ((bs = bdrv_next_all_states(bs))) { | 70 | + if (!s->data_file) { |
66 | AioContext *aio_context = bdrv_get_aio_context(bs); | 71 | + /* |
67 | 72 | + * If we don't have an external data file, s->data_file was cleared by | |
68 | @@ -XXX,XX +XXX,XX @@ int bdrv_flush_all(void) | 73 | + * qcow2_reopen_prepare() and needs to be restored. |
69 | BlockDriverState *bs = NULL; | 74 | + */ |
70 | int result = 0; | 75 | + s->data_file = state->bs->file; |
71 | |||
72 | + /* | ||
73 | + * bdrv queue is managed by record/replay, | ||
74 | + * creating new flush request for stopping | ||
75 | + * the VM may break the determinism | ||
76 | + */ | ||
77 | + if (replay_events_enabled()) { | ||
78 | + return result; | ||
79 | + } | 76 | + } |
80 | + | 77 | qcow2_update_options_abort(state->bs, state->opaque); |
81 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | 78 | g_free(state->opaque); |
82 | AioContext *aio_context = bdrv_get_aio_context(bs); | 79 | } |
83 | int ret; | ||
84 | diff --git a/cpus.c b/cpus.c | ||
85 | index XXXXXXX..XXXXXXX 100644 | ||
86 | --- a/cpus.c | ||
87 | +++ b/cpus.c | ||
88 | @@ -XXX,XX +XXX,XX @@ static int do_vm_stop(RunState state, bool send_stop) | ||
89 | } | ||
90 | |||
91 | bdrv_drain_all(); | ||
92 | - replay_disable_events(); | ||
93 | ret = bdrv_flush_all(); | ||
94 | |||
95 | return ret; | ||
96 | @@ -XXX,XX +XXX,XX @@ int vm_prepare_start(void) | ||
97 | /* We are sending this now, but the CPUs will be resumed shortly later */ | ||
98 | qapi_event_send_resume(); | ||
99 | |||
100 | - replay_enable_events(); | ||
101 | cpu_enable_ticks(); | ||
102 | runstate_set(RUN_STATE_RUNNING); | ||
103 | vm_state_notify(1, RUN_STATE_RUNNING); | ||
104 | -- | 80 | -- |
105 | 2.20.1 | 81 | 2.31.1 |
106 | 82 | ||
107 | 83 | diff view generated by jsdifflib |
1 | From: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | This patch updates the description of the command lines for using | 3 | Move the code to free a BlockReopenQueue to a separate function. |
4 | record/replay with attached block devices. | 4 | It will be used in a subsequent patch. |
5 | 5 | ||
6 | Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> | 6 | [ kwolf: Also free explicit_options and options, and explicitly |
7 | qobject_ref() the value when it continues to be used. This makes | ||
8 | future memory leaks less likely. ] | ||
9 | |||
10 | Signed-off-by: Alberto Garcia <berto@igalia.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> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | --- | 15 | --- |
9 | docs/replay.txt | 12 +++++++++--- | 16 | include/block/block.h | 1 + |
10 | 1 file changed, 9 insertions(+), 3 deletions(-) | 17 | block.c | 22 ++++++++++++++++------ |
18 | 2 files changed, 17 insertions(+), 6 deletions(-) | ||
11 | 19 | ||
12 | diff --git a/docs/replay.txt b/docs/replay.txt | 20 | diff --git a/include/block/block.h b/include/block/block.h |
13 | index XXXXXXX..XXXXXXX 100644 | 21 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/docs/replay.txt | 22 | --- a/include/block/block.h |
15 | +++ b/docs/replay.txt | 23 | +++ b/include/block/block.h |
16 | @@ -XXX,XX +XXX,XX @@ Usage of the record/replay: | 24 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, |
17 | * First, record the execution with the following command line: | 25 | BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, |
18 | qemu-system-i386 \ | 26 | BlockDriverState *bs, QDict *options, |
19 | -icount shift=7,rr=record,rrfile=replay.bin \ | 27 | bool keep_old_opts); |
20 | - -drive file=disk.qcow2,if=none,id=img-direct \ | 28 | +void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue); |
21 | + -drive file=disk.qcow2,if=none,snapshot,id=img-direct \ | 29 | int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); |
22 | -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \ | 30 | int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, |
23 | -device ide-hd,drive=img-blkreplay \ | 31 | Error **errp); |
24 | -netdev user,id=net1 -device rtl8139,netdev=net1 \ | 32 | diff --git a/block.c b/block.c |
25 | @@ -XXX,XX +XXX,XX @@ Usage of the record/replay: | 33 | index XXXXXXX..XXXXXXX 100644 |
26 | * After recording, you can replay it by using another command line: | 34 | --- a/block.c |
27 | qemu-system-i386 \ | 35 | +++ b/block.c |
28 | -icount shift=7,rr=replay,rrfile=replay.bin \ | 36 | @@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, |
29 | - -drive file=disk.qcow2,if=none,id=img-direct \ | 37 | NULL, 0, keep_old_opts); |
30 | + -drive file=disk.qcow2,if=none,snapshot,id=img-direct \ | 38 | } |
31 | -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \ | 39 | |
32 | -device ide-hd,drive=img-blkreplay \ | 40 | +void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) |
33 | -netdev user,id=net1 -device rtl8139,netdev=net1 \ | 41 | +{ |
34 | @@ -XXX,XX +XXX,XX @@ Block devices record/replay module intercepts calls of | 42 | + if (bs_queue) { |
35 | bdrv coroutine functions at the top of block drivers stack. | 43 | + BlockReopenQueueEntry *bs_entry, *next; |
36 | To record and replay block operations the drive must be configured | 44 | + QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { |
37 | as following: | 45 | + qobject_unref(bs_entry->state.explicit_options); |
38 | - -drive file=disk.qcow2,if=none,id=img-direct | 46 | + qobject_unref(bs_entry->state.options); |
39 | + -drive file=disk.qcow2,if=none,snapshot,id=img-direct | 47 | + g_free(bs_entry); |
40 | -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay | 48 | + } |
41 | -device ide-hd,drive=img-blkreplay | 49 | + g_free(bs_queue); |
42 | 50 | + } | |
43 | @@ -XXX,XX +XXX,XX @@ This snapshot is created at start of recording and restored at start | 51 | +} |
44 | of replaying. It also can be loaded while replaying to roll back | ||
45 | the execution. | ||
46 | |||
47 | +'snapshot' flag of the disk image must be removed to save the snapshots | ||
48 | +in the overlay (or original image) instead of using the temporary overlay. | ||
49 | + -drive file=disk.ovl,if=none,id=img-direct | ||
50 | + -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay | ||
51 | + -device ide-hd,drive=img-blkreplay | ||
52 | + | 52 | + |
53 | Use QEMU monitor to create additional snapshots. 'savevm <name>' command | 53 | /* |
54 | created the snapshot and 'loadvm <name>' restores it. To prevent corruption | 54 | * Reopen multiple BlockDriverStates atomically & transactionally. |
55 | of the original disk image, use overlay files linked to the original images. | 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); | ||
62 | } | ||
63 | |||
64 | cleanup: | ||
65 | - QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { | ||
66 | - g_free(bs_entry); | ||
67 | - } | ||
68 | - g_free(bs_queue); | ||
69 | + bdrv_reopen_queue_free(bs_queue); | ||
70 | |||
71 | return ret; | ||
72 | } | ||
73 | @@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state) | ||
74 | /* set BDS specific flags now */ | ||
75 | qobject_unref(bs->explicit_options); | ||
76 | qobject_unref(bs->options); | ||
77 | + qobject_ref(reopen_state->explicit_options); | ||
78 | + qobject_ref(reopen_state->options); | ||
79 | |||
80 | bs->explicit_options = reopen_state->explicit_options; | ||
81 | bs->options = reopen_state->options; | ||
56 | -- | 82 | -- |
57 | 2.20.1 | 83 | 2.31.1 |
58 | 84 | ||
59 | 85 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | As the BlockReopenQueue can contain nodes in multiple AioContexts, only | |
2 | one of which may be locked when AIO_WAIT_WHILE() can be called, we can't | ||
3 | let the caller lock the right contexts. Instead, individually lock the | ||
4 | AioContext of a single node when iterating the queue. | ||
5 | |||
6 | Reintroduce bdrv_reopen() as a wrapper for reopening a single node that | ||
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> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | --- | ||
15 | include/block/block.h | 2 ++ | ||
16 | block.c | 49 ++++++++++++++++++++++++++++++++++++------- | ||
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, | ||
35 | diff --git a/block.c b/block.c | ||
36 | index XXXXXXX..XXXXXXX 100644 | ||
37 | --- a/block.c | ||
38 | +++ b/block.c | ||
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: | ||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | -int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, | ||
115 | - Error **errp) | ||
116 | +int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, | ||
117 | + Error **errp) | ||
118 | { | ||
119 | - int ret; | ||
120 | + AioContext *ctx = bdrv_get_aio_context(bs); | ||
121 | BlockReopenQueue *queue; | ||
122 | - QDict *opts = qdict_new(); | ||
123 | - | ||
124 | - qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only); | ||
125 | + int ret; | ||
126 | |||
127 | bdrv_subtree_drained_begin(bs); | ||
128 | - queue = bdrv_reopen_queue(NULL, bs, opts, true); | ||
129 | + if (ctx != qemu_get_aio_context()) { | ||
130 | + aio_context_release(ctx); | ||
131 | + } | ||
132 | + | ||
133 | + queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts); | ||
134 | ret = bdrv_reopen_multiple(queue, errp); | ||
135 | + | ||
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 | + | ||
154 | /* | ||
155 | * Take a BDRVReopenState and check if the value of 'backing' in the | ||
156 | * reopen_state->options QDict is valid or not. | ||
157 | diff --git a/block/replication.c b/block/replication.c | ||
158 | index XXXXXXX..XXXXXXX 100644 | ||
159 | --- a/block/replication.c | ||
160 | +++ b/block/replication.c | ||
161 | @@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, | ||
162 | } | ||
163 | |||
164 | if (reopen_queue) { | ||
165 | + AioContext *ctx = bdrv_get_aio_context(bs); | ||
166 | + if (ctx != qemu_get_aio_context()) { | ||
167 | + aio_context_release(ctx); | ||
168 | + } | ||
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; | ||
202 | - | ||
203 | - BlockReopenQueue *brq; | ||
204 | Error *local_err = NULL; | ||
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); | ||
219 | -- | ||
220 | 2.31.1 | ||
221 | |||
222 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | From: Alberto Garcia <berto@igalia.com> | |
2 | |||
3 | [ kwolf: Fixed AioContext locking ] | ||
4 | |||
5 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
8 | Message-Id: <20210708114709.206487-5-kwolf@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | qapi/block-core.json | 18 +++-- | ||
12 | blockdev.c | 81 ++++++++++--------- | ||
13 | tests/qemu-iotests/155 | 9 ++- | ||
14 | tests/qemu-iotests/165 | 4 +- | ||
15 | tests/qemu-iotests/245 | 27 ++++--- | ||
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 | ||
24 | index XXXXXXX..XXXXXXX 100644 | ||
25 | --- a/qapi/block-core.json | ||
26 | +++ b/qapi/block-core.json | ||
27 | @@ -XXX,XX +XXX,XX @@ | ||
28 | ## | ||
29 | # @x-blockdev-reopen: | ||
30 | # | ||
31 | -# Reopens a block device using the given set of options. Any option | ||
32 | -# not specified will be reset to its default value regardless of its | ||
33 | -# previous status. If an option cannot be changed or a particular | ||
34 | +# Reopens one or more block devices using the given set of options. | ||
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 | ||
69 | index XXXXXXX..XXXXXXX 100644 | ||
70 | --- a/blockdev.c | ||
71 | +++ b/blockdev.c | ||
72 | @@ -XXX,XX +XXX,XX @@ fail: | ||
73 | visit_free(v); | ||
74 | } | ||
75 | |||
76 | -void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp) | ||
77 | -{ | ||
78 | - BlockDriverState *bs; | ||
79 | - AioContext *ctx; | ||
80 | - QObject *obj; | ||
81 | - Visitor *v = qobject_output_visitor_new(&obj); | ||
82 | - BlockReopenQueue *queue; | ||
83 | - QDict *qdict; | ||
84 | +void qmp_x_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) | ||
85 | +{ | ||
86 | + BlockReopenQueue *queue = NULL; | ||
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; | ||
102 | + } | ||
103 | |||
104 | - /* Check for the selected node name */ | ||
105 | - if (!options->has_node_name) { | ||
106 | - error_setg(errp, "node-name not specified"); | ||
107 | - goto fail; | ||
108 | - } | ||
109 | + bs = bdrv_find_node(options->node_name); | ||
110 | + if (!bs) { | ||
111 | + error_setg(errp, "Failed to find node with node-name='%s'", | ||
112 | + options->node_name); | ||
113 | + goto fail; | ||
114 | + } | ||
115 | |||
116 | - bs = bdrv_find_node(options->node_name); | ||
117 | - if (!bs) { | ||
118 | - error_setg(errp, "Failed to find node with node-name='%s'", | ||
119 | - options->node_name); | ||
120 | - goto fail; | ||
121 | - } | ||
122 | + /* Put all options in a QDict and flatten it */ | ||
123 | + v = qobject_output_visitor_new(&obj); | ||
124 | + visit_type_BlockdevOptions(v, NULL, &options, &error_abort); | ||
125 | + visit_complete(v, &obj); | ||
126 | + visit_free(v); | ||
127 | |||
128 | - /* Put all options in a QDict and flatten it */ | ||
129 | - visit_type_BlockdevOptions(v, NULL, &options, &error_abort); | ||
130 | - visit_complete(v, &obj); | ||
131 | - qdict = qobject_to(QDict, obj); | ||
132 | + qdict = qobject_to(QDict, obj); | ||
133 | |||
134 | - qdict_flatten(qdict); | ||
135 | + qdict_flatten(qdict); | ||
136 | |||
137 | - /* Perform the reopen operation */ | ||
138 | - ctx = bdrv_get_aio_context(bs); | ||
139 | - aio_context_acquire(ctx); | ||
140 | - bdrv_subtree_drained_begin(bs); | ||
141 | - aio_context_release(ctx); | ||
142 | + ctx = bdrv_get_aio_context(bs); | ||
143 | + aio_context_acquire(ctx); | ||
144 | |||
145 | - queue = bdrv_reopen_queue(NULL, bs, qdict, false); | ||
146 | - bdrv_reopen_multiple(queue, errp); | ||
147 | + bdrv_subtree_drained_begin(bs); | ||
148 | + queue = bdrv_reopen_queue(queue, bs, qdict, false); | ||
149 | + drained = g_slist_prepend(drained, bs); | ||
150 | |||
151 | - ctx = bdrv_get_aio_context(bs); | ||
152 | - aio_context_acquire(ctx); | ||
153 | - bdrv_subtree_drained_end(bs); | ||
154 | - aio_context_release(ctx); | ||
155 | + aio_context_release(ctx); | ||
156 | + } | ||
157 | + | ||
158 | + /* Perform the reopen operation */ | ||
159 | + bdrv_reopen_multiple(queue, errp); | ||
160 | + queue = NULL; | ||
161 | |||
162 | fail: | ||
163 | - visit_free(v); | ||
164 | + bdrv_reopen_queue_free(queue); | ||
165 | + g_slist_free_full(drained, (GDestroyNotify) bdrv_subtree_drained_end); | ||
166 | } | ||
167 | |||
168 | void qmp_blockdev_del(const char *node_name, Error **errp) | ||
169 | diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 | ||
170 | index XXXXXXX..XXXXXXX 100755 | ||
171 | --- a/tests/qemu-iotests/155 | ||
172 | +++ b/tests/qemu-iotests/155 | ||
173 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorReopen(MirrorBaseClass): | ||
174 | result = self.vm.qmp('blockdev-add', node_name="backing", | ||
175 | driver="null-co") | ||
176 | self.assert_qmp(result, 'return', {}) | ||
177 | - result = self.vm.qmp('x-blockdev-reopen', node_name="target", | ||
178 | - driver=iotests.imgfmt, file="target-file", | ||
179 | - backing="backing") | ||
180 | + result = self.vm.qmp('x-blockdev-reopen', options=[{ | ||
181 | + 'node-name': "target", | ||
182 | + 'driver': iotests.imgfmt, | ||
183 | + 'file': "target-file", | ||
184 | + 'backing': "backing" | ||
185 | + }]) | ||
186 | self.assert_qmp(result, 'return', {}) | ||
187 | |||
188 | class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen): | ||
189 | diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 | ||
190 | index XXXXXXX..XXXXXXX 100755 | ||
191 | --- a/tests/qemu-iotests/165 | ||
192 | +++ b/tests/qemu-iotests/165 | ||
193 | @@ -XXX,XX +XXX,XX @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): | ||
194 | assert sha256_1 == self.getSha256() | ||
195 | |||
196 | # Reopen to RW | ||
197 | - result = self.vm.qmp('x-blockdev-reopen', **{ | ||
198 | + result = self.vm.qmp('x-blockdev-reopen', options=[{ | ||
199 | 'node-name': 'node0', | ||
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. | ||
211 | diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 | ||
212 | index XXXXXXX..XXXXXXX 100755 | ||
213 | --- a/tests/qemu-iotests/245 | ||
214 | +++ b/tests/qemu-iotests/245 | ||
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") | ||
256 | self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'") | ||
257 | self.reopen(opts, {'driver': ''}, "Invalid parameter ''") | ||
258 | - self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string") | ||
259 | + self.reopen(opts, {'driver': None}, "Invalid parameter type for 'options[0].driver', expected: string") | ||
260 | 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=''") | ||
262 | self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef") | ||
263 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase): | ||
264 | self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'") | ||
265 | self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'") | ||
266 | self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") | ||
267 | - self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string") | ||
268 | + self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'options[0].file.filename', expected: string") | ||
269 | |||
270 | # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it | ||
271 | del opts['node-name'] | ||
272 | diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248 | ||
273 | index XXXXXXX..XXXXXXX 100755 | ||
274 | --- a/tests/qemu-iotests/248 | ||
275 | +++ b/tests/qemu-iotests/248 | ||
276 | @@ -XXX,XX +XXX,XX @@ vm.get_qmp_events() | ||
277 | |||
278 | del blockdev_opts['file']['size'] | ||
279 | vm.qmp_log('x-blockdev-reopen', filters=[filter_qmp_testfiles], | ||
280 | - **blockdev_opts) | ||
281 | + options = [ blockdev_opts ]) | ||
282 | |||
283 | vm.qmp_log('block-job-resume', device='drive0') | ||
284 | vm.event_wait('JOB_STATUS_CHANGE', timeout=1.0, | ||
285 | diff --git a/tests/qemu-iotests/248.out b/tests/qemu-iotests/248.out | ||
286 | index XXXXXXX..XXXXXXX 100644 | ||
287 | --- a/tests/qemu-iotests/248.out | ||
288 | +++ b/tests/qemu-iotests/248.out | ||
289 | @@ -XXX,XX +XXX,XX @@ | ||
290 | {"return": {}} | ||
291 | {"execute": "blockdev-mirror", "arguments": {"device": "drive0", "on-target-error": "enospc", "sync": "full", "target": "target"}} | ||
292 | {"return": {}} | ||
293 | -{"execute": "x-blockdev-reopen", "arguments": {"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}} | ||
294 | +{"execute": "x-blockdev-reopen", "arguments": {"options": [{"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}]}} | ||
295 | {"return": {}} | ||
296 | {"execute": "block-job-resume", "arguments": {"device": "drive0"}} | ||
297 | {"return": {}} | ||
298 | diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296 | ||
299 | index XXXXXXX..XXXXXXX 100755 | ||
300 | --- a/tests/qemu-iotests/296 | ||
301 | +++ b/tests/qemu-iotests/296 | ||
302 | @@ -XXX,XX +XXX,XX @@ class EncryptionSetupTestCase(iotests.QMPTestCase): | ||
303 | |||
304 | command = 'x-blockdev-reopen' if reOpen else 'blockdev-add' | ||
305 | |||
306 | - result = vm.qmp(command, ** | ||
307 | - { | ||
308 | + opts = { | ||
309 | 'driver': iotests.imgfmt, | ||
310 | 'node-name': id, | ||
311 | 'read-only': readOnly, | ||
312 | @@ -XXX,XX +XXX,XX @@ class EncryptionSetupTestCase(iotests.QMPTestCase): | ||
313 | 'filename': test_img, | ||
314 | } | ||
315 | } | ||
316 | - ) | ||
317 | + | ||
318 | + if reOpen: | ||
319 | + result = vm.qmp(command, options=[opts]) | ||
320 | + else: | ||
321 | + result = vm.qmp(command, **opts) | ||
322 | self.assert_qmp(result, 'return', {}) | ||
323 | |||
324 | |||
325 | diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298 | ||
326 | index XXXXXXX..XXXXXXX 100755 | ||
327 | --- a/tests/qemu-iotests/298 | ||
328 | +++ b/tests/qemu-iotests/298 | ||
329 | @@ -XXX,XX +XXX,XX @@ class TestPreallocateFilter(TestPreallocateBase): | ||
330 | self.check_big() | ||
331 | |||
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)) | ||
383 | -- | ||
384 | 2.31.1 | ||
385 | |||
386 | diff view generated by jsdifflib |
1 | From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | This patch disables setting '-snapshot' option on by default | 3 | This test swaps the images used by two active block devices. |
4 | in record/replay mode. This is needed for creating vmstates in record | ||
5 | and replay modes. | ||
6 | 4 | ||
7 | Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> | 5 | This is now possible thanks to the new ability to run |
8 | Acked-by: Kevin Wolf <kwolf@redhat.com> | 6 | x-blockdev-reopen on multiple devices at the same time. |
7 | |||
8 | Signed-off-by: Alberto Garcia <berto@igalia.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> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 13 | --- |
11 | vl.c | 10 ++++++++-- | 14 | tests/qemu-iotests/245 | 47 ++++++++++++++++++++++++++++++++++++++ |
12 | 1 file changed, 8 insertions(+), 2 deletions(-) | 15 | tests/qemu-iotests/245.out | 4 ++-- |
16 | 2 files changed, 49 insertions(+), 2 deletions(-) | ||
13 | 17 | ||
14 | diff --git a/vl.c b/vl.c | 18 | diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 |
19 | index XXXXXXX..XXXXXXX 100755 | ||
20 | --- a/tests/qemu-iotests/245 | ||
21 | +++ b/tests/qemu-iotests/245 | ||
22 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase): | ||
23 | '-c', 'read -P 0x40 0x40008 1', | ||
24 | '-c', 'read -P 0x80 0x40010 1', hd_path[0]) | ||
25 | |||
26 | + # Swap the disk images of two active block devices | ||
27 | + def test_swap_files(self): | ||
28 | + # Add hd0 and hd2 (none of them with backing files) | ||
29 | + opts0 = hd_opts(0) | ||
30 | + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts0) | ||
31 | + self.assert_qmp(result, 'return', {}) | ||
32 | + | ||
33 | + opts2 = hd_opts(2) | ||
34 | + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2) | ||
35 | + self.assert_qmp(result, 'return', {}) | ||
36 | + | ||
37 | + # Write different data to both block devices | ||
38 | + self.run_qemu_io("hd0", "write -P 0xa0 0 1k") | ||
39 | + self.run_qemu_io("hd2", "write -P 0xa2 0 1k") | ||
40 | + | ||
41 | + # Check that the data reads correctly | ||
42 | + self.run_qemu_io("hd0", "read -P 0xa0 0 1k") | ||
43 | + self.run_qemu_io("hd2", "read -P 0xa2 0 1k") | ||
44 | + | ||
45 | + # It's not possible to make a block device use an image that | ||
46 | + # is already being used by the other device. | ||
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).") | ||
57 | + | ||
58 | + # But we can swap the images if we reopen both devices at the | ||
59 | + # same time | ||
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") | ||
65 | + | ||
66 | + # And we can of course come back to the original state | ||
67 | + opts0['file'] = 'hd0-file' | ||
68 | + opts2['file'] = 'hd2-file' | ||
69 | + self.reopenMultiple([opts0, opts2]) | ||
70 | + self.run_qemu_io("hd0", "read -P 0xa0 0 1k") | ||
71 | + self.run_qemu_io("hd2", "read -P 0xa2 0 1k") | ||
72 | + | ||
73 | # Misc reopen tests with different block drivers | ||
74 | @iotests.skip_if_unsupported(['quorum', 'throttle']) | ||
75 | def test_misc_drivers(self): | ||
76 | diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out | ||
15 | index XXXXXXX..XXXXXXX 100644 | 77 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/vl.c | 78 | --- a/tests/qemu-iotests/245.out |
17 | +++ b/vl.c | 79 | +++ b/tests/qemu-iotests/245.out |
18 | @@ -XXX,XX +XXX,XX @@ static void configure_blockdev(BlockdevOptionsQueue *bdo_queue, | 80 | @@ -XXX,XX +XXX,XX @@ read 1/1 bytes at offset 262152 |
19 | qapi_free_BlockdevOptions(bdo->bdo); | 81 | read 1/1 bytes at offset 262160 |
20 | g_free(bdo); | 82 | 1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
21 | } | 83 | |
22 | - if (snapshot || replay_mode != REPLAY_MODE_NONE) { | 84 | -.............. |
23 | + if (snapshot) { | 85 | +............... |
24 | qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, | 86 | ---------------------------------------------------------------------- |
25 | NULL, NULL); | 87 | -Ran 24 tests |
26 | } | 88 | +Ran 25 tests |
27 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) | 89 | |
28 | drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS); | 90 | OK |
29 | break; | ||
30 | case QEMU_OPTION_snapshot: | ||
31 | - snapshot = 1; | ||
32 | + { | ||
33 | + Error *blocker = NULL; | ||
34 | + snapshot = 1; | ||
35 | + error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, | ||
36 | + "-snapshot"); | ||
37 | + replay_add_blocker(blocker); | ||
38 | + } | ||
39 | break; | ||
40 | case QEMU_OPTION_numa: | ||
41 | opts = qemu_opts_parse_noisily(qemu_find_opts("numa"), | ||
42 | -- | 91 | -- |
43 | 2.20.1 | 92 | 2.31.1 |
44 | 93 | ||
45 | 94 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 1 | From: Alberto Garcia <berto@igalia.com> | |
2 | |||
3 | This patch drops the 'x-' prefix from x-blockdev-reopen. | ||
4 | |||
5 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
8 | Message-Id: <20210708114709.206487-7-kwolf@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | qapi/block-core.json | 6 +++--- | ||
12 | blockdev.c | 2 +- | ||
13 | tests/qemu-iotests/155 | 2 +- | ||
14 | tests/qemu-iotests/165 | 2 +- | ||
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 | ||
24 | index XXXXXXX..XXXXXXX 100644 | ||
25 | --- a/qapi/block-core.json | ||
26 | +++ b/qapi/block-core.json | ||
27 | @@ -XXX,XX +XXX,XX @@ | ||
28 | { 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } | ||
29 | |||
30 | ## | ||
31 | -# @x-blockdev-reopen: | ||
32 | +# @blockdev-reopen: | ||
33 | # | ||
34 | # Reopens one or more block devices using the given set of options. | ||
35 | # Any option not specified will be reset to its default value regardless | ||
36 | @@ -XXX,XX +XXX,XX @@ | ||
37 | # image does not have a default backing file name as part of its | ||
38 | # metadata. | ||
39 | # | ||
40 | -# Since: 4.0 | ||
41 | +# Since: 6.1 | ||
42 | ## | ||
43 | -{ 'command': 'x-blockdev-reopen', | ||
44 | +{ 'command': 'blockdev-reopen', | ||
45 | 'data': { 'options': ['BlockdevOptions'] } } | ||
46 | |||
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': { | ||
87 | diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 | ||
88 | index XXXXXXX..XXXXXXX 100755 | ||
89 | --- a/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> | ||
100 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase): | ||
101 | "Expected output of %d qemu-io commands, found %d" % | ||
102 | (found, self.total_io_cmds)) | ||
103 | |||
104 | - # Run x-blockdev-reopen on a list of block devices | ||
105 | + # Run blockdev-reopen on a list of block devices | ||
106 | def reopenMultiple(self, opts, errmsg = None): | ||
107 | - result = self.vm.qmp('x-blockdev-reopen', conv_keys=False, options=opts) | ||
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): | ||
120 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase): | ||
121 | self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") | ||
122 | self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'options[0].file.filename', expected: string") | ||
123 | |||
124 | - # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it | ||
125 | + # node-name is optional in BlockdevOptions, but blockdev-reopen needs it | ||
126 | del opts['node-name'] | ||
127 | self.reopen(opts, {}, "node-name not specified") | ||
128 | |||
129 | diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248 | ||
130 | index XXXXXXX..XXXXXXX 100755 | ||
131 | --- a/tests/qemu-iotests/248 | ||
132 | +++ b/tests/qemu-iotests/248 | ||
133 | @@ -XXX,XX +XXX,XX @@ vm.event_wait('JOB_STATUS_CHANGE', timeout=3.0, | ||
134 | vm.get_qmp_events() | ||
135 | |||
136 | del blockdev_opts['file']['size'] | ||
137 | -vm.qmp_log('x-blockdev-reopen', filters=[filter_qmp_testfiles], | ||
138 | +vm.qmp_log('blockdev-reopen', filters=[filter_qmp_testfiles], | ||
139 | options = [ blockdev_opts ]) | ||
140 | |||
141 | vm.qmp_log('block-job-resume', device='drive0') | ||
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 | |||
203 | -- | ||
204 | 2.31.1 | ||
205 | |||
206 | diff view generated by jsdifflib |