1 | The following changes since commit 191710c221f65b1542f6ea7fa4d30dde6e134fd7: | 1 | The following changes since commit d922088eb4ba6bc31a99f17b32cf75e59dd306cd: |
---|---|---|---|
2 | 2 | ||
3 | Merge tag 'pull-request-2023-12-20' of https://gitlab.com/thuth/qemu into staging (2023-12-20 09:40:16 -0500) | 3 | Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging (2025-02-03 13:42:02 -0500) |
4 | 4 | ||
5 | are available in the Git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | https://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | https://repo.or.cz/qemu/kevin.git tags/for-upstream |
8 | 8 | ||
9 | for you to fetch changes up to ec25ed82df474caea009df2ef948bfed4e6d81fd: | 9 | for you to fetch changes up to fc4e394b2887e15d5f83994e4fc7b26c895c627a: |
10 | 10 | ||
11 | virtio-blk: add iothread-vq-mapping parameter (2023-12-21 22:00:38 +0100) | 11 | block: remove unused BLOCK_OP_TYPE_DATAPLANE (2025-02-06 14:51:10 +0100) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches | 14 | Block layer patches |
15 | 15 | ||
16 | - virtio-blk: Multiqueue support (configurable iothread per queue) | 16 | - Managing inactive nodes (enables QSD migration with shared storage) |
17 | - Made NBD export and hw/scsi thread-safe without AioContext lock | 17 | - Fix swapped values for BLOCK_IO_ERROR 'device' and 'qom-path' |
18 | - Fix crash when loading snapshot on inactive node | 18 | - vpc: Read images exported from Azure correctly |
19 | - scripts/qemu-gdb: Support coroutine dumps in coredumps | ||
20 | - Minor cleanups | ||
19 | 21 | ||
20 | ---------------------------------------------------------------- | 22 | ---------------------------------------------------------------- |
21 | Kevin Wolf (3): | 23 | Fabiano Rosas (1): |
22 | block: Fix crash when loading snapshot on inactive node | 24 | block: Fix leak in send_qmp_error_event |
23 | vl: Improve error message for conflicting -incoming and -loadvm | ||
24 | iotests: Basic tests for internal snapshots | ||
25 | 25 | ||
26 | Stefan Hajnoczi (30): | 26 | Kevin Wolf (16): |
27 | nbd/server: avoid per-NBDRequest nbd_client_get/put() | 27 | block: Add 'active' field to BlockDeviceInfo |
28 | nbd/server: only traverse NBDExport->clients from main loop thread | 28 | block: Allow inactivating already inactive nodes |
29 | nbd/server: introduce NBDClient->lock to protect fields | 29 | block: Inactivate external snapshot overlays when necessary |
30 | block/file-posix: set up Linux AIO and io_uring in the current thread | 30 | migration/block-active: Remove global active flag |
31 | virtio-blk: add lock to protect s->rq | 31 | block: Don't attach inactive child to active node |
32 | virtio-blk: don't lock AioContext in the completion code path | 32 | block: Fix crash on block_resize on inactive node |
33 | virtio-blk: don't lock AioContext in the submission code path | 33 | block: Add option to create inactive nodes |
34 | scsi: only access SCSIDevice->requests from one thread | 34 | block: Add blockdev-set-active QMP command |
35 | virtio-scsi: don't lock AioContext around virtio_queue_aio_attach_host_notifier() | 35 | block: Support inactive nodes in blk_insert_bs() |
36 | scsi: don't lock AioContext in I/O code path | 36 | block/export: Don't ignore image activation error in blk_exp_add() |
37 | dma-helpers: don't lock AioContext in dma_blk_cb() | 37 | block: Drain nodes before inactivating them |
38 | virtio-scsi: replace AioContext lock with tmf_bh_lock | 38 | block/export: Add option to allow export of inactive nodes |
39 | scsi: assert that callbacks run in the correct AioContext | 39 | nbd/server: Support inactive nodes |
40 | tests: remove aio_context_acquire() tests | 40 | iotests: Add filter_qtest() |
41 | aio: make aio_context_acquire()/aio_context_release() a no-op | 41 | iotests: Add qsd-migrate case |
42 | graph-lock: remove AioContext locking | 42 | iotests: Add (NBD-based) tests for inactive nodes |
43 | block: remove AioContext locking | ||
44 | block: remove bdrv_co_lock() | ||
45 | scsi: remove AioContext locking | ||
46 | aio-wait: draw equivalence between AIO_WAIT_WHILE() and AIO_WAIT_WHILE_UNLOCKED() | ||
47 | aio: remove aio_context_acquire()/aio_context_release() API | ||
48 | docs: remove AioContext lock from IOThread docs | ||
49 | scsi: remove outdated AioContext lock comment | ||
50 | job: remove outdated AioContext locking comments | ||
51 | block: remove outdated AioContext locking comments | ||
52 | block-coroutine-wrapper: use qemu_get_current_aio_context() | ||
53 | string-output-visitor: show structs as "<omitted>" | ||
54 | qdev-properties: alias all object class properties | ||
55 | qdev: add IOThreadVirtQueueMappingList property type | ||
56 | virtio-blk: add iothread-vq-mapping parameter | ||
57 | 43 | ||
58 | qapi/virtio.json | 29 ++ | 44 | Peter Krempa (1): |
59 | docs/devel/multiple-iothreads.txt | 47 +-- | 45 | block-backend: Fix argument order when calling 'qapi_event_send_block_io_error()' |
60 | hw/block/dataplane/virtio-blk.h | 3 + | 46 | |
61 | include/block/aio-wait.h | 16 +- | 47 | Peter Xu (3): |
62 | include/block/aio.h | 17 - | 48 | scripts/qemu-gdb: Always do full stack dump for python errors |
63 | include/block/block-common.h | 3 - | 49 | scripts/qemu-gdb: Simplify fs_base fetching for coroutines |
64 | include/block/block-global-state.h | 23 +- | 50 | scripts/qemu-gdb: Support coroutine dumps in coredumps |
65 | include/block/block-io.h | 12 +- | 51 | |
66 | include/block/block_int-common.h | 2 - | 52 | Philippe Mathieu-Daudé (1): |
67 | include/block/graph-lock.h | 21 +- | 53 | block: Improve blk_get_attached_dev_id() docstring |
68 | include/block/snapshot.h | 2 - | 54 | |
69 | include/hw/qdev-properties-system.h | 5 + | 55 | Stefan Hajnoczi (1): |
70 | include/hw/qdev-properties.h | 4 +- | 56 | block: remove unused BLOCK_OP_TYPE_DATAPLANE |
71 | include/hw/scsi/scsi.h | 7 +- | 57 | |
72 | include/hw/virtio/virtio-blk.h | 5 +- | 58 | Vitaly Kuznetsov (2): |
73 | include/hw/virtio/virtio-scsi.h | 17 +- | 59 | vpc: Split off vpc_ignore_current_size() helper |
74 | include/qapi/string-output-visitor.h | 6 +- | 60 | vpc: Read images exported from Azure correctly |
75 | include/qemu/job.h | 20 -- | 61 | |
76 | block.c | 363 +++------------------ | 62 | qapi/block-core.json | 44 +++- |
77 | block/backup.c | 4 +- | 63 | qapi/block-export.json | 10 +- |
78 | block/blklogwrites.c | 8 +- | 64 | include/block/block-common.h | 2 +- |
79 | block/blkverify.c | 4 +- | 65 | include/block/block-global-state.h | 6 + |
80 | block/block-backend.c | 33 +- | 66 | include/block/export.h | 3 + |
81 | block/commit.c | 16 +- | 67 | include/system/block-backend-io.h | 7 + |
82 | block/copy-before-write.c | 22 +- | 68 | migration/migration.h | 3 - |
83 | block/export/export.c | 22 +- | 69 | block.c | 64 +++++- |
84 | block/export/vhost-user-blk-server.c | 4 - | 70 | block/block-backend.c | 32 ++- |
85 | block/file-posix.c | 99 +++--- | 71 | block/export/export.c | 29 ++- |
86 | block/graph-lock.c | 44 +-- | 72 | block/monitor/block-hmp-cmds.c | 5 +- |
87 | block/io.c | 45 +-- | 73 | block/qapi.c | 1 + |
88 | block/mirror.c | 41 +-- | 74 | block/replication.c | 1 - |
89 | block/monitor/bitmap-qmp-cmds.c | 20 +- | 75 | block/vpc.c | 65 +++--- |
90 | block/monitor/block-hmp-cmds.c | 29 -- | 76 | blockdev.c | 48 ++++ |
91 | block/qapi-sysemu.c | 27 +- | 77 | blockjob.c | 2 - |
92 | block/qapi.c | 18 +- | 78 | hw/block/virtio-blk.c | 9 - |
93 | block/qcow2.c | 4 +- | 79 | hw/scsi/virtio-scsi.c | 3 - |
94 | block/quorum.c | 8 +- | 80 | migration/block-active.c | 46 ---- |
95 | block/raw-format.c | 5 - | 81 | migration/migration.c | 8 - |
96 | block/replication.c | 72 +--- | 82 | nbd/server.c | 17 ++ |
97 | block/snapshot.c | 30 +- | 83 | scripts/qemu-gdb.py | 2 + |
98 | block/stream.c | 12 +- | 84 | scripts/qemugdb/coroutine.py | 102 ++++++--- |
99 | block/vmdk.c | 20 +- | 85 | tests/qemu-iotests/iotests.py | 8 + |
100 | block/write-threshold.c | 6 - | 86 | tests/qemu-iotests/041 | 4 +- |
101 | blockdev.c | 320 ++++-------------- | 87 | tests/qemu-iotests/165 | 4 +- |
102 | blockjob.c | 30 +- | 88 | tests/qemu-iotests/184.out | 2 + |
103 | hw/block/dataplane/virtio-blk.c | 165 +++++++--- | 89 | tests/qemu-iotests/191.out | 16 ++ |
104 | hw/block/dataplane/xen-block.c | 17 +- | 90 | tests/qemu-iotests/273.out | 5 + |
105 | hw/block/virtio-blk.c | 209 +++++++----- | 91 | tests/qemu-iotests/tests/copy-before-write | 3 +- |
106 | hw/core/qdev-properties-system.c | 55 +++- | 92 | tests/qemu-iotests/tests/inactive-node-nbd | 303 +++++++++++++++++++++++++ |
107 | hw/core/qdev-properties.c | 18 +- | 93 | tests/qemu-iotests/tests/inactive-node-nbd.out | 239 +++++++++++++++++++ |
108 | hw/scsi/scsi-bus.c | 183 +++++++---- | 94 | tests/qemu-iotests/tests/migrate-bitmaps-test | 7 +- |
109 | hw/scsi/scsi-disk.c | 67 +--- | 95 | tests/qemu-iotests/tests/qsd-migrate | 140 ++++++++++++ |
110 | hw/scsi/scsi-generic.c | 20 +- | 96 | tests/qemu-iotests/tests/qsd-migrate.out | 59 +++++ |
111 | hw/scsi/virtio-scsi-dataplane.c | 8 +- | 97 | 35 files changed, 1133 insertions(+), 166 deletions(-) |
112 | hw/scsi/virtio-scsi.c | 80 ++--- | 98 | create mode 100755 tests/qemu-iotests/tests/inactive-node-nbd |
113 | job.c | 16 - | 99 | create mode 100644 tests/qemu-iotests/tests/inactive-node-nbd.out |
114 | migration/block.c | 34 +- | 100 | create mode 100755 tests/qemu-iotests/tests/qsd-migrate |
115 | migration/migration-hmp-cmds.c | 3 - | 101 | create mode 100644 tests/qemu-iotests/tests/qsd-migrate.out |
116 | migration/savevm.c | 22 -- | 102 | |
117 | nbd/server.c | 208 +++++++++--- | 103 | |
118 | net/colo-compare.c | 2 - | ||
119 | qapi/string-output-visitor.c | 16 + | ||
120 | qemu-img.c | 4 - | ||
121 | qemu-io.c | 10 +- | ||
122 | qemu-nbd.c | 2 - | ||
123 | replay/replay-debugging.c | 4 - | ||
124 | system/dma-helpers.c | 10 +- | ||
125 | system/vl.c | 4 + | ||
126 | tests/unit/test-aio.c | 67 +--- | ||
127 | tests/unit/test-bdrv-drain.c | 91 ++---- | ||
128 | tests/unit/test-bdrv-graph-mod.c | 26 +- | ||
129 | tests/unit/test-block-iothread.c | 31 -- | ||
130 | tests/unit/test-blockjob.c | 137 -------- | ||
131 | tests/unit/test-replication.c | 11 - | ||
132 | util/async.c | 14 - | ||
133 | util/vhost-user-server.c | 3 - | ||
134 | scripts/block-coroutine-wrapper.py | 13 +- | ||
135 | tests/qemu-iotests/202 | 2 +- | ||
136 | tests/qemu-iotests/203 | 3 +- | ||
137 | tests/qemu-iotests/tests/qcow2-internal-snapshots | 170 ++++++++++ | ||
138 | .../tests/qcow2-internal-snapshots.out | 107 ++++++ | ||
139 | tests/tsan/suppressions.tsan | 1 - | ||
140 | 82 files changed, 1337 insertions(+), 2041 deletions(-) | ||
141 | create mode 100755 tests/qemu-iotests/tests/qcow2-internal-snapshots | ||
142 | create mode 100644 tests/qemu-iotests/tests/qcow2-internal-snapshots.out | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Vitaly Kuznetsov <vkuznets@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The NBD clients list is currently accessed from both the export | 3 | In preparation to making changes to the logic deciding whether CHS or |
4 | AioContext and the main loop thread. When the AioContext lock is removed | 4 | 'current_size' need to be used in determining the image size, split off |
5 | there will be nothing protecting the clients list. | 5 | vpc_ignore_current_size() helper. |
6 | 6 | ||
7 | Adding a lock around the clients list is tricky because NBDClient | 7 | No functional change intended. |
8 | structs are refcounted and may be freed from the export AioContext or | ||
9 | the main loop thread. nbd_export_request_shutdown() -> client_close() -> | ||
10 | nbd_client_put() is also tricky because the list lock would be held | ||
11 | while indirectly dropping references to NDBClients. | ||
12 | 8 | ||
13 | A simpler approach is to only allow nbd_client_put() and client_close() | 9 | Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> |
14 | calls from the main loop thread. Then the NBD clients list is only | 10 | Message-ID: <20241212134504.1983757-2-vkuznets@redhat.com> |
15 | accessed from the main loop thread and no fancy locking is needed. | 11 | Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> |
16 | |||
17 | nbd_trip() just needs to reschedule itself in the main loop AioContext | ||
18 | before calling nbd_client_put() and client_close(). This costs more CPU | ||
19 | cycles per NBD request so add nbd_client_put_nonzero() to optimize the | ||
20 | common case where more references to NBDClient remain. | ||
21 | |||
22 | Note that nbd_client_get() can still be called from either thread, so | ||
23 | make NBDClient->refcount atomic. | ||
24 | |||
25 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
26 | Message-ID: <20231221192452.1785567-6-stefanha@redhat.com> | ||
27 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 12 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
28 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
29 | --- | 14 | --- |
30 | nbd/server.c | 61 +++++++++++++++++++++++++++++++++++++++++++--------- | 15 | block/vpc.c | 67 +++++++++++++++++++++++++++++------------------------ |
31 | 1 file changed, 51 insertions(+), 10 deletions(-) | 16 | 1 file changed, 37 insertions(+), 30 deletions(-) |
32 | 17 | ||
33 | diff --git a/nbd/server.c b/nbd/server.c | 18 | diff --git a/block/vpc.c b/block/vpc.c |
34 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
35 | --- a/nbd/server.c | 20 | --- a/block/vpc.c |
36 | +++ b/nbd/server.c | 21 | +++ b/block/vpc.c |
37 | @@ -XXX,XX +XXX,XX @@ struct NBDMetaContexts { | 22 | @@ -XXX,XX +XXX,XX @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts, |
38 | }; | ||
39 | |||
40 | struct NBDClient { | ||
41 | - int refcount; | ||
42 | + int refcount; /* atomic */ | ||
43 | void (*close_fn)(NBDClient *client, bool negotiated); | ||
44 | |||
45 | NBDExport *exp; | ||
46 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_receive_request(NBDClient *client, NBDRequest *reque | ||
47 | |||
48 | #define MAX_NBD_REQUESTS 16 | ||
49 | |||
50 | +/* Runs in export AioContext and main loop thread */ | ||
51 | void nbd_client_get(NBDClient *client) | ||
52 | { | ||
53 | - client->refcount++; | ||
54 | + qatomic_inc(&client->refcount); | ||
55 | } | ||
56 | |||
57 | void nbd_client_put(NBDClient *client) | ||
58 | { | ||
59 | - if (--client->refcount == 0) { | ||
60 | + assert(qemu_in_main_thread()); | ||
61 | + | ||
62 | + if (qatomic_fetch_dec(&client->refcount) == 1) { | ||
63 | /* The last reference should be dropped by client->close, | ||
64 | * which is called by client_close. | ||
65 | */ | ||
66 | @@ -XXX,XX +XXX,XX @@ void nbd_client_put(NBDClient *client) | ||
67 | } | 23 | } |
68 | } | 24 | } |
69 | 25 | ||
70 | +/* | 26 | +/* |
71 | + * Tries to release the reference to @client, but only if other references | 27 | + * Microsoft Virtual PC and Microsoft Hyper-V produce and read |
72 | + * remain. This is an optimization for the common case where we want to avoid | 28 | + * VHD image sizes differently. VPC will rely on CHS geometry, |
73 | + * the expense of scheduling nbd_client_put() in the main loop thread. | 29 | + * while Hyper-V and disk2vhd use the size specified in the footer. |
74 | + * | 30 | + * |
75 | + * Returns true upon success or false if the reference was not released because | 31 | + * We use a couple of approaches to try and determine the correct method: |
76 | + * it is the last reference. | 32 | + * look at the Creator App field, and look for images that have CHS |
33 | + * geometry that is the maximum value. | ||
34 | + * | ||
35 | + * If the CHS geometry is the maximum CHS geometry, then we assume that | ||
36 | + * the size is the footer->current_size to avoid truncation. Otherwise, | ||
37 | + * we follow the table based on footer->creator_app: | ||
38 | + * | ||
39 | + * Known creator apps: | ||
40 | + * 'vpc ' : CHS Virtual PC (uses disk geometry) | ||
41 | + * 'qemu' : CHS QEMU (uses disk geometry) | ||
42 | + * 'qem2' : current_size QEMU (uses current_size) | ||
43 | + * 'win ' : current_size Hyper-V | ||
44 | + * 'd2v ' : current_size Disk2vhd | ||
45 | + * 'tap\0' : current_size XenServer | ||
46 | + * 'CTXS' : current_size XenConverter | ||
47 | + * | ||
48 | + * The user can override the table values via drive options, however | ||
49 | + * even with an override we will still use current_size for images | ||
50 | + * that have CHS geometry of the maximum size. | ||
77 | + */ | 51 | + */ |
78 | +static bool nbd_client_put_nonzero(NBDClient *client) | 52 | +static bool vpc_ignore_current_size(VHDFooter *footer) |
79 | +{ | 53 | +{ |
80 | + int old = qatomic_read(&client->refcount); | 54 | + return !!strncmp(footer->creator_app, "win ", 4) && |
81 | + int expected; | 55 | + !!strncmp(footer->creator_app, "qem2", 4) && |
82 | + | 56 | + !!strncmp(footer->creator_app, "d2v ", 4) && |
83 | + do { | 57 | + !!strncmp(footer->creator_app, "CTXS", 4) && |
84 | + if (old == 1) { | 58 | + !!memcmp(footer->creator_app, "tap", 4)); |
85 | + return false; | ||
86 | + } | ||
87 | + | ||
88 | + expected = old; | ||
89 | + old = qatomic_cmpxchg(&client->refcount, expected, expected - 1); | ||
90 | + } while (old != expected); | ||
91 | + | ||
92 | + return true; | ||
93 | +} | 59 | +} |
94 | + | 60 | + |
95 | static void client_close(NBDClient *client, bool negotiated) | 61 | static int vpc_open(BlockDriverState *bs, QDict *options, int flags, |
62 | Error **errp) | ||
96 | { | 63 | { |
97 | + assert(qemu_in_main_thread()); | 64 | @@ -XXX,XX +XXX,XX @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, |
98 | + | 65 | bs->total_sectors = (int64_t) |
99 | if (client->closing) { | 66 | be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; |
100 | return; | 67 | |
101 | } | 68 | - /* Microsoft Virtual PC and Microsoft Hyper-V produce and read |
102 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client, | 69 | - * VHD image sizes differently. VPC will rely on CHS geometry, |
103 | static coroutine_fn void nbd_trip(void *opaque) | 70 | - * while Hyper-V and disk2vhd use the size specified in the footer. |
104 | { | 71 | - * |
105 | NBDClient *client = opaque; | 72 | - * We use a couple of approaches to try and determine the correct method: |
106 | - NBDRequestData *req; | 73 | - * look at the Creator App field, and look for images that have CHS |
107 | + NBDRequestData *req = NULL; | 74 | - * geometry that is the maximum value. |
108 | NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */ | 75 | - * |
109 | int ret; | 76 | - * If the CHS geometry is the maximum CHS geometry, then we assume that |
110 | Error *local_err = NULL; | 77 | - * the size is the footer->current_size to avoid truncation. Otherwise, |
111 | 78 | - * we follow the table based on footer->creator_app: | |
112 | + /* | 79 | - * |
113 | + * Note that nbd_client_put() and client_close() must be called from the | 80 | - * Known creator apps: |
114 | + * main loop thread. Use aio_co_reschedule_self() to switch AioContext | 81 | - * 'vpc ' : CHS Virtual PC (uses disk geometry) |
115 | + * before calling these functions. | 82 | - * 'qemu' : CHS QEMU (uses disk geometry) |
116 | + */ | 83 | - * 'qem2' : current_size QEMU (uses current_size) |
117 | + | 84 | - * 'win ' : current_size Hyper-V |
118 | trace_nbd_trip(); | 85 | - * 'd2v ' : current_size Disk2vhd |
119 | if (client->closing) { | 86 | - * 'tap\0' : current_size XenServer |
120 | - nbd_client_put(client); | 87 | - * 'CTXS' : current_size XenConverter |
121 | - return; | 88 | - * |
122 | + goto done; | 89 | - * The user can override the table values via drive options, however |
123 | } | 90 | - * even with an override we will still use current_size for images |
124 | 91 | - * that have CHS geometry of the maximum size. | |
125 | if (client->quiescing) { | 92 | - */ |
126 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_trip(void *opaque) | 93 | - use_chs = (!!strncmp(footer->creator_app, "win ", 4) && |
127 | * We're switching between AIO contexts. Don't attempt to receive a new | 94 | - !!strncmp(footer->creator_app, "qem2", 4) && |
128 | * request and kick the main context which may be waiting for us. | 95 | - !!strncmp(footer->creator_app, "d2v ", 4) && |
129 | */ | 96 | - !!strncmp(footer->creator_app, "CTXS", 4) && |
130 | - nbd_client_put(client); | 97 | - !!memcmp(footer->creator_app, "tap", 4)) || s->force_use_chs; |
131 | client->recv_coroutine = NULL; | 98 | + /* Use CHS or current_size to determine the image size. */ |
132 | aio_wait_kick(); | 99 | + use_chs = vpc_ignore_current_size(footer) || s->force_use_chs; |
133 | - return; | 100 | |
134 | + goto done; | 101 | if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) { |
135 | } | 102 | bs->total_sectors = be64_to_cpu(footer->current_size) / |
136 | |||
137 | req = nbd_request_get(client); | ||
138 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_trip(void *opaque) | ||
139 | |||
140 | qio_channel_set_cork(client->ioc, false); | ||
141 | done: | ||
142 | - nbd_request_put(req); | ||
143 | - nbd_client_put(client); | ||
144 | + if (req) { | ||
145 | + nbd_request_put(req); | ||
146 | + } | ||
147 | + if (!nbd_client_put_nonzero(client)) { | ||
148 | + aio_co_reschedule_self(qemu_get_aio_context()); | ||
149 | + nbd_client_put(client); | ||
150 | + } | ||
151 | return; | ||
152 | |||
153 | disconnect: | ||
154 | @@ -XXX,XX +XXX,XX @@ disconnect: | ||
155 | error_reportf_err(local_err, "Disconnect client, due to: "); | ||
156 | } | ||
157 | nbd_request_put(req); | ||
158 | + | ||
159 | + aio_co_reschedule_self(qemu_get_aio_context()); | ||
160 | client_close(client, true); | ||
161 | nbd_client_put(client); | ||
162 | } | ||
163 | -- | 103 | -- |
164 | 2.43.0 | 104 | 2.48.1 |
105 | |||
106 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Vitaly Kuznetsov <vkuznets@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The SCSI subsystem no longer uses the AioContext lock. Request | 3 | It was found that 'qemu-nbd' is not able to work with some disk images |
4 | processing runs exclusively in the BlockBackend's AioContext since | 4 | exported from Azure. Looking at the 512b footer (which contains VPC |
5 | "scsi: only access SCSIDevice->requests from one thread" and hence the | 5 | metadata): |
6 | lock is unnecessary. | ||
7 | 6 | ||
8 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 7 | 00000000 63 6f 6e 65 63 74 69 78 00 00 00 02 00 01 00 00 |conectix........| |
9 | Reviewed-by: Eric Blake <eblake@redhat.com> | 8 | 00000010 ff ff ff ff ff ff ff ff 2e c7 9b 96 77 61 00 00 |............wa..| |
10 | Message-ID: <20231205182011.1976568-13-stefanha@redhat.com> | 9 | 00000020 00 07 00 00 57 69 32 6b 00 00 00 01 40 00 00 00 |....Wi2k....@...| |
10 | 00000030 00 00 00 01 40 00 00 00 28 a2 10 3f 00 00 00 02 |....@...(..?....| | ||
11 | 00000040 ff ff e7 47 8c 54 df 94 bd 35 71 4c 94 5f e5 44 |...G.T...5qL._.D| | ||
12 | 00000050 44 53 92 1a 00 00 00 00 00 00 00 00 00 00 00 00 |DS..............| | ||
13 | 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| | ||
14 | |||
15 | we can see that Azure uses a different 'Creator application' -- | ||
16 | 'wa\0\0' (offset 0x1c, likely reads as 'Windows Azure') and QEMU uses this | ||
17 | field to determine how it can get image size. Apparently, Azure uses 'new' | ||
18 | method, just like Hyper-V. | ||
19 | |||
20 | Overall, it seems that only VPC and old QEMUs need to be ignored as all new | ||
21 | creator apps seem to have reliable current_size. Invert the logic and make | ||
22 | 'current_size' method the default to avoid adding every new creator app to | ||
23 | the list. | ||
24 | |||
25 | Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> | ||
26 | Message-ID: <20241212134504.1983757-3-vkuznets@redhat.com> | ||
11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 27 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 28 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 29 | --- |
14 | hw/scsi/scsi-disk.c | 1 - | 30 | block/vpc.c | 8 +++----- |
15 | 1 file changed, 1 deletion(-) | 31 | 1 file changed, 3 insertions(+), 5 deletions(-) |
16 | 32 | ||
17 | diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c | 33 | diff --git a/block/vpc.c b/block/vpc.c |
18 | index XXXXXXX..XXXXXXX 100644 | 34 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/hw/scsi/scsi-disk.c | 35 | --- a/block/vpc.c |
20 | +++ b/hw/scsi/scsi-disk.c | 36 | +++ b/block/vpc.c |
21 | @@ -XXX,XX +XXX,XX @@ done: | 37 | @@ -XXX,XX +XXX,XX @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts, |
22 | scsi_req_unref(&r->req); | 38 | * 'd2v ' : current_size Disk2vhd |
39 | * 'tap\0' : current_size XenServer | ||
40 | * 'CTXS' : current_size XenConverter | ||
41 | + * 'wa\0\0': current_size Azure | ||
42 | * | ||
43 | * The user can override the table values via drive options, however | ||
44 | * even with an override we will still use current_size for images | ||
45 | @@ -XXX,XX +XXX,XX @@ static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts, | ||
46 | */ | ||
47 | static bool vpc_ignore_current_size(VHDFooter *footer) | ||
48 | { | ||
49 | - return !!strncmp(footer->creator_app, "win ", 4) && | ||
50 | - !!strncmp(footer->creator_app, "qem2", 4) && | ||
51 | - !!strncmp(footer->creator_app, "d2v ", 4) && | ||
52 | - !!strncmp(footer->creator_app, "CTXS", 4) && | ||
53 | - !!memcmp(footer->creator_app, "tap", 4)); | ||
54 | + return !strncmp(footer->creator_app, "vpc ", 4) || | ||
55 | + !strncmp(footer->creator_app, "qemu", 4); | ||
23 | } | 56 | } |
24 | 57 | ||
25 | -/* Called with AioContext lock held */ | 58 | static int vpc_open(BlockDriverState *bs, QDict *options, int flags, |
26 | static void scsi_dma_complete(void *opaque, int ret) | ||
27 | { | ||
28 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
29 | -- | 59 | -- |
30 | 2.43.0 | 60 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Philippe Mathieu-Daudé <philmd@linaro.org> |
---|---|---|---|
2 | 2 | ||
3 | Protect the Task Management Function BH state with a lock. The TMF BH | 3 | Expose the method docstring in the header, and mention |
4 | runs in the main loop thread. An IOThread might process a TMF at the | 4 | returned value must be free'd by caller. |
5 | same time as the TMF BH is running. Therefore tmf_bh_list and tmf_bh | ||
6 | must be protected by a lock. | ||
7 | 5 | ||
8 | Run TMF request completion in the IOThread using aio_wait_bh_oneshot(). | 6 | Reported-by: Fabiano Rosas <farosas@suse.de> |
9 | This avoids more locking to protect the virtqueue and SCSI layer state. | 7 | Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> |
10 | 8 | Message-ID: <20241111170333.43833-2-philmd@linaro.org> | |
11 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
12 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
13 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | Message-ID: <20231205182011.1976568-2-stefanha@redhat.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
16 | --- | 10 | --- |
17 | include/hw/virtio/virtio-scsi.h | 3 +- | 11 | include/system/block-backend-io.h | 7 +++++++ |
18 | hw/scsi/virtio-scsi.c | 62 ++++++++++++++++++++++----------- | 12 | block/block-backend.c | 12 ++++++++---- |
19 | 2 files changed, 43 insertions(+), 22 deletions(-) | 13 | 2 files changed, 15 insertions(+), 4 deletions(-) |
20 | 14 | ||
21 | diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h | 15 | diff --git a/include/system/block-backend-io.h b/include/system/block-backend-io.h |
22 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/include/hw/virtio/virtio-scsi.h | 17 | --- a/include/system/block-backend-io.h |
24 | +++ b/include/hw/virtio/virtio-scsi.h | 18 | +++ b/include/system/block-backend-io.h |
25 | @@ -XXX,XX +XXX,XX @@ struct VirtIOSCSI { | 19 | @@ -XXX,XX +XXX,XX @@ void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow); |
26 | 20 | void blk_set_disable_request_queuing(BlockBackend *blk, bool disable); | |
27 | /* | 21 | bool blk_iostatus_is_enabled(const BlockBackend *blk); |
28 | * TMFs deferred to main loop BH. These fields are protected by | 22 | |
29 | - * virtio_scsi_acquire(). | 23 | +/* |
30 | + * tmf_bh_lock. | 24 | + * Return the qdev ID, or if no ID is assigned the QOM path, |
31 | */ | 25 | + * of the block device attached to the BlockBackend. |
32 | + QemuMutex tmf_bh_lock; | 26 | + * |
33 | QEMUBH *tmf_bh; | 27 | + * The caller is responsible for releasing the value returned |
34 | QTAILQ_HEAD(, VirtIOSCSIReq) tmf_bh_list; | 28 | + * with g_free() after use. |
35 | 29 | + */ | |
36 | diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c | 30 | char *blk_get_attached_dev_id(BlockBackend *blk); |
31 | |||
32 | BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, | ||
33 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
37 | index XXXXXXX..XXXXXXX 100644 | 34 | index XXXXXXX..XXXXXXX 100644 |
38 | --- a/hw/scsi/virtio-scsi.c | 35 | --- a/block/block-backend.c |
39 | +++ b/hw/scsi/virtio-scsi.c | 36 | +++ b/block/block-backend.c |
40 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req) | 37 | @@ -XXX,XX +XXX,XX @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) |
41 | virtio_scsi_free_req(req); | 38 | return blk->dev; |
42 | } | 39 | } |
43 | 40 | ||
44 | +static void virtio_scsi_complete_req_bh(void *opaque) | ||
45 | +{ | ||
46 | + VirtIOSCSIReq *req = opaque; | ||
47 | + | ||
48 | + virtio_scsi_complete_req(req); | ||
49 | +} | ||
50 | + | ||
51 | +/* | 41 | +/* |
52 | + * Called from virtio_scsi_do_one_tmf_bh() in main loop thread. The main loop | 42 | + * The caller is responsible for releasing the value returned |
53 | + * thread cannot touch the virtqueue since that could race with an IOThread. | 43 | + * with g_free() after use. |
54 | + */ | 44 | + */ |
55 | +static void virtio_scsi_complete_req_from_main_loop(VirtIOSCSIReq *req) | 45 | static char *blk_get_attached_dev_id_or_path(BlockBackend *blk, bool want_id) |
56 | +{ | ||
57 | + VirtIOSCSI *s = req->dev; | ||
58 | + | ||
59 | + if (!s->ctx || s->ctx == qemu_get_aio_context()) { | ||
60 | + /* No need to schedule a BH when there is no IOThread */ | ||
61 | + virtio_scsi_complete_req(req); | ||
62 | + } else { | ||
63 | + /* Run request completion in the IOThread */ | ||
64 | + aio_wait_bh_oneshot(s->ctx, virtio_scsi_complete_req_bh, req); | ||
65 | + } | ||
66 | +} | ||
67 | + | ||
68 | static void virtio_scsi_bad_req(VirtIOSCSIReq *req) | ||
69 | { | 46 | { |
70 | virtio_error(VIRTIO_DEVICE(req->dev), "wrong size for virtio-scsi headers"); | 47 | DeviceState *dev = blk->dev; |
71 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_do_one_tmf_bh(VirtIOSCSIReq *req) | 48 | @@ -XXX,XX +XXX,XX @@ static char *blk_get_attached_dev_id_or_path(BlockBackend *blk, bool want_id) |
72 | 49 | return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); | |
73 | out: | ||
74 | object_unref(OBJECT(d)); | ||
75 | - | ||
76 | - virtio_scsi_acquire(s); | ||
77 | - virtio_scsi_complete_req(req); | ||
78 | - virtio_scsi_release(s); | ||
79 | + virtio_scsi_complete_req_from_main_loop(req); | ||
80 | } | 50 | } |
81 | 51 | ||
82 | /* Some TMFs must be processed from the main loop thread */ | 52 | -/* |
83 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_do_tmf_bh(void *opaque) | 53 | - * Return the qdev ID, or if no ID is assigned the QOM path, of the block |
84 | 54 | - * device attached to the BlockBackend. | |
85 | GLOBAL_STATE_CODE(); | 55 | - */ |
86 | 56 | char *blk_get_attached_dev_id(BlockBackend *blk) | |
87 | - virtio_scsi_acquire(s); | 57 | { |
88 | + WITH_QEMU_LOCK_GUARD(&s->tmf_bh_lock) { | 58 | return blk_get_attached_dev_id_or_path(blk, true); |
89 | + QTAILQ_FOREACH_SAFE(req, &s->tmf_bh_list, next, tmp) { | ||
90 | + QTAILQ_REMOVE(&s->tmf_bh_list, req, next); | ||
91 | + QTAILQ_INSERT_TAIL(&reqs, req, next); | ||
92 | + } | ||
93 | |||
94 | - QTAILQ_FOREACH_SAFE(req, &s->tmf_bh_list, next, tmp) { | ||
95 | - QTAILQ_REMOVE(&s->tmf_bh_list, req, next); | ||
96 | - QTAILQ_INSERT_TAIL(&reqs, req, next); | ||
97 | + qemu_bh_delete(s->tmf_bh); | ||
98 | + s->tmf_bh = NULL; | ||
99 | } | ||
100 | |||
101 | - qemu_bh_delete(s->tmf_bh); | ||
102 | - s->tmf_bh = NULL; | ||
103 | - | ||
104 | - virtio_scsi_release(s); | ||
105 | - | ||
106 | QTAILQ_FOREACH_SAFE(req, &reqs, next, tmp) { | ||
107 | QTAILQ_REMOVE(&reqs, req, next); | ||
108 | virtio_scsi_do_one_tmf_bh(req); | ||
109 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_reset_tmf_bh(VirtIOSCSI *s) | ||
110 | |||
111 | GLOBAL_STATE_CODE(); | ||
112 | |||
113 | - virtio_scsi_acquire(s); | ||
114 | - | ||
115 | + /* Called after ioeventfd has been stopped, so tmf_bh_lock is not needed */ | ||
116 | if (s->tmf_bh) { | ||
117 | qemu_bh_delete(s->tmf_bh); | ||
118 | s->tmf_bh = NULL; | ||
119 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_reset_tmf_bh(VirtIOSCSI *s) | ||
120 | req->resp.tmf.response = VIRTIO_SCSI_S_TARGET_FAILURE; | ||
121 | virtio_scsi_complete_req(req); | ||
122 | } | ||
123 | - | ||
124 | - virtio_scsi_release(s); | ||
125 | } | 59 | } |
126 | 60 | ||
127 | static void virtio_scsi_defer_tmf_to_bh(VirtIOSCSIReq *req) | 61 | +/* |
62 | + * The caller is responsible for releasing the value returned | ||
63 | + * with g_free() after use. | ||
64 | + */ | ||
65 | static char *blk_get_attached_dev_path(BlockBackend *blk) | ||
128 | { | 66 | { |
129 | VirtIOSCSI *s = req->dev; | 67 | return blk_get_attached_dev_id_or_path(blk, false); |
130 | |||
131 | - QTAILQ_INSERT_TAIL(&s->tmf_bh_list, req, next); | ||
132 | + WITH_QEMU_LOCK_GUARD(&s->tmf_bh_lock) { | ||
133 | + QTAILQ_INSERT_TAIL(&s->tmf_bh_list, req, next); | ||
134 | |||
135 | - if (!s->tmf_bh) { | ||
136 | - s->tmf_bh = qemu_bh_new(virtio_scsi_do_tmf_bh, s); | ||
137 | - qemu_bh_schedule(s->tmf_bh); | ||
138 | + if (!s->tmf_bh) { | ||
139 | + s->tmf_bh = qemu_bh_new(virtio_scsi_do_tmf_bh, s); | ||
140 | + qemu_bh_schedule(s->tmf_bh); | ||
141 | + } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) | ||
146 | Error *err = NULL; | ||
147 | |||
148 | QTAILQ_INIT(&s->tmf_bh_list); | ||
149 | + qemu_mutex_init(&s->tmf_bh_lock); | ||
150 | |||
151 | virtio_scsi_common_realize(dev, | ||
152 | virtio_scsi_handle_ctrl, | ||
153 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_device_unrealize(DeviceState *dev) | ||
154 | |||
155 | qbus_set_hotplug_handler(BUS(&s->bus), NULL); | ||
156 | virtio_scsi_common_unrealize(dev); | ||
157 | + qemu_mutex_destroy(&s->tmf_bh_lock); | ||
158 | } | ||
159 | |||
160 | static Property virtio_scsi_properties[] = { | ||
161 | -- | 68 | -- |
162 | 2.43.0 | 69 | 2.48.1 |
70 | |||
71 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Fabiano Rosas <farosas@suse.de> |
---|---|---|---|
2 | 2 | ||
3 | aio_context_acquire()/aio_context_release() has been replaced by | 3 | ASAN detected a leak when running the ahci-test |
4 | fine-grained locking to protect state shared by multiple threads. The | 4 | /ahci/io/dma/lba28/retry: |
5 | AioContext lock still plays the role of balancing locking in | ||
6 | AIO_WAIT_WHILE() and many functions in QEMU either require that the | ||
7 | AioContext lock is held or not held for this reason. In other words, the | ||
8 | AioContext lock is purely there for consistency with itself and serves | ||
9 | no real purpose anymore. | ||
10 | 5 | ||
11 | Stop actually acquiring/releasing the lock in | 6 | Direct leak of 35 byte(s) in 1 object(s) allocated from: |
12 | aio_context_acquire()/aio_context_release() so that subsequent patches | 7 | #0 in malloc |
13 | can remove callers across the codebase incrementally. | 8 | #1 in __vasprintf_internal |
9 | #2 in vasprintf | ||
10 | #3 in g_vasprintf | ||
11 | #4 in g_strdup_vprintf | ||
12 | #5 in g_strdup_printf | ||
13 | #6 in object_get_canonical_path ../qom/object.c:2096:19 | ||
14 | #7 in blk_get_attached_dev_id_or_path ../block/block-backend.c:1033:12 | ||
15 | #8 in blk_get_attached_dev_path ../block/block-backend.c:1047:12 | ||
16 | #9 in send_qmp_error_event ../block/block-backend.c:2140:36 | ||
17 | #10 in blk_error_action ../block/block-backend.c:2172:9 | ||
18 | #11 in ide_handle_rw_error ../hw/ide/core.c:875:5 | ||
19 | #12 in ide_dma_cb ../hw/ide/core.c:894:13 | ||
20 | #13 in dma_complete ../system/dma-helpers.c:107:9 | ||
21 | #14 in dma_blk_cb ../system/dma-helpers.c:129:9 | ||
22 | #15 in blk_aio_complete ../block/block-backend.c:1552:9 | ||
23 | #16 in blk_aio_write_entry ../block/block-backend.c:1619:5 | ||
24 | #17 in coroutine_trampoline ../util/coroutine-ucontext.c:175:9 | ||
14 | 25 | ||
15 | I have performed "make check" and qemu-iotests stress tests across | 26 | Plug the leak by freeing the device path string. |
16 | x86-64, ppc64le, and aarch64 to confirm that there are no failures as a | ||
17 | result of eliminating the lock. | ||
18 | 27 | ||
19 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 28 | Signed-off-by: Fabiano Rosas <farosas@suse.de> |
20 | Reviewed-by: Eric Blake <eblake@redhat.com> | 29 | Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> |
21 | Acked-by: Kevin Wolf <kwolf@redhat.com> | 30 | Message-ID: <20241111145214.8261-1-farosas@suse.de> |
22 | Message-ID: <20231205182011.1976568-5-stefanha@redhat.com> | 31 | [PMD: Use g_autofree] |
32 | Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> | ||
33 | Message-ID: <20241111170333.43833-3-philmd@linaro.org> | ||
23 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 34 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
24 | --- | 35 | --- |
25 | util/async.c | 4 ++-- | 36 | block/block-backend.c | 4 ++-- |
26 | 1 file changed, 2 insertions(+), 2 deletions(-) | 37 | 1 file changed, 2 insertions(+), 2 deletions(-) |
27 | 38 | ||
28 | diff --git a/util/async.c b/util/async.c | 39 | diff --git a/block/block-backend.c b/block/block-backend.c |
29 | index XXXXXXX..XXXXXXX 100644 | 40 | index XXXXXXX..XXXXXXX 100644 |
30 | --- a/util/async.c | 41 | --- a/block/block-backend.c |
31 | +++ b/util/async.c | 42 | +++ b/block/block-backend.c |
32 | @@ -XXX,XX +XXX,XX @@ void aio_context_unref(AioContext *ctx) | 43 | @@ -XXX,XX +XXX,XX @@ static void send_qmp_error_event(BlockBackend *blk, |
33 | |||
34 | void aio_context_acquire(AioContext *ctx) | ||
35 | { | 44 | { |
36 | - qemu_rec_mutex_lock(&ctx->lock); | 45 | IoOperationType optype; |
37 | + /* TODO remove this function */ | 46 | BlockDriverState *bs = blk_bs(blk); |
38 | } | 47 | + g_autofree char *path = blk_get_attached_dev_path(blk); |
39 | 48 | ||
40 | void aio_context_release(AioContext *ctx) | 49 | optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; |
41 | { | 50 | - qapi_event_send_block_io_error(blk_name(blk), |
42 | - qemu_rec_mutex_unlock(&ctx->lock); | 51 | - blk_get_attached_dev_path(blk), |
43 | + /* TODO remove this function */ | 52 | + qapi_event_send_block_io_error(blk_name(blk), path, |
44 | } | 53 | bs ? bdrv_get_node_name(bs) : NULL, optype, |
45 | 54 | action, blk_iostatus_is_enabled(blk), | |
46 | QEMU_DEFINE_STATIC_CO_TLS(AioContext *, my_aiocontext) | 55 | error == ENOSPC, strerror(error)); |
47 | -- | 56 | -- |
48 | 2.43.0 | 57 | 2.48.1 |
58 | |||
59 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Peter Xu <peterx@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | virtio-blk and virtio-scsi devices will need a way to specify the | 3 | It's easier for either debugging plugin errors, or issue reports. |
4 | mapping between IOThreads and virtqueues. At the moment all virtqueues | ||
5 | are assigned to a single IOThread or the main loop. This single thread | ||
6 | can be a CPU bottleneck, so it is necessary to allow finer-grained | ||
7 | assignment to spread the load. | ||
8 | 4 | ||
9 | Introduce DEFINE_PROP_IOTHREAD_VQ_MAPPING_LIST() so devices can take a | 5 | Signed-off-by: Peter Xu <peterx@redhat.com> |
10 | parameter that maps virtqueues to IOThreads. The command-line syntax for | 6 | Message-ID: <20241212204801.1420528-2-peterx@redhat.com> |
11 | this new property is as follows: | ||
12 | |||
13 | --device '{"driver":"foo","iothread-vq-mapping":[{"iothread":"iothread0","vqs":[0,1,2]},...]}' | ||
14 | |||
15 | IOThreads are specified by name and virtqueues are specified by 0-based | ||
16 | index. | ||
17 | |||
18 | It will be common to simply assign virtqueues round-robin across a set | ||
19 | of IOThreads. A convenient syntax that does not require specifying | ||
20 | individual virtqueue indices is available: | ||
21 | |||
22 | --device '{"driver":"foo","iothread-vq-mapping":[{"iothread":"iothread0"},{"iothread":"iothread1"},...]}' | ||
23 | |||
24 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
25 | Message-ID: <20231220134755.814917-4-stefanha@redhat.com> | ||
26 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
27 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
28 | --- | 9 | --- |
29 | qapi/virtio.json | 29 ++++++++++++++++++ | 10 | scripts/qemu-gdb.py | 2 ++ |
30 | include/hw/qdev-properties-system.h | 5 ++++ | 11 | 1 file changed, 2 insertions(+) |
31 | hw/core/qdev-properties-system.c | 46 +++++++++++++++++++++++++++++ | ||
32 | 3 files changed, 80 insertions(+) | ||
33 | 12 | ||
34 | diff --git a/qapi/virtio.json b/qapi/virtio.json | 13 | diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py |
35 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
36 | --- a/qapi/virtio.json | 15 | --- a/scripts/qemu-gdb.py |
37 | +++ b/qapi/virtio.json | 16 | +++ b/scripts/qemu-gdb.py |
38 | @@ -XXX,XX +XXX,XX @@ | 17 | @@ -XXX,XX +XXX,XX @@ def __init__(self): |
39 | 'data': { 'path': 'str', 'queue': 'uint16', '*index': 'uint16' }, | 18 | # Default to silently passing through SIGUSR1, because QEMU sends it |
40 | 'returns': 'VirtioQueueElement', | 19 | # to itself a lot. |
41 | 'features': [ 'unstable' ] } | 20 | gdb.execute('handle SIGUSR1 pass noprint nostop') |
42 | + | 21 | +# Always print full stack for python errors, easier to debug and report issues |
43 | +## | 22 | +gdb.execute('set python print-stack full') |
44 | +# @IOThreadVirtQueueMapping: | ||
45 | +# | ||
46 | +# Describes the subset of virtqueues assigned to an IOThread. | ||
47 | +# | ||
48 | +# @iothread: the id of IOThread object | ||
49 | +# | ||
50 | +# @vqs: an optional array of virtqueue indices that will be handled by this | ||
51 | +# IOThread. When absent, virtqueues are assigned round-robin across all | ||
52 | +# IOThreadVirtQueueMappings provided. Either all IOThreadVirtQueueMappings | ||
53 | +# must have @vqs or none of them must have it. | ||
54 | +# | ||
55 | +# Since: 9.0 | ||
56 | +## | ||
57 | + | ||
58 | +{ 'struct': 'IOThreadVirtQueueMapping', | ||
59 | + 'data': { 'iothread': 'str', '*vqs': ['uint16'] } } | ||
60 | + | ||
61 | +## | ||
62 | +# @DummyVirtioForceArrays: | ||
63 | +# | ||
64 | +# Not used by QMP; hack to let us use IOThreadVirtQueueMappingList internally | ||
65 | +# | ||
66 | +# Since: 9.0 | ||
67 | +## | ||
68 | + | ||
69 | +{ 'struct': 'DummyVirtioForceArrays', | ||
70 | + 'data': { 'unused-iothread-vq-mapping': ['IOThreadVirtQueueMapping'] } } | ||
71 | diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h | ||
72 | index XXXXXXX..XXXXXXX 100644 | ||
73 | --- a/include/hw/qdev-properties-system.h | ||
74 | +++ b/include/hw/qdev-properties-system.h | ||
75 | @@ -XXX,XX +XXX,XX @@ extern const PropertyInfo qdev_prop_off_auto_pcibar; | ||
76 | extern const PropertyInfo qdev_prop_pcie_link_speed; | ||
77 | extern const PropertyInfo qdev_prop_pcie_link_width; | ||
78 | extern const PropertyInfo qdev_prop_cpus390entitlement; | ||
79 | +extern const PropertyInfo qdev_prop_iothread_vq_mapping_list; | ||
80 | |||
81 | #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ | ||
82 | DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) | ||
83 | @@ -XXX,XX +XXX,XX @@ extern const PropertyInfo qdev_prop_cpus390entitlement; | ||
84 | DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_cpus390entitlement, \ | ||
85 | CpuS390Entitlement) | ||
86 | |||
87 | +#define DEFINE_PROP_IOTHREAD_VQ_MAPPING_LIST(_name, _state, _field) \ | ||
88 | + DEFINE_PROP(_name, _state, _field, qdev_prop_iothread_vq_mapping_list, \ | ||
89 | + IOThreadVirtQueueMappingList *) | ||
90 | + | ||
91 | #endif | ||
92 | diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c | ||
93 | index XXXXXXX..XXXXXXX 100644 | ||
94 | --- a/hw/core/qdev-properties-system.c | ||
95 | +++ b/hw/core/qdev-properties-system.c | ||
96 | @@ -XXX,XX +XXX,XX @@ | ||
97 | #include "qapi/qapi-types-block.h" | ||
98 | #include "qapi/qapi-types-machine.h" | ||
99 | #include "qapi/qapi-types-migration.h" | ||
100 | +#include "qapi/qapi-visit-virtio.h" | ||
101 | #include "qapi/qmp/qerror.h" | ||
102 | #include "qemu/ctype.h" | ||
103 | #include "qemu/cutils.h" | ||
104 | @@ -XXX,XX +XXX,XX @@ const PropertyInfo qdev_prop_cpus390entitlement = { | ||
105 | .set = qdev_propinfo_set_enum, | ||
106 | .set_default_value = qdev_propinfo_set_default_value_enum, | ||
107 | }; | ||
108 | + | ||
109 | +/* --- IOThreadVirtQueueMappingList --- */ | ||
110 | + | ||
111 | +static void get_iothread_vq_mapping_list(Object *obj, Visitor *v, | ||
112 | + const char *name, void *opaque, Error **errp) | ||
113 | +{ | ||
114 | + IOThreadVirtQueueMappingList **prop_ptr = | ||
115 | + object_field_prop_ptr(obj, opaque); | ||
116 | + | ||
117 | + visit_type_IOThreadVirtQueueMappingList(v, name, prop_ptr, errp); | ||
118 | +} | ||
119 | + | ||
120 | +static void set_iothread_vq_mapping_list(Object *obj, Visitor *v, | ||
121 | + const char *name, void *opaque, Error **errp) | ||
122 | +{ | ||
123 | + IOThreadVirtQueueMappingList **prop_ptr = | ||
124 | + object_field_prop_ptr(obj, opaque); | ||
125 | + IOThreadVirtQueueMappingList *list; | ||
126 | + | ||
127 | + if (!visit_type_IOThreadVirtQueueMappingList(v, name, &list, errp)) { | ||
128 | + return; | ||
129 | + } | ||
130 | + | ||
131 | + qapi_free_IOThreadVirtQueueMappingList(*prop_ptr); | ||
132 | + *prop_ptr = list; | ||
133 | +} | ||
134 | + | ||
135 | +static void release_iothread_vq_mapping_list(Object *obj, | ||
136 | + const char *name, void *opaque) | ||
137 | +{ | ||
138 | + IOThreadVirtQueueMappingList **prop_ptr = | ||
139 | + object_field_prop_ptr(obj, opaque); | ||
140 | + | ||
141 | + qapi_free_IOThreadVirtQueueMappingList(*prop_ptr); | ||
142 | + *prop_ptr = NULL; | ||
143 | +} | ||
144 | + | ||
145 | +const PropertyInfo qdev_prop_iothread_vq_mapping_list = { | ||
146 | + .name = "IOThreadVirtQueueMappingList", | ||
147 | + .description = "IOThread virtqueue mapping list [{\"iothread\":\"<id>\", " | ||
148 | + "\"vqs\":[1,2,3,...]},...]", | ||
149 | + .get = get_iothread_vq_mapping_list, | ||
150 | + .set = set_iothread_vq_mapping_list, | ||
151 | + .release = release_iothread_vq_mapping_list, | ||
152 | +}; | ||
153 | -- | 23 | -- |
154 | 2.43.0 | 24 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Peter Xu <peterx@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | qdev_alias_all_properties() aliases a DeviceState's qdev properties onto | 3 | There're a bunch of code trying to fetch fs_base in different ways. IIUC |
4 | an Object. This is used for VirtioPCIProxy types so that --device | 4 | the simplest way instead is "$fs_base". It also has the benefit that it'll |
5 | virtio-blk-pci has properties of its embedded --device virtio-blk-device | 5 | work for both live gdb session or coredumps. |
6 | object. | ||
7 | 6 | ||
8 | Currently this function is implemented using qdev properties. Change the | 7 | Signed-off-by: Peter Xu <peterx@redhat.com> |
9 | function to use QOM object class properties instead. This works because | 8 | Message-ID: <20241212204801.1420528-3-peterx@redhat.com> |
10 | qdev properties create QOM object class properties, but it also catches | ||
11 | any QOM object class-only properties that have no qdev properties. | ||
12 | |||
13 | This change ensures that properties of devices are shown with --device | ||
14 | foo,\? even if they are QOM object class properties. | ||
15 | |||
16 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
17 | Message-ID: <20231220134755.814917-2-stefanha@redhat.com> | ||
18 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 9 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
20 | --- | 11 | --- |
21 | include/hw/qdev-properties.h | 4 ++-- | 12 | scripts/qemugdb/coroutine.py | 23 ++--------------------- |
22 | hw/core/qdev-properties.c | 18 ++++++++++-------- | 13 | 1 file changed, 2 insertions(+), 21 deletions(-) |
23 | 2 files changed, 12 insertions(+), 10 deletions(-) | ||
24 | 14 | ||
25 | diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h | 15 | diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py |
26 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/include/hw/qdev-properties.h | 17 | --- a/scripts/qemugdb/coroutine.py |
28 | +++ b/include/hw/qdev-properties.h | 18 | +++ b/scripts/qemugdb/coroutine.py |
29 | @@ -XXX,XX +XXX,XX @@ void qdev_property_add_static(DeviceState *dev, Property *prop); | 19 | @@ -XXX,XX +XXX,XX @@ |
30 | * @target: Device which has properties to be aliased | 20 | |
31 | * @source: Object to add alias properties to | 21 | VOID_PTR = gdb.lookup_type('void').pointer() |
32 | * | 22 | |
33 | - * Add alias properties to the @source object for all qdev properties on | 23 | -def get_fs_base(): |
34 | - * the @target DeviceState. | 24 | - '''Fetch %fs base value using arch_prctl(ARCH_GET_FS). This is |
35 | + * Add alias properties to the @source object for all properties on the @target | 25 | - pthread_self().''' |
36 | + * DeviceState. | 26 | - # %rsp - 120 is scratch space according to the SystemV ABI |
37 | * | 27 | - old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') |
38 | * This is useful when @target is an internal implementation object | 28 | - gdb.execute('call (int)arch_prctl(0x1003, $rsp - 120)', False, True) |
39 | * owned by @source, and you want to expose all the properties of that | 29 | - fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') |
40 | diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c | 30 | - gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) |
41 | index XXXXXXX..XXXXXXX 100644 | 31 | - return fs_base |
42 | --- a/hw/core/qdev-properties.c | 32 | - |
43 | +++ b/hw/core/qdev-properties.c | 33 | def pthread_self(): |
44 | @@ -XXX,XX +XXX,XX @@ void device_class_set_props(DeviceClass *dc, Property *props) | 34 | - '''Fetch pthread_self() from the glibc start_thread function.''' |
45 | void qdev_alias_all_properties(DeviceState *target, Object *source) | 35 | - f = gdb.newest_frame() |
46 | { | 36 | - while f.name() != 'start_thread': |
47 | ObjectClass *class; | 37 | - f = f.older() |
48 | - Property *prop; | 38 | - if f is None: |
49 | + ObjectPropertyIterator iter; | 39 | - return get_fs_base() |
50 | + ObjectProperty *prop; | 40 | - |
51 | 41 | - try: | |
52 | class = object_get_class(OBJECT(target)); | 42 | - return f.read_var("arg") |
53 | - do { | 43 | - except ValueError: |
54 | - DeviceClass *dc = DEVICE_CLASS(class); | 44 | - return get_fs_base() |
55 | 45 | + '''Fetch the base address of TLS.''' | |
56 | - for (prop = dc->props_; prop && prop->name; prop++) { | 46 | + return gdb.parse_and_eval("$fs_base") |
57 | - object_property_add_alias(source, prop->name, | 47 | |
58 | - OBJECT(target), prop->name); | 48 | def get_glibc_pointer_guard(): |
59 | + object_class_property_iter_init(&iter, class); | 49 | '''Fetch glibc pointer guard value''' |
60 | + while ((prop = object_property_iter_next(&iter))) { | ||
61 | + if (object_property_find(source, prop->name)) { | ||
62 | + continue; /* skip duplicate properties */ | ||
63 | } | ||
64 | - class = object_class_get_parent(class); | ||
65 | - } while (class != object_class_by_name(TYPE_DEVICE)); | ||
66 | + | ||
67 | + object_property_add_alias(source, prop->name, | ||
68 | + OBJECT(target), prop->name); | ||
69 | + } | ||
70 | } | ||
71 | -- | 50 | -- |
72 | 2.43.0 | 51 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Peter Xu <peterx@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Use qemu_get_current_aio_context() in mixed wrappers and coroutine | 3 | Dumping coroutines don't yet work with coredumps. Let's make it work. |
4 | wrappers so that code runs in the caller's AioContext instead of moving | ||
5 | to the BlockDriverState's AioContext. This change is necessary for the | ||
6 | multi-queue block layer where any thread can call into the block layer. | ||
7 | 4 | ||
8 | Most wrappers are IO_CODE where it's safe to use the current AioContext | 5 | We still kept most of the old code because they can be either more |
9 | nowadays. BlockDrivers and the core block layer use their own locks and | 6 | flexible, or prettier. Only add the fallbacks when they stop working. |
10 | no longer depend on the AioContext lock for thread-safety. | ||
11 | 7 | ||
12 | The bdrv_create() wrapper invokes GLOBAL_STATE code. Using the current | 8 | Currently the raw unwind is pretty ugly, but it works, like this: |
13 | AioContext is safe because this code is only called with the BQL held | ||
14 | from the main loop thread. | ||
15 | 9 | ||
16 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 10 | (gdb) qemu bt |
17 | Message-ID: <20230912231037.826804-6-stefanha@redhat.com> | 11 | #0 process_incoming_migration_co (opaque=0x0) at ../migration/migration.c:788 |
18 | Reviewed-by: Eric Blake <eblake@redhat.com> | 12 | #1 0x000055ae6c0dc4d9 in coroutine_trampoline (i0=-1711718576, i1=21934) at ../util/coroutine-ucontext.c:175 |
13 | #2 0x00007f9f59d72f40 in ??? () at /lib64/libc.so.6 | ||
14 | #3 0x00007ffd549214a0 in ??? () | ||
15 | #4 0x0000000000000000 in ??? () | ||
16 | Coroutine at 0x7f9f4c57c748: | ||
17 | #0 0x55ae6c0dc9a8 in qemu_coroutine_switch<+120> () at ../util/coroutine-ucontext.c:321 | ||
18 | #1 0x55ae6c0da2f8 in qemu_aio_coroutine_enter<+356> () at ../util/qemu-coroutine.c:293 | ||
19 | #2 0x55ae6c0da3f1 in qemu_coroutine_enter<+34> () at ../util/qemu-coroutine.c:316 | ||
20 | #3 0x55ae6baf775e in migration_incoming_process<+43> () at ../migration/migration.c:876 | ||
21 | #4 0x55ae6baf7ab4 in migration_ioc_process_incoming<+490> () at ../migration/migration.c:1008 | ||
22 | #5 0x55ae6bae9ae7 in migration_channel_process_incoming<+145> () at ../migration/channel.c:45 | ||
23 | #6 0x55ae6bb18e35 in socket_accept_incoming_migration<+118> () at ../migration/socket.c:132 | ||
24 | #7 0x55ae6be939ef in qio_net_listener_channel_func<+131> () at ../io/net-listener.c:54 | ||
25 | #8 0x55ae6be8ce1a in qio_channel_fd_source_dispatch<+78> () at ../io/channel-watch.c:84 | ||
26 | #9 0x7f9f5b26728c in g_main_context_dispatch_unlocked.lto_priv<+315> () | ||
27 | #10 0x7f9f5b267555 in g_main_context_dispatch<+36> () | ||
28 | #11 0x55ae6c0d91a7 in glib_pollfds_poll<+90> () at ../util/main-loop.c:287 | ||
29 | #12 0x55ae6c0d9235 in os_host_main_loop_wait<+128> () at ../util/main-loop.c:310 | ||
30 | #13 0x55ae6c0d9364 in main_loop_wait<+203> () at ../util/main-loop.c:589 | ||
31 | #14 0x55ae6bac212a in qemu_main_loop<+41> () at ../system/runstate.c:835 | ||
32 | #15 0x55ae6bfdf522 in qemu_default_main<+19> () at ../system/main.c:37 | ||
33 | #16 0x55ae6bfdf55f in main<+40> () at ../system/main.c:48 | ||
34 | #17 0x7f9f59d42248 in __libc_start_call_main<+119> () | ||
35 | #18 0x7f9f59d4230b in __libc_start_main_impl<+138> () | ||
36 | |||
37 | Signed-off-by: Peter Xu <peterx@redhat.com> | ||
38 | Message-ID: <20241212204801.1420528-4-peterx@redhat.com> | ||
19 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 39 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 40 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
21 | --- | 41 | --- |
22 | scripts/block-coroutine-wrapper.py | 6 ++---- | 42 | scripts/qemugdb/coroutine.py | 79 +++++++++++++++++++++++++++++++++--- |
23 | 1 file changed, 2 insertions(+), 4 deletions(-) | 43 | 1 file changed, 73 insertions(+), 6 deletions(-) |
24 | 44 | ||
25 | diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py | 45 | diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py |
26 | index XXXXXXX..XXXXXXX 100644 | 46 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/scripts/block-coroutine-wrapper.py | 47 | --- a/scripts/qemugdb/coroutine.py |
28 | +++ b/scripts/block-coroutine-wrapper.py | 48 | +++ b/scripts/qemugdb/coroutine.py |
29 | @@ -XXX,XX +XXX,XX @@ def __init__(self, wrapper_type: str, return_type: str, name: str, | 49 | @@ -XXX,XX +XXX,XX @@ def get_jmpbuf_regs(jmpbuf): |
30 | f"{self.name}") | 50 | 'r15': jmpbuf[JB_R15], |
31 | self.target_name = f'{subsystem}_{subname}' | 51 | 'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) } |
32 | 52 | ||
33 | - self.ctx = self.gen_ctx() | 53 | -def bt_jmpbuf(jmpbuf): |
34 | - | 54 | - '''Backtrace a jmpbuf''' |
35 | self.get_result = 's->ret = ' | 55 | - regs = get_jmpbuf_regs(jmpbuf) |
36 | self.ret = 'return s.ret;' | 56 | +def symbol_lookup(addr): |
37 | self.co_ret = 'return ' | 57 | + # Example: "__clone3 + 44 in section .text of /lib64/libc.so.6" |
38 | @@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str: | 58 | + result = gdb.execute(f"info symbol {hex(addr)}", to_string=True).strip() |
39 | {func.co_ret}{name}({ func.gen_list('{name}') }); | 59 | + try: |
40 | }} else {{ | 60 | + if "+" in result: |
41 | {struct_name} s = {{ | 61 | + (func, result) = result.split(" + ") |
42 | - .poll_state.ctx = {func.ctx}, | 62 | + (offset, result) = result.split(" in ") |
43 | + .poll_state.ctx = qemu_get_current_aio_context(), | 63 | + else: |
44 | .poll_state.in_progress = true, | 64 | + offset = "0" |
45 | 65 | + (func, result) = result.split(" in ") | |
46 | { func.gen_block(' .{name} = {name},') } | 66 | + func_str = f"{func}<+{offset}> ()" |
47 | @@ -XXX,XX +XXX,XX @@ def create_co_wrapper(func: FuncDecl) -> str: | 67 | + except: |
48 | {func.return_type} {func.name}({ func.gen_list('{decl}') }) | 68 | + return f"??? ({result})" |
49 | {{ | 69 | + |
50 | {struct_name} s = {{ | 70 | + # Example: Line 321 of "../util/coroutine-ucontext.c" starts at address |
51 | - .poll_state.ctx = {func.ctx}, | 71 | + # 0x55cf3894d993 <qemu_coroutine_switch+99> and ends at 0x55cf3894d9ab |
52 | + .poll_state.ctx = qemu_get_current_aio_context(), | 72 | + # <qemu_coroutine_switch+123>. |
53 | .poll_state.in_progress = true, | 73 | + result = gdb.execute(f"info line *{hex(addr)}", to_string=True).strip() |
54 | 74 | + if not result.startswith("Line "): | |
55 | { func.gen_block(' .{name} = {name},') } | 75 | + return func_str |
76 | + result = result[5:] | ||
77 | + | ||
78 | + try: | ||
79 | + result = result.split(" starts ")[0] | ||
80 | + (line, path) = result.split(" of ") | ||
81 | + path = path.replace("\"", "") | ||
82 | + except: | ||
83 | + return func_str | ||
84 | + | ||
85 | + return f"{func_str} at {path}:{line}" | ||
86 | + | ||
87 | +def dump_backtrace(regs): | ||
88 | + ''' | ||
89 | + Backtrace dump with raw registers, mimic GDB command 'bt'. | ||
90 | + ''' | ||
91 | + # Here only rbp and rip that matter.. | ||
92 | + rbp = regs['rbp'] | ||
93 | + rip = regs['rip'] | ||
94 | + i = 0 | ||
95 | + | ||
96 | + while rbp: | ||
97 | + # For all return addresses on stack, we want to look up symbol/line | ||
98 | + # on the CALL command, because the return address is the next | ||
99 | + # instruction instead of the CALL. Here -1 would work for any | ||
100 | + # sized CALL instruction. | ||
101 | + print(f"#{i} {hex(rip)} in {symbol_lookup(rip if i == 0 else rip-1)}") | ||
102 | + rip = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)} + 8)") | ||
103 | + rbp = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)})") | ||
104 | + i += 1 | ||
105 | + | ||
106 | +def dump_backtrace_live(regs): | ||
107 | + ''' | ||
108 | + Backtrace dump with gdb's 'bt' command, only usable in a live session. | ||
109 | + ''' | ||
110 | old = dict() | ||
111 | |||
112 | # remember current stack frame and select the topmost | ||
113 | @@ -XXX,XX +XXX,XX @@ def bt_jmpbuf(jmpbuf): | ||
114 | |||
115 | selected_frame.select() | ||
116 | |||
117 | +def bt_jmpbuf(jmpbuf): | ||
118 | + '''Backtrace a jmpbuf''' | ||
119 | + regs = get_jmpbuf_regs(jmpbuf) | ||
120 | + try: | ||
121 | + # This reuses gdb's "bt" command, which can be slightly prettier | ||
122 | + # but only works with live sessions. | ||
123 | + dump_backtrace_live(regs) | ||
124 | + except: | ||
125 | + # If above doesn't work, fallback to poor man's unwind | ||
126 | + dump_backtrace(regs) | ||
127 | + | ||
128 | def co_cast(co): | ||
129 | return co.cast(gdb.lookup_type('CoroutineUContext').pointer()) | ||
130 | |||
131 | @@ -XXX,XX +XXX,XX @@ def invoke(self, arg, from_tty): | ||
132 | |||
133 | gdb.execute("bt") | ||
134 | |||
135 | - if gdb.parse_and_eval("qemu_in_coroutine()") == False: | ||
136 | - return | ||
137 | + try: | ||
138 | + # This only works with a live session | ||
139 | + co_ptr = gdb.parse_and_eval("qemu_coroutine_self()") | ||
140 | + except: | ||
141 | + # Fallback to use hard-coded ucontext vars if it's coredump | ||
142 | + co_ptr = gdb.parse_and_eval("co_tls_current") | ||
143 | |||
144 | - co_ptr = gdb.parse_and_eval("qemu_coroutine_self()") | ||
145 | + if co_ptr == False: | ||
146 | + return | ||
147 | |||
148 | while True: | ||
149 | co = co_cast(co_ptr) | ||
56 | -- | 150 | -- |
57 | 2.43.0 | 151 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Peter Krempa <pkrempa@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The AioContext lock no longer exists. | 3 | Commit 7452162adec25c10 introduced 'qom-path' argument to BLOCK_IO_ERROR |
4 | event but when the event is instantiated in 'send_qmp_error_event()' the | ||
5 | arguments for 'device' and 'qom_path' in | ||
6 | qapi_event_send_block_io_error() were reversed : | ||
4 | 7 | ||
5 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 8 | Generated code for sending event: |
6 | Reviewed-by: Eric Blake <eblake@redhat.com> | 9 | |
7 | Message-ID: <20231205182011.1976568-14-stefanha@redhat.com> | 10 | void qapi_event_send_block_io_error(const char *qom_path, |
11 | const char *device, | ||
12 | const char *node_name, | ||
13 | IoOperationType operation, | ||
14 | [...] | ||
15 | |||
16 | Call inside send_qmp_error_event(): | ||
17 | |||
18 | qapi_event_send_block_io_error(blk_name(blk), | ||
19 | blk_get_attached_dev_path(blk), | ||
20 | bs ? bdrv_get_node_name(bs) : NULL, optype, | ||
21 | [...] | ||
22 | |||
23 | This results into reporting the QOM path as the device alias and vice | ||
24 | versa which in turn breaks libvirt, which expects the device alias being | ||
25 | either a valid alias or empty (which would make libvirt do the lookup by | ||
26 | node-name instead). | ||
27 | |||
28 | Cc: qemu-stable@nongnu.org | ||
29 | Fixes: 7452162adec2 ("qapi: add qom-path to BLOCK_IO_ERROR event") | ||
30 | Signed-off-by: Peter Krempa <pkrempa@redhat.com> | ||
31 | Message-ID: <09728d784888b38d7a8f09ee5e9e9c542c875e1e.1737973614.git.pkrempa@redhat.com> | ||
32 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | ||
8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 33 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 34 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 35 | --- |
11 | include/qemu/job.h | 20 -------------------- | 36 | block/block-backend.c | 2 +- |
12 | 1 file changed, 20 deletions(-) | 37 | 1 file changed, 1 insertion(+), 1 deletion(-) |
13 | 38 | ||
14 | diff --git a/include/qemu/job.h b/include/qemu/job.h | 39 | diff --git a/block/block-backend.c b/block/block-backend.c |
15 | index XXXXXXX..XXXXXXX 100644 | 40 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/include/qemu/job.h | 41 | --- a/block/block-backend.c |
17 | +++ b/include/qemu/job.h | 42 | +++ b/block/block-backend.c |
18 | @@ -XXX,XX +XXX,XX @@ typedef struct Job { | 43 | @@ -XXX,XX +XXX,XX @@ static void send_qmp_error_event(BlockBackend *blk, |
19 | 44 | g_autofree char *path = blk_get_attached_dev_path(blk); | |
20 | /** | 45 | |
21 | * The completion function that will be called when the job completes. | 46 | optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; |
22 | - * Called with AioContext lock held, since many callback implementations | 47 | - qapi_event_send_block_io_error(blk_name(blk), path, |
23 | - * use bdrv_* functions that require to hold the lock. | 48 | + qapi_event_send_block_io_error(path, blk_name(blk), |
24 | */ | 49 | bs ? bdrv_get_node_name(bs) : NULL, optype, |
25 | BlockCompletionFunc *cb; | 50 | action, blk_iostatus_is_enabled(blk), |
26 | 51 | error == ENOSPC, strerror(error)); | |
27 | @@ -XXX,XX +XXX,XX @@ struct JobDriver { | ||
28 | * | ||
29 | * This callback will not be invoked if the job has already failed. | ||
30 | * If it fails, abort and then clean will be called. | ||
31 | - * | ||
32 | - * Called with AioContext lock held, since many callbacs implementations | ||
33 | - * use bdrv_* functions that require to hold the lock. | ||
34 | */ | ||
35 | int (*prepare)(Job *job); | ||
36 | |||
37 | @@ -XXX,XX +XXX,XX @@ struct JobDriver { | ||
38 | * | ||
39 | * All jobs will complete with a call to either .commit() or .abort() but | ||
40 | * never both. | ||
41 | - * | ||
42 | - * Called with AioContext lock held, since many callback implementations | ||
43 | - * use bdrv_* functions that require to hold the lock. | ||
44 | */ | ||
45 | void (*commit)(Job *job); | ||
46 | |||
47 | @@ -XXX,XX +XXX,XX @@ struct JobDriver { | ||
48 | * | ||
49 | * All jobs will complete with a call to either .commit() or .abort() but | ||
50 | * never both. | ||
51 | - * | ||
52 | - * Called with AioContext lock held, since many callback implementations | ||
53 | - * use bdrv_* functions that require to hold the lock. | ||
54 | */ | ||
55 | void (*abort)(Job *job); | ||
56 | |||
57 | @@ -XXX,XX +XXX,XX @@ struct JobDriver { | ||
58 | * .commit() or .abort(). Regardless of which callback is invoked after | ||
59 | * completion, .clean() will always be called, even if the job does not | ||
60 | * belong to a transaction group. | ||
61 | - * | ||
62 | - * Called with AioContext lock held, since many callbacs implementations | ||
63 | - * use bdrv_* functions that require to hold the lock. | ||
64 | */ | ||
65 | void (*clean)(Job *job); | ||
66 | |||
67 | @@ -XXX,XX +XXX,XX @@ struct JobDriver { | ||
68 | * READY). | ||
69 | * (If the callback is NULL, the job is assumed to terminate | ||
70 | * without I/O.) | ||
71 | - * | ||
72 | - * Called with AioContext lock held, since many callback implementations | ||
73 | - * use bdrv_* functions that require to hold the lock. | ||
74 | */ | ||
75 | bool (*cancel)(Job *job, bool force); | ||
76 | |||
77 | |||
78 | /** | ||
79 | * Called when the job is freed. | ||
80 | - * Called with AioContext lock held, since many callback implementations | ||
81 | - * use bdrv_* functions that require to hold the lock. | ||
82 | */ | ||
83 | void (*free)(Job *job); | ||
84 | }; | ||
85 | @@ -XXX,XX +XXX,XX @@ void job_ref_locked(Job *job); | ||
86 | * Release a reference that was previously acquired with job_ref_locked() or | ||
87 | * job_create(). If it's the last reference to the object, it will be freed. | ||
88 | * | ||
89 | - * Takes AioContext lock internally to invoke a job->driver callback. | ||
90 | * Called with job lock held. | ||
91 | */ | ||
92 | void job_unref_locked(Job *job); | ||
93 | -- | 52 | -- |
94 | 2.43.0 | 53 | 2.48.1 |
54 | |||
55 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | This allows querying from QMP (and also HMP) whether an image is |
---|---|---|---|
2 | currently active or inactive (in the sense of BDRV_O_INACTIVE). | ||
2 | 3 | ||
3 | This is the big patch that removes | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | aio_context_acquire()/aio_context_release() from the block layer and | 5 | Acked-by: Fabiano Rosas <farosas@suse.de> |
5 | affected block layer users. | ||
6 | |||
7 | There isn't a clean way to split this patch and the reviewers are likely | ||
8 | the same group of people, so I decided to do it in one patch. | ||
9 | |||
10 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
11 | Reviewed-by: Eric Blake <eblake@redhat.com> | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
12 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 7 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
13 | Reviewed-by: Paul Durrant <paul@xen.org> | 8 | Message-ID: <20250204211407.381505-2-kwolf@redhat.com> |
14 | Message-ID: <20231205182011.1976568-7-stefanha@redhat.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
16 | --- | 10 | --- |
17 | include/block/block-global-state.h | 9 +- | 11 | qapi/block-core.json | 6 +++++- |
18 | include/block/block-io.h | 3 +- | 12 | include/block/block-global-state.h | 3 +++ |
19 | include/block/snapshot.h | 2 - | 13 | block.c | 4 ++++ |
20 | block.c | 234 +--------------------- | 14 | block/monitor/block-hmp-cmds.c | 5 +++-- |
21 | block/block-backend.c | 14 -- | 15 | block/qapi.c | 1 + |
22 | block/copy-before-write.c | 22 +-- | 16 | tests/qemu-iotests/184.out | 2 ++ |
23 | block/export/export.c | 22 +-- | 17 | tests/qemu-iotests/191.out | 16 ++++++++++++++++ |
24 | block/io.c | 45 +---- | 18 | tests/qemu-iotests/273.out | 5 +++++ |
25 | block/mirror.c | 19 -- | 19 | 8 files changed, 39 insertions(+), 3 deletions(-) |
26 | block/monitor/bitmap-qmp-cmds.c | 20 +- | ||
27 | block/monitor/block-hmp-cmds.c | 29 --- | ||
28 | block/qapi-sysemu.c | 27 +-- | ||
29 | block/qapi.c | 18 +- | ||
30 | block/raw-format.c | 5 - | ||
31 | block/replication.c | 58 +----- | ||
32 | block/snapshot.c | 22 +-- | ||
33 | block/write-threshold.c | 6 - | ||
34 | blockdev.c | 307 +++++------------------------ | ||
35 | blockjob.c | 18 -- | ||
36 | hw/block/dataplane/virtio-blk.c | 10 - | ||
37 | hw/block/dataplane/xen-block.c | 17 +- | ||
38 | hw/block/virtio-blk.c | 13 -- | ||
39 | hw/core/qdev-properties-system.c | 9 - | ||
40 | job.c | 16 -- | ||
41 | migration/block.c | 34 +--- | ||
42 | migration/migration-hmp-cmds.c | 3 - | ||
43 | migration/savevm.c | 22 --- | ||
44 | net/colo-compare.c | 2 - | ||
45 | qemu-img.c | 4 - | ||
46 | qemu-io.c | 10 +- | ||
47 | qemu-nbd.c | 2 - | ||
48 | replay/replay-debugging.c | 4 - | ||
49 | tests/unit/test-bdrv-drain.c | 51 +---- | ||
50 | tests/unit/test-bdrv-graph-mod.c | 6 - | ||
51 | tests/unit/test-block-iothread.c | 31 --- | ||
52 | tests/unit/test-blockjob.c | 137 ------------- | ||
53 | tests/unit/test-replication.c | 11 -- | ||
54 | util/async.c | 4 - | ||
55 | util/vhost-user-server.c | 3 - | ||
56 | scripts/block-coroutine-wrapper.py | 3 - | ||
57 | tests/tsan/suppressions.tsan | 1 - | ||
58 | 41 files changed, 104 insertions(+), 1169 deletions(-) | ||
59 | 20 | ||
21 | diff --git a/qapi/block-core.json b/qapi/block-core.json | ||
22 | index XXXXXXX..XXXXXXX 100644 | ||
23 | --- a/qapi/block-core.json | ||
24 | +++ b/qapi/block-core.json | ||
25 | @@ -XXX,XX +XXX,XX @@ | ||
26 | # @backing_file_depth: number of files in the backing file chain | ||
27 | # (since: 1.2) | ||
28 | # | ||
29 | +# @active: true if the backend is active; typical cases for inactive backends | ||
30 | +# are on the migration source instance after migration completes and on the | ||
31 | +# destination before it completes. (since: 10.0) | ||
32 | +# | ||
33 | # @encrypted: true if the backing device is encrypted | ||
34 | # | ||
35 | # @detect_zeroes: detect and optimize zero writes (Since 2.1) | ||
36 | @@ -XXX,XX +XXX,XX @@ | ||
37 | { 'struct': 'BlockDeviceInfo', | ||
38 | 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', | ||
39 | '*backing_file': 'str', 'backing_file_depth': 'int', | ||
40 | - 'encrypted': 'bool', | ||
41 | + 'active': 'bool', 'encrypted': 'bool', | ||
42 | 'detect_zeroes': 'BlockdevDetectZeroesOptions', | ||
43 | 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', | ||
44 | 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', | ||
60 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h | 45 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h |
61 | index XXXXXXX..XXXXXXX 100644 | 46 | index XXXXXXX..XXXXXXX 100644 |
62 | --- a/include/block/block-global-state.h | 47 | --- a/include/block/block-global-state.h |
63 | +++ b/include/block/block-global-state.h | 48 | +++ b/include/block/block-global-state.h |
64 | @@ -XXX,XX +XXX,XX @@ | 49 | @@ -XXX,XX +XXX,XX @@ BlockDriverState * GRAPH_RDLOCK |
65 | /* | 50 | check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, |
66 | * Global state (GS) API. These functions run under the BQL. | 51 | Error **errp); |
67 | * | 52 | |
68 | - * If a function modifies the graph, it also uses drain and/or | 53 | + |
69 | - * aio_context_acquire/release to be sure it has unique access. | 54 | +bool GRAPH_RDLOCK bdrv_is_inactive(BlockDriverState *bs); |
70 | - * aio_context locking is needed together with BQL because of | 55 | + |
71 | - * the thread-safe I/O API that concurrently runs and accesses | 56 | int no_coroutine_fn GRAPH_RDLOCK |
72 | - * the graph without the BQL. | 57 | bdrv_activate(BlockDriverState *bs, Error **errp); |
73 | + * If a function modifies the graph, it also uses the graph lock to be sure it | 58 | |
74 | + * has unique access. The graph lock is needed together with BQL because of the | ||
75 | + * thread-safe I/O API that concurrently runs and accesses the graph without | ||
76 | + * the BQL. | ||
77 | * | ||
78 | * It is important to note that not all of these functions are | ||
79 | * necessarily limited to running under the BQL, but they would | ||
80 | diff --git a/include/block/block-io.h b/include/block/block-io.h | ||
81 | index XXXXXXX..XXXXXXX 100644 | ||
82 | --- a/include/block/block-io.h | ||
83 | +++ b/include/block/block-io.h | ||
84 | @@ -XXX,XX +XXX,XX @@ | ||
85 | |||
86 | /* | ||
87 | * I/O API functions. These functions are thread-safe, and therefore | ||
88 | - * can run in any thread as long as the thread has called | ||
89 | - * aio_context_acquire/release(). | ||
90 | + * can run in any thread. | ||
91 | * | ||
92 | * These functions can only call functions from I/O and Common categories, | ||
93 | * but can be invoked by GS, "I/O or GS" and I/O APIs. | ||
94 | diff --git a/include/block/snapshot.h b/include/block/snapshot.h | ||
95 | index XXXXXXX..XXXXXXX 100644 | ||
96 | --- a/include/block/snapshot.h | ||
97 | +++ b/include/block/snapshot.h | ||
98 | @@ -XXX,XX +XXX,XX @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, | ||
99 | |||
100 | /* | ||
101 | * Group operations. All block drivers are involved. | ||
102 | - * These functions will properly handle dataplane (take aio_context_acquire | ||
103 | - * when appropriate for appropriate block drivers | ||
104 | */ | ||
105 | |||
106 | bool bdrv_all_can_snapshot(bool has_devices, strList *devices, | ||
107 | diff --git a/block.c b/block.c | 59 | diff --git a/block.c b/block.c |
108 | index XXXXXXX..XXXXXXX 100644 | 60 | index XXXXXXX..XXXXXXX 100644 |
109 | --- a/block.c | 61 | --- a/block.c |
110 | +++ b/block.c | 62 | +++ b/block.c |
111 | @@ -XXX,XX +XXX,XX @@ static int no_coroutine_fn GRAPH_UNLOCKED | 63 | @@ -XXX,XX +XXX,XX @@ void bdrv_init_with_whitelist(void) |
112 | bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, | 64 | bdrv_init(); |
113 | QDict *options, int open_flags, Error **errp) | 65 | } |
66 | |||
67 | +bool bdrv_is_inactive(BlockDriverState *bs) { | ||
68 | + return bs->open_flags & BDRV_O_INACTIVE; | ||
69 | +} | ||
70 | + | ||
71 | int bdrv_activate(BlockDriverState *bs, Error **errp) | ||
114 | { | 72 | { |
115 | - AioContext *ctx; | 73 | BdrvChild *child, *parent; |
116 | Error *local_err = NULL; | ||
117 | int i, ret; | ||
118 | GLOBAL_STATE_CODE(); | ||
119 | @@ -XXX,XX +XXX,XX @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, | ||
120 | bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF; | ||
121 | bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF; | ||
122 | |||
123 | - /* Get the context after .bdrv_open, it can change the context */ | ||
124 | - ctx = bdrv_get_aio_context(bs); | ||
125 | - aio_context_acquire(ctx); | ||
126 | - | ||
127 | ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); | ||
128 | if (ret < 0) { | ||
129 | error_setg_errno(errp, -ret, "Could not refresh total sector count"); | ||
130 | - aio_context_release(ctx); | ||
131 | return ret; | ||
132 | } | ||
133 | |||
134 | bdrv_graph_rdlock_main_loop(); | ||
135 | bdrv_refresh_limits(bs, NULL, &local_err); | ||
136 | bdrv_graph_rdunlock_main_loop(); | ||
137 | - aio_context_release(ctx); | ||
138 | |||
139 | if (local_err) { | ||
140 | error_propagate(errp, local_err); | ||
141 | @@ -XXX,XX +XXX,XX @@ bdrv_attach_child_common(BlockDriverState *child_bs, | ||
142 | Transaction *tran, Error **errp) | ||
143 | { | ||
144 | BdrvChild *new_child; | ||
145 | - AioContext *parent_ctx, *new_child_ctx; | ||
146 | + AioContext *parent_ctx; | ||
147 | AioContext *child_ctx = bdrv_get_aio_context(child_bs); | ||
148 | |||
149 | assert(child_class->get_parent_desc); | ||
150 | @@ -XXX,XX +XXX,XX @@ bdrv_attach_child_common(BlockDriverState *child_bs, | ||
151 | } | ||
152 | } | ||
153 | |||
154 | - new_child_ctx = bdrv_get_aio_context(child_bs); | ||
155 | - if (new_child_ctx != child_ctx) { | ||
156 | - aio_context_release(child_ctx); | ||
157 | - aio_context_acquire(new_child_ctx); | ||
158 | - } | ||
159 | - | ||
160 | bdrv_ref(child_bs); | ||
161 | /* | ||
162 | * Let every new BdrvChild start with a drained parent. Inserting the child | ||
163 | @@ -XXX,XX +XXX,XX @@ bdrv_attach_child_common(BlockDriverState *child_bs, | ||
164 | }; | ||
165 | tran_add(tran, &bdrv_attach_child_common_drv, s); | ||
166 | |||
167 | - if (new_child_ctx != child_ctx) { | ||
168 | - aio_context_release(new_child_ctx); | ||
169 | - aio_context_acquire(child_ctx); | ||
170 | - } | ||
171 | - | ||
172 | return new_child; | ||
173 | } | ||
174 | |||
175 | @@ -XXX,XX +XXX,XX @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | ||
176 | int ret = 0; | ||
177 | bool implicit_backing = false; | ||
178 | BlockDriverState *backing_hd; | ||
179 | - AioContext *backing_hd_ctx; | ||
180 | QDict *options; | ||
181 | QDict *tmp_parent_options = NULL; | ||
182 | Error *local_err = NULL; | ||
183 | @@ -XXX,XX +XXX,XX @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | ||
184 | |||
185 | /* Hook up the backing file link; drop our reference, bs owns the | ||
186 | * backing_hd reference now */ | ||
187 | - backing_hd_ctx = bdrv_get_aio_context(backing_hd); | ||
188 | - aio_context_acquire(backing_hd_ctx); | ||
189 | ret = bdrv_set_backing_hd(bs, backing_hd, errp); | ||
190 | bdrv_unref(backing_hd); | ||
191 | - aio_context_release(backing_hd_ctx); | ||
192 | |||
193 | if (ret < 0) { | ||
194 | goto free_exit; | ||
195 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename, | ||
196 | { | ||
197 | BlockDriverState *bs; | ||
198 | BdrvChild *child; | ||
199 | - AioContext *ctx; | ||
200 | |||
201 | GLOBAL_STATE_CODE(); | ||
202 | |||
203 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename, | ||
204 | } | ||
205 | |||
206 | bdrv_graph_wrlock(); | ||
207 | - ctx = bdrv_get_aio_context(bs); | ||
208 | - aio_context_acquire(ctx); | ||
209 | child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, | ||
210 | errp); | ||
211 | - aio_context_release(ctx); | ||
212 | bdrv_graph_wrunlock(); | ||
213 | |||
214 | return child; | ||
215 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||
216 | int64_t total_size; | ||
217 | QemuOpts *opts = NULL; | ||
218 | BlockDriverState *bs_snapshot = NULL; | ||
219 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
220 | int ret; | ||
221 | |||
222 | GLOBAL_STATE_CODE(); | ||
223 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||
224 | instead of opening 'filename' directly */ | ||
225 | |||
226 | /* Get the required size from the image */ | ||
227 | - aio_context_acquire(ctx); | ||
228 | total_size = bdrv_getlength(bs); | ||
229 | - aio_context_release(ctx); | ||
230 | |||
231 | if (total_size < 0) { | ||
232 | error_setg_errno(errp, -total_size, "Could not get image size"); | ||
233 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||
234 | goto out; | ||
235 | } | ||
236 | |||
237 | - aio_context_acquire(ctx); | ||
238 | ret = bdrv_append(bs_snapshot, bs, errp); | ||
239 | - aio_context_release(ctx); | ||
240 | - | ||
241 | if (ret < 0) { | ||
242 | bs_snapshot = NULL; | ||
243 | goto out; | ||
244 | @@ -XXX,XX +XXX,XX @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, | ||
245 | Error *local_err = NULL; | ||
246 | QDict *snapshot_options = NULL; | ||
247 | int snapshot_flags = 0; | ||
248 | - AioContext *ctx = qemu_get_aio_context(); | ||
249 | |||
250 | assert(!child_class || !flags); | ||
251 | assert(!child_class == !parent); | ||
252 | @@ -XXX,XX +XXX,XX @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, | ||
253 | /* Not requesting BLK_PERM_CONSISTENT_READ because we're only | ||
254 | * looking at the header to guess the image format. This works even | ||
255 | * in cases where a guest would not see a consistent state. */ | ||
256 | - ctx = bdrv_get_aio_context(file_bs); | ||
257 | - aio_context_acquire(ctx); | ||
258 | + AioContext *ctx = bdrv_get_aio_context(file_bs); | ||
259 | file = blk_new(ctx, 0, BLK_PERM_ALL); | ||
260 | blk_insert_bs(file, file_bs, &local_err); | ||
261 | bdrv_unref(file_bs); | ||
262 | - aio_context_release(ctx); | ||
263 | |||
264 | if (local_err) { | ||
265 | goto fail; | ||
266 | @@ -XXX,XX +XXX,XX @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, | ||
267 | goto fail; | ||
268 | } | ||
269 | |||
270 | - /* The AioContext could have changed during bdrv_open_common() */ | ||
271 | - ctx = bdrv_get_aio_context(bs); | ||
272 | - | ||
273 | if (file) { | ||
274 | - aio_context_acquire(ctx); | ||
275 | blk_unref(file); | ||
276 | - aio_context_release(ctx); | ||
277 | file = NULL; | ||
278 | } | ||
279 | |||
280 | @@ -XXX,XX +XXX,XX @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options, | ||
281 | * (snapshot_bs); thus, we have to drop the strong reference to bs | ||
282 | * (which we obtained by calling bdrv_new()). bs will not be deleted, | ||
283 | * though, because the overlay still has a reference to it. */ | ||
284 | - aio_context_acquire(ctx); | ||
285 | bdrv_unref(bs); | ||
286 | - aio_context_release(ctx); | ||
287 | bs = snapshot_bs; | ||
288 | } | ||
289 | |||
290 | return bs; | ||
291 | |||
292 | fail: | ||
293 | - aio_context_acquire(ctx); | ||
294 | blk_unref(file); | ||
295 | qobject_unref(snapshot_options); | ||
296 | qobject_unref(bs->explicit_options); | ||
297 | @@ -XXX,XX +XXX,XX @@ fail: | ||
298 | bs->options = NULL; | ||
299 | bs->explicit_options = NULL; | ||
300 | bdrv_unref(bs); | ||
301 | - aio_context_release(ctx); | ||
302 | error_propagate(errp, local_err); | ||
303 | return NULL; | ||
304 | |||
305 | close_and_fail: | ||
306 | - aio_context_acquire(ctx); | ||
307 | bdrv_unref(bs); | ||
308 | - aio_context_release(ctx); | ||
309 | qobject_unref(snapshot_options); | ||
310 | qobject_unref(options); | ||
311 | error_propagate(errp, local_err); | ||
312 | @@ -XXX,XX +XXX,XX @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) | ||
313 | if (bs_queue) { | ||
314 | BlockReopenQueueEntry *bs_entry, *next; | ||
315 | QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { | ||
316 | - AioContext *ctx = bdrv_get_aio_context(bs_entry->state.bs); | ||
317 | - | ||
318 | - aio_context_acquire(ctx); | ||
319 | bdrv_drained_end(bs_entry->state.bs); | ||
320 | - aio_context_release(ctx); | ||
321 | - | ||
322 | qobject_unref(bs_entry->state.explicit_options); | ||
323 | qobject_unref(bs_entry->state.options); | ||
324 | g_free(bs_entry); | ||
325 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
326 | { | ||
327 | int ret = -1; | ||
328 | BlockReopenQueueEntry *bs_entry, *next; | ||
329 | - AioContext *ctx; | ||
330 | Transaction *tran = tran_new(); | ||
331 | g_autoptr(GSList) refresh_list = NULL; | ||
332 | |||
333 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
334 | GLOBAL_STATE_CODE(); | ||
335 | |||
336 | QTAILQ_FOREACH(bs_entry, bs_queue, entry) { | ||
337 | - ctx = bdrv_get_aio_context(bs_entry->state.bs); | ||
338 | - aio_context_acquire(ctx); | ||
339 | ret = bdrv_flush(bs_entry->state.bs); | ||
340 | - aio_context_release(ctx); | ||
341 | if (ret < 0) { | ||
342 | error_setg_errno(errp, -ret, "Error flushing drive"); | ||
343 | goto abort; | ||
344 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
345 | |||
346 | QTAILQ_FOREACH(bs_entry, bs_queue, entry) { | ||
347 | assert(bs_entry->state.bs->quiesce_counter > 0); | ||
348 | - ctx = bdrv_get_aio_context(bs_entry->state.bs); | ||
349 | - aio_context_acquire(ctx); | ||
350 | ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp); | ||
351 | - aio_context_release(ctx); | ||
352 | if (ret < 0) { | ||
353 | goto abort; | ||
354 | } | ||
355 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
356 | * to first element. | ||
357 | */ | ||
358 | QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { | ||
359 | - ctx = bdrv_get_aio_context(bs_entry->state.bs); | ||
360 | - aio_context_acquire(ctx); | ||
361 | bdrv_reopen_commit(&bs_entry->state); | ||
362 | - aio_context_release(ctx); | ||
363 | } | ||
364 | |||
365 | bdrv_graph_wrlock(); | ||
366 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
367 | BlockDriverState *bs = bs_entry->state.bs; | ||
368 | |||
369 | if (bs->drv->bdrv_reopen_commit_post) { | ||
370 | - ctx = bdrv_get_aio_context(bs); | ||
371 | - aio_context_acquire(ctx); | ||
372 | bs->drv->bdrv_reopen_commit_post(&bs_entry->state); | ||
373 | - aio_context_release(ctx); | ||
374 | } | ||
375 | } | ||
376 | |||
377 | @@ -XXX,XX +XXX,XX @@ abort: | ||
378 | |||
379 | QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { | ||
380 | if (bs_entry->prepared) { | ||
381 | - ctx = bdrv_get_aio_context(bs_entry->state.bs); | ||
382 | - aio_context_acquire(ctx); | ||
383 | bdrv_reopen_abort(&bs_entry->state); | ||
384 | - aio_context_release(ctx); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | @@ -XXX,XX +XXX,XX @@ cleanup: | ||
389 | int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, | ||
390 | Error **errp) | ||
391 | { | ||
392 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
393 | BlockReopenQueue *queue; | ||
394 | - int ret; | ||
395 | |||
396 | GLOBAL_STATE_CODE(); | ||
397 | |||
398 | queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts); | ||
399 | |||
400 | - if (ctx != qemu_get_aio_context()) { | ||
401 | - aio_context_release(ctx); | ||
402 | - } | ||
403 | - ret = bdrv_reopen_multiple(queue, errp); | ||
404 | - | ||
405 | - if (ctx != qemu_get_aio_context()) { | ||
406 | - aio_context_acquire(ctx); | ||
407 | - } | ||
408 | - | ||
409 | - return ret; | ||
410 | + return bdrv_reopen_multiple(queue, errp); | ||
411 | } | ||
412 | |||
413 | int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, | ||
414 | @@ -XXX,XX +XXX,XX @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, | ||
415 | const char *child_name = is_backing ? "backing" : "file"; | ||
416 | QObject *value; | ||
417 | const char *str; | ||
418 | - AioContext *ctx, *old_ctx; | ||
419 | bool has_child; | ||
420 | int ret; | ||
421 | |||
422 | @@ -XXX,XX +XXX,XX @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, | ||
423 | bdrv_drained_begin(old_child_bs); | ||
424 | } | ||
425 | |||
426 | - old_ctx = bdrv_get_aio_context(bs); | ||
427 | - ctx = bdrv_get_aio_context(new_child_bs); | ||
428 | - if (old_ctx != ctx) { | ||
429 | - aio_context_release(old_ctx); | ||
430 | - aio_context_acquire(ctx); | ||
431 | - } | ||
432 | - | ||
433 | bdrv_graph_rdunlock_main_loop(); | ||
434 | bdrv_graph_wrlock(); | ||
435 | |||
436 | @@ -XXX,XX +XXX,XX @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, | ||
437 | |||
438 | bdrv_graph_wrunlock(); | ||
439 | |||
440 | - if (old_ctx != ctx) { | ||
441 | - aio_context_release(ctx); | ||
442 | - aio_context_acquire(old_ctx); | ||
443 | - } | ||
444 | - | ||
445 | if (old_child_bs) { | ||
446 | bdrv_drained_end(old_child_bs); | ||
447 | bdrv_unref(old_child_bs); | ||
448 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
449 | int ret; | ||
450 | BdrvChild *child; | ||
451 | Transaction *tran = tran_new(); | ||
452 | - AioContext *old_context, *new_context = NULL; | ||
453 | |||
454 | GLOBAL_STATE_CODE(); | ||
455 | |||
456 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
457 | assert(!bs_new->backing); | ||
458 | bdrv_graph_rdunlock_main_loop(); | ||
459 | |||
460 | - old_context = bdrv_get_aio_context(bs_top); | ||
461 | bdrv_drained_begin(bs_top); | ||
462 | - | ||
463 | - /* | ||
464 | - * bdrv_drained_begin() requires that only the AioContext of the drained | ||
465 | - * node is locked, and at this point it can still differ from the AioContext | ||
466 | - * of bs_top. | ||
467 | - */ | ||
468 | - new_context = bdrv_get_aio_context(bs_new); | ||
469 | - aio_context_release(old_context); | ||
470 | - aio_context_acquire(new_context); | ||
471 | bdrv_drained_begin(bs_new); | ||
472 | - aio_context_release(new_context); | ||
473 | - aio_context_acquire(old_context); | ||
474 | - new_context = NULL; | ||
475 | |||
476 | bdrv_graph_wrlock(); | ||
477 | |||
478 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
479 | goto out; | ||
480 | } | ||
481 | |||
482 | - /* | ||
483 | - * bdrv_attach_child_noperm could change the AioContext of bs_top and | ||
484 | - * bs_new, but at least they are in the same AioContext now. This is the | ||
485 | - * AioContext that we need to lock for the rest of the function. | ||
486 | - */ | ||
487 | - new_context = bdrv_get_aio_context(bs_top); | ||
488 | - | ||
489 | - if (old_context != new_context) { | ||
490 | - aio_context_release(old_context); | ||
491 | - aio_context_acquire(new_context); | ||
492 | - } | ||
493 | - | ||
494 | ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp); | ||
495 | if (ret < 0) { | ||
496 | goto out; | ||
497 | @@ -XXX,XX +XXX,XX @@ out: | ||
498 | bdrv_drained_end(bs_top); | ||
499 | bdrv_drained_end(bs_new); | ||
500 | |||
501 | - if (new_context && old_context != new_context) { | ||
502 | - aio_context_release(new_context); | ||
503 | - aio_context_acquire(old_context); | ||
504 | - } | ||
505 | - | ||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, | ||
510 | |||
511 | GLOBAL_STATE_CODE(); | ||
512 | |||
513 | - aio_context_release(ctx); | ||
514 | - aio_context_acquire(qemu_get_aio_context()); | ||
515 | new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags, | ||
516 | errp); | ||
517 | - aio_context_release(qemu_get_aio_context()); | ||
518 | - aio_context_acquire(ctx); | ||
519 | assert(bdrv_get_aio_context(bs) == ctx); | ||
520 | |||
521 | options = NULL; /* bdrv_new_open_driver() eats options */ | ||
522 | @@ -XXX,XX +XXX,XX @@ void bdrv_activate_all(Error **errp) | ||
523 | GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
524 | |||
525 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
526 | - AioContext *aio_context = bdrv_get_aio_context(bs); | ||
527 | int ret; | ||
528 | |||
529 | - aio_context_acquire(aio_context); | ||
530 | ret = bdrv_activate(bs, errp); | ||
531 | - aio_context_release(aio_context); | ||
532 | if (ret < 0) { | ||
533 | bdrv_next_cleanup(&it); | ||
534 | return; | ||
535 | @@ -XXX,XX +XXX,XX @@ int bdrv_inactivate_all(void) | ||
536 | BlockDriverState *bs = NULL; | ||
537 | BdrvNextIterator it; | ||
538 | int ret = 0; | ||
539 | - GSList *aio_ctxs = NULL, *ctx; | ||
540 | |||
541 | GLOBAL_STATE_CODE(); | ||
542 | GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
543 | |||
544 | - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
545 | - AioContext *aio_context = bdrv_get_aio_context(bs); | ||
546 | - | ||
547 | - if (!g_slist_find(aio_ctxs, aio_context)) { | ||
548 | - aio_ctxs = g_slist_prepend(aio_ctxs, aio_context); | ||
549 | - aio_context_acquire(aio_context); | ||
550 | - } | ||
551 | - } | ||
552 | - | ||
553 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
554 | /* Nodes with BDS parents are covered by recursion from the last | ||
555 | * parent that gets inactivated. Don't inactivate them a second | ||
556 | @@ -XXX,XX +XXX,XX @@ int bdrv_inactivate_all(void) | ||
557 | ret = bdrv_inactivate_recurse(bs); | ||
558 | if (ret < 0) { | ||
559 | bdrv_next_cleanup(&it); | ||
560 | - goto out; | ||
561 | + break; | ||
562 | } | ||
563 | } | ||
564 | |||
565 | -out: | ||
566 | - for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) { | ||
567 | - AioContext *aio_context = ctx->data; | ||
568 | - aio_context_release(aio_context); | ||
569 | - } | ||
570 | - g_slist_free(aio_ctxs); | ||
571 | - | ||
572 | return ret; | ||
573 | } | ||
574 | |||
575 | @@ -XXX,XX +XXX,XX @@ void bdrv_unref(BlockDriverState *bs) | ||
576 | static void bdrv_schedule_unref_bh(void *opaque) | ||
577 | { | ||
578 | BlockDriverState *bs = opaque; | ||
579 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
580 | |||
581 | - aio_context_acquire(ctx); | ||
582 | bdrv_unref(bs); | ||
583 | - aio_context_release(ctx); | ||
584 | } | ||
585 | |||
586 | /* | ||
587 | @@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt, | ||
588 | return; | ||
589 | } | ||
590 | |||
591 | - aio_context_acquire(qemu_get_aio_context()); | ||
592 | - | ||
593 | /* Create parameter list */ | ||
594 | create_opts = qemu_opts_append(create_opts, drv->create_opts); | ||
595 | create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); | ||
596 | @@ -XXX,XX +XXX,XX @@ out: | ||
597 | qemu_opts_del(opts); | ||
598 | qemu_opts_free(create_opts); | ||
599 | error_propagate(errp, local_err); | ||
600 | - aio_context_release(qemu_get_aio_context()); | ||
601 | } | ||
602 | |||
603 | AioContext *bdrv_get_aio_context(BlockDriverState *bs) | ||
604 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) | ||
605 | |||
606 | void coroutine_fn bdrv_co_lock(BlockDriverState *bs) | ||
607 | { | ||
608 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
609 | - | ||
610 | - /* In the main thread, bs->aio_context won't change concurrently */ | ||
611 | - assert(qemu_get_current_aio_context() == qemu_get_aio_context()); | ||
612 | - | ||
613 | - /* | ||
614 | - * We're in coroutine context, so we already hold the lock of the main | ||
615 | - * loop AioContext. Don't lock it twice to avoid deadlocks. | ||
616 | - */ | ||
617 | - assert(qemu_in_coroutine()); | ||
618 | - if (ctx != qemu_get_aio_context()) { | ||
619 | - aio_context_acquire(ctx); | ||
620 | - } | ||
621 | + /* TODO removed in next patch */ | ||
622 | } | ||
623 | |||
624 | void coroutine_fn bdrv_co_unlock(BlockDriverState *bs) | ||
625 | { | ||
626 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
627 | - | ||
628 | - assert(qemu_in_coroutine()); | ||
629 | - if (ctx != qemu_get_aio_context()) { | ||
630 | - aio_context_release(ctx); | ||
631 | - } | ||
632 | + /* TODO removed in next patch */ | ||
633 | } | ||
634 | |||
635 | static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) | ||
636 | @@ -XXX,XX +XXX,XX @@ static void bdrv_set_aio_context_commit(void *opaque) | ||
637 | BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; | ||
638 | BlockDriverState *bs = (BlockDriverState *) state->bs; | ||
639 | AioContext *new_context = state->new_ctx; | ||
640 | - AioContext *old_context = bdrv_get_aio_context(bs); | ||
641 | |||
642 | - /* | ||
643 | - * Take the old AioContex when detaching it from bs. | ||
644 | - * At this point, new_context lock is already acquired, and we are now | ||
645 | - * also taking old_context. This is safe as long as bdrv_detach_aio_context | ||
646 | - * does not call AIO_POLL_WHILE(). | ||
647 | - */ | ||
648 | - if (old_context != qemu_get_aio_context()) { | ||
649 | - aio_context_acquire(old_context); | ||
650 | - } | ||
651 | bdrv_detach_aio_context(bs); | ||
652 | - if (old_context != qemu_get_aio_context()) { | ||
653 | - aio_context_release(old_context); | ||
654 | - } | ||
655 | bdrv_attach_aio_context(bs, new_context); | ||
656 | } | ||
657 | |||
658 | @@ -XXX,XX +XXX,XX @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, | ||
659 | Transaction *tran; | ||
660 | GHashTable *visited; | ||
661 | int ret; | ||
662 | - AioContext *old_context = bdrv_get_aio_context(bs); | ||
663 | GLOBAL_STATE_CODE(); | ||
664 | |||
665 | /* | ||
666 | @@ -XXX,XX +XXX,XX @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, | ||
667 | return -EPERM; | ||
668 | } | ||
669 | |||
670 | - /* | ||
671 | - * Release old AioContext, it won't be needed anymore, as all | ||
672 | - * bdrv_drained_begin() have been called already. | ||
673 | - */ | ||
674 | - if (qemu_get_aio_context() != old_context) { | ||
675 | - aio_context_release(old_context); | ||
676 | - } | ||
677 | - | ||
678 | - /* | ||
679 | - * Acquire new AioContext since bdrv_drained_end() is going to be called | ||
680 | - * after we switched all nodes in the new AioContext, and the function | ||
681 | - * assumes that the lock of the bs is always taken. | ||
682 | - */ | ||
683 | - if (qemu_get_aio_context() != ctx) { | ||
684 | - aio_context_acquire(ctx); | ||
685 | - } | ||
686 | - | ||
687 | tran_commit(tran); | ||
688 | - | ||
689 | - if (qemu_get_aio_context() != ctx) { | ||
690 | - aio_context_release(ctx); | ||
691 | - } | ||
692 | - | ||
693 | - /* Re-acquire the old AioContext, since the caller takes and releases it. */ | ||
694 | - if (qemu_get_aio_context() != old_context) { | ||
695 | - aio_context_acquire(old_context); | ||
696 | - } | ||
697 | - | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, | ||
702 | const char *node_name, Error **errp) | ||
703 | { | ||
704 | BlockDriverState *to_replace_bs = bdrv_find_node(node_name); | ||
705 | - AioContext *aio_context; | ||
706 | |||
707 | GLOBAL_STATE_CODE(); | ||
708 | |||
709 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, | ||
710 | return NULL; | ||
711 | } | ||
712 | |||
713 | - aio_context = bdrv_get_aio_context(to_replace_bs); | ||
714 | - aio_context_acquire(aio_context); | ||
715 | - | ||
716 | if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) { | ||
717 | - to_replace_bs = NULL; | ||
718 | - goto out; | ||
719 | + return NULL; | ||
720 | } | ||
721 | |||
722 | /* We don't want arbitrary node of the BDS chain to be replaced only the top | ||
723 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, | ||
724 | "because it cannot be guaranteed that doing so would not " | ||
725 | "lead to an abrupt change of visible data", | ||
726 | node_name, parent_bs->node_name); | ||
727 | - to_replace_bs = NULL; | ||
728 | - goto out; | ||
729 | + return NULL; | ||
730 | } | ||
731 | |||
732 | -out: | ||
733 | - aio_context_release(aio_context); | ||
734 | return to_replace_bs; | ||
735 | } | ||
736 | |||
737 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
738 | index XXXXXXX..XXXXXXX 100644 | ||
739 | --- a/block/block-backend.c | ||
740 | +++ b/block/block-backend.c | ||
741 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference, | ||
742 | { | ||
743 | BlockBackend *blk; | ||
744 | BlockDriverState *bs; | ||
745 | - AioContext *ctx; | ||
746 | uint64_t perm = 0; | ||
747 | uint64_t shared = BLK_PERM_ALL; | ||
748 | |||
749 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference, | ||
750 | shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; | ||
751 | } | ||
752 | |||
753 | - aio_context_acquire(qemu_get_aio_context()); | ||
754 | bs = bdrv_open(filename, reference, options, flags, errp); | ||
755 | - aio_context_release(qemu_get_aio_context()); | ||
756 | if (!bs) { | ||
757 | return NULL; | ||
758 | } | ||
759 | |||
760 | /* bdrv_open() could have moved bs to a different AioContext */ | ||
761 | - ctx = bdrv_get_aio_context(bs); | ||
762 | blk = blk_new(bdrv_get_aio_context(bs), perm, shared); | ||
763 | blk->perm = perm; | ||
764 | blk->shared_perm = shared; | ||
765 | |||
766 | - aio_context_acquire(ctx); | ||
767 | blk_insert_bs(blk, bs, errp); | ||
768 | bdrv_unref(bs); | ||
769 | - aio_context_release(ctx); | ||
770 | |||
771 | if (!blk->root) { | ||
772 | blk_unref(blk); | ||
773 | @@ -XXX,XX +XXX,XX @@ void blk_remove_all_bs(void) | ||
774 | GLOBAL_STATE_CODE(); | ||
775 | |||
776 | while ((blk = blk_all_next(blk)) != NULL) { | ||
777 | - AioContext *ctx = blk_get_aio_context(blk); | ||
778 | - | ||
779 | - aio_context_acquire(ctx); | ||
780 | if (blk->root) { | ||
781 | blk_remove_bs(blk); | ||
782 | } | ||
783 | - aio_context_release(ctx); | ||
784 | } | ||
785 | } | ||
786 | |||
787 | @@ -XXX,XX +XXX,XX @@ int blk_commit_all(void) | ||
788 | GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
789 | |||
790 | while ((blk = blk_all_next(blk)) != NULL) { | ||
791 | - AioContext *aio_context = blk_get_aio_context(blk); | ||
792 | BlockDriverState *unfiltered_bs = bdrv_skip_filters(blk_bs(blk)); | ||
793 | |||
794 | - aio_context_acquire(aio_context); | ||
795 | if (blk_is_inserted(blk) && bdrv_cow_child(unfiltered_bs)) { | ||
796 | int ret; | ||
797 | |||
798 | ret = bdrv_commit(unfiltered_bs); | ||
799 | if (ret < 0) { | ||
800 | - aio_context_release(aio_context); | ||
801 | return ret; | ||
802 | } | ||
803 | } | ||
804 | - aio_context_release(aio_context); | ||
805 | } | ||
806 | return 0; | ||
807 | } | ||
808 | diff --git a/block/copy-before-write.c b/block/copy-before-write.c | ||
809 | index XXXXXXX..XXXXXXX 100644 | ||
810 | --- a/block/copy-before-write.c | ||
811 | +++ b/block/copy-before-write.c | ||
812 | @@ -XXX,XX +XXX,XX @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, | ||
813 | int64_t cluster_size; | ||
814 | g_autoptr(BlockdevOptions) full_opts = NULL; | ||
815 | BlockdevOptionsCbw *opts; | ||
816 | - AioContext *ctx; | ||
817 | int ret; | ||
818 | |||
819 | full_opts = cbw_parse_options(options, errp); | ||
820 | @@ -XXX,XX +XXX,XX @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, | ||
821 | |||
822 | GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
823 | |||
824 | - ctx = bdrv_get_aio_context(bs); | ||
825 | - aio_context_acquire(ctx); | ||
826 | - | ||
827 | if (opts->bitmap) { | ||
828 | bitmap = block_dirty_bitmap_lookup(opts->bitmap->node, | ||
829 | opts->bitmap->name, NULL, errp); | ||
830 | if (!bitmap) { | ||
831 | - ret = -EINVAL; | ||
832 | - goto out; | ||
833 | + return -EINVAL; | ||
834 | } | ||
835 | } | ||
836 | s->on_cbw_error = opts->has_on_cbw_error ? opts->on_cbw_error : | ||
837 | @@ -XXX,XX +XXX,XX @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, | ||
838 | s->bcs = block_copy_state_new(bs->file, s->target, bitmap, errp); | ||
839 | if (!s->bcs) { | ||
840 | error_prepend(errp, "Cannot create block-copy-state: "); | ||
841 | - ret = -EINVAL; | ||
842 | - goto out; | ||
843 | + return -EINVAL; | ||
844 | } | ||
845 | |||
846 | cluster_size = block_copy_cluster_size(s->bcs); | ||
847 | |||
848 | s->done_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); | ||
849 | if (!s->done_bitmap) { | ||
850 | - ret = -EINVAL; | ||
851 | - goto out; | ||
852 | + return -EINVAL; | ||
853 | } | ||
854 | bdrv_disable_dirty_bitmap(s->done_bitmap); | ||
855 | |||
856 | /* s->access_bitmap starts equal to bcs bitmap */ | ||
857 | s->access_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); | ||
858 | if (!s->access_bitmap) { | ||
859 | - ret = -EINVAL; | ||
860 | - goto out; | ||
861 | + return -EINVAL; | ||
862 | } | ||
863 | bdrv_disable_dirty_bitmap(s->access_bitmap); | ||
864 | bdrv_dirty_bitmap_merge_internal(s->access_bitmap, | ||
865 | @@ -XXX,XX +XXX,XX @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, | ||
866 | |||
867 | qemu_co_mutex_init(&s->lock); | ||
868 | QLIST_INIT(&s->frozen_read_reqs); | ||
869 | - | ||
870 | - ret = 0; | ||
871 | -out: | ||
872 | - aio_context_release(ctx); | ||
873 | - return ret; | ||
874 | + return 0; | ||
875 | } | ||
876 | |||
877 | static void cbw_close(BlockDriverState *bs) | ||
878 | diff --git a/block/export/export.c b/block/export/export.c | ||
879 | index XXXXXXX..XXXXXXX 100644 | ||
880 | --- a/block/export/export.c | ||
881 | +++ b/block/export/export.c | ||
882 | @@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) | ||
883 | } | ||
884 | |||
885 | ctx = bdrv_get_aio_context(bs); | ||
886 | - aio_context_acquire(ctx); | ||
887 | |||
888 | if (export->iothread) { | ||
889 | IOThread *iothread; | ||
890 | @@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) | ||
891 | set_context_errp = fixed_iothread ? errp : NULL; | ||
892 | ret = bdrv_try_change_aio_context(bs, new_ctx, NULL, set_context_errp); | ||
893 | if (ret == 0) { | ||
894 | - aio_context_release(ctx); | ||
895 | - aio_context_acquire(new_ctx); | ||
896 | ctx = new_ctx; | ||
897 | } else if (fixed_iothread) { | ||
898 | goto fail; | ||
899 | @@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) | ||
900 | assert(exp->blk != NULL); | ||
901 | |||
902 | QLIST_INSERT_HEAD(&block_exports, exp, next); | ||
903 | - | ||
904 | - aio_context_release(ctx); | ||
905 | return exp; | ||
906 | |||
907 | fail: | ||
908 | @@ -XXX,XX +XXX,XX @@ fail: | ||
909 | blk_set_dev_ops(blk, NULL, NULL); | ||
910 | blk_unref(blk); | ||
911 | } | ||
912 | - aio_context_release(ctx); | ||
913 | if (exp) { | ||
914 | g_free(exp->id); | ||
915 | g_free(exp); | ||
916 | @@ -XXX,XX +XXX,XX @@ void blk_exp_ref(BlockExport *exp) | ||
917 | static void blk_exp_delete_bh(void *opaque) | ||
918 | { | ||
919 | BlockExport *exp = opaque; | ||
920 | - AioContext *aio_context = exp->ctx; | ||
921 | - | ||
922 | - aio_context_acquire(aio_context); | ||
923 | |||
924 | assert(exp->refcount == 0); | ||
925 | QLIST_REMOVE(exp, next); | ||
926 | @@ -XXX,XX +XXX,XX @@ static void blk_exp_delete_bh(void *opaque) | ||
927 | qapi_event_send_block_export_deleted(exp->id); | ||
928 | g_free(exp->id); | ||
929 | g_free(exp); | ||
930 | - | ||
931 | - aio_context_release(aio_context); | ||
932 | } | ||
933 | |||
934 | void blk_exp_unref(BlockExport *exp) | ||
935 | @@ -XXX,XX +XXX,XX @@ void blk_exp_unref(BlockExport *exp) | ||
936 | * connections and other internally held references start to shut down. When | ||
937 | * the function returns, there may still be active references while the export | ||
938 | * is in the process of shutting down. | ||
939 | - * | ||
940 | - * Acquires exp->ctx internally. Callers must *not* hold the lock. | ||
941 | */ | ||
942 | void blk_exp_request_shutdown(BlockExport *exp) | ||
943 | { | ||
944 | - AioContext *aio_context = exp->ctx; | ||
945 | - | ||
946 | - aio_context_acquire(aio_context); | ||
947 | - | ||
948 | /* | ||
949 | * If the user doesn't own the export any more, it is already shutting | ||
950 | * down. We must not call .request_shutdown and decrease the refcount a | ||
951 | * second time. | ||
952 | */ | ||
953 | if (!exp->user_owned) { | ||
954 | - goto out; | ||
955 | + return; | ||
956 | } | ||
957 | |||
958 | exp->drv->request_shutdown(exp); | ||
959 | @@ -XXX,XX +XXX,XX @@ void blk_exp_request_shutdown(BlockExport *exp) | ||
960 | assert(exp->user_owned); | ||
961 | exp->user_owned = false; | ||
962 | blk_exp_unref(exp); | ||
963 | - | ||
964 | -out: | ||
965 | - aio_context_release(aio_context); | ||
966 | } | ||
967 | |||
968 | /* | ||
969 | diff --git a/block/io.c b/block/io.c | ||
970 | index XXXXXXX..XXXXXXX 100644 | ||
971 | --- a/block/io.c | ||
972 | +++ b/block/io.c | ||
973 | @@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque) | ||
974 | BlockDriverState *bs = data->bs; | ||
975 | |||
976 | if (bs) { | ||
977 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
978 | - aio_context_acquire(ctx); | ||
979 | bdrv_dec_in_flight(bs); | ||
980 | if (data->begin) { | ||
981 | bdrv_do_drained_begin(bs, data->parent, data->poll); | ||
982 | @@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque) | ||
983 | assert(!data->poll); | ||
984 | bdrv_do_drained_end(bs, data->parent); | ||
985 | } | ||
986 | - aio_context_release(ctx); | ||
987 | } else { | ||
988 | assert(data->begin); | ||
989 | bdrv_drain_all_begin(); | ||
990 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
991 | { | ||
992 | BdrvCoDrainData data; | ||
993 | Coroutine *self = qemu_coroutine_self(); | ||
994 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
995 | - AioContext *co_ctx = qemu_coroutine_get_aio_context(self); | ||
996 | |||
997 | /* Calling bdrv_drain() from a BH ensures the current coroutine yields and | ||
998 | * other coroutines run if they were queued by aio_co_enter(). */ | ||
999 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
1000 | bdrv_inc_in_flight(bs); | ||
1001 | } | ||
1002 | |||
1003 | - /* | ||
1004 | - * Temporarily drop the lock across yield or we would get deadlocks. | ||
1005 | - * bdrv_co_drain_bh_cb() reaquires the lock as needed. | ||
1006 | - * | ||
1007 | - * When we yield below, the lock for the current context will be | ||
1008 | - * released, so if this is actually the lock that protects bs, don't drop | ||
1009 | - * it a second time. | ||
1010 | - */ | ||
1011 | - if (ctx != co_ctx) { | ||
1012 | - aio_context_release(ctx); | ||
1013 | - } | ||
1014 | replay_bh_schedule_oneshot_event(qemu_get_aio_context(), | ||
1015 | bdrv_co_drain_bh_cb, &data); | ||
1016 | |||
1017 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, | ||
1018 | /* If we are resumed from some other event (such as an aio completion or a | ||
1019 | * timer callback), it is a bug in the caller that should be fixed. */ | ||
1020 | assert(data.done); | ||
1021 | - | ||
1022 | - /* Reacquire the AioContext of bs if we dropped it */ | ||
1023 | - if (ctx != co_ctx) { | ||
1024 | - aio_context_acquire(ctx); | ||
1025 | - } | ||
1026 | } | ||
1027 | |||
1028 | static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, | ||
1029 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_all_poll(void) | ||
1030 | GLOBAL_STATE_CODE(); | ||
1031 | GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
1032 | |||
1033 | - /* bdrv_drain_poll() can't make changes to the graph and we are holding the | ||
1034 | - * main AioContext lock, so iterating bdrv_next_all_states() is safe. */ | ||
1035 | + /* | ||
1036 | + * bdrv_drain_poll() can't make changes to the graph and we hold the BQL, | ||
1037 | + * so iterating bdrv_next_all_states() is safe. | ||
1038 | + */ | ||
1039 | while ((bs = bdrv_next_all_states(bs))) { | ||
1040 | - AioContext *aio_context = bdrv_get_aio_context(bs); | ||
1041 | - aio_context_acquire(aio_context); | ||
1042 | result |= bdrv_drain_poll(bs, NULL, true); | ||
1043 | - aio_context_release(aio_context); | ||
1044 | } | ||
1045 | |||
1046 | return result; | ||
1047 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin_nopoll(void) | ||
1048 | /* Quiesce all nodes, without polling in-flight requests yet. The graph | ||
1049 | * cannot change during this loop. */ | ||
1050 | while ((bs = bdrv_next_all_states(bs))) { | ||
1051 | - AioContext *aio_context = bdrv_get_aio_context(bs); | ||
1052 | - | ||
1053 | - aio_context_acquire(aio_context); | ||
1054 | bdrv_do_drained_begin(bs, NULL, false); | ||
1055 | - aio_context_release(aio_context); | ||
1056 | } | ||
1057 | } | ||
1058 | |||
1059 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
1060 | } | ||
1061 | |||
1062 | while ((bs = bdrv_next_all_states(bs))) { | ||
1063 | - AioContext *aio_context = bdrv_get_aio_context(bs); | ||
1064 | - | ||
1065 | - aio_context_acquire(aio_context); | ||
1066 | bdrv_do_drained_end(bs, NULL); | ||
1067 | - aio_context_release(aio_context); | ||
1068 | } | ||
1069 | |||
1070 | assert(qemu_get_current_aio_context() == qemu_get_aio_context()); | ||
1071 | @@ -XXX,XX +XXX,XX @@ int bdrv_flush_all(void) | ||
1072 | } | ||
1073 | |||
1074 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
1075 | - AioContext *aio_context = bdrv_get_aio_context(bs); | ||
1076 | - int ret; | ||
1077 | - | ||
1078 | - aio_context_acquire(aio_context); | ||
1079 | - ret = bdrv_flush(bs); | ||
1080 | + int ret = bdrv_flush(bs); | ||
1081 | if (ret < 0 && !result) { | ||
1082 | result = ret; | ||
1083 | } | ||
1084 | - aio_context_release(aio_context); | ||
1085 | } | ||
1086 | |||
1087 | return result; | ||
1088 | diff --git a/block/mirror.c b/block/mirror.c | ||
1089 | index XXXXXXX..XXXXXXX 100644 | ||
1090 | --- a/block/mirror.c | ||
1091 | +++ b/block/mirror.c | ||
1092 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) | ||
1093 | MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); | ||
1094 | BlockJob *bjob = &s->common; | ||
1095 | MirrorBDSOpaque *bs_opaque; | ||
1096 | - AioContext *replace_aio_context = NULL; | ||
1097 | BlockDriverState *src; | ||
1098 | BlockDriverState *target_bs; | ||
1099 | BlockDriverState *mirror_top_bs; | ||
1100 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) | ||
1101 | } | ||
1102 | s->prepared = true; | ||
1103 | |||
1104 | - aio_context_acquire(qemu_get_aio_context()); | ||
1105 | bdrv_graph_rdlock_main_loop(); | ||
1106 | |||
1107 | mirror_top_bs = s->mirror_top_bs; | ||
1108 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) | ||
1109 | } | ||
1110 | bdrv_graph_rdunlock_main_loop(); | ||
1111 | |||
1112 | - if (s->to_replace) { | ||
1113 | - replace_aio_context = bdrv_get_aio_context(s->to_replace); | ||
1114 | - aio_context_acquire(replace_aio_context); | ||
1115 | - } | ||
1116 | - | ||
1117 | if (s->should_complete && !abort) { | ||
1118 | BlockDriverState *to_replace = s->to_replace ?: src; | ||
1119 | bool ro = bdrv_is_read_only(to_replace); | ||
1120 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) | ||
1121 | error_free(s->replace_blocker); | ||
1122 | bdrv_unref(s->to_replace); | ||
1123 | } | ||
1124 | - if (replace_aio_context) { | ||
1125 | - aio_context_release(replace_aio_context); | ||
1126 | - } | ||
1127 | g_free(s->replaces); | ||
1128 | |||
1129 | /* | ||
1130 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) | ||
1131 | bdrv_unref(mirror_top_bs); | ||
1132 | bdrv_unref(src); | ||
1133 | |||
1134 | - aio_context_release(qemu_get_aio_context()); | ||
1135 | - | ||
1136 | return ret; | ||
1137 | } | ||
1138 | |||
1139 | @@ -XXX,XX +XXX,XX @@ static void mirror_complete(Job *job, Error **errp) | ||
1140 | |||
1141 | /* block all operations on to_replace bs */ | ||
1142 | if (s->replaces) { | ||
1143 | - AioContext *replace_aio_context; | ||
1144 | - | ||
1145 | s->to_replace = bdrv_find_node(s->replaces); | ||
1146 | if (!s->to_replace) { | ||
1147 | error_setg(errp, "Node name '%s' not found", s->replaces); | ||
1148 | return; | ||
1149 | } | ||
1150 | |||
1151 | - replace_aio_context = bdrv_get_aio_context(s->to_replace); | ||
1152 | - aio_context_acquire(replace_aio_context); | ||
1153 | - | ||
1154 | /* TODO Translate this into child freeze system. */ | ||
1155 | error_setg(&s->replace_blocker, | ||
1156 | "block device is in use by block-job-complete"); | ||
1157 | bdrv_op_block_all(s->to_replace, s->replace_blocker); | ||
1158 | bdrv_ref(s->to_replace); | ||
1159 | - | ||
1160 | - aio_context_release(replace_aio_context); | ||
1161 | } | ||
1162 | |||
1163 | s->should_complete = true; | ||
1164 | diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c | ||
1165 | index XXXXXXX..XXXXXXX 100644 | ||
1166 | --- a/block/monitor/bitmap-qmp-cmds.c | ||
1167 | +++ b/block/monitor/bitmap-qmp-cmds.c | ||
1168 | @@ -XXX,XX +XXX,XX @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||
1169 | { | ||
1170 | BlockDriverState *bs; | ||
1171 | BdrvDirtyBitmap *bitmap; | ||
1172 | - AioContext *aio_context; | ||
1173 | |||
1174 | if (!name || name[0] == '\0') { | ||
1175 | error_setg(errp, "Bitmap name cannot be empty"); | ||
1176 | @@ -XXX,XX +XXX,XX @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||
1177 | return; | ||
1178 | } | ||
1179 | |||
1180 | - aio_context = bdrv_get_aio_context(bs); | ||
1181 | - aio_context_acquire(aio_context); | ||
1182 | - | ||
1183 | if (has_granularity) { | ||
1184 | if (granularity < 512 || !is_power_of_2(granularity)) { | ||
1185 | error_setg(errp, "Granularity must be power of 2 " | ||
1186 | "and at least 512"); | ||
1187 | - goto out; | ||
1188 | + return; | ||
1189 | } | ||
1190 | } else { | ||
1191 | /* Default to cluster size, if available: */ | ||
1192 | @@ -XXX,XX +XXX,XX @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||
1193 | if (persistent && | ||
1194 | !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) | ||
1195 | { | ||
1196 | - goto out; | ||
1197 | + return; | ||
1198 | } | ||
1199 | |||
1200 | bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); | ||
1201 | if (bitmap == NULL) { | ||
1202 | - goto out; | ||
1203 | + return; | ||
1204 | } | ||
1205 | |||
1206 | if (disabled) { | ||
1207 | @@ -XXX,XX +XXX,XX @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, | ||
1208 | } | ||
1209 | |||
1210 | bdrv_dirty_bitmap_set_persistence(bitmap, persistent); | ||
1211 | - | ||
1212 | -out: | ||
1213 | - aio_context_release(aio_context); | ||
1214 | } | ||
1215 | |||
1216 | BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, | ||
1217 | @@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, | ||
1218 | { | ||
1219 | BlockDriverState *bs; | ||
1220 | BdrvDirtyBitmap *bitmap; | ||
1221 | - AioContext *aio_context; | ||
1222 | |||
1223 | GLOBAL_STATE_CODE(); | ||
1224 | |||
1225 | @@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, | ||
1226 | return NULL; | ||
1227 | } | ||
1228 | |||
1229 | - aio_context = bdrv_get_aio_context(bs); | ||
1230 | - aio_context_acquire(aio_context); | ||
1231 | - | ||
1232 | if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, | ||
1233 | errp)) { | ||
1234 | - aio_context_release(aio_context); | ||
1235 | return NULL; | ||
1236 | } | ||
1237 | |||
1238 | if (bdrv_dirty_bitmap_get_persistence(bitmap) && | ||
1239 | bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) | ||
1240 | { | ||
1241 | - aio_context_release(aio_context); | ||
1242 | return NULL; | ||
1243 | } | ||
1244 | |||
1245 | @@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, | ||
1246 | *bitmap_bs = bs; | ||
1247 | } | ||
1248 | |||
1249 | - aio_context_release(aio_context); | ||
1250 | return release ? NULL : bitmap; | ||
1251 | } | ||
1252 | |||
1253 | diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c | 74 | diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c |
1254 | index XXXXXXX..XXXXXXX 100644 | 75 | index XXXXXXX..XXXXXXX 100644 |
1255 | --- a/block/monitor/block-hmp-cmds.c | 76 | --- a/block/monitor/block-hmp-cmds.c |
1256 | +++ b/block/monitor/block-hmp-cmds.c | 77 | +++ b/block/monitor/block-hmp-cmds.c |
1257 | @@ -XXX,XX +XXX,XX @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) | 78 | @@ -XXX,XX +XXX,XX @@ static void print_block_info(Monitor *mon, BlockInfo *info, |
1258 | const char *id = qdict_get_str(qdict, "id"); | ||
1259 | BlockBackend *blk; | ||
1260 | BlockDriverState *bs; | ||
1261 | - AioContext *aio_context; | ||
1262 | Error *local_err = NULL; | ||
1263 | |||
1264 | GLOBAL_STATE_CODE(); | ||
1265 | @@ -XXX,XX +XXX,XX @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) | ||
1266 | return; | ||
1267 | } | 79 | } |
1268 | 80 | ||
1269 | - aio_context = blk_get_aio_context(blk); | 81 | if (inserted) { |
1270 | - aio_context_acquire(aio_context); | 82 | - monitor_printf(mon, ": %s (%s%s%s)\n", |
1271 | - | 83 | + monitor_printf(mon, ": %s (%s%s%s%s)\n", |
1272 | bs = blk_bs(blk); | 84 | inserted->file, |
1273 | if (bs) { | 85 | inserted->drv, |
1274 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { | 86 | inserted->ro ? ", read-only" : "", |
1275 | error_report_err(local_err); | 87 | - inserted->encrypted ? ", encrypted" : ""); |
1276 | - aio_context_release(aio_context); | 88 | + inserted->encrypted ? ", encrypted" : "", |
1277 | return; | 89 | + inserted->active ? "" : ", inactive"); |
1278 | } | ||
1279 | |||
1280 | @@ -XXX,XX +XXX,XX @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) | ||
1281 | } else { | 90 | } else { |
1282 | blk_unref(blk); | 91 | monitor_printf(mon, ": [not inserted]\n"); |
1283 | } | 92 | } |
1284 | - | ||
1285 | - aio_context_release(aio_context); | ||
1286 | } | ||
1287 | |||
1288 | void hmp_commit(Monitor *mon, const QDict *qdict) | ||
1289 | @@ -XXX,XX +XXX,XX @@ void hmp_commit(Monitor *mon, const QDict *qdict) | ||
1290 | ret = blk_commit_all(); | ||
1291 | } else { | ||
1292 | BlockDriverState *bs; | ||
1293 | - AioContext *aio_context; | ||
1294 | |||
1295 | blk = blk_by_name(device); | ||
1296 | if (!blk) { | ||
1297 | @@ -XXX,XX +XXX,XX @@ void hmp_commit(Monitor *mon, const QDict *qdict) | ||
1298 | } | ||
1299 | |||
1300 | bs = bdrv_skip_implicit_filters(blk_bs(blk)); | ||
1301 | - aio_context = bdrv_get_aio_context(bs); | ||
1302 | - aio_context_acquire(aio_context); | ||
1303 | |||
1304 | if (!blk_is_available(blk)) { | ||
1305 | error_report("Device '%s' has no medium", device); | ||
1306 | - aio_context_release(aio_context); | ||
1307 | return; | ||
1308 | } | ||
1309 | |||
1310 | ret = bdrv_commit(bs); | ||
1311 | - | ||
1312 | - aio_context_release(aio_context); | ||
1313 | } | ||
1314 | if (ret < 0) { | ||
1315 | error_report("'commit' error for '%s': %s", device, strerror(-ret)); | ||
1316 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | ||
1317 | BlockBackend *blk = NULL; | ||
1318 | BlockDriverState *bs = NULL; | ||
1319 | BlockBackend *local_blk = NULL; | ||
1320 | - AioContext *ctx = NULL; | ||
1321 | bool qdev = qdict_get_try_bool(qdict, "qdev", false); | ||
1322 | const char *device = qdict_get_str(qdict, "device"); | ||
1323 | const char *command = qdict_get_str(qdict, "command"); | ||
1324 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | ||
1325 | } | ||
1326 | } | ||
1327 | |||
1328 | - ctx = blk ? blk_get_aio_context(blk) : bdrv_get_aio_context(bs); | ||
1329 | - aio_context_acquire(ctx); | ||
1330 | - | ||
1331 | if (bs) { | ||
1332 | blk = local_blk = blk_new(bdrv_get_aio_context(bs), 0, BLK_PERM_ALL); | ||
1333 | ret = blk_insert_bs(blk, bs, &err); | ||
1334 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | ||
1335 | |||
1336 | fail: | ||
1337 | blk_unref(local_blk); | ||
1338 | - | ||
1339 | - if (ctx) { | ||
1340 | - aio_context_release(ctx); | ||
1341 | - } | ||
1342 | - | ||
1343 | hmp_handle_error(mon, err); | ||
1344 | } | ||
1345 | |||
1346 | @@ -XXX,XX +XXX,XX @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) | ||
1347 | int nb_sns, i; | ||
1348 | int total; | ||
1349 | int *global_snapshots; | ||
1350 | - AioContext *aio_context; | ||
1351 | |||
1352 | typedef struct SnapshotEntry { | ||
1353 | QEMUSnapshotInfo sn; | ||
1354 | @@ -XXX,XX +XXX,XX @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) | ||
1355 | error_report_err(err); | ||
1356 | return; | ||
1357 | } | ||
1358 | - aio_context = bdrv_get_aio_context(bs); | ||
1359 | |||
1360 | - aio_context_acquire(aio_context); | ||
1361 | nb_sns = bdrv_snapshot_list(bs, &sn_tab); | ||
1362 | - aio_context_release(aio_context); | ||
1363 | |||
1364 | if (nb_sns < 0) { | ||
1365 | monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); | ||
1366 | @@ -XXX,XX +XXX,XX @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) | ||
1367 | int bs1_nb_sns = 0; | ||
1368 | ImageEntry *ie; | ||
1369 | SnapshotEntry *se; | ||
1370 | - AioContext *ctx = bdrv_get_aio_context(bs1); | ||
1371 | |||
1372 | - aio_context_acquire(ctx); | ||
1373 | if (bdrv_can_snapshot(bs1)) { | ||
1374 | sn = NULL; | ||
1375 | bs1_nb_sns = bdrv_snapshot_list(bs1, &sn); | ||
1376 | @@ -XXX,XX +XXX,XX @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) | ||
1377 | } | ||
1378 | g_free(sn); | ||
1379 | } | ||
1380 | - aio_context_release(ctx); | ||
1381 | } | ||
1382 | |||
1383 | if (no_snapshot) { | ||
1384 | diff --git a/block/qapi-sysemu.c b/block/qapi-sysemu.c | ||
1385 | index XXXXXXX..XXXXXXX 100644 | ||
1386 | --- a/block/qapi-sysemu.c | ||
1387 | +++ b/block/qapi-sysemu.c | ||
1388 | @@ -XXX,XX +XXX,XX @@ blockdev_remove_medium(const char *device, const char *id, Error **errp) | ||
1389 | { | ||
1390 | BlockBackend *blk; | ||
1391 | BlockDriverState *bs; | ||
1392 | - AioContext *aio_context; | ||
1393 | bool has_attached_device; | ||
1394 | |||
1395 | GLOBAL_STATE_CODE(); | ||
1396 | @@ -XXX,XX +XXX,XX @@ blockdev_remove_medium(const char *device, const char *id, Error **errp) | ||
1397 | return; | ||
1398 | } | ||
1399 | |||
1400 | - aio_context = bdrv_get_aio_context(bs); | ||
1401 | - aio_context_acquire(aio_context); | ||
1402 | - | ||
1403 | bdrv_graph_rdlock_main_loop(); | ||
1404 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { | ||
1405 | bdrv_graph_rdunlock_main_loop(); | ||
1406 | - goto out; | ||
1407 | + return; | ||
1408 | } | ||
1409 | bdrv_graph_rdunlock_main_loop(); | ||
1410 | |||
1411 | @@ -XXX,XX +XXX,XX @@ blockdev_remove_medium(const char *device, const char *id, Error **errp) | ||
1412 | * value passed here (i.e. false). */ | ||
1413 | blk_dev_change_media_cb(blk, false, &error_abort); | ||
1414 | } | ||
1415 | - | ||
1416 | -out: | ||
1417 | - aio_context_release(aio_context); | ||
1418 | } | ||
1419 | |||
1420 | void qmp_blockdev_remove_medium(const char *id, Error **errp) | ||
1421 | @@ -XXX,XX +XXX,XX @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, | ||
1422 | BlockDriverState *bs, Error **errp) | ||
1423 | { | ||
1424 | Error *local_err = NULL; | ||
1425 | - AioContext *ctx; | ||
1426 | bool has_device; | ||
1427 | int ret; | ||
1428 | |||
1429 | @@ -XXX,XX +XXX,XX @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, | ||
1430 | return; | ||
1431 | } | ||
1432 | |||
1433 | - ctx = bdrv_get_aio_context(bs); | ||
1434 | - aio_context_acquire(ctx); | ||
1435 | ret = blk_insert_bs(blk, bs, errp); | ||
1436 | - aio_context_release(ctx); | ||
1437 | - | ||
1438 | if (ret < 0) { | ||
1439 | return; | ||
1440 | } | ||
1441 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_change_medium(const char *device, | ||
1442 | qdict_put_str(options, "driver", format); | ||
1443 | } | ||
1444 | |||
1445 | - aio_context_acquire(qemu_get_aio_context()); | ||
1446 | medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp); | ||
1447 | - aio_context_release(qemu_get_aio_context()); | ||
1448 | |||
1449 | if (!medium_bs) { | ||
1450 | goto fail; | ||
1451 | @@ -XXX,XX +XXX,XX @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) | ||
1452 | ThrottleConfig cfg; | ||
1453 | BlockDriverState *bs; | ||
1454 | BlockBackend *blk; | ||
1455 | - AioContext *aio_context; | ||
1456 | |||
1457 | blk = qmp_get_blk(arg->device, arg->id, errp); | ||
1458 | if (!blk) { | ||
1459 | return; | ||
1460 | } | ||
1461 | |||
1462 | - aio_context = blk_get_aio_context(blk); | ||
1463 | - aio_context_acquire(aio_context); | ||
1464 | - | ||
1465 | bs = blk_bs(blk); | ||
1466 | if (!bs) { | ||
1467 | error_setg(errp, "Device has no medium"); | ||
1468 | - goto out; | ||
1469 | + return; | ||
1470 | } | ||
1471 | |||
1472 | throttle_config_init(&cfg); | ||
1473 | @@ -XXX,XX +XXX,XX @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) | ||
1474 | } | ||
1475 | |||
1476 | if (!throttle_is_valid(&cfg, errp)) { | ||
1477 | - goto out; | ||
1478 | + return; | ||
1479 | } | ||
1480 | |||
1481 | if (throttle_enabled(&cfg)) { | ||
1482 | @@ -XXX,XX +XXX,XX @@ void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) | ||
1483 | /* If all throttling settings are set to 0, disable I/O limits */ | ||
1484 | blk_io_limits_disable(blk); | ||
1485 | } | ||
1486 | - | ||
1487 | -out: | ||
1488 | - aio_context_release(aio_context); | ||
1489 | } | ||
1490 | |||
1491 | void qmp_block_latency_histogram_set( | ||
1492 | diff --git a/block/qapi.c b/block/qapi.c | 93 | diff --git a/block/qapi.c b/block/qapi.c |
1493 | index XXXXXXX..XXXXXXX 100644 | 94 | index XXXXXXX..XXXXXXX 100644 |
1494 | --- a/block/qapi.c | 95 | --- a/block/qapi.c |
1495 | +++ b/block/qapi.c | 96 | +++ b/block/qapi.c |
1496 | @@ -XXX,XX +XXX,XX @@ bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp) | 97 | @@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, |
1497 | int ret; | 98 | info->file = g_strdup(bs->filename); |
1498 | Error *err = NULL; | 99 | info->ro = bdrv_is_read_only(bs); |
1499 | 100 | info->drv = g_strdup(bs->drv->format_name); | |
1500 | - aio_context_acquire(bdrv_get_aio_context(bs)); | 101 | + info->active = !bdrv_is_inactive(bs); |
1501 | - | 102 | info->encrypted = bs->encrypted; |
1502 | size = bdrv_getlength(bs); | 103 | |
1503 | if (size < 0) { | 104 | info->cache = g_new(BlockdevCacheInfo, 1); |
1504 | error_setg_errno(errp, -size, "Can't get image size '%s'", | 105 | diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out |
1505 | bs->exact_filename); | 106 | index XXXXXXX..XXXXXXX 100644 |
1506 | - goto out; | 107 | --- a/tests/qemu-iotests/184.out |
1507 | + return; | 108 | +++ b/tests/qemu-iotests/184.out |
1508 | } | 109 | @@ -XXX,XX +XXX,XX @@ Testing: |
1509 | 110 | { | |
1510 | bdrv_refresh_filename(bs); | 111 | "iops_rd": 0, |
1511 | @@ -XXX,XX +XXX,XX @@ bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp) | 112 | "detect_zeroes": "off", |
1512 | info->format_specific = bdrv_get_specific_info(bs, &err); | 113 | + "active": true, |
1513 | if (err) { | 114 | "image": { |
1514 | error_propagate(errp, err); | 115 | "backing-image": { |
1515 | - goto out; | 116 | "virtual-size": 1073741824, |
1516 | + return; | 117 | @@ -XXX,XX +XXX,XX @@ Testing: |
1517 | } | 118 | { |
1518 | backing_filename = bs->backing_file; | 119 | "iops_rd": 0, |
1519 | if (backing_filename[0] != '\0') { | 120 | "detect_zeroes": "off", |
1520 | @@ -XXX,XX +XXX,XX @@ bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp) | 121 | + "active": true, |
1521 | break; | 122 | "image": { |
1522 | default: | 123 | "virtual-size": 1073741824, |
1523 | error_propagate(errp, err); | 124 | "filename": "null-co://", |
1524 | - goto out; | 125 | diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out |
1525 | + return; | 126 | index XXXXXXX..XXXXXXX 100644 |
1526 | } | 127 | --- a/tests/qemu-iotests/191.out |
1527 | - | 128 | +++ b/tests/qemu-iotests/191.out |
1528 | -out: | 129 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1529 | - aio_context_release(bdrv_get_aio_context(bs)); | 130 | { |
1530 | } | 131 | "iops_rd": 0, |
1531 | 132 | "detect_zeroes": "off", | |
1532 | /** | 133 | + "active": true, |
1533 | @@ -XXX,XX +XXX,XX @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, | 134 | "image": { |
1534 | /* Just to be safe if query_nodes is not always initialized */ | 135 | "backing-image": { |
1535 | if (has_query_nodes && query_nodes) { | 136 | "virtual-size": 67108864, |
1536 | for (bs = bdrv_next_node(NULL); bs; bs = bdrv_next_node(bs)) { | 137 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1537 | - AioContext *ctx = bdrv_get_aio_context(bs); | 138 | { |
1538 | - | 139 | "iops_rd": 0, |
1539 | - aio_context_acquire(ctx); | 140 | "detect_zeroes": "off", |
1540 | QAPI_LIST_APPEND(tail, bdrv_query_bds_stats(bs, false)); | 141 | + "active": true, |
1541 | - aio_context_release(ctx); | 142 | "image": { |
1542 | } | 143 | "virtual-size": 197120, |
1543 | } else { | 144 | "filename": "TEST_DIR/t.IMGFMT.ovl2", |
1544 | for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { | 145 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1545 | - AioContext *ctx = blk_get_aio_context(blk); | 146 | { |
1546 | BlockStats *s; | 147 | "iops_rd": 0, |
1547 | char *qdev; | 148 | "detect_zeroes": "off", |
1548 | 149 | + "active": true, | |
1549 | @@ -XXX,XX +XXX,XX @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, | 150 | "image": { |
1550 | continue; | 151 | "backing-image": { |
1551 | } | 152 | "virtual-size": 67108864, |
1552 | 153 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 | |
1553 | - aio_context_acquire(ctx); | 154 | { |
1554 | s = bdrv_query_bds_stats(blk_bs(blk), true); | 155 | "iops_rd": 0, |
1555 | s->device = g_strdup(blk_name(blk)); | 156 | "detect_zeroes": "off", |
1556 | 157 | + "active": true, | |
1557 | @@ -XXX,XX +XXX,XX @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, | 158 | "image": { |
1558 | } | 159 | "virtual-size": 197120, |
1559 | 160 | "filename": "TEST_DIR/t.IMGFMT", | |
1560 | bdrv_query_blk_stats(s->stats, blk); | 161 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1561 | - aio_context_release(ctx); | 162 | { |
1562 | 163 | "iops_rd": 0, | |
1563 | QAPI_LIST_APPEND(tail, s); | 164 | "detect_zeroes": "off", |
1564 | } | 165 | + "active": true, |
1565 | diff --git a/block/raw-format.c b/block/raw-format.c | 166 | "image": { |
1566 | index XXXXXXX..XXXXXXX 100644 | 167 | "backing-image": { |
1567 | --- a/block/raw-format.c | 168 | "virtual-size": 67108864, |
1568 | +++ b/block/raw-format.c | 169 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1569 | @@ -XXX,XX +XXX,XX @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, | 170 | { |
1570 | Error **errp) | 171 | "iops_rd": 0, |
1571 | { | 172 | "detect_zeroes": "off", |
1572 | BDRVRawState *s = bs->opaque; | 173 | + "active": true, |
1573 | - AioContext *ctx; | 174 | "image": { |
1574 | bool has_size; | 175 | "virtual-size": 393216, |
1575 | uint64_t offset, size; | 176 | "filename": "TEST_DIR/t.IMGFMT.mid", |
1576 | BdrvChildRole file_role; | 177 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1577 | @@ -XXX,XX +XXX,XX @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, | 178 | { |
1578 | bs->file->bs->filename); | 179 | "iops_rd": 0, |
1579 | } | 180 | "detect_zeroes": "off", |
1580 | 181 | + "active": true, | |
1581 | - ctx = bdrv_get_aio_context(bs); | 182 | "image": { |
1582 | - aio_context_acquire(ctx); | 183 | "virtual-size": 67108864, |
1583 | ret = raw_apply_options(bs, s, offset, has_size, size, errp); | 184 | "filename": "TEST_DIR/t.IMGFMT.base", |
1584 | - aio_context_release(ctx); | 185 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1585 | - | 186 | { |
1586 | if (ret < 0) { | 187 | "iops_rd": 0, |
1587 | return ret; | 188 | "detect_zeroes": "off", |
1588 | } | 189 | + "active": true, |
1589 | diff --git a/block/replication.c b/block/replication.c | 190 | "image": { |
1590 | index XXXXXXX..XXXXXXX 100644 | 191 | "virtual-size": 393216, |
1591 | --- a/block/replication.c | 192 | "filename": "TEST_DIR/t.IMGFMT.base", |
1592 | +++ b/block/replication.c | 193 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1593 | @@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, | 194 | { |
1594 | } | 195 | "iops_rd": 0, |
1595 | 196 | "detect_zeroes": "off", | |
1596 | if (reopen_queue) { | 197 | + "active": true, |
1597 | - AioContext *ctx = bdrv_get_aio_context(bs); | 198 | "image": { |
1598 | - if (ctx != qemu_get_aio_context()) { | 199 | "backing-image": { |
1599 | - aio_context_release(ctx); | 200 | "virtual-size": 67108864, |
1600 | - } | 201 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1601 | bdrv_reopen_multiple(reopen_queue, errp); | 202 | { |
1602 | - if (ctx != qemu_get_aio_context()) { | 203 | "iops_rd": 0, |
1603 | - aio_context_acquire(ctx); | 204 | "detect_zeroes": "off", |
1604 | - } | 205 | + "active": true, |
1605 | } | 206 | "image": { |
1606 | } | 207 | "virtual-size": 197120, |
1607 | 208 | "filename": "TEST_DIR/t.IMGFMT.ovl2", | |
1608 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 209 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1609 | BlockDriverState *top_bs; | 210 | { |
1610 | BdrvChild *active_disk, *hidden_disk, *secondary_disk; | 211 | "iops_rd": 0, |
1611 | int64_t active_length, hidden_length, disk_length; | 212 | "detect_zeroes": "off", |
1612 | - AioContext *aio_context; | 213 | + "active": true, |
1613 | Error *local_err = NULL; | 214 | "image": { |
1614 | BackupPerf perf = { .use_copy_range = true, .max_workers = 1 }; | 215 | "backing-image": { |
1615 | 216 | "backing-image": { | |
1616 | GLOBAL_STATE_CODE(); | 217 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1617 | 218 | { | |
1618 | - aio_context = bdrv_get_aio_context(bs); | 219 | "iops_rd": 0, |
1619 | - aio_context_acquire(aio_context); | 220 | "detect_zeroes": "off", |
1620 | s = bs->opaque; | 221 | + "active": true, |
1621 | 222 | "image": { | |
1622 | if (s->stage == BLOCK_REPLICATION_DONE || | 223 | "virtual-size": 197120, |
1623 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 224 | "filename": "TEST_DIR/t.IMGFMT.ovl3", |
1624 | * Ignore the request because the secondary side of replication | 225 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1625 | * doesn't have to do anything anymore. | 226 | { |
1626 | */ | 227 | "iops_rd": 0, |
1627 | - aio_context_release(aio_context); | 228 | "detect_zeroes": "off", |
1628 | return; | 229 | + "active": true, |
1629 | } | 230 | "image": { |
1630 | 231 | "virtual-size": 67108864, | |
1631 | if (s->stage != BLOCK_REPLICATION_NONE) { | 232 | "filename": "TEST_DIR/t.IMGFMT.base", |
1632 | error_setg(errp, "Block replication is running or done"); | 233 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1633 | - aio_context_release(aio_context); | 234 | { |
1634 | return; | 235 | "iops_rd": 0, |
1635 | } | 236 | "detect_zeroes": "off", |
1636 | 237 | + "active": true, | |
1637 | if (s->mode != mode) { | 238 | "image": { |
1638 | error_setg(errp, "The parameter mode's value is invalid, needs %d," | 239 | "virtual-size": 393216, |
1639 | " but got %d", s->mode, mode); | 240 | "filename": "TEST_DIR/t.IMGFMT.base", |
1640 | - aio_context_release(aio_context); | 241 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1641 | return; | 242 | { |
1642 | } | 243 | "iops_rd": 0, |
1643 | 244 | "detect_zeroes": "off", | |
1644 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 245 | + "active": true, |
1645 | if (!active_disk || !active_disk->bs || !active_disk->bs->backing) { | 246 | "image": { |
1646 | error_setg(errp, "Active disk doesn't have backing file"); | 247 | "backing-image": { |
1647 | bdrv_graph_rdunlock_main_loop(); | 248 | "virtual-size": 67108864, |
1648 | - aio_context_release(aio_context); | 249 | @@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576 |
1649 | return; | 250 | { |
1650 | } | 251 | "iops_rd": 0, |
1651 | 252 | "detect_zeroes": "off", | |
1652 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 253 | + "active": true, |
1653 | if (!hidden_disk->bs || !hidden_disk->bs->backing) { | 254 | "image": { |
1654 | error_setg(errp, "Hidden disk doesn't have backing file"); | 255 | "virtual-size": 197120, |
1655 | bdrv_graph_rdunlock_main_loop(); | 256 | "filename": "TEST_DIR/t.IMGFMT", |
1656 | - aio_context_release(aio_context); | 257 | diff --git a/tests/qemu-iotests/273.out b/tests/qemu-iotests/273.out |
1657 | return; | 258 | index XXXXXXX..XXXXXXX 100644 |
1658 | } | 259 | --- a/tests/qemu-iotests/273.out |
1659 | 260 | +++ b/tests/qemu-iotests/273.out | |
1660 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 261 | @@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev |
1661 | if (!secondary_disk->bs || !bdrv_has_blk(secondary_disk->bs)) { | 262 | { |
1662 | error_setg(errp, "The secondary disk doesn't have block backend"); | 263 | "iops_rd": 0, |
1663 | bdrv_graph_rdunlock_main_loop(); | 264 | "detect_zeroes": "off", |
1664 | - aio_context_release(aio_context); | 265 | + "active": true, |
1665 | return; | 266 | "image": { |
1666 | } | 267 | "backing-image": { |
1667 | bdrv_graph_rdunlock_main_loop(); | 268 | "backing-image": { |
1668 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 269 | @@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev |
1669 | active_length != hidden_length || hidden_length != disk_length) { | 270 | { |
1670 | error_setg(errp, "Active disk, hidden disk, secondary disk's length" | 271 | "iops_rd": 0, |
1671 | " are not the same"); | 272 | "detect_zeroes": "off", |
1672 | - aio_context_release(aio_context); | 273 | + "active": true, |
1673 | return; | 274 | "image": { |
1674 | } | 275 | "virtual-size": 197120, |
1675 | 276 | "filename": "TEST_DIR/t.IMGFMT", | |
1676 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 277 | @@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev |
1677 | !hidden_disk->bs->drv->bdrv_make_empty) { | 278 | { |
1678 | error_setg(errp, | 279 | "iops_rd": 0, |
1679 | "Active disk or hidden disk doesn't support make_empty"); | 280 | "detect_zeroes": "off", |
1680 | - aio_context_release(aio_context); | 281 | + "active": true, |
1681 | bdrv_graph_rdunlock_main_loop(); | 282 | "image": { |
1682 | return; | 283 | "backing-image": { |
1683 | } | 284 | "virtual-size": 197120, |
1684 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 285 | @@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev |
1685 | reopen_backing_file(bs, true, &local_err); | 286 | { |
1686 | if (local_err) { | 287 | "iops_rd": 0, |
1687 | error_propagate(errp, local_err); | 288 | "detect_zeroes": "off", |
1688 | - aio_context_release(aio_context); | 289 | + "active": true, |
1689 | return; | 290 | "image": { |
1690 | } | 291 | "virtual-size": 197120, |
1691 | 292 | "filename": "TEST_DIR/t.IMGFMT.mid", | |
1692 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | 293 | @@ -XXX,XX +XXX,XX @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev |
1693 | if (local_err) { | 294 | { |
1694 | error_propagate(errp, local_err); | 295 | "iops_rd": 0, |
1695 | bdrv_graph_wrunlock(); | 296 | "detect_zeroes": "off", |
1696 | - aio_context_release(aio_context); | 297 | + "active": true, |
1697 | return; | 298 | "image": { |
1698 | } | 299 | "virtual-size": 197120, |
1699 | 300 | "filename": "TEST_DIR/t.IMGFMT.base", | |
1700 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
1701 | if (local_err) { | ||
1702 | error_propagate(errp, local_err); | ||
1703 | bdrv_graph_wrunlock(); | ||
1704 | - aio_context_release(aio_context); | ||
1705 | return; | ||
1706 | } | ||
1707 | |||
1708 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
1709 | error_setg(errp, "No top_bs or it is invalid"); | ||
1710 | bdrv_graph_wrunlock(); | ||
1711 | reopen_backing_file(bs, false, NULL); | ||
1712 | - aio_context_release(aio_context); | ||
1713 | return; | ||
1714 | } | ||
1715 | bdrv_op_block_all(top_bs, s->blocker); | ||
1716 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
1717 | if (local_err) { | ||
1718 | error_propagate(errp, local_err); | ||
1719 | backup_job_cleanup(bs); | ||
1720 | - aio_context_release(aio_context); | ||
1721 | return; | ||
1722 | } | ||
1723 | job_start(&s->backup_job->job); | ||
1724 | break; | ||
1725 | default: | ||
1726 | - aio_context_release(aio_context); | ||
1727 | abort(); | ||
1728 | } | ||
1729 | |||
1730 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
1731 | } | ||
1732 | |||
1733 | s->error = 0; | ||
1734 | - aio_context_release(aio_context); | ||
1735 | } | ||
1736 | |||
1737 | static void replication_do_checkpoint(ReplicationState *rs, Error **errp) | ||
1738 | { | ||
1739 | BlockDriverState *bs = rs->opaque; | ||
1740 | - BDRVReplicationState *s; | ||
1741 | - AioContext *aio_context; | ||
1742 | - | ||
1743 | - aio_context = bdrv_get_aio_context(bs); | ||
1744 | - aio_context_acquire(aio_context); | ||
1745 | - s = bs->opaque; | ||
1746 | + BDRVReplicationState *s = bs->opaque; | ||
1747 | |||
1748 | if (s->stage == BLOCK_REPLICATION_DONE || | ||
1749 | s->stage == BLOCK_REPLICATION_FAILOVER) { | ||
1750 | @@ -XXX,XX +XXX,XX @@ static void replication_do_checkpoint(ReplicationState *rs, Error **errp) | ||
1751 | * Ignore the request because the secondary side of replication | ||
1752 | * doesn't have to do anything anymore. | ||
1753 | */ | ||
1754 | - aio_context_release(aio_context); | ||
1755 | return; | ||
1756 | } | ||
1757 | |||
1758 | if (s->mode == REPLICATION_MODE_SECONDARY) { | ||
1759 | secondary_do_checkpoint(bs, errp); | ||
1760 | } | ||
1761 | - aio_context_release(aio_context); | ||
1762 | } | ||
1763 | |||
1764 | static void replication_get_error(ReplicationState *rs, Error **errp) | ||
1765 | { | ||
1766 | BlockDriverState *bs = rs->opaque; | ||
1767 | - BDRVReplicationState *s; | ||
1768 | - AioContext *aio_context; | ||
1769 | - | ||
1770 | - aio_context = bdrv_get_aio_context(bs); | ||
1771 | - aio_context_acquire(aio_context); | ||
1772 | - s = bs->opaque; | ||
1773 | + BDRVReplicationState *s = bs->opaque; | ||
1774 | |||
1775 | if (s->stage == BLOCK_REPLICATION_NONE) { | ||
1776 | error_setg(errp, "Block replication is not running"); | ||
1777 | - aio_context_release(aio_context); | ||
1778 | return; | ||
1779 | } | ||
1780 | |||
1781 | if (s->error) { | ||
1782 | error_setg(errp, "I/O error occurred"); | ||
1783 | - aio_context_release(aio_context); | ||
1784 | return; | ||
1785 | } | ||
1786 | - aio_context_release(aio_context); | ||
1787 | } | ||
1788 | |||
1789 | static void replication_done(void *opaque, int ret) | ||
1790 | @@ -XXX,XX +XXX,XX @@ static void replication_done(void *opaque, int ret) | ||
1791 | static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | ||
1792 | { | ||
1793 | BlockDriverState *bs = rs->opaque; | ||
1794 | - BDRVReplicationState *s; | ||
1795 | - AioContext *aio_context; | ||
1796 | - | ||
1797 | - aio_context = bdrv_get_aio_context(bs); | ||
1798 | - aio_context_acquire(aio_context); | ||
1799 | - s = bs->opaque; | ||
1800 | + BDRVReplicationState *s = bs->opaque; | ||
1801 | |||
1802 | if (s->stage == BLOCK_REPLICATION_DONE || | ||
1803 | s->stage == BLOCK_REPLICATION_FAILOVER) { | ||
1804 | @@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | ||
1805 | * Ignore the request because the secondary side of replication | ||
1806 | * doesn't have to do anything anymore. | ||
1807 | */ | ||
1808 | - aio_context_release(aio_context); | ||
1809 | return; | ||
1810 | } | ||
1811 | |||
1812 | if (s->stage != BLOCK_REPLICATION_RUNNING) { | ||
1813 | error_setg(errp, "Block replication is not running"); | ||
1814 | - aio_context_release(aio_context); | ||
1815 | return; | ||
1816 | } | ||
1817 | |||
1818 | @@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | ||
1819 | * disk, secondary disk in backup_job_completed(). | ||
1820 | */ | ||
1821 | if (s->backup_job) { | ||
1822 | - aio_context_release(aio_context); | ||
1823 | job_cancel_sync(&s->backup_job->job, true); | ||
1824 | - aio_context_acquire(aio_context); | ||
1825 | } | ||
1826 | |||
1827 | if (!failover) { | ||
1828 | secondary_do_checkpoint(bs, errp); | ||
1829 | s->stage = BLOCK_REPLICATION_DONE; | ||
1830 | - aio_context_release(aio_context); | ||
1831 | return; | ||
1832 | } | ||
1833 | |||
1834 | @@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | ||
1835 | bdrv_graph_rdunlock_main_loop(); | ||
1836 | break; | ||
1837 | default: | ||
1838 | - aio_context_release(aio_context); | ||
1839 | abort(); | ||
1840 | } | ||
1841 | - aio_context_release(aio_context); | ||
1842 | } | ||
1843 | |||
1844 | static const char *const replication_strong_runtime_opts[] = { | ||
1845 | diff --git a/block/snapshot.c b/block/snapshot.c | ||
1846 | index XXXXXXX..XXXXXXX 100644 | ||
1847 | --- a/block/snapshot.c | ||
1848 | +++ b/block/snapshot.c | ||
1849 | @@ -XXX,XX +XXX,XX @@ static bool GRAPH_RDLOCK bdrv_all_snapshots_includes_bs(BlockDriverState *bs) | ||
1850 | return bdrv_has_blk(bs) || QLIST_EMPTY(&bs->parents); | ||
1851 | } | ||
1852 | |||
1853 | -/* Group operations. All block drivers are involved. | ||
1854 | - * These functions will properly handle dataplane (take aio_context_acquire | ||
1855 | - * when appropriate for appropriate block drivers) */ | ||
1856 | +/* Group operations. All block drivers are involved. */ | ||
1857 | |||
1858 | bool bdrv_all_can_snapshot(bool has_devices, strList *devices, | ||
1859 | Error **errp) | ||
1860 | @@ -XXX,XX +XXX,XX @@ bool bdrv_all_can_snapshot(bool has_devices, strList *devices, | ||
1861 | iterbdrvs = bdrvs; | ||
1862 | while (iterbdrvs) { | ||
1863 | BlockDriverState *bs = iterbdrvs->data; | ||
1864 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
1865 | bool ok = true; | ||
1866 | |||
1867 | - aio_context_acquire(ctx); | ||
1868 | if (devices || bdrv_all_snapshots_includes_bs(bs)) { | ||
1869 | ok = bdrv_can_snapshot(bs); | ||
1870 | } | ||
1871 | - aio_context_release(ctx); | ||
1872 | if (!ok) { | ||
1873 | error_setg(errp, "Device '%s' is writable but does not support " | ||
1874 | "snapshots", bdrv_get_device_or_node_name(bs)); | ||
1875 | @@ -XXX,XX +XXX,XX @@ int bdrv_all_delete_snapshot(const char *name, | ||
1876 | iterbdrvs = bdrvs; | ||
1877 | while (iterbdrvs) { | ||
1878 | BlockDriverState *bs = iterbdrvs->data; | ||
1879 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
1880 | QEMUSnapshotInfo sn1, *snapshot = &sn1; | ||
1881 | int ret = 0; | ||
1882 | |||
1883 | - aio_context_acquire(ctx); | ||
1884 | if ((devices || bdrv_all_snapshots_includes_bs(bs)) && | ||
1885 | bdrv_snapshot_find(bs, snapshot, name) >= 0) | ||
1886 | { | ||
1887 | ret = bdrv_snapshot_delete(bs, snapshot->id_str, | ||
1888 | snapshot->name, errp); | ||
1889 | } | ||
1890 | - aio_context_release(ctx); | ||
1891 | if (ret < 0) { | ||
1892 | error_prepend(errp, "Could not delete snapshot '%s' on '%s': ", | ||
1893 | name, bdrv_get_device_or_node_name(bs)); | ||
1894 | @@ -XXX,XX +XXX,XX @@ int bdrv_all_goto_snapshot(const char *name, | ||
1895 | iterbdrvs = bdrvs; | ||
1896 | while (iterbdrvs) { | ||
1897 | BlockDriverState *bs = iterbdrvs->data; | ||
1898 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
1899 | bool all_snapshots_includes_bs; | ||
1900 | |||
1901 | - aio_context_acquire(ctx); | ||
1902 | bdrv_graph_rdlock_main_loop(); | ||
1903 | all_snapshots_includes_bs = bdrv_all_snapshots_includes_bs(bs); | ||
1904 | bdrv_graph_rdunlock_main_loop(); | ||
1905 | |||
1906 | ret = (devices || all_snapshots_includes_bs) ? | ||
1907 | bdrv_snapshot_goto(bs, name, errp) : 0; | ||
1908 | - aio_context_release(ctx); | ||
1909 | if (ret < 0) { | ||
1910 | bdrv_graph_rdlock_main_loop(); | ||
1911 | error_prepend(errp, "Could not load snapshot '%s' on '%s': ", | ||
1912 | @@ -XXX,XX +XXX,XX @@ int bdrv_all_has_snapshot(const char *name, | ||
1913 | iterbdrvs = bdrvs; | ||
1914 | while (iterbdrvs) { | ||
1915 | BlockDriverState *bs = iterbdrvs->data; | ||
1916 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
1917 | QEMUSnapshotInfo sn; | ||
1918 | int ret = 0; | ||
1919 | |||
1920 | - aio_context_acquire(ctx); | ||
1921 | if (devices || bdrv_all_snapshots_includes_bs(bs)) { | ||
1922 | ret = bdrv_snapshot_find(bs, &sn, name); | ||
1923 | } | ||
1924 | - aio_context_release(ctx); | ||
1925 | if (ret < 0) { | ||
1926 | if (ret == -ENOENT) { | ||
1927 | return 0; | ||
1928 | @@ -XXX,XX +XXX,XX @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, | ||
1929 | iterbdrvs = bdrvs; | ||
1930 | while (iterbdrvs) { | ||
1931 | BlockDriverState *bs = iterbdrvs->data; | ||
1932 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
1933 | int ret = 0; | ||
1934 | |||
1935 | - aio_context_acquire(ctx); | ||
1936 | if (bs == vm_state_bs) { | ||
1937 | sn->vm_state_size = vm_state_size; | ||
1938 | ret = bdrv_snapshot_create(bs, sn); | ||
1939 | @@ -XXX,XX +XXX,XX @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, | ||
1940 | sn->vm_state_size = 0; | ||
1941 | ret = bdrv_snapshot_create(bs, sn); | ||
1942 | } | ||
1943 | - aio_context_release(ctx); | ||
1944 | if (ret < 0) { | ||
1945 | error_setg(errp, "Could not create snapshot '%s' on '%s'", | ||
1946 | sn->name, bdrv_get_device_or_node_name(bs)); | ||
1947 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, | ||
1948 | iterbdrvs = bdrvs; | ||
1949 | while (iterbdrvs) { | ||
1950 | BlockDriverState *bs = iterbdrvs->data; | ||
1951 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
1952 | bool found = false; | ||
1953 | |||
1954 | - aio_context_acquire(ctx); | ||
1955 | found = (devices || bdrv_all_snapshots_includes_bs(bs)) && | ||
1956 | bdrv_can_snapshot(bs); | ||
1957 | - aio_context_release(ctx); | ||
1958 | |||
1959 | if (vmstate_bs) { | ||
1960 | if (g_str_equal(vmstate_bs, | ||
1961 | diff --git a/block/write-threshold.c b/block/write-threshold.c | ||
1962 | index XXXXXXX..XXXXXXX 100644 | ||
1963 | --- a/block/write-threshold.c | ||
1964 | +++ b/block/write-threshold.c | ||
1965 | @@ -XXX,XX +XXX,XX @@ void qmp_block_set_write_threshold(const char *node_name, | ||
1966 | Error **errp) | ||
1967 | { | ||
1968 | BlockDriverState *bs; | ||
1969 | - AioContext *aio_context; | ||
1970 | |||
1971 | bs = bdrv_find_node(node_name); | ||
1972 | if (!bs) { | ||
1973 | @@ -XXX,XX +XXX,XX @@ void qmp_block_set_write_threshold(const char *node_name, | ||
1974 | return; | ||
1975 | } | ||
1976 | |||
1977 | - aio_context = bdrv_get_aio_context(bs); | ||
1978 | - aio_context_acquire(aio_context); | ||
1979 | - | ||
1980 | bdrv_write_threshold_set(bs, threshold_bytes); | ||
1981 | - | ||
1982 | - aio_context_release(aio_context); | ||
1983 | } | ||
1984 | |||
1985 | void bdrv_write_threshold_check_write(BlockDriverState *bs, int64_t offset, | ||
1986 | diff --git a/blockdev.c b/blockdev.c | ||
1987 | index XXXXXXX..XXXXXXX 100644 | ||
1988 | --- a/blockdev.c | ||
1989 | +++ b/blockdev.c | ||
1990 | @@ -XXX,XX +XXX,XX @@ err_no_opts: | ||
1991 | /* Takes the ownership of bs_opts */ | ||
1992 | BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) | ||
1993 | { | ||
1994 | - BlockDriverState *bs; | ||
1995 | int bdrv_flags = 0; | ||
1996 | |||
1997 | GLOBAL_STATE_CODE(); | ||
1998 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) | ||
1999 | bdrv_flags |= BDRV_O_INACTIVE; | ||
2000 | } | ||
2001 | |||
2002 | - aio_context_acquire(qemu_get_aio_context()); | ||
2003 | - bs = bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp); | ||
2004 | - aio_context_release(qemu_get_aio_context()); | ||
2005 | - | ||
2006 | - return bs; | ||
2007 | + return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp); | ||
2008 | } | ||
2009 | |||
2010 | void blockdev_close_all_bdrv_states(void) | ||
2011 | @@ -XXX,XX +XXX,XX @@ void blockdev_close_all_bdrv_states(void) | ||
2012 | |||
2013 | GLOBAL_STATE_CODE(); | ||
2014 | QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) { | ||
2015 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
2016 | - | ||
2017 | - aio_context_acquire(ctx); | ||
2018 | bdrv_unref(bs); | ||
2019 | - aio_context_release(ctx); | ||
2020 | } | ||
2021 | } | ||
2022 | |||
2023 | @@ -XXX,XX +XXX,XX @@ fail: | ||
2024 | static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) | ||
2025 | { | ||
2026 | BlockDriverState *bs; | ||
2027 | - AioContext *aio_context; | ||
2028 | |||
2029 | GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
2030 | |||
2031 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) | ||
2032 | return NULL; | ||
2033 | } | ||
2034 | |||
2035 | - aio_context = bdrv_get_aio_context(bs); | ||
2036 | - aio_context_acquire(aio_context); | ||
2037 | - | ||
2038 | if (!bdrv_is_inserted(bs)) { | ||
2039 | error_setg(errp, "Device has no medium"); | ||
2040 | bs = NULL; | ||
2041 | } | ||
2042 | |||
2043 | - aio_context_release(aio_context); | ||
2044 | - | ||
2045 | return bs; | ||
2046 | } | ||
2047 | |||
2048 | @@ -XXX,XX +XXX,XX @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, | ||
2049 | Error **errp) | ||
2050 | { | ||
2051 | BlockDriverState *bs; | ||
2052 | - AioContext *aio_context; | ||
2053 | QEMUSnapshotInfo sn; | ||
2054 | Error *local_err = NULL; | ||
2055 | SnapshotInfo *info = NULL; | ||
2056 | @@ -XXX,XX +XXX,XX @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, | ||
2057 | if (!bs) { | ||
2058 | return NULL; | ||
2059 | } | ||
2060 | - aio_context = bdrv_get_aio_context(bs); | ||
2061 | - aio_context_acquire(aio_context); | ||
2062 | |||
2063 | if (!id && !name) { | ||
2064 | error_setg(errp, "Name or id must be provided"); | ||
2065 | - goto out_aio_context; | ||
2066 | + return NULL; | ||
2067 | } | ||
2068 | |||
2069 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { | ||
2070 | - goto out_aio_context; | ||
2071 | + return NULL; | ||
2072 | } | ||
2073 | |||
2074 | ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); | ||
2075 | if (local_err) { | ||
2076 | error_propagate(errp, local_err); | ||
2077 | - goto out_aio_context; | ||
2078 | + return NULL; | ||
2079 | } | ||
2080 | if (!ret) { | ||
2081 | error_setg(errp, | ||
2082 | "Snapshot with id '%s' and name '%s' does not exist on " | ||
2083 | "device '%s'", | ||
2084 | STR_OR_NULL(id), STR_OR_NULL(name), device); | ||
2085 | - goto out_aio_context; | ||
2086 | + return NULL; | ||
2087 | } | ||
2088 | |||
2089 | bdrv_snapshot_delete(bs, id, name, &local_err); | ||
2090 | if (local_err) { | ||
2091 | error_propagate(errp, local_err); | ||
2092 | - goto out_aio_context; | ||
2093 | + return NULL; | ||
2094 | } | ||
2095 | |||
2096 | - aio_context_release(aio_context); | ||
2097 | - | ||
2098 | info = g_new0(SnapshotInfo, 1); | ||
2099 | info->id = g_strdup(sn.id_str); | ||
2100 | info->name = g_strdup(sn.name); | ||
2101 | @@ -XXX,XX +XXX,XX @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, | ||
2102 | } | ||
2103 | |||
2104 | return info; | ||
2105 | - | ||
2106 | -out_aio_context: | ||
2107 | - aio_context_release(aio_context); | ||
2108 | - return NULL; | ||
2109 | } | ||
2110 | |||
2111 | /* internal snapshot private data */ | ||
2112 | @@ -XXX,XX +XXX,XX @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, | ||
2113 | bool ret; | ||
2114 | int64_t rt; | ||
2115 | InternalSnapshotState *state = g_new0(InternalSnapshotState, 1); | ||
2116 | - AioContext *aio_context; | ||
2117 | int ret1; | ||
2118 | |||
2119 | GLOBAL_STATE_CODE(); | ||
2120 | @@ -XXX,XX +XXX,XX @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, | ||
2121 | return; | ||
2122 | } | ||
2123 | |||
2124 | - aio_context = bdrv_get_aio_context(bs); | ||
2125 | - aio_context_acquire(aio_context); | ||
2126 | - | ||
2127 | state->bs = bs; | ||
2128 | |||
2129 | /* Paired with .clean() */ | ||
2130 | bdrv_drained_begin(bs); | ||
2131 | |||
2132 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { | ||
2133 | - goto out; | ||
2134 | + return; | ||
2135 | } | ||
2136 | |||
2137 | if (bdrv_is_read_only(bs)) { | ||
2138 | error_setg(errp, "Device '%s' is read only", device); | ||
2139 | - goto out; | ||
2140 | + return; | ||
2141 | } | ||
2142 | |||
2143 | if (!bdrv_can_snapshot(bs)) { | ||
2144 | error_setg(errp, "Block format '%s' used by device '%s' " | ||
2145 | "does not support internal snapshots", | ||
2146 | bs->drv->format_name, device); | ||
2147 | - goto out; | ||
2148 | + return; | ||
2149 | } | ||
2150 | |||
2151 | if (!strlen(name)) { | ||
2152 | error_setg(errp, "Name is empty"); | ||
2153 | - goto out; | ||
2154 | + return; | ||
2155 | } | ||
2156 | |||
2157 | /* check whether a snapshot with name exist */ | ||
2158 | @@ -XXX,XX +XXX,XX @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, | ||
2159 | &local_err); | ||
2160 | if (local_err) { | ||
2161 | error_propagate(errp, local_err); | ||
2162 | - goto out; | ||
2163 | + return; | ||
2164 | } else if (ret) { | ||
2165 | error_setg(errp, | ||
2166 | "Snapshot with name '%s' already exists on device '%s'", | ||
2167 | name, device); | ||
2168 | - goto out; | ||
2169 | + return; | ||
2170 | } | ||
2171 | |||
2172 | /* 3. take the snapshot */ | ||
2173 | @@ -XXX,XX +XXX,XX @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, | ||
2174 | error_setg_errno(errp, -ret1, | ||
2175 | "Failed to create snapshot '%s' on device '%s'", | ||
2176 | name, device); | ||
2177 | - goto out; | ||
2178 | + return; | ||
2179 | } | ||
2180 | |||
2181 | /* 4. succeed, mark a snapshot is created */ | ||
2182 | state->created = true; | ||
2183 | - | ||
2184 | -out: | ||
2185 | - aio_context_release(aio_context); | ||
2186 | } | ||
2187 | |||
2188 | static void internal_snapshot_abort(void *opaque) | ||
2189 | @@ -XXX,XX +XXX,XX @@ static void internal_snapshot_abort(void *opaque) | ||
2190 | InternalSnapshotState *state = opaque; | ||
2191 | BlockDriverState *bs = state->bs; | ||
2192 | QEMUSnapshotInfo *sn = &state->sn; | ||
2193 | - AioContext *aio_context; | ||
2194 | Error *local_error = NULL; | ||
2195 | |||
2196 | GLOBAL_STATE_CODE(); | ||
2197 | @@ -XXX,XX +XXX,XX @@ static void internal_snapshot_abort(void *opaque) | ||
2198 | return; | ||
2199 | } | ||
2200 | |||
2201 | - aio_context = bdrv_get_aio_context(state->bs); | ||
2202 | - aio_context_acquire(aio_context); | ||
2203 | - | ||
2204 | if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { | ||
2205 | error_reportf_err(local_error, | ||
2206 | "Failed to delete snapshot with id '%s' and " | ||
2207 | @@ -XXX,XX +XXX,XX @@ static void internal_snapshot_abort(void *opaque) | ||
2208 | sn->id_str, sn->name, | ||
2209 | bdrv_get_device_name(bs)); | ||
2210 | } | ||
2211 | - | ||
2212 | - aio_context_release(aio_context); | ||
2213 | } | ||
2214 | |||
2215 | static void internal_snapshot_clean(void *opaque) | ||
2216 | { | ||
2217 | g_autofree InternalSnapshotState *state = opaque; | ||
2218 | - AioContext *aio_context; | ||
2219 | |||
2220 | if (!state->bs) { | ||
2221 | return; | ||
2222 | } | ||
2223 | |||
2224 | - aio_context = bdrv_get_aio_context(state->bs); | ||
2225 | - aio_context_acquire(aio_context); | ||
2226 | - | ||
2227 | bdrv_drained_end(state->bs); | ||
2228 | - | ||
2229 | - aio_context_release(aio_context); | ||
2230 | } | ||
2231 | |||
2232 | /* external snapshot private data */ | ||
2233 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action, | ||
2234 | /* File name of the new image (for 'blockdev-snapshot-sync') */ | ||
2235 | const char *new_image_file; | ||
2236 | ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1); | ||
2237 | - AioContext *aio_context; | ||
2238 | uint64_t perm, shared; | ||
2239 | |||
2240 | /* TODO We'll eventually have to take a writer lock in this function */ | ||
2241 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action, | ||
2242 | return; | ||
2243 | } | ||
2244 | |||
2245 | - aio_context = bdrv_get_aio_context(state->old_bs); | ||
2246 | - aio_context_acquire(aio_context); | ||
2247 | - | ||
2248 | /* Paired with .clean() */ | ||
2249 | bdrv_drained_begin(state->old_bs); | ||
2250 | |||
2251 | if (!bdrv_is_inserted(state->old_bs)) { | ||
2252 | error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); | ||
2253 | - goto out; | ||
2254 | + return; | ||
2255 | } | ||
2256 | |||
2257 | if (bdrv_op_is_blocked(state->old_bs, | ||
2258 | BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) { | ||
2259 | - goto out; | ||
2260 | + return; | ||
2261 | } | ||
2262 | |||
2263 | if (!bdrv_is_read_only(state->old_bs)) { | ||
2264 | if (bdrv_flush(state->old_bs)) { | ||
2265 | error_setg(errp, QERR_IO_ERROR); | ||
2266 | - goto out; | ||
2267 | + return; | ||
2268 | } | ||
2269 | } | ||
2270 | |||
2271 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action, | ||
2272 | |||
2273 | if (node_name && !snapshot_node_name) { | ||
2274 | error_setg(errp, "New overlay node-name missing"); | ||
2275 | - goto out; | ||
2276 | + return; | ||
2277 | } | ||
2278 | |||
2279 | if (snapshot_node_name && | ||
2280 | bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { | ||
2281 | error_setg(errp, "New overlay node-name already in use"); | ||
2282 | - goto out; | ||
2283 | + return; | ||
2284 | } | ||
2285 | |||
2286 | flags = state->old_bs->open_flags; | ||
2287 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action, | ||
2288 | int64_t size = bdrv_getlength(state->old_bs); | ||
2289 | if (size < 0) { | ||
2290 | error_setg_errno(errp, -size, "bdrv_getlength failed"); | ||
2291 | - goto out; | ||
2292 | + return; | ||
2293 | } | ||
2294 | bdrv_refresh_filename(state->old_bs); | ||
2295 | |||
2296 | - aio_context_release(aio_context); | ||
2297 | bdrv_img_create(new_image_file, format, | ||
2298 | state->old_bs->filename, | ||
2299 | state->old_bs->drv->format_name, | ||
2300 | NULL, size, flags, false, &local_err); | ||
2301 | - aio_context_acquire(aio_context); | ||
2302 | |||
2303 | if (local_err) { | ||
2304 | error_propagate(errp, local_err); | ||
2305 | - goto out; | ||
2306 | + return; | ||
2307 | } | ||
2308 | } | ||
2309 | |||
2310 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action, | ||
2311 | } | ||
2312 | qdict_put_str(options, "driver", format); | ||
2313 | } | ||
2314 | - aio_context_release(aio_context); | ||
2315 | |||
2316 | - aio_context_acquire(qemu_get_aio_context()); | ||
2317 | state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags, | ||
2318 | errp); | ||
2319 | - aio_context_release(qemu_get_aio_context()); | ||
2320 | |||
2321 | /* We will manually add the backing_hd field to the bs later */ | ||
2322 | if (!state->new_bs) { | ||
2323 | return; | ||
2324 | } | ||
2325 | |||
2326 | - aio_context_acquire(aio_context); | ||
2327 | - | ||
2328 | /* | ||
2329 | * Allow attaching a backing file to an overlay that's already in use only | ||
2330 | * if the parents don't assume that they are already seeing a valid image. | ||
2331 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action, | ||
2332 | bdrv_get_cumulative_perm(state->new_bs, &perm, &shared); | ||
2333 | if (perm & BLK_PERM_CONSISTENT_READ) { | ||
2334 | error_setg(errp, "The overlay is already in use"); | ||
2335 | - goto out; | ||
2336 | + return; | ||
2337 | } | ||
2338 | |||
2339 | if (state->new_bs->drv->is_filter) { | ||
2340 | error_setg(errp, "Filters cannot be used as overlays"); | ||
2341 | - goto out; | ||
2342 | + return; | ||
2343 | } | ||
2344 | |||
2345 | if (bdrv_cow_child(state->new_bs)) { | ||
2346 | error_setg(errp, "The overlay already has a backing image"); | ||
2347 | - goto out; | ||
2348 | + return; | ||
2349 | } | ||
2350 | |||
2351 | if (!state->new_bs->drv->supports_backing) { | ||
2352 | error_setg(errp, "The overlay does not support backing images"); | ||
2353 | - goto out; | ||
2354 | + return; | ||
2355 | } | ||
2356 | |||
2357 | ret = bdrv_append(state->new_bs, state->old_bs, errp); | ||
2358 | if (ret < 0) { | ||
2359 | - goto out; | ||
2360 | + return; | ||
2361 | } | ||
2362 | state->overlay_appended = true; | ||
2363 | - | ||
2364 | -out: | ||
2365 | - aio_context_release(aio_context); | ||
2366 | } | ||
2367 | |||
2368 | static void external_snapshot_commit(void *opaque) | ||
2369 | { | ||
2370 | ExternalSnapshotState *state = opaque; | ||
2371 | - AioContext *aio_context; | ||
2372 | - | ||
2373 | - aio_context = bdrv_get_aio_context(state->old_bs); | ||
2374 | - aio_context_acquire(aio_context); | ||
2375 | |||
2376 | /* We don't need (or want) to use the transactional | ||
2377 | * bdrv_reopen_multiple() across all the entries at once, because we | ||
2378 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_commit(void *opaque) | ||
2379 | if (!qatomic_read(&state->old_bs->copy_on_read)) { | ||
2380 | bdrv_reopen_set_read_only(state->old_bs, true, NULL); | ||
2381 | } | ||
2382 | - | ||
2383 | - aio_context_release(aio_context); | ||
2384 | } | ||
2385 | |||
2386 | static void external_snapshot_abort(void *opaque) | ||
2387 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(void *opaque) | ||
2388 | int ret; | ||
2389 | |||
2390 | aio_context = bdrv_get_aio_context(state->old_bs); | ||
2391 | - aio_context_acquire(aio_context); | ||
2392 | |||
2393 | bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd() | ||
2394 | close state->old_bs; we need it */ | ||
2395 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(void *opaque) | ||
2396 | */ | ||
2397 | tmp_context = bdrv_get_aio_context(state->old_bs); | ||
2398 | if (aio_context != tmp_context) { | ||
2399 | - aio_context_release(aio_context); | ||
2400 | - aio_context_acquire(tmp_context); | ||
2401 | - | ||
2402 | ret = bdrv_try_change_aio_context(state->old_bs, | ||
2403 | aio_context, NULL, NULL); | ||
2404 | assert(ret == 0); | ||
2405 | - | ||
2406 | - aio_context_release(tmp_context); | ||
2407 | - aio_context_acquire(aio_context); | ||
2408 | } | ||
2409 | |||
2410 | bdrv_drained_begin(state->new_bs); | ||
2411 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(void *opaque) | ||
2412 | bdrv_drained_end(state->new_bs); | ||
2413 | |||
2414 | bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ | ||
2415 | - | ||
2416 | - aio_context_release(aio_context); | ||
2417 | } | ||
2418 | } | ||
2419 | } | ||
2420 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(void *opaque) | ||
2421 | static void external_snapshot_clean(void *opaque) | ||
2422 | { | ||
2423 | g_autofree ExternalSnapshotState *state = opaque; | ||
2424 | - AioContext *aio_context; | ||
2425 | |||
2426 | if (!state->old_bs) { | ||
2427 | return; | ||
2428 | } | ||
2429 | |||
2430 | - aio_context = bdrv_get_aio_context(state->old_bs); | ||
2431 | - aio_context_acquire(aio_context); | ||
2432 | - | ||
2433 | bdrv_drained_end(state->old_bs); | ||
2434 | bdrv_unref(state->new_bs); | ||
2435 | - | ||
2436 | - aio_context_release(aio_context); | ||
2437 | } | ||
2438 | |||
2439 | typedef struct DriveBackupState { | ||
2440 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_action(DriveBackup *backup, | ||
2441 | BlockDriverState *target_bs; | ||
2442 | BlockDriverState *source = NULL; | ||
2443 | AioContext *aio_context; | ||
2444 | - AioContext *old_context; | ||
2445 | const char *format; | ||
2446 | QDict *options; | ||
2447 | Error *local_err = NULL; | ||
2448 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_action(DriveBackup *backup, | ||
2449 | } | ||
2450 | |||
2451 | aio_context = bdrv_get_aio_context(bs); | ||
2452 | - aio_context_acquire(aio_context); | ||
2453 | |||
2454 | state->bs = bs; | ||
2455 | /* Paired with .clean() */ | ||
2456 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_action(DriveBackup *backup, | ||
2457 | bdrv_graph_rdlock_main_loop(); | ||
2458 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { | ||
2459 | bdrv_graph_rdunlock_main_loop(); | ||
2460 | - goto out; | ||
2461 | + return; | ||
2462 | } | ||
2463 | |||
2464 | flags = bs->open_flags | BDRV_O_RDWR; | ||
2465 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_action(DriveBackup *backup, | ||
2466 | size = bdrv_getlength(bs); | ||
2467 | if (size < 0) { | ||
2468 | error_setg_errno(errp, -size, "bdrv_getlength failed"); | ||
2469 | - goto out; | ||
2470 | + return; | ||
2471 | } | ||
2472 | |||
2473 | if (backup->mode != NEW_IMAGE_MODE_EXISTING) { | ||
2474 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_action(DriveBackup *backup, | ||
2475 | |||
2476 | if (local_err) { | ||
2477 | error_propagate(errp, local_err); | ||
2478 | - goto out; | ||
2479 | + return; | ||
2480 | } | ||
2481 | |||
2482 | options = qdict_new(); | ||
2483 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_action(DriveBackup *backup, | ||
2484 | if (format) { | ||
2485 | qdict_put_str(options, "driver", format); | ||
2486 | } | ||
2487 | - aio_context_release(aio_context); | ||
2488 | |||
2489 | - aio_context_acquire(qemu_get_aio_context()); | ||
2490 | target_bs = bdrv_open(backup->target, NULL, options, flags, errp); | ||
2491 | - aio_context_release(qemu_get_aio_context()); | ||
2492 | - | ||
2493 | if (!target_bs) { | ||
2494 | return; | ||
2495 | } | ||
2496 | |||
2497 | - /* Honor bdrv_try_change_aio_context() context acquisition requirements. */ | ||
2498 | - old_context = bdrv_get_aio_context(target_bs); | ||
2499 | - aio_context_acquire(old_context); | ||
2500 | - | ||
2501 | ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); | ||
2502 | if (ret < 0) { | ||
2503 | bdrv_unref(target_bs); | ||
2504 | - aio_context_release(old_context); | ||
2505 | return; | ||
2506 | } | ||
2507 | |||
2508 | - aio_context_release(old_context); | ||
2509 | - aio_context_acquire(aio_context); | ||
2510 | - | ||
2511 | if (set_backing_hd) { | ||
2512 | if (bdrv_set_backing_hd(target_bs, source, errp) < 0) { | ||
2513 | goto unref; | ||
2514 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_action(DriveBackup *backup, | ||
2515 | |||
2516 | unref: | ||
2517 | bdrv_unref(target_bs); | ||
2518 | -out: | ||
2519 | - aio_context_release(aio_context); | ||
2520 | } | ||
2521 | |||
2522 | static void drive_backup_commit(void *opaque) | ||
2523 | { | ||
2524 | DriveBackupState *state = opaque; | ||
2525 | - AioContext *aio_context; | ||
2526 | - | ||
2527 | - aio_context = bdrv_get_aio_context(state->bs); | ||
2528 | - aio_context_acquire(aio_context); | ||
2529 | |||
2530 | assert(state->job); | ||
2531 | job_start(&state->job->job); | ||
2532 | - | ||
2533 | - aio_context_release(aio_context); | ||
2534 | } | ||
2535 | |||
2536 | static void drive_backup_abort(void *opaque) | ||
2537 | @@ -XXX,XX +XXX,XX @@ static void drive_backup_abort(void *opaque) | ||
2538 | static void drive_backup_clean(void *opaque) | ||
2539 | { | ||
2540 | g_autofree DriveBackupState *state = opaque; | ||
2541 | - AioContext *aio_context; | ||
2542 | |||
2543 | if (!state->bs) { | ||
2544 | return; | ||
2545 | } | ||
2546 | |||
2547 | - aio_context = bdrv_get_aio_context(state->bs); | ||
2548 | - aio_context_acquire(aio_context); | ||
2549 | - | ||
2550 | bdrv_drained_end(state->bs); | ||
2551 | - | ||
2552 | - aio_context_release(aio_context); | ||
2553 | } | ||
2554 | |||
2555 | typedef struct BlockdevBackupState { | ||
2556 | @@ -XXX,XX +XXX,XX @@ static void blockdev_backup_action(BlockdevBackup *backup, | ||
2557 | BlockDriverState *bs; | ||
2558 | BlockDriverState *target_bs; | ||
2559 | AioContext *aio_context; | ||
2560 | - AioContext *old_context; | ||
2561 | int ret; | ||
2562 | |||
2563 | tran_add(tran, &blockdev_backup_drv, state); | ||
2564 | @@ -XXX,XX +XXX,XX @@ static void blockdev_backup_action(BlockdevBackup *backup, | ||
2565 | |||
2566 | /* Honor bdrv_try_change_aio_context() context acquisition requirements. */ | ||
2567 | aio_context = bdrv_get_aio_context(bs); | ||
2568 | - old_context = bdrv_get_aio_context(target_bs); | ||
2569 | - aio_context_acquire(old_context); | ||
2570 | |||
2571 | ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); | ||
2572 | if (ret < 0) { | ||
2573 | - aio_context_release(old_context); | ||
2574 | return; | ||
2575 | } | ||
2576 | |||
2577 | - aio_context_release(old_context); | ||
2578 | - aio_context_acquire(aio_context); | ||
2579 | state->bs = bs; | ||
2580 | |||
2581 | /* Paired with .clean() */ | ||
2582 | @@ -XXX,XX +XXX,XX @@ static void blockdev_backup_action(BlockdevBackup *backup, | ||
2583 | state->job = do_backup_common(qapi_BlockdevBackup_base(backup), | ||
2584 | bs, target_bs, aio_context, | ||
2585 | block_job_txn, errp); | ||
2586 | - | ||
2587 | - aio_context_release(aio_context); | ||
2588 | } | ||
2589 | |||
2590 | static void blockdev_backup_commit(void *opaque) | ||
2591 | { | ||
2592 | BlockdevBackupState *state = opaque; | ||
2593 | - AioContext *aio_context; | ||
2594 | - | ||
2595 | - aio_context = bdrv_get_aio_context(state->bs); | ||
2596 | - aio_context_acquire(aio_context); | ||
2597 | |||
2598 | assert(state->job); | ||
2599 | job_start(&state->job->job); | ||
2600 | - | ||
2601 | - aio_context_release(aio_context); | ||
2602 | } | ||
2603 | |||
2604 | static void blockdev_backup_abort(void *opaque) | ||
2605 | @@ -XXX,XX +XXX,XX @@ static void blockdev_backup_abort(void *opaque) | ||
2606 | static void blockdev_backup_clean(void *opaque) | ||
2607 | { | ||
2608 | g_autofree BlockdevBackupState *state = opaque; | ||
2609 | - AioContext *aio_context; | ||
2610 | |||
2611 | if (!state->bs) { | ||
2612 | return; | ||
2613 | } | ||
2614 | |||
2615 | - aio_context = bdrv_get_aio_context(state->bs); | ||
2616 | - aio_context_acquire(aio_context); | ||
2617 | - | ||
2618 | bdrv_drained_end(state->bs); | ||
2619 | - | ||
2620 | - aio_context_release(aio_context); | ||
2621 | } | ||
2622 | |||
2623 | typedef struct BlockDirtyBitmapState { | ||
2624 | @@ -XXX,XX +XXX,XX @@ void qmp_block_stream(const char *job_id, const char *device, | ||
2625 | } | ||
2626 | |||
2627 | aio_context = bdrv_get_aio_context(bs); | ||
2628 | - aio_context_acquire(aio_context); | ||
2629 | |||
2630 | bdrv_graph_rdlock_main_loop(); | ||
2631 | if (base) { | ||
2632 | @@ -XXX,XX +XXX,XX @@ void qmp_block_stream(const char *job_id, const char *device, | ||
2633 | if (!base_bs && backing_file) { | ||
2634 | error_setg(errp, "backing file specified, but streaming the " | ||
2635 | "entire chain"); | ||
2636 | - goto out; | ||
2637 | + return; | ||
2638 | } | ||
2639 | |||
2640 | if (has_auto_finalize && !auto_finalize) { | ||
2641 | @@ -XXX,XX +XXX,XX @@ void qmp_block_stream(const char *job_id, const char *device, | ||
2642 | filter_node_name, &local_err); | ||
2643 | if (local_err) { | ||
2644 | error_propagate(errp, local_err); | ||
2645 | - goto out; | ||
2646 | + return; | ||
2647 | } | ||
2648 | |||
2649 | trace_qmp_block_stream(bs); | ||
2650 | - | ||
2651 | -out: | ||
2652 | - aio_context_release(aio_context); | ||
2653 | return; | ||
2654 | |||
2655 | out_rdlock: | ||
2656 | bdrv_graph_rdunlock_main_loop(); | ||
2657 | - aio_context_release(aio_context); | ||
2658 | } | ||
2659 | |||
2660 | void qmp_block_commit(const char *job_id, const char *device, | ||
2661 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(const char *job_id, const char *device, | ||
2662 | } | ||
2663 | |||
2664 | aio_context = bdrv_get_aio_context(bs); | ||
2665 | - aio_context_acquire(aio_context); | ||
2666 | |||
2667 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { | ||
2668 | - goto out; | ||
2669 | + return; | ||
2670 | } | ||
2671 | |||
2672 | /* default top_bs is the active layer */ | ||
2673 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(const char *job_id, const char *device, | ||
2674 | |||
2675 | if (top_node && top) { | ||
2676 | error_setg(errp, "'top-node' and 'top' are mutually exclusive"); | ||
2677 | - goto out; | ||
2678 | + return; | ||
2679 | } else if (top_node) { | ||
2680 | top_bs = bdrv_lookup_bs(NULL, top_node, errp); | ||
2681 | if (top_bs == NULL) { | ||
2682 | - goto out; | ||
2683 | + return; | ||
2684 | } | ||
2685 | if (!bdrv_chain_contains(bs, top_bs)) { | ||
2686 | error_setg(errp, "'%s' is not in this backing file chain", | ||
2687 | top_node); | ||
2688 | - goto out; | ||
2689 | + return; | ||
2690 | } | ||
2691 | } else if (top) { | ||
2692 | /* This strcmp() is just a shortcut, there is no need to | ||
2693 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(const char *job_id, const char *device, | ||
2694 | |||
2695 | if (top_bs == NULL) { | ||
2696 | error_setg(errp, "Top image file %s not found", top ? top : "NULL"); | ||
2697 | - goto out; | ||
2698 | + return; | ||
2699 | } | ||
2700 | |||
2701 | assert(bdrv_get_aio_context(top_bs) == aio_context); | ||
2702 | |||
2703 | if (base_node && base) { | ||
2704 | error_setg(errp, "'base-node' and 'base' are mutually exclusive"); | ||
2705 | - goto out; | ||
2706 | + return; | ||
2707 | } else if (base_node) { | ||
2708 | base_bs = bdrv_lookup_bs(NULL, base_node, errp); | ||
2709 | if (base_bs == NULL) { | ||
2710 | - goto out; | ||
2711 | + return; | ||
2712 | } | ||
2713 | if (!bdrv_chain_contains(top_bs, base_bs)) { | ||
2714 | error_setg(errp, "'%s' is not in this backing file chain", | ||
2715 | base_node); | ||
2716 | - goto out; | ||
2717 | + return; | ||
2718 | } | ||
2719 | } else if (base) { | ||
2720 | base_bs = bdrv_find_backing_image(top_bs, base); | ||
2721 | if (base_bs == NULL) { | ||
2722 | error_setg(errp, "Can't find '%s' in the backing chain", base); | ||
2723 | - goto out; | ||
2724 | + return; | ||
2725 | } | ||
2726 | } else { | ||
2727 | base_bs = bdrv_find_base(top_bs); | ||
2728 | if (base_bs == NULL) { | ||
2729 | error_setg(errp, "There is no backimg image"); | ||
2730 | - goto out; | ||
2731 | + return; | ||
2732 | } | ||
2733 | } | ||
2734 | |||
2735 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(const char *job_id, const char *device, | ||
2736 | iter = bdrv_filter_or_cow_bs(iter)) | ||
2737 | { | ||
2738 | if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { | ||
2739 | - goto out; | ||
2740 | + return; | ||
2741 | } | ||
2742 | } | ||
2743 | |||
2744 | /* Do not allow attempts to commit an image into itself */ | ||
2745 | if (top_bs == base_bs) { | ||
2746 | error_setg(errp, "cannot commit an image into itself"); | ||
2747 | - goto out; | ||
2748 | + return; | ||
2749 | } | ||
2750 | |||
2751 | /* | ||
2752 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(const char *job_id, const char *device, | ||
2753 | error_setg(errp, "'backing-file' specified, but 'top' has a " | ||
2754 | "writer on it"); | ||
2755 | } | ||
2756 | - goto out; | ||
2757 | + return; | ||
2758 | } | ||
2759 | if (!job_id) { | ||
2760 | /* | ||
2761 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(const char *job_id, const char *device, | ||
2762 | } else { | ||
2763 | BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); | ||
2764 | if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { | ||
2765 | - goto out; | ||
2766 | + return; | ||
2767 | } | ||
2768 | commit_start(job_id, bs, base_bs, top_bs, job_flags, | ||
2769 | speed, on_error, backing_file, | ||
2770 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(const char *job_id, const char *device, | ||
2771 | } | ||
2772 | if (local_err != NULL) { | ||
2773 | error_propagate(errp, local_err); | ||
2774 | - goto out; | ||
2775 | + return; | ||
2776 | } | ||
2777 | - | ||
2778 | -out: | ||
2779 | - aio_context_release(aio_context); | ||
2780 | } | ||
2781 | |||
2782 | /* Common QMP interface for drive-backup and blockdev-backup */ | ||
2783 | @@ -XXX,XX +XXX,XX @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, | ||
2784 | |||
2785 | if (replaces) { | ||
2786 | BlockDriverState *to_replace_bs; | ||
2787 | - AioContext *aio_context; | ||
2788 | - AioContext *replace_aio_context; | ||
2789 | int64_t bs_size, replace_size; | ||
2790 | |||
2791 | bs_size = bdrv_getlength(bs); | ||
2792 | @@ -XXX,XX +XXX,XX @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, | ||
2793 | return; | ||
2794 | } | ||
2795 | |||
2796 | - aio_context = bdrv_get_aio_context(bs); | ||
2797 | - replace_aio_context = bdrv_get_aio_context(to_replace_bs); | ||
2798 | - /* | ||
2799 | - * bdrv_getlength() is a co-wrapper and uses AIO_WAIT_WHILE. Be sure not | ||
2800 | - * to acquire the same AioContext twice. | ||
2801 | - */ | ||
2802 | - if (replace_aio_context != aio_context) { | ||
2803 | - aio_context_acquire(replace_aio_context); | ||
2804 | - } | ||
2805 | replace_size = bdrv_getlength(to_replace_bs); | ||
2806 | - if (replace_aio_context != aio_context) { | ||
2807 | - aio_context_release(replace_aio_context); | ||
2808 | - } | ||
2809 | |||
2810 | if (replace_size < 0) { | ||
2811 | error_setg_errno(errp, -replace_size, | ||
2812 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||
2813 | BlockDriverState *bs; | ||
2814 | BlockDriverState *target_backing_bs, *target_bs; | ||
2815 | AioContext *aio_context; | ||
2816 | - AioContext *old_context; | ||
2817 | BlockMirrorBackingMode backing_mode; | ||
2818 | Error *local_err = NULL; | ||
2819 | QDict *options = NULL; | ||
2820 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||
2821 | } | ||
2822 | |||
2823 | aio_context = bdrv_get_aio_context(bs); | ||
2824 | - aio_context_acquire(aio_context); | ||
2825 | |||
2826 | if (!arg->has_mode) { | ||
2827 | arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; | ||
2828 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||
2829 | size = bdrv_getlength(bs); | ||
2830 | if (size < 0) { | ||
2831 | error_setg_errno(errp, -size, "bdrv_getlength failed"); | ||
2832 | - goto out; | ||
2833 | + return; | ||
2834 | } | ||
2835 | |||
2836 | if (arg->replaces) { | ||
2837 | if (!arg->node_name) { | ||
2838 | error_setg(errp, "a node-name must be provided when replacing a" | ||
2839 | " named node of the graph"); | ||
2840 | - goto out; | ||
2841 | + return; | ||
2842 | } | ||
2843 | } | ||
2844 | |||
2845 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||
2846 | |||
2847 | if (local_err) { | ||
2848 | error_propagate(errp, local_err); | ||
2849 | - goto out; | ||
2850 | + return; | ||
2851 | } | ||
2852 | |||
2853 | options = qdict_new(); | ||
2854 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||
2855 | if (format) { | ||
2856 | qdict_put_str(options, "driver", format); | ||
2857 | } | ||
2858 | - aio_context_release(aio_context); | ||
2859 | |||
2860 | /* Mirroring takes care of copy-on-write using the source's backing | ||
2861 | * file. | ||
2862 | */ | ||
2863 | - aio_context_acquire(qemu_get_aio_context()); | ||
2864 | target_bs = bdrv_open(arg->target, NULL, options, flags, errp); | ||
2865 | - aio_context_release(qemu_get_aio_context()); | ||
2866 | - | ||
2867 | if (!target_bs) { | ||
2868 | return; | ||
2869 | } | ||
2870 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||
2871 | bdrv_graph_rdunlock_main_loop(); | ||
2872 | |||
2873 | |||
2874 | - /* Honor bdrv_try_change_aio_context() context acquisition requirements. */ | ||
2875 | - old_context = bdrv_get_aio_context(target_bs); | ||
2876 | - aio_context_acquire(old_context); | ||
2877 | - | ||
2878 | ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); | ||
2879 | if (ret < 0) { | ||
2880 | bdrv_unref(target_bs); | ||
2881 | - aio_context_release(old_context); | ||
2882 | return; | ||
2883 | } | ||
2884 | |||
2885 | - aio_context_release(old_context); | ||
2886 | - aio_context_acquire(aio_context); | ||
2887 | - | ||
2888 | blockdev_mirror_common(arg->job_id, bs, target_bs, | ||
2889 | arg->replaces, arg->sync, | ||
2890 | backing_mode, zero_target, | ||
2891 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) | ||
2892 | arg->has_auto_dismiss, arg->auto_dismiss, | ||
2893 | errp); | ||
2894 | bdrv_unref(target_bs); | ||
2895 | -out: | ||
2896 | - aio_context_release(aio_context); | ||
2897 | } | ||
2898 | |||
2899 | void qmp_blockdev_mirror(const char *job_id, | ||
2900 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_mirror(const char *job_id, | ||
2901 | BlockDriverState *bs; | ||
2902 | BlockDriverState *target_bs; | ||
2903 | AioContext *aio_context; | ||
2904 | - AioContext *old_context; | ||
2905 | BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN; | ||
2906 | bool zero_target; | ||
2907 | int ret; | ||
2908 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_mirror(const char *job_id, | ||
2909 | |||
2910 | zero_target = (sync == MIRROR_SYNC_MODE_FULL); | ||
2911 | |||
2912 | - /* Honor bdrv_try_change_aio_context() context acquisition requirements. */ | ||
2913 | - old_context = bdrv_get_aio_context(target_bs); | ||
2914 | aio_context = bdrv_get_aio_context(bs); | ||
2915 | - aio_context_acquire(old_context); | ||
2916 | |||
2917 | ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); | ||
2918 | - | ||
2919 | - aio_context_release(old_context); | ||
2920 | - aio_context_acquire(aio_context); | ||
2921 | - | ||
2922 | if (ret < 0) { | ||
2923 | - goto out; | ||
2924 | + return; | ||
2925 | } | ||
2926 | |||
2927 | blockdev_mirror_common(job_id, bs, target_bs, | ||
2928 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_mirror(const char *job_id, | ||
2929 | has_auto_finalize, auto_finalize, | ||
2930 | has_auto_dismiss, auto_dismiss, | ||
2931 | errp); | ||
2932 | -out: | ||
2933 | - aio_context_release(aio_context); | ||
2934 | } | ||
2935 | |||
2936 | /* | ||
2937 | @@ -XXX,XX +XXX,XX @@ void qmp_change_backing_file(const char *device, | ||
2938 | Error **errp) | ||
2939 | { | ||
2940 | BlockDriverState *bs = NULL; | ||
2941 | - AioContext *aio_context; | ||
2942 | BlockDriverState *image_bs = NULL; | ||
2943 | Error *local_err = NULL; | ||
2944 | bool ro; | ||
2945 | @@ -XXX,XX +XXX,XX @@ void qmp_change_backing_file(const char *device, | ||
2946 | return; | ||
2947 | } | ||
2948 | |||
2949 | - aio_context = bdrv_get_aio_context(bs); | ||
2950 | - aio_context_acquire(aio_context); | ||
2951 | - | ||
2952 | bdrv_graph_rdlock_main_loop(); | ||
2953 | |||
2954 | image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); | ||
2955 | @@ -XXX,XX +XXX,XX @@ void qmp_change_backing_file(const char *device, | ||
2956 | |||
2957 | if (ro) { | ||
2958 | if (bdrv_reopen_set_read_only(image_bs, false, errp) != 0) { | ||
2959 | - goto out; | ||
2960 | + return; | ||
2961 | } | ||
2962 | } | ||
2963 | |||
2964 | @@ -XXX,XX +XXX,XX @@ void qmp_change_backing_file(const char *device, | ||
2965 | if (ro) { | ||
2966 | bdrv_reopen_set_read_only(image_bs, true, errp); | ||
2967 | } | ||
2968 | - | ||
2969 | -out: | ||
2970 | - aio_context_release(aio_context); | ||
2971 | return; | ||
2972 | |||
2973 | out_rdlock: | ||
2974 | bdrv_graph_rdunlock_main_loop(); | ||
2975 | - aio_context_release(aio_context); | ||
2976 | } | ||
2977 | |||
2978 | void qmp_blockdev_add(BlockdevOptions *options, Error **errp) | ||
2979 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) | ||
2980 | for (; reopen_list != NULL; reopen_list = reopen_list->next) { | ||
2981 | BlockdevOptions *options = reopen_list->value; | ||
2982 | BlockDriverState *bs; | ||
2983 | - AioContext *ctx; | ||
2984 | QObject *obj; | ||
2985 | Visitor *v; | ||
2986 | QDict *qdict; | ||
2987 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) | ||
2988 | |||
2989 | qdict_flatten(qdict); | ||
2990 | |||
2991 | - ctx = bdrv_get_aio_context(bs); | ||
2992 | - aio_context_acquire(ctx); | ||
2993 | - | ||
2994 | queue = bdrv_reopen_queue(queue, bs, qdict, false); | ||
2995 | - | ||
2996 | - aio_context_release(ctx); | ||
2997 | } | ||
2998 | |||
2999 | /* Perform the reopen operation */ | ||
3000 | @@ -XXX,XX +XXX,XX @@ fail: | ||
3001 | |||
3002 | void qmp_blockdev_del(const char *node_name, Error **errp) | ||
3003 | { | ||
3004 | - AioContext *aio_context; | ||
3005 | BlockDriverState *bs; | ||
3006 | |||
3007 | GLOBAL_STATE_CODE(); | ||
3008 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_del(const char *node_name, Error **errp) | ||
3009 | error_setg(errp, "Node %s is in use", node_name); | ||
3010 | return; | ||
3011 | } | ||
3012 | - aio_context = bdrv_get_aio_context(bs); | ||
3013 | - aio_context_acquire(aio_context); | ||
3014 | |||
3015 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) { | ||
3016 | - goto out; | ||
3017 | + return; | ||
3018 | } | ||
3019 | |||
3020 | if (!QTAILQ_IN_USE(bs, monitor_list)) { | ||
3021 | error_setg(errp, "Node %s is not owned by the monitor", | ||
3022 | bs->node_name); | ||
3023 | - goto out; | ||
3024 | + return; | ||
3025 | } | ||
3026 | |||
3027 | if (bs->refcnt > 1) { | ||
3028 | error_setg(errp, "Block device %s is in use", | ||
3029 | bdrv_get_device_or_node_name(bs)); | ||
3030 | - goto out; | ||
3031 | + return; | ||
3032 | } | ||
3033 | |||
3034 | QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); | ||
3035 | bdrv_unref(bs); | ||
3036 | - | ||
3037 | -out: | ||
3038 | - aio_context_release(aio_context); | ||
3039 | } | ||
3040 | |||
3041 | static BdrvChild * GRAPH_RDLOCK | ||
3042 | @@ -XXX,XX +XXX,XX @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp) | ||
3043 | void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, | ||
3044 | bool has_force, bool force, Error **errp) | ||
3045 | { | ||
3046 | - AioContext *old_context; | ||
3047 | AioContext *new_context; | ||
3048 | BlockDriverState *bs; | ||
3049 | |||
3050 | @@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, | ||
3051 | new_context = qemu_get_aio_context(); | ||
3052 | } | ||
3053 | |||
3054 | - old_context = bdrv_get_aio_context(bs); | ||
3055 | - aio_context_acquire(old_context); | ||
3056 | - | ||
3057 | bdrv_try_change_aio_context(bs, new_context, NULL, errp); | ||
3058 | - | ||
3059 | - aio_context_release(old_context); | ||
3060 | } | ||
3061 | |||
3062 | QemuOptsList qemu_common_drive_opts = { | ||
3063 | diff --git a/blockjob.c b/blockjob.c | ||
3064 | index XXXXXXX..XXXXXXX 100644 | ||
3065 | --- a/blockjob.c | ||
3066 | +++ b/blockjob.c | ||
3067 | @@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job) | ||
3068 | * one to make sure that such a concurrent access does not attempt | ||
3069 | * to process an already freed BdrvChild. | ||
3070 | */ | ||
3071 | - aio_context_release(job->job.aio_context); | ||
3072 | bdrv_graph_wrlock(); | ||
3073 | - aio_context_acquire(job->job.aio_context); | ||
3074 | while (job->nodes) { | ||
3075 | GSList *l = job->nodes; | ||
3076 | BdrvChild *c = l->data; | ||
3077 | @@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, | ||
3078 | uint64_t perm, uint64_t shared_perm, Error **errp) | ||
3079 | { | ||
3080 | BdrvChild *c; | ||
3081 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
3082 | - bool need_context_ops; | ||
3083 | GLOBAL_STATE_CODE(); | ||
3084 | |||
3085 | bdrv_ref(bs); | ||
3086 | |||
3087 | - need_context_ops = ctx != job->job.aio_context; | ||
3088 | - | ||
3089 | - if (need_context_ops) { | ||
3090 | - if (job->job.aio_context != qemu_get_aio_context()) { | ||
3091 | - aio_context_release(job->job.aio_context); | ||
3092 | - } | ||
3093 | - aio_context_acquire(ctx); | ||
3094 | - } | ||
3095 | c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job, | ||
3096 | errp); | ||
3097 | - if (need_context_ops) { | ||
3098 | - aio_context_release(ctx); | ||
3099 | - if (job->job.aio_context != qemu_get_aio_context()) { | ||
3100 | - aio_context_acquire(job->job.aio_context); | ||
3101 | - } | ||
3102 | - } | ||
3103 | if (c == NULL) { | ||
3104 | return -EPERM; | ||
3105 | } | ||
3106 | diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c | ||
3107 | index XXXXXXX..XXXXXXX 100644 | ||
3108 | --- a/hw/block/dataplane/virtio-blk.c | ||
3109 | +++ b/hw/block/dataplane/virtio-blk.c | ||
3110 | @@ -XXX,XX +XXX,XX @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) | ||
3111 | VirtIOBlockDataPlane *s = vblk->dataplane; | ||
3112 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vblk))); | ||
3113 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | ||
3114 | - AioContext *old_context; | ||
3115 | unsigned i; | ||
3116 | unsigned nvqs = s->conf->num_queues; | ||
3117 | Error *local_err = NULL; | ||
3118 | @@ -XXX,XX +XXX,XX @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) | ||
3119 | |||
3120 | trace_virtio_blk_data_plane_start(s); | ||
3121 | |||
3122 | - old_context = blk_get_aio_context(s->conf->conf.blk); | ||
3123 | - aio_context_acquire(old_context); | ||
3124 | r = blk_set_aio_context(s->conf->conf.blk, s->ctx, &local_err); | ||
3125 | - aio_context_release(old_context); | ||
3126 | if (r < 0) { | ||
3127 | error_report_err(local_err); | ||
3128 | goto fail_aio_context; | ||
3129 | @@ -XXX,XX +XXX,XX @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) | ||
3130 | |||
3131 | /* Get this show started by hooking up our callbacks */ | ||
3132 | if (!blk_in_drain(s->conf->conf.blk)) { | ||
3133 | - aio_context_acquire(s->ctx); | ||
3134 | for (i = 0; i < nvqs; i++) { | ||
3135 | VirtQueue *vq = virtio_get_queue(s->vdev, i); | ||
3136 | |||
3137 | virtio_queue_aio_attach_host_notifier(vq, s->ctx); | ||
3138 | } | ||
3139 | - aio_context_release(s->ctx); | ||
3140 | } | ||
3141 | return 0; | ||
3142 | |||
3143 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) | ||
3144 | */ | ||
3145 | vblk->dataplane_started = false; | ||
3146 | |||
3147 | - aio_context_acquire(s->ctx); | ||
3148 | - | ||
3149 | /* Wait for virtio_blk_dma_restart_bh() and in flight I/O to complete */ | ||
3150 | blk_drain(s->conf->conf.blk); | ||
3151 | |||
3152 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) | ||
3153 | */ | ||
3154 | blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context(), NULL); | ||
3155 | |||
3156 | - aio_context_release(s->ctx); | ||
3157 | - | ||
3158 | /* Clean up guest notifier (irq) */ | ||
3159 | k->set_guest_notifiers(qbus->parent, nvqs, false); | ||
3160 | |||
3161 | diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c | ||
3162 | index XXXXXXX..XXXXXXX 100644 | ||
3163 | --- a/hw/block/dataplane/xen-block.c | ||
3164 | +++ b/hw/block/dataplane/xen-block.c | ||
3165 | @@ -XXX,XX +XXX,XX @@ static void xen_block_complete_aio(void *opaque, int ret) | ||
3166 | XenBlockRequest *request = opaque; | ||
3167 | XenBlockDataPlane *dataplane = request->dataplane; | ||
3168 | |||
3169 | - aio_context_acquire(dataplane->ctx); | ||
3170 | - | ||
3171 | if (ret != 0) { | ||
3172 | error_report("%s I/O error", | ||
3173 | request->req.operation == BLKIF_OP_READ ? | ||
3174 | @@ -XXX,XX +XXX,XX @@ static void xen_block_complete_aio(void *opaque, int ret) | ||
3175 | if (request->presync) { | ||
3176 | request->presync = 0; | ||
3177 | xen_block_do_aio(request); | ||
3178 | - goto done; | ||
3179 | + return; | ||
3180 | } | ||
3181 | if (request->aio_inflight > 0) { | ||
3182 | - goto done; | ||
3183 | + return; | ||
3184 | } | ||
3185 | |||
3186 | switch (request->req.operation) { | ||
3187 | @@ -XXX,XX +XXX,XX @@ static void xen_block_complete_aio(void *opaque, int ret) | ||
3188 | if (dataplane->more_work) { | ||
3189 | qemu_bh_schedule(dataplane->bh); | ||
3190 | } | ||
3191 | - | ||
3192 | -done: | ||
3193 | - aio_context_release(dataplane->ctx); | ||
3194 | } | ||
3195 | |||
3196 | static bool xen_block_split_discard(XenBlockRequest *request, | ||
3197 | @@ -XXX,XX +XXX,XX @@ static void xen_block_dataplane_bh(void *opaque) | ||
3198 | { | ||
3199 | XenBlockDataPlane *dataplane = opaque; | ||
3200 | |||
3201 | - aio_context_acquire(dataplane->ctx); | ||
3202 | xen_block_handle_requests(dataplane); | ||
3203 | - aio_context_release(dataplane->ctx); | ||
3204 | } | ||
3205 | |||
3206 | static bool xen_block_dataplane_event(void *opaque) | ||
3207 | @@ -XXX,XX +XXX,XX @@ void xen_block_dataplane_stop(XenBlockDataPlane *dataplane) | ||
3208 | xen_block_dataplane_detach(dataplane); | ||
3209 | } | ||
3210 | |||
3211 | - aio_context_acquire(dataplane->ctx); | ||
3212 | /* Xen doesn't have multiple users for nodes, so this can't fail */ | ||
3213 | blk_set_aio_context(dataplane->blk, qemu_get_aio_context(), &error_abort); | ||
3214 | - aio_context_release(dataplane->ctx); | ||
3215 | |||
3216 | /* | ||
3217 | * Now that the context has been moved onto the main thread, cancel | ||
3218 | @@ -XXX,XX +XXX,XX @@ void xen_block_dataplane_start(XenBlockDataPlane *dataplane, | ||
3219 | { | ||
3220 | ERRP_GUARD(); | ||
3221 | XenDevice *xendev = dataplane->xendev; | ||
3222 | - AioContext *old_context; | ||
3223 | unsigned int ring_size; | ||
3224 | unsigned int i; | ||
3225 | |||
3226 | @@ -XXX,XX +XXX,XX @@ void xen_block_dataplane_start(XenBlockDataPlane *dataplane, | ||
3227 | goto stop; | ||
3228 | } | ||
3229 | |||
3230 | - old_context = blk_get_aio_context(dataplane->blk); | ||
3231 | - aio_context_acquire(old_context); | ||
3232 | /* If other users keep the BlockBackend in the iothread, that's ok */ | ||
3233 | blk_set_aio_context(dataplane->blk, dataplane->ctx, NULL); | ||
3234 | - aio_context_release(old_context); | ||
3235 | |||
3236 | if (!blk_in_drain(dataplane->blk)) { | ||
3237 | xen_block_dataplane_attach(dataplane); | ||
3238 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
3239 | index XXXXXXX..XXXXXXX 100644 | ||
3240 | --- a/hw/block/virtio-blk.c | ||
3241 | +++ b/hw/block/virtio-blk.c | ||
3242 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_dma_restart_cb(void *opaque, bool running, | ||
3243 | static void virtio_blk_reset(VirtIODevice *vdev) | ||
3244 | { | ||
3245 | VirtIOBlock *s = VIRTIO_BLK(vdev); | ||
3246 | - AioContext *ctx; | ||
3247 | VirtIOBlockReq *req; | ||
3248 | |||
3249 | /* Dataplane has stopped... */ | ||
3250 | assert(!s->dataplane_started); | ||
3251 | |||
3252 | /* ...but requests may still be in flight. */ | ||
3253 | - ctx = blk_get_aio_context(s->blk); | ||
3254 | - aio_context_acquire(ctx); | ||
3255 | blk_drain(s->blk); | ||
3256 | - aio_context_release(ctx); | ||
3257 | |||
3258 | /* We drop queued requests after blk_drain() because blk_drain() itself can | ||
3259 | * produce them. */ | ||
3260 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) | ||
3261 | uint64_t capacity; | ||
3262 | int64_t length; | ||
3263 | int blk_size = conf->logical_block_size; | ||
3264 | - AioContext *ctx; | ||
3265 | - | ||
3266 | - ctx = blk_get_aio_context(s->blk); | ||
3267 | - aio_context_acquire(ctx); | ||
3268 | |||
3269 | blk_get_geometry(s->blk, &capacity); | ||
3270 | memset(&blkcfg, 0, sizeof(blkcfg)); | ||
3271 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) | ||
3272 | * per track (cylinder). | ||
3273 | */ | ||
3274 | length = blk_getlength(s->blk); | ||
3275 | - aio_context_release(ctx); | ||
3276 | if (length > 0 && length / conf->heads / conf->secs % blk_size) { | ||
3277 | blkcfg.geometry.sectors = conf->secs & ~s->sector_mask; | ||
3278 | } else { | ||
3279 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) | ||
3280 | |||
3281 | memcpy(&blkcfg, config, s->config_size); | ||
3282 | |||
3283 | - aio_context_acquire(blk_get_aio_context(s->blk)); | ||
3284 | blk_set_enable_write_cache(s->blk, blkcfg.wce != 0); | ||
3285 | - aio_context_release(blk_get_aio_context(s->blk)); | ||
3286 | } | ||
3287 | |||
3288 | static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, | ||
3289 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) | ||
3290 | * s->blk would erroneously be placed in writethrough mode. | ||
3291 | */ | ||
3292 | if (!virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) { | ||
3293 | - aio_context_acquire(blk_get_aio_context(s->blk)); | ||
3294 | blk_set_enable_write_cache(s->blk, | ||
3295 | virtio_vdev_has_feature(vdev, | ||
3296 | VIRTIO_BLK_F_WCE)); | ||
3297 | - aio_context_release(blk_get_aio_context(s->blk)); | ||
3298 | } | ||
3299 | } | ||
3300 | |||
3301 | diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c | ||
3302 | index XXXXXXX..XXXXXXX 100644 | ||
3303 | --- a/hw/core/qdev-properties-system.c | ||
3304 | +++ b/hw/core/qdev-properties-system.c | ||
3305 | @@ -XXX,XX +XXX,XX @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, | ||
3306 | "node"); | ||
3307 | } | ||
3308 | |||
3309 | - aio_context_acquire(ctx); | ||
3310 | blk_replace_bs(blk, bs, errp); | ||
3311 | - aio_context_release(ctx); | ||
3312 | return; | ||
3313 | } | ||
3314 | |||
3315 | @@ -XXX,XX +XXX,XX @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, | ||
3316 | 0, BLK_PERM_ALL); | ||
3317 | blk_created = true; | ||
3318 | |||
3319 | - aio_context_acquire(ctx); | ||
3320 | ret = blk_insert_bs(blk, bs, errp); | ||
3321 | - aio_context_release(ctx); | ||
3322 | - | ||
3323 | if (ret < 0) { | ||
3324 | goto fail; | ||
3325 | } | ||
3326 | @@ -XXX,XX +XXX,XX @@ static void release_drive(Object *obj, const char *name, void *opaque) | ||
3327 | BlockBackend **ptr = object_field_prop_ptr(obj, prop); | ||
3328 | |||
3329 | if (*ptr) { | ||
3330 | - AioContext *ctx = blk_get_aio_context(*ptr); | ||
3331 | - | ||
3332 | - aio_context_acquire(ctx); | ||
3333 | blockdev_auto_del(*ptr); | ||
3334 | blk_detach_dev(*ptr, dev); | ||
3335 | - aio_context_release(ctx); | ||
3336 | } | ||
3337 | } | ||
3338 | |||
3339 | diff --git a/job.c b/job.c | ||
3340 | index XXXXXXX..XXXXXXX 100644 | ||
3341 | --- a/job.c | ||
3342 | +++ b/job.c | ||
3343 | @@ -XXX,XX +XXX,XX @@ void job_unref_locked(Job *job) | ||
3344 | assert(!job->txn); | ||
3345 | |||
3346 | if (job->driver->free) { | ||
3347 | - AioContext *aio_context = job->aio_context; | ||
3348 | job_unlock(); | ||
3349 | - /* FIXME: aiocontext lock is required because cb calls blk_unref */ | ||
3350 | - aio_context_acquire(aio_context); | ||
3351 | job->driver->free(job); | ||
3352 | - aio_context_release(aio_context); | ||
3353 | job_lock(); | ||
3354 | } | ||
3355 | |||
3356 | @@ -XXX,XX +XXX,XX @@ static void job_clean(Job *job) | ||
3357 | |||
3358 | /* | ||
3359 | * Called with job_mutex held, but releases it temporarily. | ||
3360 | - * Takes AioContext lock internally to invoke a job->driver callback. | ||
3361 | */ | ||
3362 | static int job_finalize_single_locked(Job *job) | ||
3363 | { | ||
3364 | int job_ret; | ||
3365 | - AioContext *ctx = job->aio_context; | ||
3366 | |||
3367 | assert(job_is_completed_locked(job)); | ||
3368 | |||
3369 | @@ -XXX,XX +XXX,XX @@ static int job_finalize_single_locked(Job *job) | ||
3370 | |||
3371 | job_ret = job->ret; | ||
3372 | job_unlock(); | ||
3373 | - aio_context_acquire(ctx); | ||
3374 | |||
3375 | if (!job_ret) { | ||
3376 | job_commit(job); | ||
3377 | @@ -XXX,XX +XXX,XX @@ static int job_finalize_single_locked(Job *job) | ||
3378 | job->cb(job->opaque, job_ret); | ||
3379 | } | ||
3380 | |||
3381 | - aio_context_release(ctx); | ||
3382 | job_lock(); | ||
3383 | |||
3384 | /* Emit events only if we actually started */ | ||
3385 | @@ -XXX,XX +XXX,XX @@ static int job_finalize_single_locked(Job *job) | ||
3386 | |||
3387 | /* | ||
3388 | * Called with job_mutex held, but releases it temporarily. | ||
3389 | - * Takes AioContext lock internally to invoke a job->driver callback. | ||
3390 | */ | ||
3391 | static void job_cancel_async_locked(Job *job, bool force) | ||
3392 | { | ||
3393 | - AioContext *ctx = job->aio_context; | ||
3394 | GLOBAL_STATE_CODE(); | ||
3395 | if (job->driver->cancel) { | ||
3396 | job_unlock(); | ||
3397 | - aio_context_acquire(ctx); | ||
3398 | force = job->driver->cancel(job, force); | ||
3399 | - aio_context_release(ctx); | ||
3400 | job_lock(); | ||
3401 | } else { | ||
3402 | /* No .cancel() means the job will behave as if force-cancelled */ | ||
3403 | @@ -XXX,XX +XXX,XX @@ static void job_cancel_async_locked(Job *job, bool force) | ||
3404 | |||
3405 | /* | ||
3406 | * Called with job_mutex held, but releases it temporarily. | ||
3407 | - * Takes AioContext lock internally to invoke a job->driver callback. | ||
3408 | */ | ||
3409 | static void job_completed_txn_abort_locked(Job *job) | ||
3410 | { | ||
3411 | @@ -XXX,XX +XXX,XX @@ static void job_completed_txn_abort_locked(Job *job) | ||
3412 | static int job_prepare_locked(Job *job) | ||
3413 | { | ||
3414 | int ret; | ||
3415 | - AioContext *ctx = job->aio_context; | ||
3416 | |||
3417 | GLOBAL_STATE_CODE(); | ||
3418 | |||
3419 | if (job->ret == 0 && job->driver->prepare) { | ||
3420 | job_unlock(); | ||
3421 | - aio_context_acquire(ctx); | ||
3422 | ret = job->driver->prepare(job); | ||
3423 | - aio_context_release(ctx); | ||
3424 | job_lock(); | ||
3425 | job->ret = ret; | ||
3426 | job_update_rc_locked(job); | ||
3427 | diff --git a/migration/block.c b/migration/block.c | ||
3428 | index XXXXXXX..XXXXXXX 100644 | ||
3429 | --- a/migration/block.c | ||
3430 | +++ b/migration/block.c | ||
3431 | @@ -XXX,XX +XXX,XX @@ typedef struct BlkMigDevState { | ||
3432 | /* Protected by block migration lock. */ | ||
3433 | int64_t completed_sectors; | ||
3434 | |||
3435 | - /* During migration this is protected by iothread lock / AioContext. | ||
3436 | + /* During migration this is protected by bdrv_dirty_bitmap_lock(). | ||
3437 | * Allocation and free happen during setup and cleanup respectively. | ||
3438 | */ | ||
3439 | BdrvDirtyBitmap *dirty_bitmap; | ||
3440 | @@ -XXX,XX +XXX,XX @@ typedef struct BlkMigState { | ||
3441 | int prev_progress; | ||
3442 | int bulk_completed; | ||
3443 | |||
3444 | - /* Lock must be taken _inside_ the iothread lock and any AioContexts. */ | ||
3445 | + /* Lock must be taken _inside_ the iothread lock. */ | ||
3446 | QemuMutex lock; | ||
3447 | } BlkMigState; | ||
3448 | |||
3449 | @@ -XXX,XX +XXX,XX @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) | ||
3450 | |||
3451 | if (bmds->shared_base) { | ||
3452 | qemu_mutex_lock_iothread(); | ||
3453 | - aio_context_acquire(blk_get_aio_context(bb)); | ||
3454 | /* Skip unallocated sectors; intentionally treats failure or | ||
3455 | * partial sector as an allocated sector */ | ||
3456 | while (cur_sector < total_sectors && | ||
3457 | @@ -XXX,XX +XXX,XX @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) | ||
3458 | } | ||
3459 | cur_sector += count >> BDRV_SECTOR_BITS; | ||
3460 | } | ||
3461 | - aio_context_release(blk_get_aio_context(bb)); | ||
3462 | qemu_mutex_unlock_iothread(); | ||
3463 | } | ||
3464 | |||
3465 | @@ -XXX,XX +XXX,XX @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) | ||
3466 | block_mig_state.submitted++; | ||
3467 | blk_mig_unlock(); | ||
3468 | |||
3469 | - /* We do not know if bs is under the main thread (and thus does | ||
3470 | - * not acquire the AioContext when doing AIO) or rather under | ||
3471 | - * dataplane. Thus acquire both the iothread mutex and the | ||
3472 | - * AioContext. | ||
3473 | - * | ||
3474 | - * This is ugly and will disappear when we make bdrv_* thread-safe, | ||
3475 | - * without the need to acquire the AioContext. | ||
3476 | + /* | ||
3477 | + * The migration thread does not have an AioContext. Lock the BQL so that | ||
3478 | + * I/O runs in the main loop AioContext (see | ||
3479 | + * qemu_get_current_aio_context()). | ||
3480 | */ | ||
3481 | qemu_mutex_lock_iothread(); | ||
3482 | - aio_context_acquire(blk_get_aio_context(bmds->blk)); | ||
3483 | bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector * BDRV_SECTOR_SIZE, | ||
3484 | nr_sectors * BDRV_SECTOR_SIZE); | ||
3485 | blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov, | ||
3486 | 0, blk_mig_read_cb, blk); | ||
3487 | - aio_context_release(blk_get_aio_context(bmds->blk)); | ||
3488 | qemu_mutex_unlock_iothread(); | ||
3489 | |||
3490 | bmds->cur_sector = cur_sector + nr_sectors; | ||
3491 | @@ -XXX,XX +XXX,XX @@ static void blk_mig_reset_dirty_cursor(void) | ||
3492 | } | ||
3493 | } | ||
3494 | |||
3495 | -/* Called with iothread lock and AioContext taken. */ | ||
3496 | +/* Called with iothread lock taken. */ | ||
3497 | |||
3498 | static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, | ||
3499 | int is_async) | ||
3500 | @@ -XXX,XX +XXX,XX @@ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) | ||
3501 | int ret = 1; | ||
3502 | |||
3503 | QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||
3504 | - aio_context_acquire(blk_get_aio_context(bmds->blk)); | ||
3505 | ret = mig_save_device_dirty(f, bmds, is_async); | ||
3506 | - aio_context_release(blk_get_aio_context(bmds->blk)); | ||
3507 | if (ret <= 0) { | ||
3508 | break; | ||
3509 | } | ||
3510 | @@ -XXX,XX +XXX,XX @@ static int64_t get_remaining_dirty(void) | ||
3511 | int64_t dirty = 0; | ||
3512 | |||
3513 | QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||
3514 | - aio_context_acquire(blk_get_aio_context(bmds->blk)); | ||
3515 | + bdrv_dirty_bitmap_lock(bmds->dirty_bitmap); | ||
3516 | dirty += bdrv_get_dirty_count(bmds->dirty_bitmap); | ||
3517 | - aio_context_release(blk_get_aio_context(bmds->blk)); | ||
3518 | + bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); | ||
3519 | } | ||
3520 | |||
3521 | return dirty; | ||
3522 | @@ -XXX,XX +XXX,XX @@ static void block_migration_cleanup_bmds(void) | ||
3523 | { | ||
3524 | BlkMigDevState *bmds; | ||
3525 | BlockDriverState *bs; | ||
3526 | - AioContext *ctx; | ||
3527 | |||
3528 | unset_dirty_tracking(); | ||
3529 | |||
3530 | @@ -XXX,XX +XXX,XX @@ static void block_migration_cleanup_bmds(void) | ||
3531 | bdrv_op_unblock_all(bs, bmds->blocker); | ||
3532 | } | ||
3533 | error_free(bmds->blocker); | ||
3534 | - | ||
3535 | - /* Save ctx, because bmds->blk can disappear during blk_unref. */ | ||
3536 | - ctx = blk_get_aio_context(bmds->blk); | ||
3537 | - aio_context_acquire(ctx); | ||
3538 | blk_unref(bmds->blk); | ||
3539 | - aio_context_release(ctx); | ||
3540 | - | ||
3541 | g_free(bmds->blk_name); | ||
3542 | g_free(bmds->aio_bitmap); | ||
3543 | g_free(bmds); | ||
3544 | diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c | ||
3545 | index XXXXXXX..XXXXXXX 100644 | ||
3546 | --- a/migration/migration-hmp-cmds.c | ||
3547 | +++ b/migration/migration-hmp-cmds.c | ||
3548 | @@ -XXX,XX +XXX,XX @@ static void vm_completion(ReadLineState *rs, const char *str) | ||
3549 | |||
3550 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
3551 | SnapshotInfoList *snapshots, *snapshot; | ||
3552 | - AioContext *ctx = bdrv_get_aio_context(bs); | ||
3553 | bool ok = false; | ||
3554 | |||
3555 | - aio_context_acquire(ctx); | ||
3556 | if (bdrv_can_snapshot(bs)) { | ||
3557 | ok = bdrv_query_snapshot_info_list(bs, &snapshots, NULL) == 0; | ||
3558 | } | ||
3559 | - aio_context_release(ctx); | ||
3560 | if (!ok) { | ||
3561 | continue; | ||
3562 | } | ||
3563 | diff --git a/migration/savevm.c b/migration/savevm.c | ||
3564 | index XXXXXXX..XXXXXXX 100644 | ||
3565 | --- a/migration/savevm.c | ||
3566 | +++ b/migration/savevm.c | ||
3567 | @@ -XXX,XX +XXX,XX @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, | ||
3568 | int saved_vm_running; | ||
3569 | uint64_t vm_state_size; | ||
3570 | g_autoptr(GDateTime) now = g_date_time_new_now_local(); | ||
3571 | - AioContext *aio_context; | ||
3572 | |||
3573 | GLOBAL_STATE_CODE(); | ||
3574 | |||
3575 | @@ -XXX,XX +XXX,XX @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, | ||
3576 | if (bs == NULL) { | ||
3577 | return false; | ||
3578 | } | ||
3579 | - aio_context = bdrv_get_aio_context(bs); | ||
3580 | |||
3581 | saved_vm_running = runstate_is_running(); | ||
3582 | |||
3583 | @@ -XXX,XX +XXX,XX @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, | ||
3584 | |||
3585 | bdrv_drain_all_begin(); | ||
3586 | |||
3587 | - aio_context_acquire(aio_context); | ||
3588 | - | ||
3589 | memset(sn, 0, sizeof(*sn)); | ||
3590 | |||
3591 | /* fill auxiliary fields */ | ||
3592 | @@ -XXX,XX +XXX,XX @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, | ||
3593 | goto the_end; | ||
3594 | } | ||
3595 | |||
3596 | - /* The bdrv_all_create_snapshot() call that follows acquires the AioContext | ||
3597 | - * for itself. BDRV_POLL_WHILE() does not support nested locking because | ||
3598 | - * it only releases the lock once. Therefore synchronous I/O will deadlock | ||
3599 | - * unless we release the AioContext before bdrv_all_create_snapshot(). | ||
3600 | - */ | ||
3601 | - aio_context_release(aio_context); | ||
3602 | - aio_context = NULL; | ||
3603 | - | ||
3604 | ret = bdrv_all_create_snapshot(sn, bs, vm_state_size, | ||
3605 | has_devices, devices, errp); | ||
3606 | if (ret < 0) { | ||
3607 | @@ -XXX,XX +XXX,XX @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate, | ||
3608 | ret = 0; | ||
3609 | |||
3610 | the_end: | ||
3611 | - if (aio_context) { | ||
3612 | - aio_context_release(aio_context); | ||
3613 | - } | ||
3614 | - | ||
3615 | bdrv_drain_all_end(); | ||
3616 | |||
3617 | if (saved_vm_running) { | ||
3618 | @@ -XXX,XX +XXX,XX @@ bool load_snapshot(const char *name, const char *vmstate, | ||
3619 | QEMUSnapshotInfo sn; | ||
3620 | QEMUFile *f; | ||
3621 | int ret; | ||
3622 | - AioContext *aio_context; | ||
3623 | MigrationIncomingState *mis = migration_incoming_get_current(); | ||
3624 | |||
3625 | if (!bdrv_all_can_snapshot(has_devices, devices, errp)) { | ||
3626 | @@ -XXX,XX +XXX,XX @@ bool load_snapshot(const char *name, const char *vmstate, | ||
3627 | if (!bs_vm_state) { | ||
3628 | return false; | ||
3629 | } | ||
3630 | - aio_context = bdrv_get_aio_context(bs_vm_state); | ||
3631 | |||
3632 | /* Don't even try to load empty VM states */ | ||
3633 | - aio_context_acquire(aio_context); | ||
3634 | ret = bdrv_snapshot_find(bs_vm_state, &sn, name); | ||
3635 | - aio_context_release(aio_context); | ||
3636 | if (ret < 0) { | ||
3637 | return false; | ||
3638 | } else if (sn.vm_state_size == 0) { | ||
3639 | @@ -XXX,XX +XXX,XX @@ bool load_snapshot(const char *name, const char *vmstate, | ||
3640 | ret = -EINVAL; | ||
3641 | goto err_drain; | ||
3642 | } | ||
3643 | - aio_context_acquire(aio_context); | ||
3644 | ret = qemu_loadvm_state(f); | ||
3645 | migration_incoming_state_destroy(); | ||
3646 | - aio_context_release(aio_context); | ||
3647 | |||
3648 | bdrv_drain_all_end(); | ||
3649 | |||
3650 | diff --git a/net/colo-compare.c b/net/colo-compare.c | ||
3651 | index XXXXXXX..XXXXXXX 100644 | ||
3652 | --- a/net/colo-compare.c | ||
3653 | +++ b/net/colo-compare.c | ||
3654 | @@ -XXX,XX +XXX,XX @@ static void colo_compare_finalize(Object *obj) | ||
3655 | qemu_bh_delete(s->event_bh); | ||
3656 | |||
3657 | AioContext *ctx = iothread_get_aio_context(s->iothread); | ||
3658 | - aio_context_acquire(ctx); | ||
3659 | AIO_WAIT_WHILE(ctx, !s->out_sendco.done); | ||
3660 | if (s->notify_dev) { | ||
3661 | AIO_WAIT_WHILE(ctx, !s->notify_sendco.done); | ||
3662 | } | ||
3663 | - aio_context_release(ctx); | ||
3664 | |||
3665 | /* Release all unhandled packets after compare thead exited */ | ||
3666 | g_queue_foreach(&s->conn_list, colo_flush_packets, s); | ||
3667 | diff --git a/qemu-img.c b/qemu-img.c | ||
3668 | index XXXXXXX..XXXXXXX 100644 | ||
3669 | --- a/qemu-img.c | ||
3670 | +++ b/qemu-img.c | ||
3671 | @@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv) | ||
3672 | Error *local_err = NULL; | ||
3673 | CommonBlockJobCBInfo cbi; | ||
3674 | bool image_opts = false; | ||
3675 | - AioContext *aio_context; | ||
3676 | int64_t rate_limit = 0; | ||
3677 | |||
3678 | fmt = NULL; | ||
3679 | @@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv) | ||
3680 | .bs = bs, | ||
3681 | }; | ||
3682 | |||
3683 | - aio_context = bdrv_get_aio_context(bs); | ||
3684 | - aio_context_acquire(aio_context); | ||
3685 | commit_active_start("commit", bs, base_bs, JOB_DEFAULT, rate_limit, | ||
3686 | BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, | ||
3687 | &cbi, false, &local_err); | ||
3688 | - aio_context_release(aio_context); | ||
3689 | if (local_err) { | ||
3690 | goto done; | ||
3691 | } | ||
3692 | diff --git a/qemu-io.c b/qemu-io.c | ||
3693 | index XXXXXXX..XXXXXXX 100644 | ||
3694 | --- a/qemu-io.c | ||
3695 | +++ b/qemu-io.c | ||
3696 | @@ -XXX,XX +XXX,XX @@ static void prep_fetchline(void *opaque) | ||
3697 | |||
3698 | static int do_qemuio_command(const char *cmd) | ||
3699 | { | ||
3700 | - int ret; | ||
3701 | - AioContext *ctx = | ||
3702 | - qemuio_blk ? blk_get_aio_context(qemuio_blk) : qemu_get_aio_context(); | ||
3703 | - | ||
3704 | - aio_context_acquire(ctx); | ||
3705 | - ret = qemuio_command(qemuio_blk, cmd); | ||
3706 | - aio_context_release(ctx); | ||
3707 | - | ||
3708 | - return ret; | ||
3709 | + return qemuio_command(qemuio_blk, cmd); | ||
3710 | } | ||
3711 | |||
3712 | static int command_loop(void) | ||
3713 | diff --git a/qemu-nbd.c b/qemu-nbd.c | ||
3714 | index XXXXXXX..XXXXXXX 100644 | ||
3715 | --- a/qemu-nbd.c | ||
3716 | +++ b/qemu-nbd.c | ||
3717 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
3718 | qdict_put_str(raw_opts, "file", bs->node_name); | ||
3719 | qdict_put_int(raw_opts, "offset", dev_offset); | ||
3720 | |||
3721 | - aio_context_acquire(qemu_get_aio_context()); | ||
3722 | bs = bdrv_open(NULL, NULL, raw_opts, flags, &error_fatal); | ||
3723 | - aio_context_release(qemu_get_aio_context()); | ||
3724 | |||
3725 | blk_remove_bs(blk); | ||
3726 | blk_insert_bs(blk, bs, &error_fatal); | ||
3727 | diff --git a/replay/replay-debugging.c b/replay/replay-debugging.c | ||
3728 | index XXXXXXX..XXXXXXX 100644 | ||
3729 | --- a/replay/replay-debugging.c | ||
3730 | +++ b/replay/replay-debugging.c | ||
3731 | @@ -XXX,XX +XXX,XX @@ static char *replay_find_nearest_snapshot(int64_t icount, | ||
3732 | char *ret = NULL; | ||
3733 | int rv; | ||
3734 | int nb_sns, i; | ||
3735 | - AioContext *aio_context; | ||
3736 | |||
3737 | *snapshot_icount = -1; | ||
3738 | |||
3739 | @@ -XXX,XX +XXX,XX @@ static char *replay_find_nearest_snapshot(int64_t icount, | ||
3740 | if (!bs) { | ||
3741 | goto fail; | ||
3742 | } | ||
3743 | - aio_context = bdrv_get_aio_context(bs); | ||
3744 | |||
3745 | - aio_context_acquire(aio_context); | ||
3746 | nb_sns = bdrv_snapshot_list(bs, &sn_tab); | ||
3747 | - aio_context_release(aio_context); | ||
3748 | |||
3749 | for (i = 0; i < nb_sns; i++) { | ||
3750 | rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL); | ||
3751 | diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c | ||
3752 | index XXXXXXX..XXXXXXX 100644 | ||
3753 | --- a/tests/unit/test-bdrv-drain.c | ||
3754 | +++ b/tests/unit/test-bdrv-drain.c | ||
3755 | @@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) | ||
3756 | |||
3757 | static void do_drain_begin_unlocked(enum drain_type drain_type, BlockDriverState *bs) | ||
3758 | { | ||
3759 | - if (drain_type != BDRV_DRAIN_ALL) { | ||
3760 | - aio_context_acquire(bdrv_get_aio_context(bs)); | ||
3761 | - } | ||
3762 | do_drain_begin(drain_type, bs); | ||
3763 | - if (drain_type != BDRV_DRAIN_ALL) { | ||
3764 | - aio_context_release(bdrv_get_aio_context(bs)); | ||
3765 | - } | ||
3766 | } | ||
3767 | |||
3768 | static BlockBackend * no_coroutine_fn test_setup(void) | ||
3769 | @@ -XXX,XX +XXX,XX @@ static BlockBackend * no_coroutine_fn test_setup(void) | ||
3770 | |||
3771 | static void do_drain_end_unlocked(enum drain_type drain_type, BlockDriverState *bs) | ||
3772 | { | ||
3773 | - if (drain_type != BDRV_DRAIN_ALL) { | ||
3774 | - aio_context_acquire(bdrv_get_aio_context(bs)); | ||
3775 | - } | ||
3776 | do_drain_end(drain_type, bs); | ||
3777 | - if (drain_type != BDRV_DRAIN_ALL) { | ||
3778 | - aio_context_release(bdrv_get_aio_context(bs)); | ||
3779 | - } | ||
3780 | } | ||
3781 | |||
3782 | /* | ||
3783 | @@ -XXX,XX +XXX,XX @@ static void test_iothread_main_thread_bh(void *opaque) | ||
3784 | { | ||
3785 | struct test_iothread_data *data = opaque; | ||
3786 | |||
3787 | - /* Test that the AioContext is not yet locked in a random BH that is | ||
3788 | - * executed during drain, otherwise this would deadlock. */ | ||
3789 | - aio_context_acquire(bdrv_get_aio_context(data->bs)); | ||
3790 | bdrv_flush(data->bs); | ||
3791 | bdrv_dec_in_flight(data->bs); /* incremented by test_iothread_common() */ | ||
3792 | - aio_context_release(bdrv_get_aio_context(data->bs)); | ||
3793 | } | ||
3794 | |||
3795 | /* | ||
3796 | @@ -XXX,XX +XXX,XX @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) | ||
3797 | blk_set_disable_request_queuing(blk, true); | ||
3798 | |||
3799 | blk_set_aio_context(blk, ctx_a, &error_abort); | ||
3800 | - aio_context_acquire(ctx_a); | ||
3801 | |||
3802 | s->bh_indirection_ctx = ctx_b; | ||
3803 | |||
3804 | @@ -XXX,XX +XXX,XX @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) | ||
3805 | g_assert(acb != NULL); | ||
3806 | g_assert_cmpint(aio_ret, ==, -EINPROGRESS); | ||
3807 | |||
3808 | - aio_context_release(ctx_a); | ||
3809 | - | ||
3810 | data = (struct test_iothread_data) { | ||
3811 | .bs = bs, | ||
3812 | .drain_type = drain_type, | ||
3813 | @@ -XXX,XX +XXX,XX @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) | ||
3814 | |||
3815 | switch (drain_thread) { | ||
3816 | case 0: | ||
3817 | - if (drain_type != BDRV_DRAIN_ALL) { | ||
3818 | - aio_context_acquire(ctx_a); | ||
3819 | - } | ||
3820 | - | ||
3821 | /* | ||
3822 | * Increment in_flight so that do_drain_begin() waits for | ||
3823 | * test_iothread_main_thread_bh(). This prevents the race between | ||
3824 | @@ -XXX,XX +XXX,XX @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) | ||
3825 | do_drain_begin(drain_type, bs); | ||
3826 | g_assert_cmpint(bs->in_flight, ==, 0); | ||
3827 | |||
3828 | - if (drain_type != BDRV_DRAIN_ALL) { | ||
3829 | - aio_context_release(ctx_a); | ||
3830 | - } | ||
3831 | qemu_event_wait(&done_event); | ||
3832 | - if (drain_type != BDRV_DRAIN_ALL) { | ||
3833 | - aio_context_acquire(ctx_a); | ||
3834 | - } | ||
3835 | |||
3836 | g_assert_cmpint(aio_ret, ==, 0); | ||
3837 | do_drain_end(drain_type, bs); | ||
3838 | - | ||
3839 | - if (drain_type != BDRV_DRAIN_ALL) { | ||
3840 | - aio_context_release(ctx_a); | ||
3841 | - } | ||
3842 | break; | ||
3843 | case 1: | ||
3844 | co = qemu_coroutine_create(test_iothread_drain_co_entry, &data); | ||
3845 | @@ -XXX,XX +XXX,XX @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) | ||
3846 | g_assert_not_reached(); | ||
3847 | } | ||
3848 | |||
3849 | - aio_context_acquire(ctx_a); | ||
3850 | blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); | ||
3851 | - aio_context_release(ctx_a); | ||
3852 | |||
3853 | bdrv_unref(bs); | ||
3854 | blk_unref(blk); | ||
3855 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
3856 | BlockJob *job; | ||
3857 | TestBlockJob *tjob; | ||
3858 | IOThread *iothread = NULL; | ||
3859 | - AioContext *ctx; | ||
3860 | int ret; | ||
3861 | |||
3862 | src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, | ||
3863 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
3864 | } | ||
3865 | |||
3866 | if (use_iothread) { | ||
3867 | + AioContext *ctx; | ||
3868 | + | ||
3869 | iothread = iothread_new(); | ||
3870 | ctx = iothread_get_aio_context(iothread); | ||
3871 | blk_set_aio_context(blk_src, ctx, &error_abort); | ||
3872 | - } else { | ||
3873 | - ctx = qemu_get_aio_context(); | ||
3874 | } | ||
3875 | |||
3876 | target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, | ||
3877 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
3878 | blk_insert_bs(blk_target, target, &error_abort); | ||
3879 | blk_set_allow_aio_context_change(blk_target, true); | ||
3880 | |||
3881 | - aio_context_acquire(ctx); | ||
3882 | tjob = block_job_create("job0", &test_job_driver, NULL, src, | ||
3883 | 0, BLK_PERM_ALL, | ||
3884 | 0, 0, NULL, NULL, &error_abort); | ||
3885 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
3886 | tjob->prepare_ret = -EIO; | ||
3887 | break; | ||
3888 | } | ||
3889 | - aio_context_release(ctx); | ||
3890 | |||
3891 | job_start(&job->job); | ||
3892 | |||
3893 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
3894 | } | ||
3895 | g_assert_cmpint(ret, ==, (result == TEST_JOB_SUCCESS ? 0 : -EIO)); | ||
3896 | |||
3897 | - aio_context_acquire(ctx); | ||
3898 | if (use_iothread) { | ||
3899 | blk_set_aio_context(blk_src, qemu_get_aio_context(), &error_abort); | ||
3900 | assert(blk_get_aio_context(blk_target) == qemu_get_aio_context()); | ||
3901 | } | ||
3902 | - aio_context_release(ctx); | ||
3903 | |||
3904 | blk_unref(blk_src); | ||
3905 | blk_unref(blk_target); | ||
3906 | @@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void) | ||
3907 | g_assert_cmpint(base_s->drain_count, ==, 1); | ||
3908 | g_assert_cmpint(base->in_flight, ==, 0); | ||
3909 | |||
3910 | - aio_context_acquire(qemu_get_aio_context()); | ||
3911 | bdrv_append(overlay, base, &error_abort); | ||
3912 | - aio_context_release(qemu_get_aio_context()); | ||
3913 | |||
3914 | g_assert_cmpint(base->in_flight, ==, 0); | ||
3915 | g_assert_cmpint(overlay->in_flight, ==, 0); | ||
3916 | @@ -XXX,XX +XXX,XX @@ static void test_set_aio_context(void) | ||
3917 | |||
3918 | bdrv_drained_begin(bs); | ||
3919 | bdrv_try_change_aio_context(bs, ctx_a, NULL, &error_abort); | ||
3920 | - | ||
3921 | - aio_context_acquire(ctx_a); | ||
3922 | bdrv_drained_end(bs); | ||
3923 | |||
3924 | bdrv_drained_begin(bs); | ||
3925 | bdrv_try_change_aio_context(bs, ctx_b, NULL, &error_abort); | ||
3926 | - aio_context_release(ctx_a); | ||
3927 | - aio_context_acquire(ctx_b); | ||
3928 | bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort); | ||
3929 | - aio_context_release(ctx_b); | ||
3930 | bdrv_drained_end(bs); | ||
3931 | |||
3932 | bdrv_unref(bs); | ||
3933 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
3934 | index XXXXXXX..XXXXXXX 100644 | ||
3935 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
3936 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
3937 | @@ -XXX,XX +XXX,XX @@ static void test_update_perm_tree(void) | ||
3938 | BDRV_CHILD_DATA, &error_abort); | ||
3939 | bdrv_graph_wrunlock(); | ||
3940 | |||
3941 | - aio_context_acquire(qemu_get_aio_context()); | ||
3942 | ret = bdrv_append(filter, bs, NULL); | ||
3943 | g_assert_cmpint(ret, <, 0); | ||
3944 | - aio_context_release(qemu_get_aio_context()); | ||
3945 | |||
3946 | bdrv_unref(filter); | ||
3947 | blk_unref(root); | ||
3948 | @@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void) | ||
3949 | bdrv_attach_child(filter, target, "target", &child_of_bds, | ||
3950 | BDRV_CHILD_DATA, &error_abort); | ||
3951 | bdrv_graph_wrunlock(); | ||
3952 | - aio_context_acquire(qemu_get_aio_context()); | ||
3953 | bdrv_append(filter, bs, &error_abort); | ||
3954 | - aio_context_release(qemu_get_aio_context()); | ||
3955 | |||
3956 | bdrv_graph_rdlock_main_loop(); | ||
3957 | g_assert(target->backing->bs == bs); | ||
3958 | @@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void) | ||
3959 | &error_abort); | ||
3960 | bdrv_graph_wrunlock(); | ||
3961 | |||
3962 | - aio_context_acquire(qemu_get_aio_context()); | ||
3963 | bdrv_append(fl, base, &error_abort); | ||
3964 | - aio_context_release(qemu_get_aio_context()); | ||
3965 | bdrv_unref(fl); | ||
3966 | bdrv_unref(top); | ||
3967 | } | ||
3968 | diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c | ||
3969 | index XXXXXXX..XXXXXXX 100644 | ||
3970 | --- a/tests/unit/test-block-iothread.c | ||
3971 | +++ b/tests/unit/test-block-iothread.c | ||
3972 | @@ -XXX,XX +XXX,XX @@ static void test_sync_op(const void *opaque) | ||
3973 | bdrv_graph_rdunlock_main_loop(); | ||
3974 | |||
3975 | blk_set_aio_context(blk, ctx, &error_abort); | ||
3976 | - aio_context_acquire(ctx); | ||
3977 | if (t->fn) { | ||
3978 | t->fn(c); | ||
3979 | } | ||
3980 | @@ -XXX,XX +XXX,XX @@ static void test_sync_op(const void *opaque) | ||
3981 | t->blkfn(blk); | ||
3982 | } | ||
3983 | blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); | ||
3984 | - aio_context_release(ctx); | ||
3985 | |||
3986 | bdrv_unref(bs); | ||
3987 | blk_unref(blk); | ||
3988 | @@ -XXX,XX +XXX,XX @@ static void test_attach_blockjob(void) | ||
3989 | aio_poll(qemu_get_aio_context(), false); | ||
3990 | } | ||
3991 | |||
3992 | - aio_context_acquire(ctx); | ||
3993 | blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); | ||
3994 | - aio_context_release(ctx); | ||
3995 | |||
3996 | tjob->n = 0; | ||
3997 | while (tjob->n == 0) { | ||
3998 | @@ -XXX,XX +XXX,XX @@ static void test_attach_blockjob(void) | ||
3999 | WITH_JOB_LOCK_GUARD() { | ||
4000 | job_complete_sync_locked(&tjob->common.job, &error_abort); | ||
4001 | } | ||
4002 | - aio_context_acquire(ctx); | ||
4003 | blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); | ||
4004 | - aio_context_release(ctx); | ||
4005 | |||
4006 | bdrv_unref(bs); | ||
4007 | blk_unref(blk); | ||
4008 | @@ -XXX,XX +XXX,XX @@ static void test_propagate_basic(void) | ||
4009 | |||
4010 | /* Switch the AioContext back */ | ||
4011 | main_ctx = qemu_get_aio_context(); | ||
4012 | - aio_context_acquire(ctx); | ||
4013 | blk_set_aio_context(blk, main_ctx, &error_abort); | ||
4014 | - aio_context_release(ctx); | ||
4015 | g_assert(blk_get_aio_context(blk) == main_ctx); | ||
4016 | g_assert(bdrv_get_aio_context(bs_a) == main_ctx); | ||
4017 | g_assert(bdrv_get_aio_context(bs_verify) == main_ctx); | ||
4018 | @@ -XXX,XX +XXX,XX @@ static void test_propagate_diamond(void) | ||
4019 | |||
4020 | /* Switch the AioContext back */ | ||
4021 | main_ctx = qemu_get_aio_context(); | ||
4022 | - aio_context_acquire(ctx); | ||
4023 | blk_set_aio_context(blk, main_ctx, &error_abort); | ||
4024 | - aio_context_release(ctx); | ||
4025 | g_assert(blk_get_aio_context(blk) == main_ctx); | ||
4026 | g_assert(bdrv_get_aio_context(bs_verify) == main_ctx); | ||
4027 | g_assert(bdrv_get_aio_context(bs_a) == main_ctx); | ||
4028 | @@ -XXX,XX +XXX,XX @@ static void test_propagate_mirror(void) | ||
4029 | &error_abort); | ||
4030 | |||
4031 | /* Start a mirror job */ | ||
4032 | - aio_context_acquire(main_ctx); | ||
4033 | mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0, | ||
4034 | MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, | ||
4035 | BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, | ||
4036 | false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, | ||
4037 | &error_abort); | ||
4038 | - aio_context_release(main_ctx); | ||
4039 | |||
4040 | WITH_JOB_LOCK_GUARD() { | ||
4041 | job = job_get_locked("job0"); | ||
4042 | @@ -XXX,XX +XXX,XX @@ static void test_propagate_mirror(void) | ||
4043 | g_assert(job->aio_context == ctx); | ||
4044 | |||
4045 | /* Change the AioContext of target */ | ||
4046 | - aio_context_acquire(ctx); | ||
4047 | bdrv_try_change_aio_context(target, main_ctx, NULL, &error_abort); | ||
4048 | - aio_context_release(ctx); | ||
4049 | g_assert(bdrv_get_aio_context(src) == main_ctx); | ||
4050 | g_assert(bdrv_get_aio_context(target) == main_ctx); | ||
4051 | g_assert(bdrv_get_aio_context(filter) == main_ctx); | ||
4052 | @@ -XXX,XX +XXX,XX @@ static void test_propagate_mirror(void) | ||
4053 | g_assert(bdrv_get_aio_context(filter) == main_ctx); | ||
4054 | |||
4055 | /* ...unless we explicitly allow it */ | ||
4056 | - aio_context_acquire(ctx); | ||
4057 | blk_set_allow_aio_context_change(blk, true); | ||
4058 | bdrv_try_change_aio_context(target, ctx, NULL, &error_abort); | ||
4059 | - aio_context_release(ctx); | ||
4060 | |||
4061 | g_assert(blk_get_aio_context(blk) == ctx); | ||
4062 | g_assert(bdrv_get_aio_context(src) == ctx); | ||
4063 | @@ -XXX,XX +XXX,XX @@ static void test_propagate_mirror(void) | ||
4064 | |||
4065 | job_cancel_sync_all(); | ||
4066 | |||
4067 | - aio_context_acquire(ctx); | ||
4068 | blk_set_aio_context(blk, main_ctx, &error_abort); | ||
4069 | bdrv_try_change_aio_context(target, main_ctx, NULL, &error_abort); | ||
4070 | - aio_context_release(ctx); | ||
4071 | |||
4072 | blk_unref(blk); | ||
4073 | bdrv_unref(src); | ||
4074 | @@ -XXX,XX +XXX,XX @@ static void test_attach_second_node(void) | ||
4075 | BlockDriverState *bs, *filter; | ||
4076 | QDict *options; | ||
4077 | |||
4078 | - aio_context_acquire(main_ctx); | ||
4079 | blk = blk_new(ctx, BLK_PERM_ALL, BLK_PERM_ALL); | ||
4080 | bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); | ||
4081 | blk_insert_bs(blk, bs, &error_abort); | ||
4082 | @@ -XXX,XX +XXX,XX @@ static void test_attach_second_node(void) | ||
4083 | qdict_put_str(options, "file", "base"); | ||
4084 | |||
4085 | filter = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); | ||
4086 | - aio_context_release(main_ctx); | ||
4087 | |||
4088 | g_assert(blk_get_aio_context(blk) == ctx); | ||
4089 | g_assert(bdrv_get_aio_context(bs) == ctx); | ||
4090 | g_assert(bdrv_get_aio_context(filter) == ctx); | ||
4091 | |||
4092 | - aio_context_acquire(ctx); | ||
4093 | blk_set_aio_context(blk, main_ctx, &error_abort); | ||
4094 | - aio_context_release(ctx); | ||
4095 | g_assert(blk_get_aio_context(blk) == main_ctx); | ||
4096 | g_assert(bdrv_get_aio_context(bs) == main_ctx); | ||
4097 | g_assert(bdrv_get_aio_context(filter) == main_ctx); | ||
4098 | @@ -XXX,XX +XXX,XX @@ static void test_attach_preserve_blk_ctx(void) | ||
4099 | { | ||
4100 | IOThread *iothread = iothread_new(); | ||
4101 | AioContext *ctx = iothread_get_aio_context(iothread); | ||
4102 | - AioContext *main_ctx = qemu_get_aio_context(); | ||
4103 | BlockBackend *blk; | ||
4104 | BlockDriverState *bs; | ||
4105 | |||
4106 | - aio_context_acquire(main_ctx); | ||
4107 | blk = blk_new(ctx, BLK_PERM_ALL, BLK_PERM_ALL); | ||
4108 | bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); | ||
4109 | bs->total_sectors = 65536 / BDRV_SECTOR_SIZE; | ||
4110 | @@ -XXX,XX +XXX,XX @@ static void test_attach_preserve_blk_ctx(void) | ||
4111 | blk_insert_bs(blk, bs, &error_abort); | ||
4112 | g_assert(blk_get_aio_context(blk) == ctx); | ||
4113 | g_assert(bdrv_get_aio_context(bs) == ctx); | ||
4114 | - aio_context_release(main_ctx); | ||
4115 | |||
4116 | /* Remove the node again */ | ||
4117 | - aio_context_acquire(ctx); | ||
4118 | blk_remove_bs(blk); | ||
4119 | - aio_context_release(ctx); | ||
4120 | g_assert(blk_get_aio_context(blk) == ctx); | ||
4121 | g_assert(bdrv_get_aio_context(bs) == qemu_get_aio_context()); | ||
4122 | |||
4123 | /* Re-attach the node */ | ||
4124 | - aio_context_acquire(main_ctx); | ||
4125 | blk_insert_bs(blk, bs, &error_abort); | ||
4126 | - aio_context_release(main_ctx); | ||
4127 | g_assert(blk_get_aio_context(blk) == ctx); | ||
4128 | g_assert(bdrv_get_aio_context(bs) == ctx); | ||
4129 | |||
4130 | - aio_context_acquire(ctx); | ||
4131 | blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); | ||
4132 | - aio_context_release(ctx); | ||
4133 | bdrv_unref(bs); | ||
4134 | blk_unref(blk); | ||
4135 | } | ||
4136 | diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c | ||
4137 | index XXXXXXX..XXXXXXX 100644 | ||
4138 | --- a/tests/unit/test-blockjob.c | ||
4139 | +++ b/tests/unit/test-blockjob.c | ||
4140 | @@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s) | ||
4141 | BlockJob *job = &s->common; | ||
4142 | BlockBackend *blk = s->blk; | ||
4143 | JobStatus sts = job->job.status; | ||
4144 | - AioContext *ctx = job->job.aio_context; | ||
4145 | |||
4146 | job_cancel_sync(&job->job, true); | ||
4147 | WITH_JOB_LOCK_GUARD() { | ||
4148 | @@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s) | ||
4149 | job_unref_locked(&job->job); | ||
4150 | } | ||
4151 | |||
4152 | - aio_context_acquire(ctx); | ||
4153 | destroy_blk(blk); | ||
4154 | - aio_context_release(ctx); | ||
4155 | |||
4156 | } | ||
4157 | |||
4158 | @@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void) | ||
4159 | cancel_common(s); | ||
4160 | } | ||
4161 | |||
4162 | -/* (See test_yielding_driver for the job description) */ | ||
4163 | -typedef struct YieldingJob { | ||
4164 | - BlockJob common; | ||
4165 | - bool should_complete; | ||
4166 | -} YieldingJob; | ||
4167 | - | ||
4168 | -static void yielding_job_complete(Job *job, Error **errp) | ||
4169 | -{ | ||
4170 | - YieldingJob *s = container_of(job, YieldingJob, common.job); | ||
4171 | - s->should_complete = true; | ||
4172 | - job_enter(job); | ||
4173 | -} | ||
4174 | - | ||
4175 | -static int coroutine_fn yielding_job_run(Job *job, Error **errp) | ||
4176 | -{ | ||
4177 | - YieldingJob *s = container_of(job, YieldingJob, common.job); | ||
4178 | - | ||
4179 | - job_transition_to_ready(job); | ||
4180 | - | ||
4181 | - while (!s->should_complete) { | ||
4182 | - job_yield(job); | ||
4183 | - } | ||
4184 | - | ||
4185 | - return 0; | ||
4186 | -} | ||
4187 | - | ||
4188 | -/* | ||
4189 | - * This job transitions immediately to the READY state, and then | ||
4190 | - * yields until it is to complete. | ||
4191 | - */ | ||
4192 | -static const BlockJobDriver test_yielding_driver = { | ||
4193 | - .job_driver = { | ||
4194 | - .instance_size = sizeof(YieldingJob), | ||
4195 | - .free = block_job_free, | ||
4196 | - .user_resume = block_job_user_resume, | ||
4197 | - .run = yielding_job_run, | ||
4198 | - .complete = yielding_job_complete, | ||
4199 | - }, | ||
4200 | -}; | ||
4201 | - | ||
4202 | -/* | ||
4203 | - * Test that job_complete_locked() works even on jobs that are in a paused | ||
4204 | - * state (i.e., STANDBY). | ||
4205 | - * | ||
4206 | - * To do this, run YieldingJob in an IO thread, get it into the READY | ||
4207 | - * state, then have a drained section. Before ending the section, | ||
4208 | - * acquire the context so the job will not be entered and will thus | ||
4209 | - * remain on STANDBY. | ||
4210 | - * | ||
4211 | - * job_complete_locked() should still work without error. | ||
4212 | - * | ||
4213 | - * Note that on the QMP interface, it is impossible to lock an IO | ||
4214 | - * thread before a drained section ends. In practice, the | ||
4215 | - * bdrv_drain_all_end() and the aio_context_acquire() will be | ||
4216 | - * reversed. However, that makes for worse reproducibility here: | ||
4217 | - * Sometimes, the job would no longer be in STANDBY then but already | ||
4218 | - * be started. We cannot prevent that, because the IO thread runs | ||
4219 | - * concurrently. We can only prevent it by taking the lock before | ||
4220 | - * ending the drained section, so we do that. | ||
4221 | - * | ||
4222 | - * (You can reverse the order of operations and most of the time the | ||
4223 | - * test will pass, but sometimes the assert(status == STANDBY) will | ||
4224 | - * fail.) | ||
4225 | - */ | ||
4226 | -static void test_complete_in_standby(void) | ||
4227 | -{ | ||
4228 | - BlockBackend *blk; | ||
4229 | - IOThread *iothread; | ||
4230 | - AioContext *ctx; | ||
4231 | - Job *job; | ||
4232 | - BlockJob *bjob; | ||
4233 | - | ||
4234 | - /* Create a test drive, move it to an IO thread */ | ||
4235 | - blk = create_blk(NULL); | ||
4236 | - iothread = iothread_new(); | ||
4237 | - | ||
4238 | - ctx = iothread_get_aio_context(iothread); | ||
4239 | - blk_set_aio_context(blk, ctx, &error_abort); | ||
4240 | - | ||
4241 | - /* Create our test job */ | ||
4242 | - bjob = mk_job(blk, "job", &test_yielding_driver, true, | ||
4243 | - JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); | ||
4244 | - job = &bjob->job; | ||
4245 | - assert_job_status_is(job, JOB_STATUS_CREATED); | ||
4246 | - | ||
4247 | - /* Wait for the job to become READY */ | ||
4248 | - job_start(job); | ||
4249 | - /* | ||
4250 | - * Here we are waiting for the status to change, so don't bother | ||
4251 | - * protecting the read every time. | ||
4252 | - */ | ||
4253 | - AIO_WAIT_WHILE_UNLOCKED(ctx, job->status != JOB_STATUS_READY); | ||
4254 | - | ||
4255 | - /* Begin the drained section, pausing the job */ | ||
4256 | - bdrv_drain_all_begin(); | ||
4257 | - assert_job_status_is(job, JOB_STATUS_STANDBY); | ||
4258 | - | ||
4259 | - /* Lock the IO thread to prevent the job from being run */ | ||
4260 | - aio_context_acquire(ctx); | ||
4261 | - /* This will schedule the job to resume it */ | ||
4262 | - bdrv_drain_all_end(); | ||
4263 | - aio_context_release(ctx); | ||
4264 | - | ||
4265 | - WITH_JOB_LOCK_GUARD() { | ||
4266 | - /* But the job cannot run, so it will remain on standby */ | ||
4267 | - assert(job->status == JOB_STATUS_STANDBY); | ||
4268 | - | ||
4269 | - /* Even though the job is on standby, this should work */ | ||
4270 | - job_complete_locked(job, &error_abort); | ||
4271 | - | ||
4272 | - /* The test is done now, clean up. */ | ||
4273 | - job_finish_sync_locked(job, NULL, &error_abort); | ||
4274 | - assert(job->status == JOB_STATUS_PENDING); | ||
4275 | - | ||
4276 | - job_finalize_locked(job, &error_abort); | ||
4277 | - assert(job->status == JOB_STATUS_CONCLUDED); | ||
4278 | - | ||
4279 | - job_dismiss_locked(&job, &error_abort); | ||
4280 | - } | ||
4281 | - | ||
4282 | - aio_context_acquire(ctx); | ||
4283 | - destroy_blk(blk); | ||
4284 | - aio_context_release(ctx); | ||
4285 | - iothread_join(iothread); | ||
4286 | -} | ||
4287 | - | ||
4288 | int main(int argc, char **argv) | ||
4289 | { | ||
4290 | qemu_init_main_loop(&error_abort); | ||
4291 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
4292 | g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); | ||
4293 | g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); | ||
4294 | g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); | ||
4295 | - | ||
4296 | - /* | ||
4297 | - * This test is flaky and sometimes fails in CI and otherwise: | ||
4298 | - * don't run unless user opts in via environment variable. | ||
4299 | - */ | ||
4300 | - if (getenv("QEMU_TEST_FLAKY_TESTS")) { | ||
4301 | - g_test_add_func("/blockjob/complete_in_standby", test_complete_in_standby); | ||
4302 | - } | ||
4303 | return g_test_run(); | ||
4304 | } | ||
4305 | diff --git a/tests/unit/test-replication.c b/tests/unit/test-replication.c | ||
4306 | index XXXXXXX..XXXXXXX 100644 | ||
4307 | --- a/tests/unit/test-replication.c | ||
4308 | +++ b/tests/unit/test-replication.c | ||
4309 | @@ -XXX,XX +XXX,XX @@ static BlockBackend *start_primary(void) | ||
4310 | static void teardown_primary(void) | ||
4311 | { | ||
4312 | BlockBackend *blk; | ||
4313 | - AioContext *ctx; | ||
4314 | |||
4315 | /* remove P_ID */ | ||
4316 | blk = blk_by_name(P_ID); | ||
4317 | assert(blk); | ||
4318 | |||
4319 | - ctx = blk_get_aio_context(blk); | ||
4320 | - aio_context_acquire(ctx); | ||
4321 | monitor_remove_blk(blk); | ||
4322 | blk_unref(blk); | ||
4323 | - aio_context_release(ctx); | ||
4324 | } | ||
4325 | |||
4326 | static void test_primary_read(void) | ||
4327 | @@ -XXX,XX +XXX,XX @@ static void teardown_secondary(void) | ||
4328 | { | ||
4329 | /* only need to destroy two BBs */ | ||
4330 | BlockBackend *blk; | ||
4331 | - AioContext *ctx; | ||
4332 | |||
4333 | /* remove S_LOCAL_DISK_ID */ | ||
4334 | blk = blk_by_name(S_LOCAL_DISK_ID); | ||
4335 | assert(blk); | ||
4336 | |||
4337 | - ctx = blk_get_aio_context(blk); | ||
4338 | - aio_context_acquire(ctx); | ||
4339 | monitor_remove_blk(blk); | ||
4340 | blk_unref(blk); | ||
4341 | - aio_context_release(ctx); | ||
4342 | |||
4343 | /* remove S_ID */ | ||
4344 | blk = blk_by_name(S_ID); | ||
4345 | assert(blk); | ||
4346 | |||
4347 | - ctx = blk_get_aio_context(blk); | ||
4348 | - aio_context_acquire(ctx); | ||
4349 | monitor_remove_blk(blk); | ||
4350 | blk_unref(blk); | ||
4351 | - aio_context_release(ctx); | ||
4352 | } | ||
4353 | |||
4354 | static void test_secondary_read(void) | ||
4355 | diff --git a/util/async.c b/util/async.c | ||
4356 | index XXXXXXX..XXXXXXX 100644 | ||
4357 | --- a/util/async.c | ||
4358 | +++ b/util/async.c | ||
4359 | @@ -XXX,XX +XXX,XX @@ static void co_schedule_bh_cb(void *opaque) | ||
4360 | Coroutine *co = QSLIST_FIRST(&straight); | ||
4361 | QSLIST_REMOVE_HEAD(&straight, co_scheduled_next); | ||
4362 | trace_aio_co_schedule_bh_cb(ctx, co); | ||
4363 | - aio_context_acquire(ctx); | ||
4364 | |||
4365 | /* Protected by write barrier in qemu_aio_coroutine_enter */ | ||
4366 | qatomic_set(&co->scheduled, NULL); | ||
4367 | qemu_aio_coroutine_enter(ctx, co); | ||
4368 | - aio_context_release(ctx); | ||
4369 | } | ||
4370 | } | ||
4371 | |||
4372 | @@ -XXX,XX +XXX,XX @@ void aio_co_enter(AioContext *ctx, Coroutine *co) | ||
4373 | assert(self != co); | ||
4374 | QSIMPLEQ_INSERT_TAIL(&self->co_queue_wakeup, co, co_queue_next); | ||
4375 | } else { | ||
4376 | - aio_context_acquire(ctx); | ||
4377 | qemu_aio_coroutine_enter(ctx, co); | ||
4378 | - aio_context_release(ctx); | ||
4379 | } | ||
4380 | } | ||
4381 | |||
4382 | diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c | ||
4383 | index XXXXXXX..XXXXXXX 100644 | ||
4384 | --- a/util/vhost-user-server.c | ||
4385 | +++ b/util/vhost-user-server.c | ||
4386 | @@ -XXX,XX +XXX,XX @@ static void vu_accept(QIONetListener *listener, QIOChannelSocket *sioc, | ||
4387 | |||
4388 | qio_channel_set_follow_coroutine_ctx(server->ioc, true); | ||
4389 | |||
4390 | - /* Attaching the AioContext starts the vu_client_trip coroutine */ | ||
4391 | - aio_context_acquire(server->ctx); | ||
4392 | vhost_user_server_attach_aio_context(server, server->ctx); | ||
4393 | - aio_context_release(server->ctx); | ||
4394 | } | ||
4395 | |||
4396 | /* server->ctx acquired by caller */ | ||
4397 | diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py | ||
4398 | index XXXXXXX..XXXXXXX 100644 | ||
4399 | --- a/scripts/block-coroutine-wrapper.py | ||
4400 | +++ b/scripts/block-coroutine-wrapper.py | ||
4401 | @@ -XXX,XX +XXX,XX @@ def gen_no_co_wrapper(func: FuncDecl) -> str: | ||
4402 | static void {name}_bh(void *opaque) | ||
4403 | {{ | ||
4404 | {struct_name} *s = opaque; | ||
4405 | - AioContext *ctx = {func.gen_ctx('s->')}; | ||
4406 | |||
4407 | {graph_lock} | ||
4408 | - aio_context_acquire(ctx); | ||
4409 | {func.get_result}{name}({ func.gen_list('s->{name}') }); | ||
4410 | - aio_context_release(ctx); | ||
4411 | {graph_unlock} | ||
4412 | |||
4413 | aio_co_wake(s->co); | ||
4414 | diff --git a/tests/tsan/suppressions.tsan b/tests/tsan/suppressions.tsan | ||
4415 | index XXXXXXX..XXXXXXX 100644 | ||
4416 | --- a/tests/tsan/suppressions.tsan | ||
4417 | +++ b/tests/tsan/suppressions.tsan | ||
4418 | @@ -XXX,XX +XXX,XX @@ | ||
4419 | |||
4420 | # TSan reports a double lock on RECURSIVE mutexes. | ||
4421 | # Since the recursive lock is intentional, we choose to ignore it. | ||
4422 | -mutex:aio_context_acquire | ||
4423 | mutex:pthread_mutex_lock | ||
4424 | |||
4425 | # TSan reports a race between pthread_mutex_init() and | ||
4426 | -- | 301 | -- |
4427 | 2.43.0 | 302 | 2.48.1 | diff view generated by jsdifflib |
1 | bdrv_is_read_only() only checks if the node is configured to be | 1 | What we wanted to catch with the assertion is cases where the recursion |
---|---|---|---|
2 | read-only eventually, but even if it returns false, writing to the node | 2 | finds that a child was inactive before its parent. This should never |
3 | may not be permitted at the moment (because it's inactive). | 3 | happen. But if the user tries to inactivate an image that is already |
4 | 4 | inactive, that's harmless and we don't want to fail the assertion. | |
5 | bdrv_is_writable() checks that the node can be written to right now, and | ||
6 | this is what the snapshot operations really need. | ||
7 | |||
8 | Change bdrv_can_snapshot() to use bdrv_is_writable() to fix crashes like | ||
9 | the following: | ||
10 | |||
11 | $ ./qemu-system-x86_64 -hda /tmp/test.qcow2 -loadvm foo -incoming defer | ||
12 | qemu-system-x86_64: ../block/io.c:1990: int bdrv_co_write_req_prepare(BdrvChild *, int64_t, int64_t, BdrvTrackedRequest *, int): Assertion `!(bs->open_flags & BDRV_O_INACTIVE)' failed. | ||
13 | |||
14 | The resulting error message after this patch isn't perfect yet, but at | ||
15 | least it doesn't crash any more: | ||
16 | |||
17 | $ ./qemu-system-x86_64 -hda /tmp/test.qcow2 -loadvm foo -incoming defer | ||
18 | qemu-system-x86_64: Device 'ide0-hd0' is writable but does not support snapshots | ||
19 | 5 | ||
20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
21 | Message-ID: <20231201142520.32255-2-kwolf@redhat.com> | 7 | Acked-by: Fabiano Rosas <farosas@suse.de> |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
10 | Message-ID: <20250204211407.381505-3-kwolf@redhat.com> | ||
22 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
23 | --- | 12 | --- |
24 | block/snapshot.c | 4 +++- | 13 | block.c | 16 ++++++++++++---- |
25 | 1 file changed, 3 insertions(+), 1 deletion(-) | 14 | 1 file changed, 12 insertions(+), 4 deletions(-) |
26 | 15 | ||
27 | diff --git a/block/snapshot.c b/block/snapshot.c | 16 | diff --git a/block.c b/block.c |
28 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
29 | --- a/block/snapshot.c | 18 | --- a/block.c |
30 | +++ b/block/snapshot.c | 19 | +++ b/block.c |
31 | @@ -XXX,XX +XXX,XX @@ bdrv_snapshot_fallback(BlockDriverState *bs) | 20 | @@ -XXX,XX +XXX,XX @@ bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) |
32 | int bdrv_can_snapshot(BlockDriverState *bs) | 21 | return false; |
22 | } | ||
23 | |||
24 | -static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs) | ||
25 | +static int GRAPH_RDLOCK | ||
26 | +bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level) | ||
33 | { | 27 | { |
34 | BlockDriver *drv = bs->drv; | 28 | BdrvChild *child, *parent; |
35 | + | 29 | int ret; |
36 | GLOBAL_STATE_CODE(); | 30 | @@ -XXX,XX +XXX,XX @@ static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs) |
37 | - if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { | ||
38 | + | ||
39 | + if (!drv || !bdrv_is_inserted(bs) || !bdrv_is_writable(bs)) { | ||
40 | return 0; | 31 | return 0; |
41 | } | 32 | } |
42 | 33 | ||
34 | - assert(!(bs->open_flags & BDRV_O_INACTIVE)); | ||
35 | + /* | ||
36 | + * Inactivating an already inactive node on user request is harmless, but if | ||
37 | + * a child is already inactive before its parent, that's bad. | ||
38 | + */ | ||
39 | + if (bs->open_flags & BDRV_O_INACTIVE) { | ||
40 | + assert(top_level); | ||
41 | + return 0; | ||
42 | + } | ||
43 | |||
44 | /* Inactivate this node */ | ||
45 | if (bs->drv->bdrv_inactivate) { | ||
46 | @@ -XXX,XX +XXX,XX @@ static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs) | ||
47 | |||
48 | /* Recursively inactivate children */ | ||
49 | QLIST_FOREACH(child, &bs->children, next) { | ||
50 | - ret = bdrv_inactivate_recurse(child->bs); | ||
51 | + ret = bdrv_inactivate_recurse(child->bs, false); | ||
52 | if (ret < 0) { | ||
53 | return ret; | ||
54 | } | ||
55 | @@ -XXX,XX +XXX,XX @@ int bdrv_inactivate_all(void) | ||
56 | if (bdrv_has_bds_parent(bs, false)) { | ||
57 | continue; | ||
58 | } | ||
59 | - ret = bdrv_inactivate_recurse(bs); | ||
60 | + ret = bdrv_inactivate_recurse(bs, true); | ||
61 | if (ret < 0) { | ||
62 | bdrv_next_cleanup(&it); | ||
63 | break; | ||
43 | -- | 64 | -- |
44 | 2.43.0 | 65 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | Putting an active block node on top of an inactive one is strictly |
---|---|---|---|
2 | speaking an invalid configuration and the next patch will turn it into a | ||
3 | hard error. | ||
2 | 4 | ||
3 | Add the iothread-vq-mapping parameter to assign virtqueues to IOThreads. | 5 | However, taking a snapshot while disk images are inactive after |
4 | Store the vq:AioContext mapping in the new struct | 6 | completing migration has an important use case: After migrating to a |
5 | VirtIOBlockDataPlane->vq_aio_context[] field and refactor the code to | 7 | file, taking an external snapshot is what is needed to take a full VM |
6 | use the per-vq AioContext instead of the BlockDriverState's AioContext. | 8 | snapshot. |
7 | 9 | ||
8 | Reimplement --device virtio-blk-pci,iothread= and non-IOThread mode by | 10 | In order for this to keep working after the later patches, change |
9 | assigning all virtqueues to the IOThread and main loop's AioContext in | 11 | creating a snapshot such that it automatically inactivates an overlay |
10 | vq_aio_context[], respectively. | 12 | that is added on top of an already inactive node. |
11 | 13 | ||
12 | The comment in struct VirtIOBlockDataPlane about EventNotifiers is | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | stale. Remove it. | 15 | Acked-by: Fabiano Rosas <farosas@suse.de> |
14 | 16 | Reviewed-by: Eric Blake <eblake@redhat.com> | |
15 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 17 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
16 | Message-ID: <20231220134755.814917-5-stefanha@redhat.com> | 18 | Message-ID: <20250204211407.381505-4-kwolf@redhat.com> |
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
18 | --- | 20 | --- |
19 | hw/block/dataplane/virtio-blk.h | 3 + | 21 | blockdev.c | 16 ++++++++++++++++ |
20 | include/hw/virtio/virtio-blk.h | 2 + | 22 | 1 file changed, 16 insertions(+) |
21 | hw/block/dataplane/virtio-blk.c | 155 ++++++++++++++++++++++++-------- | ||
22 | hw/block/virtio-blk.c | 92 ++++++++++++++++--- | ||
23 | 4 files changed, 202 insertions(+), 50 deletions(-) | ||
24 | 23 | ||
25 | diff --git a/hw/block/dataplane/virtio-blk.h b/hw/block/dataplane/virtio-blk.h | 24 | diff --git a/blockdev.c b/blockdev.c |
26 | index XXXXXXX..XXXXXXX 100644 | 25 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/hw/block/dataplane/virtio-blk.h | 26 | --- a/blockdev.c |
28 | +++ b/hw/block/dataplane/virtio-blk.h | 27 | +++ b/blockdev.c |
29 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq); | 28 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action, |
30 | int virtio_blk_data_plane_start(VirtIODevice *vdev); | ||
31 | void virtio_blk_data_plane_stop(VirtIODevice *vdev); | ||
32 | |||
33 | +void virtio_blk_data_plane_detach(VirtIOBlockDataPlane *s); | ||
34 | +void virtio_blk_data_plane_attach(VirtIOBlockDataPlane *s); | ||
35 | + | ||
36 | #endif /* HW_DATAPLANE_VIRTIO_BLK_H */ | ||
37 | diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h | ||
38 | index XXXXXXX..XXXXXXX 100644 | ||
39 | --- a/include/hw/virtio/virtio-blk.h | ||
40 | +++ b/include/hw/virtio/virtio-blk.h | ||
41 | @@ -XXX,XX +XXX,XX @@ | ||
42 | #include "sysemu/block-backend.h" | ||
43 | #include "sysemu/block-ram-registrar.h" | ||
44 | #include "qom/object.h" | ||
45 | +#include "qapi/qapi-types-virtio.h" | ||
46 | |||
47 | #define TYPE_VIRTIO_BLK "virtio-blk-device" | ||
48 | OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlock, VIRTIO_BLK) | ||
49 | @@ -XXX,XX +XXX,XX @@ struct VirtIOBlkConf | ||
50 | { | ||
51 | BlockConf conf; | ||
52 | IOThread *iothread; | ||
53 | + IOThreadVirtQueueMappingList *iothread_vq_mapping_list; | ||
54 | char *serial; | ||
55 | uint32_t request_merging; | ||
56 | uint16_t num_queues; | ||
57 | diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c | ||
58 | index XXXXXXX..XXXXXXX 100644 | ||
59 | --- a/hw/block/dataplane/virtio-blk.c | ||
60 | +++ b/hw/block/dataplane/virtio-blk.c | ||
61 | @@ -XXX,XX +XXX,XX @@ struct VirtIOBlockDataPlane { | ||
62 | VirtIOBlkConf *conf; | ||
63 | VirtIODevice *vdev; | ||
64 | |||
65 | - /* Note that these EventNotifiers are assigned by value. This is | ||
66 | - * fine as long as you do not call event_notifier_cleanup on them | ||
67 | - * (because you don't own the file descriptor or handle; you just | ||
68 | - * use it). | ||
69 | + /* | ||
70 | + * The AioContext for each virtqueue. The BlockDriverState will use the | ||
71 | + * first element as its AioContext. | ||
72 | */ | ||
73 | - IOThread *iothread; | ||
74 | - AioContext *ctx; | ||
75 | + AioContext **vq_aio_context; | ||
76 | }; | ||
77 | |||
78 | /* Raise an interrupt to signal guest, if necessary */ | ||
79 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq) | ||
80 | virtio_notify_irqfd(s->vdev, vq); | ||
81 | } | ||
82 | |||
83 | +/* Generate vq:AioContext mappings from a validated iothread-vq-mapping list */ | ||
84 | +static void | ||
85 | +apply_vq_mapping(IOThreadVirtQueueMappingList *iothread_vq_mapping_list, | ||
86 | + AioContext **vq_aio_context, uint16_t num_queues) | ||
87 | +{ | ||
88 | + IOThreadVirtQueueMappingList *node; | ||
89 | + size_t num_iothreads = 0; | ||
90 | + size_t cur_iothread = 0; | ||
91 | + | ||
92 | + for (node = iothread_vq_mapping_list; node; node = node->next) { | ||
93 | + num_iothreads++; | ||
94 | + } | ||
95 | + | ||
96 | + for (node = iothread_vq_mapping_list; node; node = node->next) { | ||
97 | + IOThread *iothread = iothread_by_id(node->value->iothread); | ||
98 | + AioContext *ctx = iothread_get_aio_context(iothread); | ||
99 | + | ||
100 | + /* Released in virtio_blk_data_plane_destroy() */ | ||
101 | + object_ref(OBJECT(iothread)); | ||
102 | + | ||
103 | + if (node->value->vqs) { | ||
104 | + uint16List *vq; | ||
105 | + | ||
106 | + /* Explicit vq:IOThread assignment */ | ||
107 | + for (vq = node->value->vqs; vq; vq = vq->next) { | ||
108 | + vq_aio_context[vq->value] = ctx; | ||
109 | + } | ||
110 | + } else { | ||
111 | + /* Round-robin vq:IOThread assignment */ | ||
112 | + for (unsigned i = cur_iothread; i < num_queues; | ||
113 | + i += num_iothreads) { | ||
114 | + vq_aio_context[i] = ctx; | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | + cur_iothread++; | ||
119 | + } | ||
120 | +} | ||
121 | + | ||
122 | /* Context: QEMU global mutex held */ | ||
123 | bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, | ||
124 | VirtIOBlockDataPlane **dataplane, | ||
125 | @@ -XXX,XX +XXX,XX @@ bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, | ||
126 | |||
127 | *dataplane = NULL; | ||
128 | |||
129 | - if (conf->iothread) { | ||
130 | + if (conf->iothread || conf->iothread_vq_mapping_list) { | ||
131 | if (!k->set_guest_notifiers || !k->ioeventfd_assign) { | ||
132 | error_setg(errp, | ||
133 | "device is incompatible with iothread " | ||
134 | @@ -XXX,XX +XXX,XX @@ bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, | ||
135 | s = g_new0(VirtIOBlockDataPlane, 1); | ||
136 | s->vdev = vdev; | ||
137 | s->conf = conf; | ||
138 | + s->vq_aio_context = g_new(AioContext *, conf->num_queues); | ||
139 | + | ||
140 | + if (conf->iothread_vq_mapping_list) { | ||
141 | + apply_vq_mapping(conf->iothread_vq_mapping_list, s->vq_aio_context, | ||
142 | + conf->num_queues); | ||
143 | + } else if (conf->iothread) { | ||
144 | + AioContext *ctx = iothread_get_aio_context(conf->iothread); | ||
145 | + for (unsigned i = 0; i < conf->num_queues; i++) { | ||
146 | + s->vq_aio_context[i] = ctx; | ||
147 | + } | ||
148 | |||
149 | - if (conf->iothread) { | ||
150 | - s->iothread = conf->iothread; | ||
151 | - object_ref(OBJECT(s->iothread)); | ||
152 | - s->ctx = iothread_get_aio_context(s->iothread); | ||
153 | + /* Released in virtio_blk_data_plane_destroy() */ | ||
154 | + object_ref(OBJECT(conf->iothread)); | ||
155 | } else { | ||
156 | - s->ctx = qemu_get_aio_context(); | ||
157 | + AioContext *ctx = qemu_get_aio_context(); | ||
158 | + for (unsigned i = 0; i < conf->num_queues; i++) { | ||
159 | + s->vq_aio_context[i] = ctx; | ||
160 | + } | ||
161 | } | ||
162 | |||
163 | *dataplane = s; | ||
164 | @@ -XXX,XX +XXX,XX @@ bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, | ||
165 | void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) | ||
166 | { | ||
167 | VirtIOBlock *vblk; | ||
168 | + VirtIOBlkConf *conf = s->conf; | ||
169 | |||
170 | if (!s) { | ||
171 | return; | ||
172 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) | ||
173 | |||
174 | vblk = VIRTIO_BLK(s->vdev); | ||
175 | assert(!vblk->dataplane_started); | ||
176 | - if (s->iothread) { | ||
177 | - object_unref(OBJECT(s->iothread)); | ||
178 | + | ||
179 | + if (conf->iothread_vq_mapping_list) { | ||
180 | + IOThreadVirtQueueMappingList *node; | ||
181 | + | ||
182 | + for (node = conf->iothread_vq_mapping_list; node; node = node->next) { | ||
183 | + IOThread *iothread = iothread_by_id(node->value->iothread); | ||
184 | + object_unref(OBJECT(iothread)); | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
188 | + if (conf->iothread) { | ||
189 | + object_unref(OBJECT(conf->iothread)); | ||
190 | } | ||
191 | + | ||
192 | + g_free(s->vq_aio_context); | ||
193 | g_free(s); | ||
194 | } | ||
195 | |||
196 | @@ -XXX,XX +XXX,XX @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) | ||
197 | |||
198 | trace_virtio_blk_data_plane_start(s); | ||
199 | |||
200 | - r = blk_set_aio_context(s->conf->conf.blk, s->ctx, &local_err); | ||
201 | + r = blk_set_aio_context(s->conf->conf.blk, s->vq_aio_context[0], | ||
202 | + &local_err); | ||
203 | if (r < 0) { | ||
204 | error_report_err(local_err); | ||
205 | goto fail_aio_context; | ||
206 | } | ||
207 | |||
208 | - /* Kick right away to begin processing requests already in vring */ | ||
209 | - for (i = 0; i < nvqs; i++) { | ||
210 | - VirtQueue *vq = virtio_get_queue(s->vdev, i); | ||
211 | - | ||
212 | - event_notifier_set(virtio_queue_get_host_notifier(vq)); | ||
213 | - } | ||
214 | - | ||
215 | /* | ||
216 | * These fields must be visible to the IOThread when it processes the | ||
217 | * virtqueue, otherwise it will think dataplane has not started yet. | ||
218 | @@ -XXX,XX +XXX,XX @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) | ||
219 | if (!blk_in_drain(s->conf->conf.blk)) { | ||
220 | for (i = 0; i < nvqs; i++) { | ||
221 | VirtQueue *vq = virtio_get_queue(s->vdev, i); | ||
222 | + AioContext *ctx = s->vq_aio_context[i]; | ||
223 | |||
224 | - virtio_queue_aio_attach_host_notifier(vq, s->ctx); | ||
225 | + /* Kick right away to begin processing requests already in vring */ | ||
226 | + event_notifier_set(virtio_queue_get_host_notifier(vq)); | ||
227 | + | ||
228 | + virtio_queue_aio_attach_host_notifier(vq, ctx); | ||
229 | } | ||
230 | } | ||
231 | return 0; | ||
232 | @@ -XXX,XX +XXX,XX @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) | ||
233 | * | ||
234 | * Context: BH in IOThread | ||
235 | */ | ||
236 | -static void virtio_blk_data_plane_stop_bh(void *opaque) | ||
237 | +static void virtio_blk_data_plane_stop_vq_bh(void *opaque) | ||
238 | { | ||
239 | - VirtIOBlockDataPlane *s = opaque; | ||
240 | - unsigned i; | ||
241 | - | ||
242 | - for (i = 0; i < s->conf->num_queues; i++) { | ||
243 | - VirtQueue *vq = virtio_get_queue(s->vdev, i); | ||
244 | - EventNotifier *host_notifier = virtio_queue_get_host_notifier(vq); | ||
245 | + VirtQueue *vq = opaque; | ||
246 | + EventNotifier *host_notifier = virtio_queue_get_host_notifier(vq); | ||
247 | |||
248 | - virtio_queue_aio_detach_host_notifier(vq, s->ctx); | ||
249 | + virtio_queue_aio_detach_host_notifier(vq, qemu_get_current_aio_context()); | ||
250 | |||
251 | - /* | ||
252 | - * Test and clear notifier after disabling event, in case poll callback | ||
253 | - * didn't have time to run. | ||
254 | - */ | ||
255 | - virtio_queue_host_notifier_read(host_notifier); | ||
256 | - } | ||
257 | + /* | ||
258 | + * Test and clear notifier after disabling event, in case poll callback | ||
259 | + * didn't have time to run. | ||
260 | + */ | ||
261 | + virtio_queue_host_notifier_read(host_notifier); | ||
262 | } | ||
263 | |||
264 | /* Context: QEMU global mutex held */ | ||
265 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) | ||
266 | trace_virtio_blk_data_plane_stop(s); | ||
267 | |||
268 | if (!blk_in_drain(s->conf->conf.blk)) { | ||
269 | - aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s); | ||
270 | + for (i = 0; i < nvqs; i++) { | ||
271 | + VirtQueue *vq = virtio_get_queue(s->vdev, i); | ||
272 | + AioContext *ctx = s->vq_aio_context[i]; | ||
273 | + | ||
274 | + aio_wait_bh_oneshot(ctx, virtio_blk_data_plane_stop_vq_bh, vq); | ||
275 | + } | ||
276 | } | ||
277 | |||
278 | /* | ||
279 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) | ||
280 | |||
281 | s->stopping = false; | ||
282 | } | ||
283 | + | ||
284 | +void virtio_blk_data_plane_detach(VirtIOBlockDataPlane *s) | ||
285 | +{ | ||
286 | + VirtIODevice *vdev = VIRTIO_DEVICE(s->vdev); | ||
287 | + | ||
288 | + for (uint16_t i = 0; i < s->conf->num_queues; i++) { | ||
289 | + VirtQueue *vq = virtio_get_queue(vdev, i); | ||
290 | + virtio_queue_aio_detach_host_notifier(vq, s->vq_aio_context[i]); | ||
291 | + } | ||
292 | +} | ||
293 | + | ||
294 | +void virtio_blk_data_plane_attach(VirtIOBlockDataPlane *s) | ||
295 | +{ | ||
296 | + VirtIODevice *vdev = VIRTIO_DEVICE(s->vdev); | ||
297 | + | ||
298 | + for (uint16_t i = 0; i < s->conf->num_queues; i++) { | ||
299 | + VirtQueue *vq = virtio_get_queue(vdev, i); | ||
300 | + virtio_queue_aio_attach_host_notifier(vq, s->vq_aio_context[i]); | ||
301 | + } | ||
302 | +} | ||
303 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
304 | index XXXXXXX..XXXXXXX 100644 | ||
305 | --- a/hw/block/virtio-blk.c | ||
306 | +++ b/hw/block/virtio-blk.c | ||
307 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) | ||
308 | return; | ||
309 | } | ||
310 | } | ||
311 | + | ||
312 | virtio_blk_handle_vq(s, vq); | ||
313 | } | ||
314 | |||
315 | @@ -XXX,XX +XXX,XX @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | +static bool | ||
320 | +validate_iothread_vq_mapping_list(IOThreadVirtQueueMappingList *list, | ||
321 | + uint16_t num_queues, Error **errp) | ||
322 | +{ | ||
323 | + g_autofree unsigned long *vqs = bitmap_new(num_queues); | ||
324 | + g_autoptr(GHashTable) iothreads = | ||
325 | + g_hash_table_new(g_str_hash, g_str_equal); | ||
326 | + | ||
327 | + for (IOThreadVirtQueueMappingList *node = list; node; node = node->next) { | ||
328 | + const char *name = node->value->iothread; | ||
329 | + uint16List *vq; | ||
330 | + | ||
331 | + if (!iothread_by_id(name)) { | ||
332 | + error_setg(errp, "IOThread \"%s\" object does not exist", name); | ||
333 | + return false; | ||
334 | + } | ||
335 | + | ||
336 | + if (!g_hash_table_add(iothreads, (gpointer)name)) { | ||
337 | + error_setg(errp, | ||
338 | + "duplicate IOThread name \"%s\" in iothread-vq-mapping", | ||
339 | + name); | ||
340 | + return false; | ||
341 | + } | ||
342 | + | ||
343 | + if (node != list) { | ||
344 | + if (!!node->value->vqs != !!list->value->vqs) { | ||
345 | + error_setg(errp, "either all items in iothread-vq-mapping " | ||
346 | + "must have vqs or none of them must have it"); | ||
347 | + return false; | ||
348 | + } | ||
349 | + } | ||
350 | + | ||
351 | + for (vq = node->value->vqs; vq; vq = vq->next) { | ||
352 | + if (vq->value >= num_queues) { | ||
353 | + error_setg(errp, "vq index %u for IOThread \"%s\" must be " | ||
354 | + "less than num_queues %u in iothread-vq-mapping", | ||
355 | + vq->value, name, num_queues); | ||
356 | + return false; | ||
357 | + } | ||
358 | + | ||
359 | + if (test_and_set_bit(vq->value, vqs)) { | ||
360 | + error_setg(errp, "cannot assign vq %u to IOThread \"%s\" " | ||
361 | + "because it is already assigned", vq->value, name); | ||
362 | + return false; | ||
363 | + } | ||
364 | + } | ||
365 | + } | ||
366 | + | ||
367 | + if (list->value->vqs) { | ||
368 | + for (uint16_t i = 0; i < num_queues; i++) { | ||
369 | + if (!test_bit(i, vqs)) { | ||
370 | + error_setg(errp, | ||
371 | + "missing vq %u IOThread assignment in iothread-vq-mapping", | ||
372 | + i); | ||
373 | + return false; | ||
374 | + } | ||
375 | + } | ||
376 | + } | ||
377 | + | ||
378 | + return true; | ||
379 | +} | ||
380 | + | ||
381 | static void virtio_resize_cb(void *opaque) | ||
382 | { | ||
383 | VirtIODevice *vdev = opaque; | ||
384 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_resize(void *opaque) | ||
385 | static void virtio_blk_drained_begin(void *opaque) | ||
386 | { | ||
387 | VirtIOBlock *s = opaque; | ||
388 | - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); | ||
389 | - AioContext *ctx = blk_get_aio_context(s->conf.conf.blk); | ||
390 | |||
391 | if (!s->dataplane || !s->dataplane_started) { | ||
392 | return; | 29 | return; |
393 | } | 30 | } |
394 | 31 | ||
395 | - for (uint16_t i = 0; i < s->conf.num_queues; i++) { | 32 | + /* |
396 | - VirtQueue *vq = virtio_get_queue(vdev, i); | 33 | + * Older QEMU versions have allowed adding an active parent node to an |
397 | - virtio_queue_aio_detach_host_notifier(vq, ctx); | 34 | + * inactive child node. This is unsafe in the general case, but there is an |
398 | - } | 35 | + * important use case, which is taking a VM snapshot with migration to file |
399 | + virtio_blk_data_plane_detach(s->dataplane); | 36 | + * and then adding an external snapshot while the VM is still stopped and |
400 | } | 37 | + * images are inactive. Requiring the user to explicitly create the overlay |
401 | 38 | + * as inactive would break compatibility, so just do it automatically here | |
402 | /* Resume virtqueue ioeventfd processing after drain */ | 39 | + * to keep this working. |
403 | static void virtio_blk_drained_end(void *opaque) | 40 | + */ |
404 | { | 41 | + if (bdrv_is_inactive(state->old_bs) && !bdrv_is_inactive(state->new_bs)) { |
405 | VirtIOBlock *s = opaque; | 42 | + ret = bdrv_inactivate(state->new_bs, errp); |
406 | - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); | 43 | + if (ret < 0) { |
407 | - AioContext *ctx = blk_get_aio_context(s->conf.conf.blk); | ||
408 | |||
409 | if (!s->dataplane || !s->dataplane_started) { | ||
410 | return; | ||
411 | } | ||
412 | |||
413 | - for (uint16_t i = 0; i < s->conf.num_queues; i++) { | ||
414 | - VirtQueue *vq = virtio_get_queue(vdev, i); | ||
415 | - virtio_queue_aio_attach_host_notifier(vq, ctx); | ||
416 | - } | ||
417 | + virtio_blk_data_plane_attach(s->dataplane); | ||
418 | } | ||
419 | |||
420 | static const BlockDevOps virtio_block_ops = { | ||
421 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) | ||
422 | return; | ||
423 | } | ||
424 | |||
425 | + if (conf->iothread_vq_mapping_list) { | ||
426 | + if (conf->iothread) { | ||
427 | + error_setg(errp, "iothread and iothread-vq-mapping properties " | ||
428 | + "cannot be set at the same time"); | ||
429 | + return; | ||
430 | + } | ||
431 | + | ||
432 | + if (!validate_iothread_vq_mapping_list(conf->iothread_vq_mapping_list, | ||
433 | + conf->num_queues, errp)) { | ||
434 | + return; | 44 | + return; |
435 | + } | 45 | + } |
436 | + } | 46 | + } |
437 | + | 47 | + |
438 | s->config_size = virtio_get_config_size(&virtio_blk_cfg_size_params, | 48 | ret = bdrv_append(state->new_bs, state->old_bs, errp); |
439 | s->host_features); | 49 | if (ret < 0) { |
440 | virtio_init(vdev, VIRTIO_ID_BLOCK, s->config_size); | 50 | return; |
441 | @@ -XXX,XX +XXX,XX @@ static Property virtio_blk_properties[] = { | ||
442 | DEFINE_PROP_BOOL("seg-max-adjust", VirtIOBlock, conf.seg_max_adjust, true), | ||
443 | DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, | ||
444 | IOThread *), | ||
445 | + DEFINE_PROP_IOTHREAD_VQ_MAPPING_LIST("iothread-vq-mapping", VirtIOBlock, | ||
446 | + conf.iothread_vq_mapping_list), | ||
447 | DEFINE_PROP_BIT64("discard", VirtIOBlock, host_features, | ||
448 | VIRTIO_BLK_F_DISCARD, true), | ||
449 | DEFINE_PROP_BOOL("report-discard-granularity", VirtIOBlock, | ||
450 | -- | 51 | -- |
451 | 2.43.0 | 52 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | Block devices have an individual active state, a single global flag |
---|---|---|---|
2 | can't cover this correctly. This becomes more important as we allow | ||
3 | users to manually manage which nodes are active or inactive. | ||
2 | 4 | ||
3 | Stop depending on the AioContext lock and instead access | 5 | Now that it's allowed to call bdrv_inactivate_all() even when some |
4 | SCSIDevice->requests from only one thread at a time: | 6 | nodes are already inactive, we can remove the flag and just |
5 | - When the VM is running only the BlockBackend's AioContext may access | 7 | unconditionally call bdrv_inactivate_all() and, more importantly, |
6 | the requests list. | 8 | bdrv_activate_all() before we make use of the nodes. |
7 | - When the VM is stopped only the main loop may access the requests | ||
8 | list. | ||
9 | 9 | ||
10 | These constraints protect the requests list without the need for locking | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | in the I/O code path. | 11 | Acked-by: Fabiano Rosas <farosas@suse.de> |
12 | |||
13 | Note that multiple IOThreads are not supported yet because the code | ||
14 | assumes all SCSIRequests are executed from a single AioContext. Leave | ||
15 | that as future work. | ||
16 | |||
17 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
18 | Reviewed-by: Eric Blake <eblake@redhat.com> | 12 | Reviewed-by: Eric Blake <eblake@redhat.com> |
19 | Message-ID: <20231204164259.1515217-2-stefanha@redhat.com> | 13 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
14 | Message-ID: <20250204211407.381505-5-kwolf@redhat.com> | ||
20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
21 | --- | 16 | --- |
22 | include/hw/scsi/scsi.h | 7 +- | 17 | migration/migration.h | 3 --- |
23 | hw/scsi/scsi-bus.c | 181 ++++++++++++++++++++++++++++------------- | 18 | migration/block-active.c | 46 ---------------------------------------- |
24 | 2 files changed, 131 insertions(+), 57 deletions(-) | 19 | migration/migration.c | 8 ------- |
20 | 3 files changed, 57 deletions(-) | ||
25 | 21 | ||
26 | diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h | 22 | diff --git a/migration/migration.h b/migration/migration.h |
27 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
28 | --- a/include/hw/scsi/scsi.h | 24 | --- a/migration/migration.h |
29 | +++ b/include/hw/scsi/scsi.h | 25 | +++ b/migration/migration.h |
30 | @@ -XXX,XX +XXX,XX @@ struct SCSIDevice | 26 | @@ -XXX,XX +XXX,XX @@ void migration_bitmap_sync_precopy(bool last_stage); |
31 | { | 27 | void dirty_bitmap_mig_init(void); |
32 | DeviceState qdev; | 28 | bool should_send_vmdesc(void); |
33 | VMChangeStateEntry *vmsentry; | 29 | |
34 | - QEMUBH *bh; | 30 | -/* migration/block-active.c */ |
35 | uint32_t id; | 31 | -void migration_block_active_setup(bool active); |
36 | BlockConf conf; | 32 | - |
37 | SCSISense unit_attention; | 33 | #endif |
38 | bool sense_is_ua; | 34 | diff --git a/migration/block-active.c b/migration/block-active.c |
39 | uint8_t sense[SCSI_SENSE_BUF_SIZE]; | ||
40 | uint32_t sense_len; | ||
41 | + | ||
42 | + /* | ||
43 | + * The requests list is only accessed from the AioContext that executes | ||
44 | + * requests or from the main loop when IOThread processing is stopped. | ||
45 | + */ | ||
46 | QTAILQ_HEAD(, SCSIRequest) requests; | ||
47 | + | ||
48 | uint32_t channel; | ||
49 | uint32_t lun; | ||
50 | int blocksize; | ||
51 | diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c | ||
52 | index XXXXXXX..XXXXXXX 100644 | 35 | index XXXXXXX..XXXXXXX 100644 |
53 | --- a/hw/scsi/scsi-bus.c | 36 | --- a/migration/block-active.c |
54 | +++ b/hw/scsi/scsi-bus.c | 37 | +++ b/migration/block-active.c |
55 | @@ -XXX,XX +XXX,XX @@ SCSIDevice *scsi_device_get(SCSIBus *bus, int channel, int id, int lun) | 38 | @@ -XXX,XX +XXX,XX @@ |
56 | return d; | 39 | #include "qemu/error-report.h" |
57 | } | 40 | #include "trace.h" |
58 | 41 | ||
59 | +/* | 42 | -/* |
60 | + * Invoke @fn() for each enqueued request in device @s. Must be called from the | 43 | - * Migration-only cache to remember the block layer activation status. |
61 | + * main loop thread while the guest is stopped. This is only suitable for | 44 | - * Protected by BQL. |
62 | + * vmstate ->put(), use scsi_device_for_each_req_async() for other cases. | 45 | - * |
63 | + */ | 46 | - * We need this because.. |
64 | +static void scsi_device_for_each_req_sync(SCSIDevice *s, | 47 | - * |
65 | + void (*fn)(SCSIRequest *, void *), | 48 | - * - Migration can fail after block devices are invalidated (during |
66 | + void *opaque) | 49 | - * switchover phase). When that happens, we need to be able to recover |
67 | +{ | 50 | - * the block drive status by re-activating them. |
68 | + SCSIRequest *req; | 51 | - * |
69 | + SCSIRequest *next_req; | 52 | - * - Currently bdrv_inactivate_all() is not safe to be invoked on top of |
70 | + | 53 | - * invalidated drives (even if bdrv_activate_all() is actually safe to be |
71 | + assert(!runstate_is_running()); | 54 | - * called any time!). It means remembering this could help migration to |
72 | + assert(qemu_in_main_thread()); | 55 | - * make sure it won't invalidate twice in a row, crashing QEMU. It can |
73 | + | 56 | - * happen when we migrate a PAUSED VM from host1 to host2, then migrate |
74 | + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) { | 57 | - * again to host3 without starting it. TODO: a cleaner solution is to |
75 | + fn(req, opaque); | 58 | - * allow safe invoke of bdrv_inactivate_all() at anytime, like |
76 | + } | 59 | - * bdrv_activate_all(). |
77 | +} | 60 | - * |
78 | + | 61 | - * For freshly started QEMU, the flag is initialized to TRUE reflecting the |
79 | +typedef struct { | 62 | - * scenario where QEMU owns block device ownerships. |
80 | + SCSIDevice *s; | 63 | - * |
81 | + void (*fn)(SCSIRequest *, void *); | 64 | - * For incoming QEMU taking a migration stream, the flag is initialized to |
82 | + void *fn_opaque; | 65 | - * FALSE reflecting that the incoming side doesn't own the block devices, |
83 | +} SCSIDeviceForEachReqAsyncData; | 66 | - * not until switchover happens. |
84 | + | 67 | - */ |
85 | +static void scsi_device_for_each_req_async_bh(void *opaque) | 68 | -static bool migration_block_active; |
86 | +{ | ||
87 | + g_autofree SCSIDeviceForEachReqAsyncData *data = opaque; | ||
88 | + SCSIDevice *s = data->s; | ||
89 | + AioContext *ctx; | ||
90 | + SCSIRequest *req; | ||
91 | + SCSIRequest *next; | ||
92 | + | ||
93 | + /* | ||
94 | + * If the AioContext changed before this BH was called then reschedule into | ||
95 | + * the new AioContext before accessing ->requests. This can happen when | ||
96 | + * scsi_device_for_each_req_async() is called and then the AioContext is | ||
97 | + * changed before BHs are run. | ||
98 | + */ | ||
99 | + ctx = blk_get_aio_context(s->conf.blk); | ||
100 | + if (ctx != qemu_get_current_aio_context()) { | ||
101 | + aio_bh_schedule_oneshot(ctx, scsi_device_for_each_req_async_bh, | ||
102 | + g_steal_pointer(&data)); | ||
103 | + return; | ||
104 | + } | ||
105 | + | ||
106 | + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { | ||
107 | + data->fn(req, data->fn_opaque); | ||
108 | + } | ||
109 | + | ||
110 | + /* Drop the reference taken by scsi_device_for_each_req_async() */ | ||
111 | + object_unref(OBJECT(s)); | ||
112 | +} | ||
113 | + | ||
114 | +/* | ||
115 | + * Schedule @fn() to be invoked for each enqueued request in device @s. @fn() | ||
116 | + * runs in the AioContext that is executing the request. | ||
117 | + */ | ||
118 | +static void scsi_device_for_each_req_async(SCSIDevice *s, | ||
119 | + void (*fn)(SCSIRequest *, void *), | ||
120 | + void *opaque) | ||
121 | +{ | ||
122 | + assert(qemu_in_main_thread()); | ||
123 | + | ||
124 | + SCSIDeviceForEachReqAsyncData *data = | ||
125 | + g_new(SCSIDeviceForEachReqAsyncData, 1); | ||
126 | + | ||
127 | + data->s = s; | ||
128 | + data->fn = fn; | ||
129 | + data->fn_opaque = opaque; | ||
130 | + | ||
131 | + /* | ||
132 | + * Hold a reference to the SCSIDevice until | ||
133 | + * scsi_device_for_each_req_async_bh() finishes. | ||
134 | + */ | ||
135 | + object_ref(OBJECT(s)); | ||
136 | + | ||
137 | + aio_bh_schedule_oneshot(blk_get_aio_context(s->conf.blk), | ||
138 | + scsi_device_for_each_req_async_bh, | ||
139 | + data); | ||
140 | +} | ||
141 | + | ||
142 | static void scsi_device_realize(SCSIDevice *s, Error **errp) | ||
143 | { | ||
144 | SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); | ||
145 | @@ -XXX,XX +XXX,XX @@ void scsi_bus_init_named(SCSIBus *bus, size_t bus_size, DeviceState *host, | ||
146 | qbus_set_bus_hotplug_handler(BUS(bus)); | ||
147 | } | ||
148 | |||
149 | -static void scsi_dma_restart_bh(void *opaque) | ||
150 | +void scsi_req_retry(SCSIRequest *req) | ||
151 | { | ||
152 | - SCSIDevice *s = opaque; | ||
153 | - SCSIRequest *req, *next; | ||
154 | - | 69 | - |
155 | - qemu_bh_delete(s->bh); | 70 | -/* Setup the disk activation status */ |
156 | - s->bh = NULL; | 71 | -void migration_block_active_setup(bool active) |
157 | + req->retry = true; | 72 | -{ |
158 | +} | 73 | - migration_block_active = active; |
159 | |||
160 | - aio_context_acquire(blk_get_aio_context(s->conf.blk)); | ||
161 | - QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { | ||
162 | - scsi_req_ref(req); | ||
163 | - if (req->retry) { | ||
164 | - req->retry = false; | ||
165 | - switch (req->cmd.mode) { | ||
166 | +/* Called in the AioContext that is executing the request */ | ||
167 | +static void scsi_dma_restart_req(SCSIRequest *req, void *opaque) | ||
168 | +{ | ||
169 | + scsi_req_ref(req); | ||
170 | + if (req->retry) { | ||
171 | + req->retry = false; | ||
172 | + switch (req->cmd.mode) { | ||
173 | case SCSI_XFER_FROM_DEV: | ||
174 | case SCSI_XFER_TO_DEV: | ||
175 | scsi_req_continue(req); | ||
176 | @@ -XXX,XX +XXX,XX @@ static void scsi_dma_restart_bh(void *opaque) | ||
177 | scsi_req_dequeue(req); | ||
178 | scsi_req_enqueue(req); | ||
179 | break; | ||
180 | - } | ||
181 | } | ||
182 | - scsi_req_unref(req); | ||
183 | } | ||
184 | - aio_context_release(blk_get_aio_context(s->conf.blk)); | ||
185 | - /* Drop the reference that was acquired in scsi_dma_restart_cb */ | ||
186 | - object_unref(OBJECT(s)); | ||
187 | -} | 74 | -} |
188 | - | 75 | - |
189 | -void scsi_req_retry(SCSIRequest *req) | 76 | bool migration_block_activate(Error **errp) |
190 | -{ | 77 | { |
191 | - /* No need to save a reference, because scsi_dma_restart_bh just | 78 | ERRP_GUARD(); |
192 | - * looks at the request list. */ | 79 | |
193 | - req->retry = true; | 80 | assert(bql_locked()); |
194 | + scsi_req_unref(req); | 81 | |
82 | - if (migration_block_active) { | ||
83 | - trace_migration_block_activation("active-skipped"); | ||
84 | - return true; | ||
85 | - } | ||
86 | - | ||
87 | trace_migration_block_activation("active"); | ||
88 | |||
89 | bdrv_activate_all(errp); | ||
90 | @@ -XXX,XX +XXX,XX @@ bool migration_block_activate(Error **errp) | ||
91 | return false; | ||
92 | } | ||
93 | |||
94 | - migration_block_active = true; | ||
95 | return true; | ||
195 | } | 96 | } |
196 | 97 | ||
197 | static void scsi_dma_restart_cb(void *opaque, bool running, RunState state) | 98 | @@ -XXX,XX +XXX,XX @@ bool migration_block_inactivate(void) |
198 | { | 99 | |
199 | SCSIDevice *s = opaque; | 100 | assert(bql_locked()); |
200 | 101 | ||
201 | + assert(qemu_in_main_thread()); | 102 | - if (!migration_block_active) { |
202 | + | 103 | - trace_migration_block_activation("inactive-skipped"); |
203 | if (!running) { | 104 | - return true; |
105 | - } | ||
106 | - | ||
107 | trace_migration_block_activation("inactive"); | ||
108 | |||
109 | ret = bdrv_inactivate_all(); | ||
110 | @@ -XXX,XX +XXX,XX @@ bool migration_block_inactivate(void) | ||
111 | return false; | ||
112 | } | ||
113 | |||
114 | - migration_block_active = false; | ||
115 | return true; | ||
116 | } | ||
117 | diff --git a/migration/migration.c b/migration/migration.c | ||
118 | index XXXXXXX..XXXXXXX 100644 | ||
119 | --- a/migration/migration.c | ||
120 | +++ b/migration/migration.c | ||
121 | @@ -XXX,XX +XXX,XX @@ void qmp_migrate_incoming(const char *uri, bool has_channels, | ||
204 | return; | 122 | return; |
205 | } | 123 | } |
206 | - if (!s->bh) { | 124 | |
207 | - AioContext *ctx = blk_get_aio_context(s->conf.blk); | 125 | - /* |
208 | - /* The reference is dropped in scsi_dma_restart_bh.*/ | 126 | - * Newly setup incoming QEMU. Mark the block active state to reflect |
209 | - object_ref(OBJECT(s)); | 127 | - * that the src currently owns the disks. |
210 | - s->bh = aio_bh_new_guarded(ctx, scsi_dma_restart_bh, s, | 128 | - */ |
211 | - &DEVICE(s)->mem_reentrancy_guard); | 129 | - migration_block_active_setup(false); |
212 | - qemu_bh_schedule(s->bh); | 130 | - |
213 | - } | 131 | once = false; |
214 | + | ||
215 | + scsi_device_for_each_req_async(s, scsi_dma_restart_req, NULL); | ||
216 | } | 132 | } |
217 | 133 | ||
218 | static bool scsi_bus_is_address_free(SCSIBus *bus, | 134 | @@ -XXX,XX +XXX,XX @@ static void migration_instance_init(Object *obj) |
219 | @@ -XXX,XX +XXX,XX @@ void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense) | 135 | ms->state = MIGRATION_STATUS_NONE; |
220 | } | 136 | ms->mbps = -1; |
221 | } | 137 | ms->pages_per_second = -1; |
222 | 138 | - /* Freshly started QEMU owns all the block devices */ | |
223 | +static void scsi_device_purge_one_req(SCSIRequest *req, void *opaque) | 139 | - migration_block_active_setup(true); |
224 | +{ | 140 | qemu_sem_init(&ms->pause_sem, 0); |
225 | + scsi_req_cancel_async(req, NULL); | 141 | qemu_mutex_init(&ms->error_mutex); |
226 | +} | ||
227 | + | ||
228 | void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) | ||
229 | { | ||
230 | - SCSIRequest *req; | ||
231 | + scsi_device_for_each_req_async(sdev, scsi_device_purge_one_req, NULL); | ||
232 | |||
233 | aio_context_acquire(blk_get_aio_context(sdev->conf.blk)); | ||
234 | - while (!QTAILQ_EMPTY(&sdev->requests)) { | ||
235 | - req = QTAILQ_FIRST(&sdev->requests); | ||
236 | - scsi_req_cancel_async(req, NULL); | ||
237 | - } | ||
238 | blk_drain(sdev->conf.blk); | ||
239 | aio_context_release(blk_get_aio_context(sdev->conf.blk)); | ||
240 | scsi_device_set_ua(sdev, sense); | ||
241 | @@ -XXX,XX +XXX,XX @@ static char *scsibus_get_fw_dev_path(DeviceState *dev) | ||
242 | |||
243 | /* SCSI request list. For simplicity, pv points to the whole device */ | ||
244 | |||
245 | +static void put_scsi_req(SCSIRequest *req, void *opaque) | ||
246 | +{ | ||
247 | + QEMUFile *f = opaque; | ||
248 | + | ||
249 | + assert(!req->io_canceled); | ||
250 | + assert(req->status == -1 && req->host_status == -1); | ||
251 | + assert(req->enqueued); | ||
252 | + | ||
253 | + qemu_put_sbyte(f, req->retry ? 1 : 2); | ||
254 | + qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); | ||
255 | + qemu_put_be32s(f, &req->tag); | ||
256 | + qemu_put_be32s(f, &req->lun); | ||
257 | + if (req->bus->info->save_request) { | ||
258 | + req->bus->info->save_request(f, req); | ||
259 | + } | ||
260 | + if (req->ops->save_request) { | ||
261 | + req->ops->save_request(f, req); | ||
262 | + } | ||
263 | +} | ||
264 | + | ||
265 | static int put_scsi_requests(QEMUFile *f, void *pv, size_t size, | ||
266 | const VMStateField *field, JSONWriter *vmdesc) | ||
267 | { | ||
268 | SCSIDevice *s = pv; | ||
269 | - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); | ||
270 | - SCSIRequest *req; | ||
271 | |||
272 | - QTAILQ_FOREACH(req, &s->requests, next) { | ||
273 | - assert(!req->io_canceled); | ||
274 | - assert(req->status == -1 && req->host_status == -1); | ||
275 | - assert(req->enqueued); | ||
276 | - | ||
277 | - qemu_put_sbyte(f, req->retry ? 1 : 2); | ||
278 | - qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); | ||
279 | - qemu_put_be32s(f, &req->tag); | ||
280 | - qemu_put_be32s(f, &req->lun); | ||
281 | - if (bus->info->save_request) { | ||
282 | - bus->info->save_request(f, req); | ||
283 | - } | ||
284 | - if (req->ops->save_request) { | ||
285 | - req->ops->save_request(f, req); | ||
286 | - } | ||
287 | - } | ||
288 | + scsi_device_for_each_req_sync(s, put_scsi_req, f); | ||
289 | qemu_put_sbyte(f, 0); | ||
290 | - | ||
291 | return 0; | ||
292 | } | ||
293 | 142 | ||
294 | -- | 143 | -- |
295 | 2.43.0 | 144 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | An active node makes unrestricted use of its children and would possibly |
---|---|---|---|
2 | run into assertion failures when it operates on an inactive child node. | ||
2 | 3 | ||
3 | s->rq is accessed from IO_CODE and GLOBAL_STATE_CODE. Introduce a lock | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | to protect s->rq and eliminate reliance on the AioContext lock. | 5 | Acked-by: Fabiano Rosas <farosas@suse.de> |
5 | |||
6 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
7 | Message-ID: <20230914140101.1065008-3-stefanha@redhat.com> | ||
8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | Reviewed-by: Eric Blake <eblake@redhat.com> | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
10 | Reviewed-by: Michael S. Tsirkin <mst@redhat.com> | 7 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
8 | Message-ID: <20250204211407.381505-6-kwolf@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 10 | --- |
13 | include/hw/virtio/virtio-blk.h | 3 +- | 11 | block.c | 5 +++++ |
14 | hw/block/virtio-blk.c | 67 +++++++++++++++++++++++----------- | 12 | 1 file changed, 5 insertions(+) |
15 | 2 files changed, 47 insertions(+), 23 deletions(-) | ||
16 | 13 | ||
17 | diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h | 14 | diff --git a/block.c b/block.c |
18 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/include/hw/virtio/virtio-blk.h | 16 | --- a/block.c |
20 | +++ b/include/hw/virtio/virtio-blk.h | 17 | +++ b/block.c |
21 | @@ -XXX,XX +XXX,XX @@ struct VirtIOBlockReq; | 18 | @@ -XXX,XX +XXX,XX @@ bdrv_attach_child_noperm(BlockDriverState *parent_bs, |
22 | struct VirtIOBlock { | 19 | child_bs->node_name, child_name, parent_bs->node_name); |
23 | VirtIODevice parent_obj; | 20 | return NULL; |
24 | BlockBackend *blk; | 21 | } |
25 | - void *rq; | 22 | + if (bdrv_is_inactive(child_bs) && !bdrv_is_inactive(parent_bs)) { |
26 | + QemuMutex rq_lock; | 23 | + error_setg(errp, "Inactive '%s' can't be a %s child of active '%s'", |
27 | + void *rq; /* protected by rq_lock */ | 24 | + child_bs->node_name, child_name, parent_bs->node_name); |
28 | VirtIOBlkConf conf; | 25 | + return NULL; |
29 | unsigned short sector_mask; | ||
30 | bool original_wce; | ||
31 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/hw/block/virtio-blk.c | ||
34 | +++ b/hw/block/virtio-blk.c | ||
35 | @@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, | ||
36 | /* Break the link as the next request is going to be parsed from the | ||
37 | * ring again. Otherwise we may end up doing a double completion! */ | ||
38 | req->mr_next = NULL; | ||
39 | - req->next = s->rq; | ||
40 | - s->rq = req; | ||
41 | + | ||
42 | + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { | ||
43 | + req->next = s->rq; | ||
44 | + s->rq = req; | ||
45 | + } | ||
46 | } else if (action == BLOCK_ERROR_ACTION_REPORT) { | ||
47 | virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); | ||
48 | if (acct_failed) { | ||
49 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_dma_restart_bh(void *opaque) | ||
50 | { | ||
51 | VirtIOBlock *s = opaque; | ||
52 | |||
53 | - VirtIOBlockReq *req = s->rq; | ||
54 | + VirtIOBlockReq *req; | ||
55 | MultiReqBuffer mrb = {}; | ||
56 | |||
57 | - s->rq = NULL; | ||
58 | + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { | ||
59 | + req = s->rq; | ||
60 | + s->rq = NULL; | ||
61 | + } | 26 | + } |
62 | 27 | ||
63 | aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | 28 | bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); |
64 | while (req) { | 29 | bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL, |
65 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_reset(VirtIODevice *vdev) | ||
66 | AioContext *ctx; | ||
67 | VirtIOBlockReq *req; | ||
68 | |||
69 | + /* Dataplane has stopped... */ | ||
70 | + assert(!s->dataplane_started); | ||
71 | + | ||
72 | + /* ...but requests may still be in flight. */ | ||
73 | ctx = blk_get_aio_context(s->blk); | ||
74 | aio_context_acquire(ctx); | ||
75 | blk_drain(s->blk); | ||
76 | + aio_context_release(ctx); | ||
77 | |||
78 | /* We drop queued requests after blk_drain() because blk_drain() itself can | ||
79 | * produce them. */ | ||
80 | - while (s->rq) { | ||
81 | - req = s->rq; | ||
82 | - s->rq = req->next; | ||
83 | - virtqueue_detach_element(req->vq, &req->elem, 0); | ||
84 | - virtio_blk_free_request(req); | ||
85 | - } | ||
86 | + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { | ||
87 | + while (s->rq) { | ||
88 | + req = s->rq; | ||
89 | + s->rq = req->next; | ||
90 | |||
91 | - aio_context_release(ctx); | ||
92 | + /* No other threads can access req->vq here */ | ||
93 | + virtqueue_detach_element(req->vq, &req->elem, 0); | ||
94 | + | ||
95 | + virtio_blk_free_request(req); | ||
96 | + } | ||
97 | + } | ||
98 | |||
99 | - assert(!s->dataplane_started); | ||
100 | blk_set_enable_write_cache(s->blk, s->original_wce); | ||
101 | } | ||
102 | |||
103 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) | ||
104 | static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) | ||
105 | { | ||
106 | VirtIOBlock *s = VIRTIO_BLK(vdev); | ||
107 | - VirtIOBlockReq *req = s->rq; | ||
108 | |||
109 | - while (req) { | ||
110 | - qemu_put_sbyte(f, 1); | ||
111 | + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { | ||
112 | + VirtIOBlockReq *req = s->rq; | ||
113 | |||
114 | - if (s->conf.num_queues > 1) { | ||
115 | - qemu_put_be32(f, virtio_get_queue_index(req->vq)); | ||
116 | - } | ||
117 | + while (req) { | ||
118 | + qemu_put_sbyte(f, 1); | ||
119 | |||
120 | - qemu_put_virtqueue_element(vdev, f, &req->elem); | ||
121 | - req = req->next; | ||
122 | + if (s->conf.num_queues > 1) { | ||
123 | + qemu_put_be32(f, virtio_get_queue_index(req->vq)); | ||
124 | + } | ||
125 | + | ||
126 | + qemu_put_virtqueue_element(vdev, f, &req->elem); | ||
127 | + req = req->next; | ||
128 | + } | ||
129 | } | ||
130 | + | ||
131 | qemu_put_sbyte(f, 0); | ||
132 | } | ||
133 | |||
134 | @@ -XXX,XX +XXX,XX @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, | ||
135 | |||
136 | req = qemu_get_virtqueue_element(vdev, f, sizeof(VirtIOBlockReq)); | ||
137 | virtio_blk_init_request(s, virtio_get_queue(vdev, vq_idx), req); | ||
138 | - req->next = s->rq; | ||
139 | - s->rq = req; | ||
140 | + | ||
141 | + WITH_QEMU_LOCK_GUARD(&s->rq_lock) { | ||
142 | + req->next = s->rq; | ||
143 | + s->rq = req; | ||
144 | + } | ||
145 | } | ||
146 | |||
147 | return 0; | ||
148 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) | ||
149 | s->host_features); | ||
150 | virtio_init(vdev, VIRTIO_ID_BLOCK, s->config_size); | ||
151 | |||
152 | + qemu_mutex_init(&s->rq_lock); | ||
153 | + | ||
154 | s->blk = conf->conf.blk; | ||
155 | s->rq = NULL; | ||
156 | s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1; | ||
157 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_unrealize(DeviceState *dev) | ||
158 | virtio_del_queue(vdev, i); | ||
159 | } | ||
160 | qemu_coroutine_dec_pool_size(conf->num_queues * conf->queue_size / 2); | ||
161 | + qemu_mutex_destroy(&s->rq_lock); | ||
162 | blk_ram_registrar_destroy(&s->blk_ram_registrar); | ||
163 | qemu_del_vm_change_state_handler(s->change); | ||
164 | blockdev_mark_auto_del(s->blk); | ||
165 | -- | 30 | -- |
166 | 2.43.0 | 31 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | In order for block_resize to fail gracefully on an inactive node instead |
---|---|---|---|
2 | of crashing with an assertion failure in bdrv_co_write_req_prepare() | ||
3 | (called from bdrv_co_truncate()), we need to check for inactive nodes | ||
4 | also when they are attached as a root node and make sure that | ||
5 | BLK_PERM_RESIZE isn't among the permissions allowed for inactive nodes. | ||
6 | To this effect, don't enumerate the permissions that are incompatible | ||
7 | with inactive nodes any more, but allow only BLK_PERM_CONSISTENT_READ | ||
8 | for them. | ||
2 | 9 | ||
3 | The AioContext lock no longer exists. | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | 11 | Acked-by: Fabiano Rosas <farosas@suse.de> | |
5 | There is one noteworthy change: | ||
6 | |||
7 | - * More specifically, these functions use BDRV_POLL_WHILE(bs), which | ||
8 | - * requires the caller to be either in the main thread and hold | ||
9 | - * the BlockdriverState (bs) AioContext lock, or directly in the | ||
10 | - * home thread that runs the bs AioContext. Calling them from | ||
11 | - * another thread in another AioContext would cause deadlocks. | ||
12 | + * More specifically, these functions use BDRV_POLL_WHILE(bs), which requires | ||
13 | + * the caller to be either in the main thread or directly in the home thread | ||
14 | + * that runs the bs AioContext. Calling them from another thread in another | ||
15 | + * AioContext would cause deadlocks. | ||
16 | |||
17 | I am not sure whether deadlocks are still possible. Maybe they have just | ||
18 | moved to the fine-grained locks that have replaced the AioContext. Since | ||
19 | I am not sure if the deadlocks are gone, I have kept the substance | ||
20 | unchanged and just removed mention of the AioContext. | ||
21 | |||
22 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
23 | Reviewed-by: Eric Blake <eblake@redhat.com> | 12 | Reviewed-by: Eric Blake <eblake@redhat.com> |
24 | Message-ID: <20231205182011.1976568-15-stefanha@redhat.com> | 13 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
25 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 14 | Message-ID: <20250204211407.381505-7-kwolf@redhat.com> |
26 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
27 | --- | 16 | --- |
28 | include/block/block-common.h | 3 -- | 17 | block.c | 7 +++++++ |
29 | include/block/block-io.h | 9 ++-- | 18 | block/block-backend.c | 2 +- |
30 | include/block/block_int-common.h | 2 - | 19 | 2 files changed, 8 insertions(+), 1 deletion(-) |
31 | block.c | 73 ++++++---------------------- | ||
32 | block/block-backend.c | 8 --- | ||
33 | block/export/vhost-user-blk-server.c | 4 -- | ||
34 | tests/qemu-iotests/202 | 2 +- | ||
35 | tests/qemu-iotests/203 | 3 +- | ||
36 | 8 files changed, 22 insertions(+), 82 deletions(-) | ||
37 | 20 | ||
38 | diff --git a/include/block/block-common.h b/include/block/block-common.h | ||
39 | index XXXXXXX..XXXXXXX 100644 | ||
40 | --- a/include/block/block-common.h | ||
41 | +++ b/include/block/block-common.h | ||
42 | @@ -XXX,XX +XXX,XX @@ | ||
43 | * automatically takes the graph rdlock when calling the wrapped function. In | ||
44 | * the same way, no_co_wrapper_bdrv_wrlock functions automatically take the | ||
45 | * graph wrlock. | ||
46 | - * | ||
47 | - * If the first parameter of the function is a BlockDriverState, BdrvChild or | ||
48 | - * BlockBackend pointer, the AioContext lock for it is taken in the wrapper. | ||
49 | */ | ||
50 | #define no_co_wrapper | ||
51 | #define no_co_wrapper_bdrv_rdlock | ||
52 | diff --git a/include/block/block-io.h b/include/block/block-io.h | ||
53 | index XXXXXXX..XXXXXXX 100644 | ||
54 | --- a/include/block/block-io.h | ||
55 | +++ b/include/block/block-io.h | ||
56 | @@ -XXX,XX +XXX,XX @@ bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, | ||
57 | * "I/O or GS" API functions. These functions can run without | ||
58 | * the BQL, but only in one specific iothread/main loop. | ||
59 | * | ||
60 | - * More specifically, these functions use BDRV_POLL_WHILE(bs), which | ||
61 | - * requires the caller to be either in the main thread and hold | ||
62 | - * the BlockdriverState (bs) AioContext lock, or directly in the | ||
63 | - * home thread that runs the bs AioContext. Calling them from | ||
64 | - * another thread in another AioContext would cause deadlocks. | ||
65 | + * More specifically, these functions use BDRV_POLL_WHILE(bs), which requires | ||
66 | + * the caller to be either in the main thread or directly in the home thread | ||
67 | + * that runs the bs AioContext. Calling them from another thread in another | ||
68 | + * AioContext would cause deadlocks. | ||
69 | * | ||
70 | * Therefore, these functions are not proper I/O, because they | ||
71 | * can't run in *any* iothreads, but only in a specific one. | ||
72 | diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h | ||
73 | index XXXXXXX..XXXXXXX 100644 | ||
74 | --- a/include/block/block_int-common.h | ||
75 | +++ b/include/block/block_int-common.h | ||
76 | @@ -XXX,XX +XXX,XX @@ struct BlockDriverState { | ||
77 | /* The error object in use for blocking operations on backing_hd */ | ||
78 | Error *backing_blocker; | ||
79 | |||
80 | - /* Protected by AioContext lock */ | ||
81 | - | ||
82 | /* | ||
83 | * If we are reading a disk image, give its size in sectors. | ||
84 | * Generally read-only; it is written to by load_snapshot and | ||
85 | diff --git a/block.c b/block.c | 21 | diff --git a/block.c b/block.c |
86 | index XXXXXXX..XXXXXXX 100644 | 22 | index XXXXXXX..XXXXXXX 100644 |
87 | --- a/block.c | 23 | --- a/block.c |
88 | +++ b/block.c | 24 | +++ b/block.c |
89 | @@ -XXX,XX +XXX,XX @@ out: | ||
90 | g_free(gen_node_name); | ||
91 | } | ||
92 | |||
93 | -/* | ||
94 | - * The caller must always hold @bs AioContext lock, because this function calls | ||
95 | - * bdrv_refresh_total_sectors() which polls when called from non-coroutine | ||
96 | - * context. | ||
97 | - */ | ||
98 | static int no_coroutine_fn GRAPH_UNLOCKED | ||
99 | bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, | ||
100 | QDict *options, int open_flags, Error **errp) | ||
101 | @@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) | ||
102 | * Replaces the node that a BdrvChild points to without updating permissions. | ||
103 | * | ||
104 | * If @new_bs is non-NULL, the parent of @child must already be drained through | ||
105 | - * @child and the caller must hold the AioContext lock for @new_bs. | ||
106 | + * @child. | ||
107 | */ | ||
108 | static void GRAPH_WRLOCK | ||
109 | bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) | ||
110 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_attach_child_common_drv = { | ||
111 | * | ||
112 | * Returns new created child. | ||
113 | * | ||
114 | - * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and | ||
115 | - * @child_bs can move to a different AioContext in this function. Callers must | ||
116 | - * make sure that their AioContext locking is still correct after this. | ||
117 | + * Both @parent_bs and @child_bs can move to a different AioContext in this | ||
118 | + * function. | ||
119 | */ | ||
120 | static BdrvChild * GRAPH_WRLOCK | ||
121 | bdrv_attach_child_common(BlockDriverState *child_bs, | ||
122 | @@ -XXX,XX +XXX,XX @@ bdrv_attach_child_common(BlockDriverState *child_bs, | 25 | @@ -XXX,XX +XXX,XX @@ bdrv_attach_child_common(BlockDriverState *child_bs, |
123 | /* | 26 | assert(child_class->get_parent_desc); |
124 | * Function doesn't update permissions, caller is responsible for this. | 27 | GLOBAL_STATE_CODE(); |
125 | * | 28 | |
126 | - * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and | 29 | + if (bdrv_is_inactive(child_bs) && (perm & ~BLK_PERM_CONSISTENT_READ)) { |
127 | - * @child_bs can move to a different AioContext in this function. Callers must | 30 | + g_autofree char *perm_names = bdrv_perm_names(perm); |
128 | - * make sure that their AioContext locking is still correct after this. | 31 | + error_setg(errp, "Permission '%s' unavailable on inactive node", |
129 | + * Both @parent_bs and @child_bs can move to a different AioContext in this | 32 | + perm_names); |
130 | + * function. | 33 | + return NULL; |
131 | * | 34 | + } |
132 | * After calling this function, the transaction @tran may only be completed | 35 | + |
133 | * while holding a writer lock for the graph. | 36 | new_child = g_new(BdrvChild, 1); |
134 | @@ -XXX,XX +XXX,XX @@ bdrv_attach_child_noperm(BlockDriverState *parent_bs, | 37 | *new_child = (BdrvChild) { |
135 | * | 38 | .bs = NULL, |
136 | * On failure NULL is returned, errp is set and the reference to | ||
137 | * child_bs is also dropped. | ||
138 | - * | ||
139 | - * The caller must hold the AioContext lock @child_bs, but not that of @ctx | ||
140 | - * (unless @child_bs is already in @ctx). | ||
141 | */ | ||
142 | BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
143 | const char *child_name, | ||
144 | @@ -XXX,XX +XXX,XX @@ out: | ||
145 | * | ||
146 | * On failure NULL is returned, errp is set and the reference to | ||
147 | * child_bs is also dropped. | ||
148 | - * | ||
149 | - * If @parent_bs and @child_bs are in different AioContexts, the caller must | ||
150 | - * hold the AioContext lock for @child_bs, but not for @parent_bs. | ||
151 | */ | ||
152 | BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
153 | BlockDriverState *child_bs, | ||
154 | @@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) | ||
155 | * | ||
156 | * Function doesn't update permissions, caller is responsible for this. | ||
157 | * | ||
158 | - * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and | ||
159 | - * @child_bs can move to a different AioContext in this function. Callers must | ||
160 | - * make sure that their AioContext locking is still correct after this. | ||
161 | + * Both @parent_bs and @child_bs can move to a different AioContext in this | ||
162 | + * function. | ||
163 | * | ||
164 | * After calling this function, the transaction @tran may only be completed | ||
165 | * while holding a writer lock for the graph. | ||
166 | @@ -XXX,XX +XXX,XX @@ out: | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | - * The caller must hold the AioContext lock for @backing_hd. Both @bs and | ||
171 | - * @backing_hd can move to a different AioContext in this function. Callers must | ||
172 | - * make sure that their AioContext locking is still correct after this. | ||
173 | + * Both @bs and @backing_hd can move to a different AioContext in this | ||
174 | + * function. | ||
175 | * | ||
176 | * If a backing child is already present (i.e. we're detaching a node), that | ||
177 | * child node must be drained. | ||
178 | @@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||
179 | * itself, all options starting with "${bdref_key}." are considered part of the | ||
180 | * BlockdevRef. | ||
181 | * | ||
182 | - * The caller must hold the main AioContext lock. | ||
183 | - * | ||
184 | * TODO Can this be unified with bdrv_open_image()? | ||
185 | */ | ||
186 | int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | ||
187 | @@ -XXX,XX +XXX,XX @@ done: | ||
188 | * | ||
189 | * The BlockdevRef will be removed from the options QDict. | ||
190 | * | ||
191 | - * The caller must hold the lock of the main AioContext and no other AioContext. | ||
192 | - * @parent can move to a different AioContext in this function. Callers must | ||
193 | - * make sure that their AioContext locking is still correct after this. | ||
194 | + * @parent can move to a different AioContext in this function. | ||
195 | */ | ||
196 | BdrvChild *bdrv_open_child(const char *filename, | ||
197 | QDict *options, const char *bdref_key, | ||
198 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename, | ||
199 | /* | ||
200 | * Wrapper on bdrv_open_child() for most popular case: open primary child of bs. | ||
201 | * | ||
202 | - * The caller must hold the lock of the main AioContext and no other AioContext. | ||
203 | - * @parent can move to a different AioContext in this function. Callers must | ||
204 | - * make sure that their AioContext locking is still correct after this. | ||
205 | + * @parent can move to a different AioContext in this function. | ||
206 | */ | ||
207 | int bdrv_open_file_child(const char *filename, | ||
208 | QDict *options, const char *bdref_key, | ||
209 | @@ -XXX,XX +XXX,XX @@ out: | ||
210 | * The reference parameter may be used to specify an existing block device which | ||
211 | * should be opened. If specified, neither options nor a filename may be given, | ||
212 | * nor can an existing BDS be reused (that is, *pbs has to be NULL). | ||
213 | - * | ||
214 | - * The caller must always hold the main AioContext lock. | ||
215 | */ | ||
216 | static BlockDriverState * no_coroutine_fn | ||
217 | bdrv_open_inherit(const char *filename, const char *reference, QDict *options, | ||
218 | @@ -XXX,XX +XXX,XX @@ close_and_fail: | ||
219 | return NULL; | ||
220 | } | ||
221 | |||
222 | -/* The caller must always hold the main AioContext lock. */ | ||
223 | BlockDriverState *bdrv_open(const char *filename, const char *reference, | ||
224 | QDict *options, int flags, Error **errp) | ||
225 | { | ||
226 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, | ||
227 | * | ||
228 | * Return 0 on success, otherwise return < 0 and set @errp. | ||
229 | * | ||
230 | - * The caller must hold the AioContext lock of @reopen_state->bs. | ||
231 | * @reopen_state->bs can move to a different AioContext in this function. | ||
232 | - * Callers must make sure that their AioContext locking is still correct after | ||
233 | - * this. | ||
234 | */ | ||
235 | static int GRAPH_UNLOCKED | ||
236 | bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, | ||
237 | @@ -XXX,XX +XXX,XX @@ out_rdlock: | ||
238 | * It is the responsibility of the caller to then call the abort() or | ||
239 | * commit() for any other BDS that have been left in a prepare() state | ||
240 | * | ||
241 | - * The caller must hold the AioContext lock of @reopen_state->bs. | ||
242 | - * | ||
243 | * After calling this function, the transaction @change_child_tran may only be | ||
244 | * completed while holding a writer lock for the graph. | ||
245 | */ | ||
246 | @@ -XXX,XX +XXX,XX @@ int bdrv_drop_filter(BlockDriverState *bs, Error **errp) | ||
247 | * child. | ||
248 | * | ||
249 | * This function does not create any image files. | ||
250 | - * | ||
251 | - * The caller must hold the AioContext lock for @bs_top. | ||
252 | */ | ||
253 | int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
254 | Error **errp) | ||
255 | @@ -XXX,XX +XXX,XX @@ static void bdrv_delete(BlockDriverState *bs) | ||
256 | * after the call (even on failure), so if the caller intends to reuse the | ||
257 | * dictionary, it needs to use qobject_ref() before calling bdrv_open. | ||
258 | * | ||
259 | - * The caller holds the AioContext lock for @bs. It must make sure that @bs | ||
260 | - * stays in the same AioContext, i.e. @options must not refer to nodes in a | ||
261 | - * different AioContext. | ||
262 | + * The caller must make sure that @bs stays in the same AioContext, i.e. | ||
263 | + * @options must not refer to nodes in a different AioContext. | ||
264 | */ | ||
265 | BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, | ||
266 | int flags, Error **errp) | ||
267 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv set_aio_context = { | ||
268 | * | ||
269 | * Must be called from the main AioContext. | ||
270 | * | ||
271 | - * The caller must own the AioContext lock for the old AioContext of bs, but it | ||
272 | - * must not own the AioContext lock for new_context (unless new_context is the | ||
273 | - * same as the current context of bs). | ||
274 | - * | ||
275 | * @visited will accumulate all visited BdrvChild objects. The caller is | ||
276 | * responsible for freeing the list afterwards. | ||
277 | */ | ||
278 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, | ||
279 | * | ||
280 | * If ignore_child is not NULL, that child (and its subgraph) will not | ||
281 | * be touched. | ||
282 | - * | ||
283 | - * This function still requires the caller to take the bs current | ||
284 | - * AioContext lock, otherwise draining will fail since AIO_WAIT_WHILE | ||
285 | - * assumes the lock is always held if bs is in another AioContext. | ||
286 | - * For the same reason, it temporarily also holds the new AioContext, since | ||
287 | - * bdrv_drained_end calls BDRV_POLL_WHILE that assumes the lock is taken too. | ||
288 | - * Therefore the new AioContext lock must not be taken by the caller. | ||
289 | */ | ||
290 | int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, | ||
291 | BdrvChild *ignore_child, Error **errp) | ||
292 | @@ -XXX,XX +XXX,XX @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, | ||
293 | |||
294 | /* | ||
295 | * Linear phase: go through all callbacks collected in the transaction. | ||
296 | - * Run all callbacks collected in the recursion to switch all nodes | ||
297 | - * AioContext lock (transaction commit), or undo all changes done in the | ||
298 | + * Run all callbacks collected in the recursion to switch every node's | ||
299 | + * AioContext (transaction commit), or undo all changes done in the | ||
300 | * recursion (transaction abort). | ||
301 | */ | ||
302 | |||
303 | diff --git a/block/block-backend.c b/block/block-backend.c | 39 | diff --git a/block/block-backend.c b/block/block-backend.c |
304 | index XXXXXXX..XXXXXXX 100644 | 40 | index XXXXXXX..XXXXXXX 100644 |
305 | --- a/block/block-backend.c | 41 | --- a/block/block-backend.c |
306 | +++ b/block/block-backend.c | 42 | +++ b/block/block-backend.c |
307 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm) | 43 | @@ -XXX,XX +XXX,XX @@ static bool blk_can_inactivate(BlockBackend *blk) |
308 | * Both sets of permissions can be changed later using blk_set_perm(). | 44 | * guest. For block job BBs that satisfy this, we can just allow |
309 | * | 45 | * it. This is the case for mirror job source, which is required |
310 | * Return the new BlockBackend on success, null on failure. | 46 | * by libvirt non-shared block migration. */ |
311 | - * | 47 | - if (!(blk->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED))) { |
312 | - * Callers must hold the AioContext lock of @bs. | 48 | + if (!(blk->perm & ~BLK_PERM_CONSISTENT_READ)) { |
313 | */ | 49 | return true; |
314 | BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, | 50 | } |
315 | uint64_t shared_perm, Error **errp) | ||
316 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, | ||
317 | * Just as with bdrv_open(), after having called this function the reference to | ||
318 | * @options belongs to the block layer (even on failure). | ||
319 | * | ||
320 | - * Called without holding an AioContext lock. | ||
321 | - * | ||
322 | * TODO: Remove @filename and @flags; it should be possible to specify a whole | ||
323 | * BDS tree just by specifying the @options QDict (or @reference, | ||
324 | * alternatively). At the time of adding this function, this is not possible, | ||
325 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_by_public(BlockBackendPublic *public) | ||
326 | |||
327 | /* | ||
328 | * Disassociates the currently associated BlockDriverState from @blk. | ||
329 | - * | ||
330 | - * The caller must hold the AioContext lock for the BlockBackend. | ||
331 | */ | ||
332 | void blk_remove_bs(BlockBackend *blk) | ||
333 | { | ||
334 | @@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk) | ||
335 | |||
336 | /* | ||
337 | * Associates a new BlockDriverState with @blk. | ||
338 | - * | ||
339 | - * Callers must hold the AioContext lock of @bs. | ||
340 | */ | ||
341 | int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) | ||
342 | { | ||
343 | diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c | ||
344 | index XXXXXXX..XXXXXXX 100644 | ||
345 | --- a/block/export/vhost-user-blk-server.c | ||
346 | +++ b/block/export/vhost-user-blk-server.c | ||
347 | @@ -XXX,XX +XXX,XX @@ static void vu_blk_exp_resize(void *opaque) | ||
348 | vu_config_change_msg(&vexp->vu_server.vu_dev); | ||
349 | } | ||
350 | |||
351 | -/* Called with vexp->export.ctx acquired */ | ||
352 | static void vu_blk_drained_begin(void *opaque) | ||
353 | { | ||
354 | VuBlkExport *vexp = opaque; | ||
355 | @@ -XXX,XX +XXX,XX @@ static void vu_blk_drained_begin(void *opaque) | ||
356 | vhost_user_server_detach_aio_context(&vexp->vu_server); | ||
357 | } | ||
358 | |||
359 | -/* Called with vexp->export.blk AioContext acquired */ | ||
360 | static void vu_blk_drained_end(void *opaque) | ||
361 | { | ||
362 | VuBlkExport *vexp = opaque; | ||
363 | @@ -XXX,XX +XXX,XX @@ static void vu_blk_drained_end(void *opaque) | ||
364 | * Ensures that bdrv_drained_begin() waits until in-flight requests complete | ||
365 | * and the server->co_trip coroutine has terminated. It will be restarted in | ||
366 | * vhost_user_server_attach_aio_context(). | ||
367 | - * | ||
368 | - * Called with vexp->export.ctx acquired. | ||
369 | */ | ||
370 | static bool vu_blk_drained_poll(void *opaque) | ||
371 | { | ||
372 | diff --git a/tests/qemu-iotests/202 b/tests/qemu-iotests/202 | ||
373 | index XXXXXXX..XXXXXXX 100755 | ||
374 | --- a/tests/qemu-iotests/202 | ||
375 | +++ b/tests/qemu-iotests/202 | ||
376 | @@ -XXX,XX +XXX,XX @@ | ||
377 | # Check that QMP 'transaction' blockdev-snapshot-sync with multiple drives on a | ||
378 | # single IOThread completes successfully. This particular command triggered a | ||
379 | # hang due to recursive AioContext locking and BDRV_POLL_WHILE(). Protect | ||
380 | -# against regressions. | ||
381 | +# against regressions even though the AioContext lock no longer exists. | ||
382 | |||
383 | import iotests | ||
384 | |||
385 | diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203 | ||
386 | index XXXXXXX..XXXXXXX 100755 | ||
387 | --- a/tests/qemu-iotests/203 | ||
388 | +++ b/tests/qemu-iotests/203 | ||
389 | @@ -XXX,XX +XXX,XX @@ | ||
390 | # Check that QMP 'migrate' with multiple drives on a single IOThread completes | ||
391 | # successfully. This particular command triggered a hang in the source QEMU | ||
392 | # process due to recursive AioContext locking in bdrv_invalidate_all() and | ||
393 | -# BDRV_POLL_WHILE(). | ||
394 | +# BDRV_POLL_WHILE(). Protect against regressions even though the AioContext | ||
395 | +# lock no longer exists. | ||
396 | |||
397 | import iotests | ||
398 | 51 | ||
399 | -- | 52 | -- |
400 | 2.43.0 | 53 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | In QEMU, nodes are automatically created inactive while expecting an |
---|---|---|---|
2 | incoming migration (i.e. RUN_STATE_INMIGRATE). In qemu-storage-daemon, | ||
3 | the notion of runstates doesn't exist. It also wouldn't necessarily make | ||
4 | sense to introduce it because a single daemon can serve multiple VMs | ||
5 | that can be in different states. | ||
2 | 6 | ||
3 | NBDClient has a number of fields that are accessed by both the export | 7 | Therefore, allow the user to explicitly open images as inactive with a |
4 | AioContext and the main loop thread. When the AioContext lock is removed | 8 | new option. The default is as before: Nodes are usually active, except |
5 | these fields will need another form of protection. | 9 | when created during RUN_STATE_INMIGRATE. |
6 | 10 | ||
7 | Add NBDClient->lock and protect fields that are accessed by both | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | threads. Also add assertions where possible and otherwise add doc | 12 | Acked-by: Fabiano Rosas <farosas@suse.de> |
9 | comments stating assumptions about which thread and lock holding. | 13 | Reviewed-by: Eric Blake <eblake@redhat.com> |
10 | 14 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | |
11 | Note this patch moves the client->recv_coroutine assertion from | 15 | Message-ID: <20250204211407.381505-8-kwolf@redhat.com> |
12 | nbd_co_receive_request() to nbd_trip() where client->lock is held. | ||
13 | |||
14 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
15 | Message-ID: <20231221192452.1785567-7-stefanha@redhat.com> | ||
16 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
18 | --- | 17 | --- |
19 | nbd/server.c | 144 +++++++++++++++++++++++++++++++++++++++------------ | 18 | qapi/block-core.json | 6 ++++++ |
20 | 1 file changed, 111 insertions(+), 33 deletions(-) | 19 | include/block/block-common.h | 1 + |
20 | block.c | 9 +++++++++ | ||
21 | 3 files changed, 16 insertions(+) | ||
21 | 22 | ||
22 | diff --git a/nbd/server.c b/nbd/server.c | 23 | diff --git a/qapi/block-core.json b/qapi/block-core.json |
23 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
24 | --- a/nbd/server.c | 25 | --- a/qapi/block-core.json |
25 | +++ b/nbd/server.c | 26 | +++ b/qapi/block-core.json |
26 | @@ -XXX,XX +XXX,XX @@ struct NBDClient { | 27 | @@ -XXX,XX +XXX,XX @@ |
27 | int refcount; /* atomic */ | 28 | # |
28 | void (*close_fn)(NBDClient *client, bool negotiated); | 29 | # @cache: cache-related options |
29 | 30 | # | |
30 | + QemuMutex lock; | 31 | +# @active: whether the block node should be activated (default: true). |
31 | + | 32 | +# Having inactive block nodes is useful primarily for migration because it |
32 | NBDExport *exp; | 33 | +# allows opening an image on the destination while the source is still |
33 | QCryptoTLSCreds *tlscreds; | 34 | +# holding locks for it. (Since 10.0) |
34 | char *tlsauthz; | 35 | +# |
35 | QIOChannelSocket *sioc; /* The underlying data channel */ | 36 | # @read-only: whether the block device should be read-only (default: |
36 | QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ | 37 | # false). Note that some block drivers support only read-only |
37 | 38 | # access, either generally or in certain configurations. In this | |
38 | - Coroutine *recv_coroutine; | 39 | @@ -XXX,XX +XXX,XX @@ |
39 | + Coroutine *recv_coroutine; /* protected by lock */ | 40 | '*node-name': 'str', |
40 | 41 | '*discard': 'BlockdevDiscardOptions', | |
41 | CoMutex send_lock; | 42 | '*cache': 'BlockdevCacheOptions', |
42 | Coroutine *send_coroutine; | 43 | + '*active': 'bool', |
43 | 44 | '*read-only': 'bool', | |
44 | - bool read_yielding; | 45 | '*auto-read-only': 'bool', |
45 | - bool quiescing; | 46 | '*force-share': 'bool', |
46 | + bool read_yielding; /* protected by lock */ | 47 | diff --git a/include/block/block-common.h b/include/block/block-common.h |
47 | + bool quiescing; /* protected by lock */ | 48 | index XXXXXXX..XXXXXXX 100644 |
48 | 49 | --- a/include/block/block-common.h | |
49 | QTAILQ_ENTRY(NBDClient) next; | 50 | +++ b/include/block/block-common.h |
50 | - int nb_requests; | 51 | @@ -XXX,XX +XXX,XX @@ typedef enum { |
51 | - bool closing; | 52 | #define BDRV_OPT_AUTO_READ_ONLY "auto-read-only" |
52 | + int nb_requests; /* protected by lock */ | 53 | #define BDRV_OPT_DISCARD "discard" |
53 | + bool closing; /* protected by lock */ | 54 | #define BDRV_OPT_FORCE_SHARE "force-share" |
54 | 55 | +#define BDRV_OPT_ACTIVE "active" | |
55 | uint32_t check_align; /* If non-zero, check for aligned client requests */ | 56 | |
56 | 57 | ||
57 | @@ -XXX,XX +XXX,XX @@ nbd_read_eof(NBDClient *client, void *buffer, size_t size, Error **errp) | 58 | #define BDRV_SECTOR_BITS 9 |
58 | 59 | diff --git a/block.c b/block.c | |
59 | len = qio_channel_readv(client->ioc, &iov, 1, errp); | 60 | index XXXXXXX..XXXXXXX 100644 |
60 | if (len == QIO_CHANNEL_ERR_BLOCK) { | 61 | --- a/block.c |
61 | - client->read_yielding = true; | 62 | +++ b/block.c |
62 | + WITH_QEMU_LOCK_GUARD(&client->lock) { | 63 | @@ -XXX,XX +XXX,XX @@ static void update_flags_from_options(int *flags, QemuOpts *opts) |
63 | + client->read_yielding = true; | 64 | if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) { |
64 | + | 65 | *flags |= BDRV_O_AUTO_RDONLY; |
65 | + /* Prompt main loop thread to re-run nbd_drained_poll() */ | ||
66 | + aio_wait_kick(); | ||
67 | + } | ||
68 | qio_channel_yield(client->ioc, G_IO_IN); | ||
69 | - client->read_yielding = false; | ||
70 | - if (client->quiescing) { | ||
71 | - return -EAGAIN; | ||
72 | + WITH_QEMU_LOCK_GUARD(&client->lock) { | ||
73 | + client->read_yielding = false; | ||
74 | + if (client->quiescing) { | ||
75 | + return -EAGAIN; | ||
76 | + } | ||
77 | } | ||
78 | continue; | ||
79 | } else if (len < 0) { | ||
80 | @@ -XXX,XX +XXX,XX @@ void nbd_client_put(NBDClient *client) | ||
81 | blk_exp_unref(&client->exp->common); | ||
82 | } | ||
83 | g_free(client->contexts.bitmaps); | ||
84 | + qemu_mutex_destroy(&client->lock); | ||
85 | g_free(client); | ||
86 | } | ||
87 | } | ||
88 | @@ -XXX,XX +XXX,XX @@ static void client_close(NBDClient *client, bool negotiated) | ||
89 | { | ||
90 | assert(qemu_in_main_thread()); | ||
91 | |||
92 | - if (client->closing) { | ||
93 | - return; | ||
94 | - } | ||
95 | + WITH_QEMU_LOCK_GUARD(&client->lock) { | ||
96 | + if (client->closing) { | ||
97 | + return; | ||
98 | + } | ||
99 | |||
100 | - client->closing = true; | ||
101 | + client->closing = true; | ||
102 | + } | ||
103 | |||
104 | /* Force requests to finish. They will drop their own references, | ||
105 | * then we'll close the socket and free the NBDClient. | ||
106 | @@ -XXX,XX +XXX,XX @@ static void client_close(NBDClient *client, bool negotiated) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | +/* Runs in export AioContext with client->lock held */ | ||
111 | static NBDRequestData *nbd_request_get(NBDClient *client) | ||
112 | { | ||
113 | NBDRequestData *req; | ||
114 | @@ -XXX,XX +XXX,XX @@ static NBDRequestData *nbd_request_get(NBDClient *client) | ||
115 | return req; | ||
116 | } | ||
117 | |||
118 | +/* Runs in export AioContext with client->lock held */ | ||
119 | static void nbd_request_put(NBDRequestData *req) | ||
120 | { | ||
121 | NBDClient *client = req->client; | ||
122 | @@ -XXX,XX +XXX,XX @@ static void blk_aio_attached(AioContext *ctx, void *opaque) | ||
123 | NBDExport *exp = opaque; | ||
124 | NBDClient *client; | ||
125 | |||
126 | + assert(qemu_in_main_thread()); | ||
127 | + | ||
128 | trace_nbd_blk_aio_attached(exp->name, ctx); | ||
129 | |||
130 | exp->common.ctx = ctx; | ||
131 | |||
132 | QTAILQ_FOREACH(client, &exp->clients, next) { | ||
133 | - assert(client->nb_requests == 0); | ||
134 | - assert(client->recv_coroutine == NULL); | ||
135 | - assert(client->send_coroutine == NULL); | ||
136 | + WITH_QEMU_LOCK_GUARD(&client->lock) { | ||
137 | + assert(client->nb_requests == 0); | ||
138 | + assert(client->recv_coroutine == NULL); | ||
139 | + assert(client->send_coroutine == NULL); | ||
140 | + } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | @@ -XXX,XX +XXX,XX @@ static void blk_aio_detach(void *opaque) | ||
145 | { | ||
146 | NBDExport *exp = opaque; | ||
147 | |||
148 | + assert(qemu_in_main_thread()); | ||
149 | + | ||
150 | trace_nbd_blk_aio_detach(exp->name, exp->common.ctx); | ||
151 | |||
152 | exp->common.ctx = NULL; | ||
153 | @@ -XXX,XX +XXX,XX @@ static void nbd_drained_begin(void *opaque) | ||
154 | NBDExport *exp = opaque; | ||
155 | NBDClient *client; | ||
156 | |||
157 | + assert(qemu_in_main_thread()); | ||
158 | + | ||
159 | QTAILQ_FOREACH(client, &exp->clients, next) { | ||
160 | - client->quiescing = true; | ||
161 | + WITH_QEMU_LOCK_GUARD(&client->lock) { | ||
162 | + client->quiescing = true; | ||
163 | + } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | @@ -XXX,XX +XXX,XX @@ static void nbd_drained_end(void *opaque) | ||
168 | NBDExport *exp = opaque; | ||
169 | NBDClient *client; | ||
170 | |||
171 | + assert(qemu_in_main_thread()); | ||
172 | + | ||
173 | QTAILQ_FOREACH(client, &exp->clients, next) { | ||
174 | - client->quiescing = false; | ||
175 | - nbd_client_receive_next_request(client); | ||
176 | + WITH_QEMU_LOCK_GUARD(&client->lock) { | ||
177 | + client->quiescing = false; | ||
178 | + nbd_client_receive_next_request(client); | ||
179 | + } | ||
180 | } | ||
181 | } | ||
182 | |||
183 | +/* Runs in export AioContext */ | ||
184 | +static void nbd_wake_read_bh(void *opaque) | ||
185 | +{ | ||
186 | + NBDClient *client = opaque; | ||
187 | + qio_channel_wake_read(client->ioc); | ||
188 | +} | ||
189 | + | ||
190 | static bool nbd_drained_poll(void *opaque) | ||
191 | { | ||
192 | NBDExport *exp = opaque; | ||
193 | NBDClient *client; | ||
194 | |||
195 | + assert(qemu_in_main_thread()); | ||
196 | + | ||
197 | QTAILQ_FOREACH(client, &exp->clients, next) { | ||
198 | - if (client->nb_requests != 0) { | ||
199 | - /* | ||
200 | - * If there's a coroutine waiting for a request on nbd_read_eof() | ||
201 | - * enter it here so we don't depend on the client to wake it up. | ||
202 | - */ | ||
203 | - if (client->recv_coroutine != NULL && client->read_yielding) { | ||
204 | - qio_channel_wake_read(client->ioc); | ||
205 | - } | ||
206 | + WITH_QEMU_LOCK_GUARD(&client->lock) { | ||
207 | + if (client->nb_requests != 0) { | ||
208 | + /* | ||
209 | + * If there's a coroutine waiting for a request on nbd_read_eof() | ||
210 | + * enter it here so we don't depend on the client to wake it up. | ||
211 | + * | ||
212 | + * Schedule a BH in the export AioContext to avoid missing the | ||
213 | + * wake up due to the race between qio_channel_wake_read() and | ||
214 | + * qio_channel_yield(). | ||
215 | + */ | ||
216 | + if (client->recv_coroutine != NULL && client->read_yielding) { | ||
217 | + aio_bh_schedule_oneshot(nbd_export_aio_context(client->exp), | ||
218 | + nbd_wake_read_bh, client); | ||
219 | + } | ||
220 | |||
221 | - return true; | ||
222 | + return true; | ||
223 | + } | ||
224 | } | ||
225 | } | ||
226 | |||
227 | @@ -XXX,XX +XXX,XX @@ static void nbd_eject_notifier(Notifier *n, void *data) | ||
228 | { | ||
229 | NBDExport *exp = container_of(n, NBDExport, eject_notifier); | ||
230 | |||
231 | + assert(qemu_in_main_thread()); | ||
232 | + | ||
233 | blk_exp_request_shutdown(&exp->common); | ||
234 | } | ||
235 | |||
236 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_co_receive_request(NBDRequestData *req, | ||
237 | int ret; | ||
238 | |||
239 | g_assert(qemu_in_coroutine()); | ||
240 | - assert(client->recv_coroutine == qemu_coroutine_self()); | ||
241 | ret = nbd_receive_request(client, request, errp); | ||
242 | if (ret < 0) { | ||
243 | return ret; | ||
244 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_trip(void *opaque) | ||
245 | */ | ||
246 | |||
247 | trace_nbd_trip(); | ||
248 | + | ||
249 | + qemu_mutex_lock(&client->lock); | ||
250 | + | ||
251 | if (client->closing) { | ||
252 | goto done; | ||
253 | } | ||
254 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_trip(void *opaque) | ||
255 | } | ||
256 | |||
257 | req = nbd_request_get(client); | ||
258 | - ret = nbd_co_receive_request(req, &request, &local_err); | ||
259 | + | ||
260 | + /* | ||
261 | + * nbd_co_receive_request() returns -EAGAIN when nbd_drained_begin() has | ||
262 | + * set client->quiescing but by the time we get back nbd_drained_end() may | ||
263 | + * have already cleared client->quiescing. In that case we try again | ||
264 | + * because nothing else will spawn an nbd_trip() coroutine until we set | ||
265 | + * client->recv_coroutine = NULL further down. | ||
266 | + */ | ||
267 | + do { | ||
268 | + assert(client->recv_coroutine == qemu_coroutine_self()); | ||
269 | + qemu_mutex_unlock(&client->lock); | ||
270 | + ret = nbd_co_receive_request(req, &request, &local_err); | ||
271 | + qemu_mutex_lock(&client->lock); | ||
272 | + } while (ret == -EAGAIN && !client->quiescing); | ||
273 | + | ||
274 | client->recv_coroutine = NULL; | ||
275 | |||
276 | if (client->closing) { | ||
277 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_trip(void *opaque) | ||
278 | } | ||
279 | |||
280 | if (ret == -EAGAIN) { | ||
281 | - assert(client->quiescing); | ||
282 | goto done; | ||
283 | } | ||
284 | |||
285 | nbd_client_receive_next_request(client); | ||
286 | + | ||
287 | if (ret == -EIO) { | ||
288 | goto disconnect; | ||
289 | } | ||
290 | |||
291 | + qemu_mutex_unlock(&client->lock); | ||
292 | qio_channel_set_cork(client->ioc, true); | ||
293 | |||
294 | if (ret < 0) { | ||
295 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_trip(void *opaque) | ||
296 | g_free(request.contexts->bitmaps); | ||
297 | g_free(request.contexts); | ||
298 | } | 66 | } |
299 | + | 67 | + |
300 | + qio_channel_set_cork(client->ioc, false); | 68 | + if (!qemu_opt_get_bool_del(opts, BDRV_OPT_ACTIVE, true)) { |
301 | + qemu_mutex_lock(&client->lock); | 69 | + *flags |= BDRV_O_INACTIVE; |
302 | + | ||
303 | if (ret < 0) { | ||
304 | error_prepend(&local_err, "Failed to send reply: "); | ||
305 | goto disconnect; | ||
306 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_trip(void *opaque) | ||
307 | goto disconnect; | ||
308 | } | ||
309 | |||
310 | - qio_channel_set_cork(client->ioc, false); | ||
311 | done: | ||
312 | if (req) { | ||
313 | nbd_request_put(req); | ||
314 | } | ||
315 | + | ||
316 | + qemu_mutex_unlock(&client->lock); | ||
317 | + | ||
318 | if (!nbd_client_put_nonzero(client)) { | ||
319 | aio_co_reschedule_self(qemu_get_aio_context()); | ||
320 | nbd_client_put(client); | ||
321 | @@ -XXX,XX +XXX,XX @@ disconnect: | ||
322 | if (local_err) { | ||
323 | error_reportf_err(local_err, "Disconnect client, due to: "); | ||
324 | } | ||
325 | + | ||
326 | nbd_request_put(req); | ||
327 | + qemu_mutex_unlock(&client->lock); | ||
328 | |||
329 | aio_co_reschedule_self(qemu_get_aio_context()); | ||
330 | client_close(client, true); | ||
331 | nbd_client_put(client); | ||
332 | } | ||
333 | |||
334 | +/* | ||
335 | + * Runs in export AioContext and main loop thread. Caller must hold | ||
336 | + * client->lock. | ||
337 | + */ | ||
338 | static void nbd_client_receive_next_request(NBDClient *client) | ||
339 | { | ||
340 | if (!client->recv_coroutine && client->nb_requests < MAX_NBD_REQUESTS && | ||
341 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_co_client_start(void *opaque) | ||
342 | return; | ||
343 | } | ||
344 | |||
345 | - nbd_client_receive_next_request(client); | ||
346 | + WITH_QEMU_LOCK_GUARD(&client->lock) { | ||
347 | + nbd_client_receive_next_request(client); | ||
348 | + } | 70 | + } |
349 | } | 71 | } |
350 | 72 | ||
351 | /* | 73 | static void update_options_from_flags(QDict *options, int flags) |
352 | @@ -XXX,XX +XXX,XX @@ void nbd_client_new(QIOChannelSocket *sioc, | 74 | @@ -XXX,XX +XXX,XX @@ QemuOptsList bdrv_runtime_opts = { |
353 | Coroutine *co; | 75 | .type = QEMU_OPT_BOOL, |
354 | 76 | .help = "Ignore flush requests", | |
355 | client = g_new0(NBDClient, 1); | 77 | }, |
356 | + qemu_mutex_init(&client->lock); | 78 | + { |
357 | client->refcount = 1; | 79 | + .name = BDRV_OPT_ACTIVE, |
358 | client->tlscreds = tlscreds; | 80 | + .type = QEMU_OPT_BOOL, |
359 | if (tlscreds) { | 81 | + .help = "Node is activated", |
82 | + }, | ||
83 | { | ||
84 | .name = BDRV_OPT_READ_ONLY, | ||
85 | .type = QEMU_OPT_BOOL, | ||
360 | -- | 86 | -- |
361 | 2.43.0 | 87 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | The system emulator tries to automatically activate and inactivate block |
---|---|---|---|
2 | nodes at the right point during migration. However, there are still | ||
3 | cases where it's necessary that the user can do this manually. | ||
2 | 4 | ||
3 | The bdrv_co_lock() and bdrv_co_unlock() functions are already no-ops. | 5 | Images are only activated on the destination VM of a migration when the |
4 | Remove them. | 6 | VM is actually resumed. If the VM was paused, this doesn't happen |
7 | automatically. The user may want to perform some operation on a block | ||
8 | device (e.g. taking a snapshot or starting a block job) without also | ||
9 | resuming the VM yet. This is an example where a manual command is | ||
10 | necessary. | ||
5 | 11 | ||
6 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 12 | Another example is VM migration when the image files are opened by an |
7 | Message-ID: <20231205182011.1976568-8-stefanha@redhat.com> | 13 | external qemu-storage-daemon instance on each side. In this case, the |
8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 14 | process that needs to hand over the images isn't even part of the |
15 | migration and can't know when the migration completes. Management tools | ||
16 | need a way to explicitly inactivate images on the source and activate | ||
17 | them on the destination. | ||
18 | |||
19 | This adds a new blockdev-set-active QMP command that lets the user | ||
20 | change the status of individual nodes (this is necessary in | ||
21 | qemu-storage-daemon because it could be serving multiple VMs and only | ||
22 | one of them migrates at a time). For convenience, operating on all | ||
23 | devices (like QEMU does automatically during migration) is offered as an | ||
24 | option, too, and can be used in the context of single VM. | ||
25 | |||
26 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
27 | Acked-by: Fabiano Rosas <farosas@suse.de> | ||
28 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
29 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
30 | Message-ID: <20250204211407.381505-9-kwolf@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 31 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 32 | --- |
11 | include/block/block-global-state.h | 14 -------------- | 33 | qapi/block-core.json | 32 ++++++++++++++++++++++++++++++ |
12 | block.c | 10 ---------- | 34 | include/block/block-global-state.h | 3 +++ |
13 | blockdev.c | 5 ----- | 35 | block.c | 21 ++++++++++++++++++++ |
14 | 3 files changed, 29 deletions(-) | 36 | blockdev.c | 32 ++++++++++++++++++++++++++++++ |
37 | 4 files changed, 88 insertions(+) | ||
15 | 38 | ||
39 | diff --git a/qapi/block-core.json b/qapi/block-core.json | ||
40 | index XXXXXXX..XXXXXXX 100644 | ||
41 | --- a/qapi/block-core.json | ||
42 | +++ b/qapi/block-core.json | ||
43 | @@ -XXX,XX +XXX,XX @@ | ||
44 | { 'command': 'blockdev-del', 'data': { 'node-name': 'str' }, | ||
45 | 'allow-preconfig': true } | ||
46 | |||
47 | +## | ||
48 | +# @blockdev-set-active: | ||
49 | +# | ||
50 | +# Activate or inactivate a block device. Use this to manage the handover of | ||
51 | +# block devices on migration with qemu-storage-daemon. | ||
52 | +# | ||
53 | +# Activating a node automatically activates all of its child nodes first. | ||
54 | +# Inactivating a node automatically inactivates any of its child nodes that are | ||
55 | +# not in use by a still active node. | ||
56 | +# | ||
57 | +# @node-name: Name of the graph node to activate or inactivate. By default, all | ||
58 | +# nodes are affected by the operation. | ||
59 | +# | ||
60 | +# @active: true if the nodes should be active when the command returns success, | ||
61 | +# false if they should be inactive. | ||
62 | +# | ||
63 | +# Since: 10.0 | ||
64 | +# | ||
65 | +# .. qmp-example:: | ||
66 | +# | ||
67 | +# -> { "execute": "blockdev-set-active", | ||
68 | +# "arguments": { | ||
69 | +# "node-name": "node0", | ||
70 | +# "active": false | ||
71 | +# } | ||
72 | +# } | ||
73 | +# <- { "return": {} } | ||
74 | +## | ||
75 | +{ 'command': 'blockdev-set-active', | ||
76 | + 'data': { '*node-name': 'str', 'active': 'bool' }, | ||
77 | + 'allow-preconfig': true } | ||
78 | + | ||
79 | ## | ||
80 | # @BlockdevCreateOptionsFile: | ||
81 | # | ||
16 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h | 82 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h |
17 | index XXXXXXX..XXXXXXX 100644 | 83 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/include/block/block-global-state.h | 84 | --- a/include/block/block-global-state.h |
19 | +++ b/include/block/block-global-state.h | 85 | +++ b/include/block/block-global-state.h |
20 | @@ -XXX,XX +XXX,XX @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag); | 86 | @@ -XXX,XX +XXX,XX @@ bdrv_activate(BlockDriverState *bs, Error **errp); |
21 | int bdrv_debug_resume(BlockDriverState *bs, const char *tag); | 87 | int coroutine_fn no_co_wrapper_bdrv_rdlock |
22 | bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag); | 88 | bdrv_co_activate(BlockDriverState *bs, Error **errp); |
23 | 89 | ||
24 | -/** | 90 | +int no_coroutine_fn |
25 | - * Locks the AioContext of @bs if it's not the current AioContext. This avoids | 91 | +bdrv_inactivate(BlockDriverState *bs, Error **errp); |
26 | - * double locking which could lead to deadlocks: This is a coroutine_fn, so we | 92 | + |
27 | - * know we already own the lock of the current AioContext. | 93 | void bdrv_activate_all(Error **errp); |
28 | - * | 94 | int bdrv_inactivate_all(void); |
29 | - * May only be called in the main thread. | 95 | |
30 | - */ | ||
31 | -void coroutine_fn bdrv_co_lock(BlockDriverState *bs); | ||
32 | - | ||
33 | -/** | ||
34 | - * Unlocks the AioContext of @bs if it's not the current AioContext. | ||
35 | - */ | ||
36 | -void coroutine_fn bdrv_co_unlock(BlockDriverState *bs); | ||
37 | - | ||
38 | bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, | ||
39 | GHashTable *visited, Transaction *tran, | ||
40 | Error **errp); | ||
41 | diff --git a/block.c b/block.c | 96 | diff --git a/block.c b/block.c |
42 | index XXXXXXX..XXXXXXX 100644 | 97 | index XXXXXXX..XXXXXXX 100644 |
43 | --- a/block.c | 98 | --- a/block.c |
44 | +++ b/block.c | 99 | +++ b/block.c |
45 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) | 100 | @@ -XXX,XX +XXX,XX @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level) |
46 | bdrv_dec_in_flight(bs); | 101 | return 0; |
47 | } | 102 | } |
48 | 103 | ||
49 | -void coroutine_fn bdrv_co_lock(BlockDriverState *bs) | 104 | +int bdrv_inactivate(BlockDriverState *bs, Error **errp) |
50 | -{ | 105 | +{ |
51 | - /* TODO removed in next patch */ | 106 | + int ret; |
52 | -} | 107 | + |
53 | - | 108 | + GLOBAL_STATE_CODE(); |
54 | -void coroutine_fn bdrv_co_unlock(BlockDriverState *bs) | 109 | + GRAPH_RDLOCK_GUARD_MAINLOOP(); |
55 | -{ | 110 | + |
56 | - /* TODO removed in next patch */ | 111 | + if (bdrv_has_bds_parent(bs, true)) { |
57 | -} | 112 | + error_setg(errp, "Node has active parent node"); |
58 | - | 113 | + return -EPERM; |
59 | static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) | 114 | + } |
115 | + | ||
116 | + ret = bdrv_inactivate_recurse(bs, true); | ||
117 | + if (ret < 0) { | ||
118 | + error_setg_errno(errp, -ret, "Failed to inactivate node"); | ||
119 | + return ret; | ||
120 | + } | ||
121 | + | ||
122 | + return 0; | ||
123 | +} | ||
124 | + | ||
125 | int bdrv_inactivate_all(void) | ||
60 | { | 126 | { |
61 | GLOBAL_STATE_CODE(); | 127 | BlockDriverState *bs = NULL; |
62 | diff --git a/blockdev.c b/blockdev.c | 128 | diff --git a/blockdev.c b/blockdev.c |
63 | index XXXXXXX..XXXXXXX 100644 | 129 | index XXXXXXX..XXXXXXX 100644 |
64 | --- a/blockdev.c | 130 | --- a/blockdev.c |
65 | +++ b/blockdev.c | 131 | +++ b/blockdev.c |
66 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qmp_block_resize(const char *device, const char *node_name, | 132 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_del(const char *node_name, Error **errp) |
67 | return; | 133 | bdrv_unref(bs); |
68 | } | ||
69 | |||
70 | - bdrv_co_lock(bs); | ||
71 | bdrv_drained_begin(bs); | ||
72 | - bdrv_co_unlock(bs); | ||
73 | |||
74 | old_ctx = bdrv_co_enter(bs); | ||
75 | blk_co_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp); | ||
76 | bdrv_co_leave(bs, old_ctx); | ||
77 | |||
78 | - bdrv_co_lock(bs); | ||
79 | bdrv_drained_end(bs); | ||
80 | - bdrv_co_unlock(bs); | ||
81 | - | ||
82 | blk_co_unref(blk); | ||
83 | } | 134 | } |
84 | 135 | ||
136 | +void qmp_blockdev_set_active(const char *node_name, bool active, Error **errp) | ||
137 | +{ | ||
138 | + int ret; | ||
139 | + | ||
140 | + GLOBAL_STATE_CODE(); | ||
141 | + GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
142 | + | ||
143 | + if (!node_name) { | ||
144 | + if (active) { | ||
145 | + bdrv_activate_all(errp); | ||
146 | + } else { | ||
147 | + ret = bdrv_inactivate_all(); | ||
148 | + if (ret < 0) { | ||
149 | + error_setg_errno(errp, -ret, "Failed to inactivate all nodes"); | ||
150 | + } | ||
151 | + } | ||
152 | + } else { | ||
153 | + BlockDriverState *bs = bdrv_find_node(node_name); | ||
154 | + if (!bs) { | ||
155 | + error_setg(errp, "Failed to find node with node-name='%s'", | ||
156 | + node_name); | ||
157 | + return; | ||
158 | + } | ||
159 | + | ||
160 | + if (active) { | ||
161 | + bdrv_activate(bs, errp); | ||
162 | + } else { | ||
163 | + bdrv_inactivate(bs, errp); | ||
164 | + } | ||
165 | + } | ||
166 | +} | ||
167 | + | ||
168 | static BdrvChild * GRAPH_RDLOCK | ||
169 | bdrv_find_child(BlockDriverState *parent_bs, const char *child_name) | ||
170 | { | ||
85 | -- | 171 | -- |
86 | 2.43.0 | 172 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | Device models have a relatively complex way to set up their block |
---|---|---|---|
2 | backends, in which blk_attach_dev() sets blk->disable_perm = true. | ||
3 | We want to support inactive images in exports, too, so that | ||
4 | qemu-storage-daemon can be used with migration. Because they don't use | ||
5 | blk_attach_dev(), they need another way to set this flag. The most | ||
6 | convenient is to do this automatically when an inactive node is attached | ||
7 | to a BlockBackend that can be inactivated. | ||
2 | 8 | ||
3 | Stop acquiring/releasing the AioContext lock in | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | bdrv_graph_wrlock()/bdrv_graph_unlock() since the lock no longer has any | 10 | Acked-by: Fabiano Rosas <farosas@suse.de> |
5 | effect. | ||
6 | |||
7 | The distinction between bdrv_graph_wrunlock() and | ||
8 | bdrv_graph_wrunlock_ctx() becomes meaningless and they can be collapsed | ||
9 | into one function. | ||
10 | |||
11 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
12 | Reviewed-by: Eric Blake <eblake@redhat.com> | 11 | Reviewed-by: Eric Blake <eblake@redhat.com> |
13 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 12 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
14 | Message-ID: <20231205182011.1976568-6-stefanha@redhat.com> | 13 | Message-ID: <20250204211407.381505-10-kwolf@redhat.com> |
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
16 | --- | 15 | --- |
17 | include/block/graph-lock.h | 21 ++----------- | 16 | block/block-backend.c | 14 ++++++++++++-- |
18 | block.c | 50 +++++++++++++++--------------- | 17 | 1 file changed, 12 insertions(+), 2 deletions(-) |
19 | block/backup.c | 4 +-- | ||
20 | block/blklogwrites.c | 8 ++--- | ||
21 | block/blkverify.c | 4 +-- | ||
22 | block/block-backend.c | 11 +++---- | ||
23 | block/commit.c | 16 +++++----- | ||
24 | block/graph-lock.c | 44 ++------------------------ | ||
25 | block/mirror.c | 22 ++++++------- | ||
26 | block/qcow2.c | 4 +-- | ||
27 | block/quorum.c | 8 ++--- | ||
28 | block/replication.c | 14 ++++----- | ||
29 | block/snapshot.c | 4 +-- | ||
30 | block/stream.c | 12 +++---- | ||
31 | block/vmdk.c | 20 ++++++------ | ||
32 | blockdev.c | 8 ++--- | ||
33 | blockjob.c | 12 +++---- | ||
34 | tests/unit/test-bdrv-drain.c | 40 ++++++++++++------------ | ||
35 | tests/unit/test-bdrv-graph-mod.c | 20 ++++++------ | ||
36 | scripts/block-coroutine-wrapper.py | 4 +-- | ||
37 | 20 files changed, 133 insertions(+), 193 deletions(-) | ||
38 | 18 | ||
39 | diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h | ||
40 | index XXXXXXX..XXXXXXX 100644 | ||
41 | --- a/include/block/graph-lock.h | ||
42 | +++ b/include/block/graph-lock.h | ||
43 | @@ -XXX,XX +XXX,XX @@ void unregister_aiocontext(AioContext *ctx); | ||
44 | * | ||
45 | * The wrlock can only be taken from the main loop, with BQL held, as only the | ||
46 | * main loop is allowed to modify the graph. | ||
47 | - * | ||
48 | - * If @bs is non-NULL, its AioContext is temporarily released. | ||
49 | - * | ||
50 | - * This function polls. Callers must not hold the lock of any AioContext other | ||
51 | - * than the current one and the one of @bs. | ||
52 | */ | ||
53 | void no_coroutine_fn TSA_ACQUIRE(graph_lock) TSA_NO_TSA | ||
54 | -bdrv_graph_wrlock(BlockDriverState *bs); | ||
55 | +bdrv_graph_wrlock(void); | ||
56 | |||
57 | /* | ||
58 | * bdrv_graph_wrunlock: | ||
59 | * Write finished, reset global has_writer to 0 and restart | ||
60 | * all readers that are waiting. | ||
61 | - * | ||
62 | - * If @bs is non-NULL, its AioContext is temporarily released. | ||
63 | - */ | ||
64 | -void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA | ||
65 | -bdrv_graph_wrunlock(BlockDriverState *bs); | ||
66 | - | ||
67 | -/* | ||
68 | - * bdrv_graph_wrunlock_ctx: | ||
69 | - * Write finished, reset global has_writer to 0 and restart | ||
70 | - * all readers that are waiting. | ||
71 | - * | ||
72 | - * If @ctx is non-NULL, its lock is temporarily released. | ||
73 | */ | ||
74 | void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA | ||
75 | -bdrv_graph_wrunlock_ctx(AioContext *ctx); | ||
76 | +bdrv_graph_wrunlock(void); | ||
77 | |||
78 | /* | ||
79 | * bdrv_graph_co_rdlock: | ||
80 | diff --git a/block.c b/block.c | ||
81 | index XXXXXXX..XXXXXXX 100644 | ||
82 | --- a/block.c | ||
83 | +++ b/block.c | ||
84 | @@ -XXX,XX +XXX,XX @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, | ||
85 | open_failed: | ||
86 | bs->drv = NULL; | ||
87 | |||
88 | - bdrv_graph_wrlock(NULL); | ||
89 | + bdrv_graph_wrlock(); | ||
90 | if (bs->file != NULL) { | ||
91 | bdrv_unref_child(bs, bs->file); | ||
92 | assert(!bs->file); | ||
93 | } | ||
94 | - bdrv_graph_wrunlock(NULL); | ||
95 | + bdrv_graph_wrunlock(); | ||
96 | |||
97 | g_free(bs->opaque); | ||
98 | bs->opaque = NULL; | ||
99 | @@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||
100 | |||
101 | bdrv_ref(drain_bs); | ||
102 | bdrv_drained_begin(drain_bs); | ||
103 | - bdrv_graph_wrlock(backing_hd); | ||
104 | + bdrv_graph_wrlock(); | ||
105 | ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); | ||
106 | - bdrv_graph_wrunlock(backing_hd); | ||
107 | + bdrv_graph_wrunlock(); | ||
108 | bdrv_drained_end(drain_bs); | ||
109 | bdrv_unref(drain_bs); | ||
110 | |||
111 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename, | ||
112 | return NULL; | ||
113 | } | ||
114 | |||
115 | - bdrv_graph_wrlock(NULL); | ||
116 | + bdrv_graph_wrlock(); | ||
117 | ctx = bdrv_get_aio_context(bs); | ||
118 | aio_context_acquire(ctx); | ||
119 | child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, | ||
120 | errp); | ||
121 | aio_context_release(ctx); | ||
122 | - bdrv_graph_wrunlock(NULL); | ||
123 | + bdrv_graph_wrunlock(); | ||
124 | |||
125 | return child; | ||
126 | } | ||
127 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
128 | aio_context_release(ctx); | ||
129 | } | ||
130 | |||
131 | - bdrv_graph_wrlock(NULL); | ||
132 | + bdrv_graph_wrlock(); | ||
133 | tran_commit(tran); | ||
134 | - bdrv_graph_wrunlock(NULL); | ||
135 | + bdrv_graph_wrunlock(); | ||
136 | |||
137 | QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { | ||
138 | BlockDriverState *bs = bs_entry->state.bs; | ||
139 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
140 | goto cleanup; | ||
141 | |||
142 | abort: | ||
143 | - bdrv_graph_wrlock(NULL); | ||
144 | + bdrv_graph_wrlock(); | ||
145 | tran_abort(tran); | ||
146 | - bdrv_graph_wrunlock(NULL); | ||
147 | + bdrv_graph_wrunlock(); | ||
148 | |||
149 | QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { | ||
150 | if (bs_entry->prepared) { | ||
151 | @@ -XXX,XX +XXX,XX @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, | ||
152 | } | ||
153 | |||
154 | bdrv_graph_rdunlock_main_loop(); | ||
155 | - bdrv_graph_wrlock(new_child_bs); | ||
156 | + bdrv_graph_wrlock(); | ||
157 | |||
158 | ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, | ||
159 | tran, errp); | ||
160 | |||
161 | - bdrv_graph_wrunlock_ctx(ctx); | ||
162 | + bdrv_graph_wrunlock(); | ||
163 | |||
164 | if (old_ctx != ctx) { | ||
165 | aio_context_release(ctx); | ||
166 | @@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs) | ||
167 | bs->drv = NULL; | ||
168 | } | ||
169 | |||
170 | - bdrv_graph_wrlock(bs); | ||
171 | + bdrv_graph_wrlock(); | ||
172 | QLIST_FOREACH_SAFE(child, &bs->children, next, next) { | ||
173 | bdrv_unref_child(bs, child); | ||
174 | } | ||
175 | |||
176 | assert(!bs->backing); | ||
177 | assert(!bs->file); | ||
178 | - bdrv_graph_wrunlock(bs); | ||
179 | + bdrv_graph_wrunlock(); | ||
180 | |||
181 | g_free(bs->opaque); | ||
182 | bs->opaque = NULL; | ||
183 | @@ -XXX,XX +XXX,XX @@ int bdrv_drop_filter(BlockDriverState *bs, Error **errp) | ||
184 | bdrv_graph_rdunlock_main_loop(); | ||
185 | |||
186 | bdrv_drained_begin(child_bs); | ||
187 | - bdrv_graph_wrlock(bs); | ||
188 | + bdrv_graph_wrlock(); | ||
189 | ret = bdrv_replace_node_common(bs, child_bs, true, true, errp); | ||
190 | - bdrv_graph_wrunlock(bs); | ||
191 | + bdrv_graph_wrunlock(); | ||
192 | bdrv_drained_end(child_bs); | ||
193 | |||
194 | return ret; | ||
195 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
196 | aio_context_acquire(old_context); | ||
197 | new_context = NULL; | ||
198 | |||
199 | - bdrv_graph_wrlock(bs_top); | ||
200 | + bdrv_graph_wrlock(); | ||
201 | |||
202 | child = bdrv_attach_child_noperm(bs_new, bs_top, "backing", | ||
203 | &child_of_bds, bdrv_backing_role(bs_new), | ||
204 | @@ -XXX,XX +XXX,XX @@ out: | ||
205 | tran_finalize(tran, ret); | ||
206 | |||
207 | bdrv_refresh_limits(bs_top, NULL, NULL); | ||
208 | - bdrv_graph_wrunlock(bs_top); | ||
209 | + bdrv_graph_wrunlock(); | ||
210 | |||
211 | bdrv_drained_end(bs_top); | ||
212 | bdrv_drained_end(bs_new); | ||
213 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, | ||
214 | bdrv_ref(old_bs); | ||
215 | bdrv_drained_begin(old_bs); | ||
216 | bdrv_drained_begin(new_bs); | ||
217 | - bdrv_graph_wrlock(new_bs); | ||
218 | + bdrv_graph_wrlock(); | ||
219 | |||
220 | bdrv_replace_child_tran(child, new_bs, tran); | ||
221 | |||
222 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, | ||
223 | |||
224 | tran_finalize(tran, ret); | ||
225 | |||
226 | - bdrv_graph_wrunlock(new_bs); | ||
227 | + bdrv_graph_wrunlock(); | ||
228 | bdrv_drained_end(old_bs); | ||
229 | bdrv_drained_end(new_bs); | ||
230 | bdrv_unref(old_bs); | ||
231 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, | ||
232 | bdrv_ref(bs); | ||
233 | bdrv_drained_begin(bs); | ||
234 | bdrv_drained_begin(new_node_bs); | ||
235 | - bdrv_graph_wrlock(new_node_bs); | ||
236 | + bdrv_graph_wrlock(); | ||
237 | ret = bdrv_replace_node(bs, new_node_bs, errp); | ||
238 | - bdrv_graph_wrunlock(new_node_bs); | ||
239 | + bdrv_graph_wrunlock(); | ||
240 | bdrv_drained_end(new_node_bs); | ||
241 | bdrv_drained_end(bs); | ||
242 | bdrv_unref(bs); | ||
243 | @@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, | ||
244 | |||
245 | bdrv_ref(top); | ||
246 | bdrv_drained_begin(base); | ||
247 | - bdrv_graph_wrlock(base); | ||
248 | + bdrv_graph_wrlock(); | ||
249 | |||
250 | if (!top->drv || !base->drv) { | ||
251 | goto exit_wrlock; | ||
252 | @@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, | ||
253 | * That's a FIXME. | ||
254 | */ | ||
255 | bdrv_replace_node_common(top, base, false, false, &local_err); | ||
256 | - bdrv_graph_wrunlock(base); | ||
257 | + bdrv_graph_wrunlock(); | ||
258 | |||
259 | if (local_err) { | ||
260 | error_report_err(local_err); | ||
261 | @@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, | ||
262 | goto exit; | ||
263 | |||
264 | exit_wrlock: | ||
265 | - bdrv_graph_wrunlock(base); | ||
266 | + bdrv_graph_wrunlock(); | ||
267 | exit: | ||
268 | bdrv_drained_end(base); | ||
269 | bdrv_unref(top); | ||
270 | diff --git a/block/backup.c b/block/backup.c | ||
271 | index XXXXXXX..XXXXXXX 100644 | ||
272 | --- a/block/backup.c | ||
273 | +++ b/block/backup.c | ||
274 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
275 | block_copy_set_speed(bcs, speed); | ||
276 | |||
277 | /* Required permissions are taken by copy-before-write filter target */ | ||
278 | - bdrv_graph_wrlock(target); | ||
279 | + bdrv_graph_wrlock(); | ||
280 | block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | ||
281 | &error_abort); | ||
282 | - bdrv_graph_wrunlock(target); | ||
283 | + bdrv_graph_wrunlock(); | ||
284 | |||
285 | return &job->common; | ||
286 | |||
287 | diff --git a/block/blklogwrites.c b/block/blklogwrites.c | ||
288 | index XXXXXXX..XXXXXXX 100644 | ||
289 | --- a/block/blklogwrites.c | ||
290 | +++ b/block/blklogwrites.c | ||
291 | @@ -XXX,XX +XXX,XX @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, | ||
292 | ret = 0; | ||
293 | fail_log: | ||
294 | if (ret < 0) { | ||
295 | - bdrv_graph_wrlock(NULL); | ||
296 | + bdrv_graph_wrlock(); | ||
297 | bdrv_unref_child(bs, s->log_file); | ||
298 | - bdrv_graph_wrunlock(NULL); | ||
299 | + bdrv_graph_wrunlock(); | ||
300 | s->log_file = NULL; | ||
301 | } | ||
302 | fail: | ||
303 | @@ -XXX,XX +XXX,XX @@ static void blk_log_writes_close(BlockDriverState *bs) | ||
304 | { | ||
305 | BDRVBlkLogWritesState *s = bs->opaque; | ||
306 | |||
307 | - bdrv_graph_wrlock(NULL); | ||
308 | + bdrv_graph_wrlock(); | ||
309 | bdrv_unref_child(bs, s->log_file); | ||
310 | s->log_file = NULL; | ||
311 | - bdrv_graph_wrunlock(NULL); | ||
312 | + bdrv_graph_wrunlock(); | ||
313 | } | ||
314 | |||
315 | static int64_t coroutine_fn GRAPH_RDLOCK | ||
316 | diff --git a/block/blkverify.c b/block/blkverify.c | ||
317 | index XXXXXXX..XXXXXXX 100644 | ||
318 | --- a/block/blkverify.c | ||
319 | +++ b/block/blkverify.c | ||
320 | @@ -XXX,XX +XXX,XX @@ static void blkverify_close(BlockDriverState *bs) | ||
321 | { | ||
322 | BDRVBlkverifyState *s = bs->opaque; | ||
323 | |||
324 | - bdrv_graph_wrlock(NULL); | ||
325 | + bdrv_graph_wrlock(); | ||
326 | bdrv_unref_child(bs, s->test_file); | ||
327 | s->test_file = NULL; | ||
328 | - bdrv_graph_wrunlock(NULL); | ||
329 | + bdrv_graph_wrunlock(); | ||
330 | } | ||
331 | |||
332 | static int64_t coroutine_fn GRAPH_RDLOCK | ||
333 | diff --git a/block/block-backend.c b/block/block-backend.c | 19 | diff --git a/block/block-backend.c b/block/block-backend.c |
334 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
335 | --- a/block/block-backend.c | 21 | --- a/block/block-backend.c |
336 | +++ b/block/block-backend.c | 22 | +++ b/block/block-backend.c |
337 | @@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk) | 23 | @@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk) |
338 | { | ||
339 | ThrottleGroupMember *tgm = &blk->public.throttle_group_member; | ||
340 | BdrvChild *root; | ||
341 | - AioContext *ctx; | ||
342 | |||
343 | GLOBAL_STATE_CODE(); | ||
344 | |||
345 | @@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk) | ||
346 | root = blk->root; | ||
347 | blk->root = NULL; | ||
348 | |||
349 | - ctx = bdrv_get_aio_context(root->bs); | ||
350 | - bdrv_graph_wrlock(root->bs); | ||
351 | + bdrv_graph_wrlock(); | ||
352 | bdrv_root_unref_child(root); | ||
353 | - bdrv_graph_wrunlock_ctx(ctx); | ||
354 | + bdrv_graph_wrunlock(); | ||
355 | } | ||
356 | |||
357 | /* | ||
358 | @@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk) | ||
359 | int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) | 24 | int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) |
360 | { | 25 | { |
361 | ThrottleGroupMember *tgm = &blk->public.throttle_group_member; | 26 | ThrottleGroupMember *tgm = &blk->public.throttle_group_member; |
362 | - AioContext *ctx = bdrv_get_aio_context(bs); | 27 | + uint64_t perm, shared_perm; |
363 | 28 | ||
364 | GLOBAL_STATE_CODE(); | 29 | GLOBAL_STATE_CODE(); |
365 | bdrv_ref(bs); | 30 | bdrv_ref(bs); |
366 | - bdrv_graph_wrlock(bs); | 31 | bdrv_graph_wrlock(); |
367 | + bdrv_graph_wrlock(); | 32 | + |
33 | + if ((bs->open_flags & BDRV_O_INACTIVE) && blk_can_inactivate(blk)) { | ||
34 | + blk->disable_perm = true; | ||
35 | + perm = 0; | ||
36 | + shared_perm = BLK_PERM_ALL; | ||
37 | + } else { | ||
38 | + perm = blk->perm; | ||
39 | + shared_perm = blk->shared_perm; | ||
40 | + } | ||
41 | + | ||
368 | blk->root = bdrv_root_attach_child(bs, "root", &child_root, | 42 | blk->root = bdrv_root_attach_child(bs, "root", &child_root, |
369 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | 43 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, |
370 | blk->perm, blk->shared_perm, | 44 | - blk->perm, blk->shared_perm, |
371 | blk, errp); | 45 | - blk, errp); |
372 | - bdrv_graph_wrunlock_ctx(ctx); | 46 | + perm, shared_perm, blk, errp); |
373 | + bdrv_graph_wrunlock(); | 47 | bdrv_graph_wrunlock(); |
374 | if (blk->root == NULL) { | 48 | if (blk->root == NULL) { |
375 | return -EPERM; | 49 | return -EPERM; |
376 | } | ||
377 | diff --git a/block/commit.c b/block/commit.c | ||
378 | index XXXXXXX..XXXXXXX 100644 | ||
379 | --- a/block/commit.c | ||
380 | +++ b/block/commit.c | ||
381 | @@ -XXX,XX +XXX,XX @@ static void commit_abort(Job *job) | ||
382 | bdrv_graph_rdunlock_main_loop(); | ||
383 | |||
384 | bdrv_drained_begin(commit_top_backing_bs); | ||
385 | - bdrv_graph_wrlock(commit_top_backing_bs); | ||
386 | + bdrv_graph_wrlock(); | ||
387 | bdrv_replace_node(s->commit_top_bs, commit_top_backing_bs, &error_abort); | ||
388 | - bdrv_graph_wrunlock(commit_top_backing_bs); | ||
389 | + bdrv_graph_wrunlock(); | ||
390 | bdrv_drained_end(commit_top_backing_bs); | ||
391 | |||
392 | bdrv_unref(s->commit_top_bs); | ||
393 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||
394 | * this is the responsibility of the interface (i.e. whoever calls | ||
395 | * commit_start()). | ||
396 | */ | ||
397 | - bdrv_graph_wrlock(top); | ||
398 | + bdrv_graph_wrlock(); | ||
399 | s->base_overlay = bdrv_find_overlay(top, base); | ||
400 | assert(s->base_overlay); | ||
401 | |||
402 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||
403 | ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, | ||
404 | iter_shared_perms, errp); | ||
405 | if (ret < 0) { | ||
406 | - bdrv_graph_wrunlock(top); | ||
407 | + bdrv_graph_wrunlock(); | ||
408 | goto fail; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { | ||
413 | - bdrv_graph_wrunlock(top); | ||
414 | + bdrv_graph_wrunlock(); | ||
415 | goto fail; | ||
416 | } | ||
417 | s->chain_frozen = true; | ||
418 | |||
419 | ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); | ||
420 | - bdrv_graph_wrunlock(top); | ||
421 | + bdrv_graph_wrunlock(); | ||
422 | |||
423 | if (ret < 0) { | ||
424 | goto fail; | ||
425 | @@ -XXX,XX +XXX,XX @@ fail: | ||
426 | * otherwise this would fail because of lack of permissions. */ | ||
427 | if (commit_top_bs) { | ||
428 | bdrv_drained_begin(top); | ||
429 | - bdrv_graph_wrlock(top); | ||
430 | + bdrv_graph_wrlock(); | ||
431 | bdrv_replace_node(commit_top_bs, top, &error_abort); | ||
432 | - bdrv_graph_wrunlock(top); | ||
433 | + bdrv_graph_wrunlock(); | ||
434 | bdrv_drained_end(top); | ||
435 | } | ||
436 | } | ||
437 | diff --git a/block/graph-lock.c b/block/graph-lock.c | ||
438 | index XXXXXXX..XXXXXXX 100644 | ||
439 | --- a/block/graph-lock.c | ||
440 | +++ b/block/graph-lock.c | ||
441 | @@ -XXX,XX +XXX,XX @@ static uint32_t reader_count(void) | ||
442 | return rd; | ||
443 | } | ||
444 | |||
445 | -void no_coroutine_fn bdrv_graph_wrlock(BlockDriverState *bs) | ||
446 | +void no_coroutine_fn bdrv_graph_wrlock(void) | ||
447 | { | ||
448 | - AioContext *ctx = NULL; | ||
449 | - | ||
450 | GLOBAL_STATE_CODE(); | ||
451 | assert(!qatomic_read(&has_writer)); | ||
452 | assert(!qemu_in_coroutine()); | ||
453 | |||
454 | - /* | ||
455 | - * Release only non-mainloop AioContext. The mainloop often relies on the | ||
456 | - * BQL and doesn't lock the main AioContext before doing things. | ||
457 | - */ | ||
458 | - if (bs) { | ||
459 | - ctx = bdrv_get_aio_context(bs); | ||
460 | - if (ctx != qemu_get_aio_context()) { | ||
461 | - aio_context_release(ctx); | ||
462 | - } else { | ||
463 | - ctx = NULL; | ||
464 | - } | ||
465 | - } | ||
466 | - | ||
467 | /* Make sure that constantly arriving new I/O doesn't cause starvation */ | ||
468 | bdrv_drain_all_begin_nopoll(); | ||
469 | |||
470 | @@ -XXX,XX +XXX,XX @@ void no_coroutine_fn bdrv_graph_wrlock(BlockDriverState *bs) | ||
471 | } while (reader_count() >= 1); | ||
472 | |||
473 | bdrv_drain_all_end(); | ||
474 | - | ||
475 | - if (ctx) { | ||
476 | - aio_context_acquire(bdrv_get_aio_context(bs)); | ||
477 | - } | ||
478 | } | ||
479 | |||
480 | -void no_coroutine_fn bdrv_graph_wrunlock_ctx(AioContext *ctx) | ||
481 | +void no_coroutine_fn bdrv_graph_wrunlock(void) | ||
482 | { | ||
483 | GLOBAL_STATE_CODE(); | ||
484 | assert(qatomic_read(&has_writer)); | ||
485 | |||
486 | - /* | ||
487 | - * Release only non-mainloop AioContext. The mainloop often relies on the | ||
488 | - * BQL and doesn't lock the main AioContext before doing things. | ||
489 | - */ | ||
490 | - if (ctx && ctx != qemu_get_aio_context()) { | ||
491 | - aio_context_release(ctx); | ||
492 | - } else { | ||
493 | - ctx = NULL; | ||
494 | - } | ||
495 | - | ||
496 | WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) { | ||
497 | /* | ||
498 | * No need for memory barriers, this works in pair with | ||
499 | @@ -XXX,XX +XXX,XX @@ void no_coroutine_fn bdrv_graph_wrunlock_ctx(AioContext *ctx) | ||
500 | * progress. | ||
501 | */ | ||
502 | aio_bh_poll(qemu_get_aio_context()); | ||
503 | - | ||
504 | - if (ctx) { | ||
505 | - aio_context_acquire(ctx); | ||
506 | - } | ||
507 | -} | ||
508 | - | ||
509 | -void no_coroutine_fn bdrv_graph_wrunlock(BlockDriverState *bs) | ||
510 | -{ | ||
511 | - AioContext *ctx = bs ? bdrv_get_aio_context(bs) : NULL; | ||
512 | - | ||
513 | - bdrv_graph_wrunlock_ctx(ctx); | ||
514 | } | ||
515 | |||
516 | void coroutine_fn bdrv_graph_co_rdlock(void) | ||
517 | diff --git a/block/mirror.c b/block/mirror.c | ||
518 | index XXXXXXX..XXXXXXX 100644 | ||
519 | --- a/block/mirror.c | ||
520 | +++ b/block/mirror.c | ||
521 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) | ||
522 | * check for an op blocker on @to_replace, and we have our own | ||
523 | * there. | ||
524 | */ | ||
525 | - bdrv_graph_wrlock(target_bs); | ||
526 | + bdrv_graph_wrlock(); | ||
527 | if (bdrv_recurse_can_replace(src, to_replace)) { | ||
528 | bdrv_replace_node(to_replace, target_bs, &local_err); | ||
529 | } else { | ||
530 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) | ||
531 | "would not lead to an abrupt change of visible data", | ||
532 | to_replace->node_name, target_bs->node_name); | ||
533 | } | ||
534 | - bdrv_graph_wrunlock(target_bs); | ||
535 | + bdrv_graph_wrunlock(); | ||
536 | bdrv_drained_end(to_replace); | ||
537 | if (local_err) { | ||
538 | error_report_err(local_err); | ||
539 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) | ||
540 | * valid. | ||
541 | */ | ||
542 | block_job_remove_all_bdrv(bjob); | ||
543 | - bdrv_graph_wrlock(mirror_top_bs); | ||
544 | + bdrv_graph_wrlock(); | ||
545 | bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); | ||
546 | - bdrv_graph_wrunlock(mirror_top_bs); | ||
547 | + bdrv_graph_wrunlock(); | ||
548 | |||
549 | bdrv_drained_end(target_bs); | ||
550 | bdrv_unref(target_bs); | ||
551 | @@ -XXX,XX +XXX,XX @@ static BlockJob *mirror_start_job( | ||
552 | */ | ||
553 | bdrv_disable_dirty_bitmap(s->dirty_bitmap); | ||
554 | |||
555 | - bdrv_graph_wrlock(bs); | ||
556 | + bdrv_graph_wrlock(); | ||
557 | ret = block_job_add_bdrv(&s->common, "source", bs, 0, | ||
558 | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | | ||
559 | BLK_PERM_CONSISTENT_READ, | ||
560 | errp); | ||
561 | if (ret < 0) { | ||
562 | - bdrv_graph_wrunlock(bs); | ||
563 | + bdrv_graph_wrunlock(); | ||
564 | goto fail; | ||
565 | } | ||
566 | |||
567 | @@ -XXX,XX +XXX,XX @@ static BlockJob *mirror_start_job( | ||
568 | ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, | ||
569 | iter_shared_perms, errp); | ||
570 | if (ret < 0) { | ||
571 | - bdrv_graph_wrunlock(bs); | ||
572 | + bdrv_graph_wrunlock(); | ||
573 | goto fail; | ||
574 | } | ||
575 | } | ||
576 | |||
577 | if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { | ||
578 | - bdrv_graph_wrunlock(bs); | ||
579 | + bdrv_graph_wrunlock(); | ||
580 | goto fail; | ||
581 | } | ||
582 | } | ||
583 | - bdrv_graph_wrunlock(bs); | ||
584 | + bdrv_graph_wrunlock(); | ||
585 | |||
586 | QTAILQ_INIT(&s->ops_in_flight); | ||
587 | |||
588 | @@ -XXX,XX +XXX,XX @@ fail: | ||
589 | |||
590 | bs_opaque->stop = true; | ||
591 | bdrv_drained_begin(bs); | ||
592 | - bdrv_graph_wrlock(bs); | ||
593 | + bdrv_graph_wrlock(); | ||
594 | assert(mirror_top_bs->backing->bs == bs); | ||
595 | bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, | ||
596 | &error_abort); | ||
597 | bdrv_replace_node(mirror_top_bs, bs, &error_abort); | ||
598 | - bdrv_graph_wrunlock(bs); | ||
599 | + bdrv_graph_wrunlock(); | ||
600 | bdrv_drained_end(bs); | ||
601 | |||
602 | bdrv_unref(mirror_top_bs); | ||
603 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
604 | index XXXXXXX..XXXXXXX 100644 | ||
605 | --- a/block/qcow2.c | ||
606 | +++ b/block/qcow2.c | ||
607 | @@ -XXX,XX +XXX,XX @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file) | ||
608 | if (close_data_file && has_data_file(bs)) { | ||
609 | GLOBAL_STATE_CODE(); | ||
610 | bdrv_graph_rdunlock_main_loop(); | ||
611 | - bdrv_graph_wrlock(NULL); | ||
612 | + bdrv_graph_wrlock(); | ||
613 | bdrv_unref_child(bs, s->data_file); | ||
614 | - bdrv_graph_wrunlock(NULL); | ||
615 | + bdrv_graph_wrunlock(); | ||
616 | s->data_file = NULL; | ||
617 | bdrv_graph_rdlock_main_loop(); | ||
618 | } | ||
619 | diff --git a/block/quorum.c b/block/quorum.c | ||
620 | index XXXXXXX..XXXXXXX 100644 | ||
621 | --- a/block/quorum.c | ||
622 | +++ b/block/quorum.c | ||
623 | @@ -XXX,XX +XXX,XX @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||
624 | |||
625 | close_exit: | ||
626 | /* cleanup on error */ | ||
627 | - bdrv_graph_wrlock(NULL); | ||
628 | + bdrv_graph_wrlock(); | ||
629 | for (i = 0; i < s->num_children; i++) { | ||
630 | if (!opened[i]) { | ||
631 | continue; | ||
632 | } | ||
633 | bdrv_unref_child(bs, s->children[i]); | ||
634 | } | ||
635 | - bdrv_graph_wrunlock(NULL); | ||
636 | + bdrv_graph_wrunlock(); | ||
637 | g_free(s->children); | ||
638 | g_free(opened); | ||
639 | exit: | ||
640 | @@ -XXX,XX +XXX,XX @@ static void quorum_close(BlockDriverState *bs) | ||
641 | BDRVQuorumState *s = bs->opaque; | ||
642 | int i; | ||
643 | |||
644 | - bdrv_graph_wrlock(NULL); | ||
645 | + bdrv_graph_wrlock(); | ||
646 | for (i = 0; i < s->num_children; i++) { | ||
647 | bdrv_unref_child(bs, s->children[i]); | ||
648 | } | ||
649 | - bdrv_graph_wrunlock(NULL); | ||
650 | + bdrv_graph_wrunlock(); | ||
651 | |||
652 | g_free(s->children); | ||
653 | } | ||
654 | diff --git a/block/replication.c b/block/replication.c | ||
655 | index XXXXXXX..XXXXXXX 100644 | ||
656 | --- a/block/replication.c | ||
657 | +++ b/block/replication.c | ||
658 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
659 | return; | ||
660 | } | ||
661 | |||
662 | - bdrv_graph_wrlock(bs); | ||
663 | + bdrv_graph_wrlock(); | ||
664 | |||
665 | bdrv_ref(hidden_disk->bs); | ||
666 | s->hidden_disk = bdrv_attach_child(bs, hidden_disk->bs, "hidden disk", | ||
667 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
668 | &local_err); | ||
669 | if (local_err) { | ||
670 | error_propagate(errp, local_err); | ||
671 | - bdrv_graph_wrunlock(bs); | ||
672 | + bdrv_graph_wrunlock(); | ||
673 | aio_context_release(aio_context); | ||
674 | return; | ||
675 | } | ||
676 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
677 | BDRV_CHILD_DATA, &local_err); | ||
678 | if (local_err) { | ||
679 | error_propagate(errp, local_err); | ||
680 | - bdrv_graph_wrunlock(bs); | ||
681 | + bdrv_graph_wrunlock(); | ||
682 | aio_context_release(aio_context); | ||
683 | return; | ||
684 | } | ||
685 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
686 | if (!top_bs || !bdrv_is_root_node(top_bs) || | ||
687 | !check_top_bs(top_bs, bs)) { | ||
688 | error_setg(errp, "No top_bs or it is invalid"); | ||
689 | - bdrv_graph_wrunlock(bs); | ||
690 | + bdrv_graph_wrunlock(); | ||
691 | reopen_backing_file(bs, false, NULL); | ||
692 | aio_context_release(aio_context); | ||
693 | return; | ||
694 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
695 | bdrv_op_block_all(top_bs, s->blocker); | ||
696 | bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker); | ||
697 | |||
698 | - bdrv_graph_wrunlock(bs); | ||
699 | + bdrv_graph_wrunlock(); | ||
700 | |||
701 | s->backup_job = backup_job_create( | ||
702 | NULL, s->secondary_disk->bs, s->hidden_disk->bs, | ||
703 | @@ -XXX,XX +XXX,XX @@ static void replication_done(void *opaque, int ret) | ||
704 | if (ret == 0) { | ||
705 | s->stage = BLOCK_REPLICATION_DONE; | ||
706 | |||
707 | - bdrv_graph_wrlock(NULL); | ||
708 | + bdrv_graph_wrlock(); | ||
709 | bdrv_unref_child(bs, s->secondary_disk); | ||
710 | s->secondary_disk = NULL; | ||
711 | bdrv_unref_child(bs, s->hidden_disk); | ||
712 | s->hidden_disk = NULL; | ||
713 | - bdrv_graph_wrunlock(NULL); | ||
714 | + bdrv_graph_wrunlock(); | ||
715 | |||
716 | s->error = 0; | ||
717 | } else { | ||
718 | diff --git a/block/snapshot.c b/block/snapshot.c | ||
719 | index XXXXXXX..XXXXXXX 100644 | ||
720 | --- a/block/snapshot.c | ||
721 | +++ b/block/snapshot.c | ||
722 | @@ -XXX,XX +XXX,XX @@ int bdrv_snapshot_goto(BlockDriverState *bs, | ||
723 | } | ||
724 | |||
725 | /* .bdrv_open() will re-attach it */ | ||
726 | - bdrv_graph_wrlock(NULL); | ||
727 | + bdrv_graph_wrlock(); | ||
728 | bdrv_unref_child(bs, fallback); | ||
729 | - bdrv_graph_wrunlock(NULL); | ||
730 | + bdrv_graph_wrunlock(); | ||
731 | |||
732 | ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); | ||
733 | open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); | ||
734 | diff --git a/block/stream.c b/block/stream.c | ||
735 | index XXXXXXX..XXXXXXX 100644 | ||
736 | --- a/block/stream.c | ||
737 | +++ b/block/stream.c | ||
738 | @@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job) | ||
739 | } | ||
740 | } | ||
741 | |||
742 | - bdrv_graph_wrlock(s->target_bs); | ||
743 | + bdrv_graph_wrlock(); | ||
744 | bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err); | ||
745 | - bdrv_graph_wrunlock(s->target_bs); | ||
746 | + bdrv_graph_wrunlock(); | ||
747 | |||
748 | /* | ||
749 | * This call will do I/O, so the graph can change again from here on. | ||
750 | @@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs, | ||
751 | * already have our own plans. Also don't allow resize as the image size is | ||
752 | * queried only at the job start and then cached. | ||
753 | */ | ||
754 | - bdrv_graph_wrlock(bs); | ||
755 | + bdrv_graph_wrlock(); | ||
756 | if (block_job_add_bdrv(&s->common, "active node", bs, 0, | ||
757 | basic_flags | BLK_PERM_WRITE, errp)) { | ||
758 | - bdrv_graph_wrunlock(bs); | ||
759 | + bdrv_graph_wrunlock(); | ||
760 | goto fail; | ||
761 | } | ||
762 | |||
763 | @@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs, | ||
764 | ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, | ||
765 | basic_flags, errp); | ||
766 | if (ret < 0) { | ||
767 | - bdrv_graph_wrunlock(bs); | ||
768 | + bdrv_graph_wrunlock(); | ||
769 | goto fail; | ||
770 | } | ||
771 | } | ||
772 | - bdrv_graph_wrunlock(bs); | ||
773 | + bdrv_graph_wrunlock(); | ||
774 | |||
775 | s->base_overlay = base_overlay; | ||
776 | s->above_base = above_base; | ||
777 | diff --git a/block/vmdk.c b/block/vmdk.c | ||
778 | index XXXXXXX..XXXXXXX 100644 | ||
779 | --- a/block/vmdk.c | ||
780 | +++ b/block/vmdk.c | ||
781 | @@ -XXX,XX +XXX,XX @@ static void vmdk_free_extents(BlockDriverState *bs) | ||
782 | BDRVVmdkState *s = bs->opaque; | ||
783 | VmdkExtent *e; | ||
784 | |||
785 | - bdrv_graph_wrlock(NULL); | ||
786 | + bdrv_graph_wrlock(); | ||
787 | for (i = 0; i < s->num_extents; i++) { | ||
788 | e = &s->extents[i]; | ||
789 | g_free(e->l1_table); | ||
790 | @@ -XXX,XX +XXX,XX @@ static void vmdk_free_extents(BlockDriverState *bs) | ||
791 | bdrv_unref_child(bs, e->file); | ||
792 | } | ||
793 | } | ||
794 | - bdrv_graph_wrunlock(NULL); | ||
795 | + bdrv_graph_wrunlock(); | ||
796 | |||
797 | g_free(s->extents); | ||
798 | } | ||
799 | @@ -XXX,XX +XXX,XX @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, | ||
800 | 0, 0, 0, 0, 0, &extent, errp); | ||
801 | if (ret < 0) { | ||
802 | bdrv_graph_rdunlock_main_loop(); | ||
803 | - bdrv_graph_wrlock(NULL); | ||
804 | + bdrv_graph_wrlock(); | ||
805 | bdrv_unref_child(bs, extent_file); | ||
806 | - bdrv_graph_wrunlock(NULL); | ||
807 | + bdrv_graph_wrunlock(); | ||
808 | bdrv_graph_rdlock_main_loop(); | ||
809 | goto out; | ||
810 | } | ||
811 | @@ -XXX,XX +XXX,XX @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, | ||
812 | g_free(buf); | ||
813 | if (ret) { | ||
814 | bdrv_graph_rdunlock_main_loop(); | ||
815 | - bdrv_graph_wrlock(NULL); | ||
816 | + bdrv_graph_wrlock(); | ||
817 | bdrv_unref_child(bs, extent_file); | ||
818 | - bdrv_graph_wrunlock(NULL); | ||
819 | + bdrv_graph_wrunlock(); | ||
820 | bdrv_graph_rdlock_main_loop(); | ||
821 | goto out; | ||
822 | } | ||
823 | @@ -XXX,XX +XXX,XX @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, | ||
824 | ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp); | ||
825 | if (ret) { | ||
826 | bdrv_graph_rdunlock_main_loop(); | ||
827 | - bdrv_graph_wrlock(NULL); | ||
828 | + bdrv_graph_wrlock(); | ||
829 | bdrv_unref_child(bs, extent_file); | ||
830 | - bdrv_graph_wrunlock(NULL); | ||
831 | + bdrv_graph_wrunlock(); | ||
832 | bdrv_graph_rdlock_main_loop(); | ||
833 | goto out; | ||
834 | } | ||
835 | @@ -XXX,XX +XXX,XX @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, | ||
836 | } else { | ||
837 | error_setg(errp, "Unsupported extent type '%s'", type); | ||
838 | bdrv_graph_rdunlock_main_loop(); | ||
839 | - bdrv_graph_wrlock(NULL); | ||
840 | + bdrv_graph_wrlock(); | ||
841 | bdrv_unref_child(bs, extent_file); | ||
842 | - bdrv_graph_wrunlock(NULL); | ||
843 | + bdrv_graph_wrunlock(); | ||
844 | bdrv_graph_rdlock_main_loop(); | ||
845 | ret = -ENOTSUP; | ||
846 | goto out; | ||
847 | diff --git a/blockdev.c b/blockdev.c | ||
848 | index XXXXXXX..XXXXXXX 100644 | ||
849 | --- a/blockdev.c | ||
850 | +++ b/blockdev.c | ||
851 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(void *opaque) | ||
852 | } | ||
853 | |||
854 | bdrv_drained_begin(state->new_bs); | ||
855 | - bdrv_graph_wrlock(state->old_bs); | ||
856 | + bdrv_graph_wrlock(); | ||
857 | bdrv_replace_node(state->new_bs, state->old_bs, &error_abort); | ||
858 | - bdrv_graph_wrunlock(state->old_bs); | ||
859 | + bdrv_graph_wrunlock(); | ||
860 | bdrv_drained_end(state->new_bs); | ||
861 | |||
862 | bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ | ||
863 | @@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_change(const char *parent, const char *child, | ||
864 | BlockDriverState *parent_bs, *new_bs = NULL; | ||
865 | BdrvChild *p_child; | ||
866 | |||
867 | - bdrv_graph_wrlock(NULL); | ||
868 | + bdrv_graph_wrlock(); | ||
869 | |||
870 | parent_bs = bdrv_lookup_bs(parent, parent, errp); | ||
871 | if (!parent_bs) { | ||
872 | @@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_change(const char *parent, const char *child, | ||
873 | } | ||
874 | |||
875 | out: | ||
876 | - bdrv_graph_wrunlock(NULL); | ||
877 | + bdrv_graph_wrunlock(); | ||
878 | } | ||
879 | |||
880 | BlockJobInfoList *qmp_query_block_jobs(Error **errp) | ||
881 | diff --git a/blockjob.c b/blockjob.c | ||
882 | index XXXXXXX..XXXXXXX 100644 | ||
883 | --- a/blockjob.c | ||
884 | +++ b/blockjob.c | ||
885 | @@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job) | ||
886 | * to process an already freed BdrvChild. | ||
887 | */ | ||
888 | aio_context_release(job->job.aio_context); | ||
889 | - bdrv_graph_wrlock(NULL); | ||
890 | + bdrv_graph_wrlock(); | ||
891 | aio_context_acquire(job->job.aio_context); | ||
892 | while (job->nodes) { | ||
893 | GSList *l = job->nodes; | ||
894 | @@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job) | ||
895 | |||
896 | g_slist_free_1(l); | ||
897 | } | ||
898 | - bdrv_graph_wrunlock_ctx(job->job.aio_context); | ||
899 | + bdrv_graph_wrunlock(); | ||
900 | } | ||
901 | |||
902 | bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) | ||
903 | @@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, | ||
904 | int ret; | ||
905 | GLOBAL_STATE_CODE(); | ||
906 | |||
907 | - bdrv_graph_wrlock(bs); | ||
908 | + bdrv_graph_wrlock(); | ||
909 | |||
910 | if (job_id == NULL && !(flags & JOB_INTERNAL)) { | ||
911 | job_id = bdrv_get_device_name(bs); | ||
912 | @@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, | ||
913 | job = job_create(job_id, &driver->job_driver, txn, bdrv_get_aio_context(bs), | ||
914 | flags, cb, opaque, errp); | ||
915 | if (job == NULL) { | ||
916 | - bdrv_graph_wrunlock(bs); | ||
917 | + bdrv_graph_wrunlock(); | ||
918 | return NULL; | ||
919 | } | ||
920 | |||
921 | @@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, | ||
922 | goto fail; | ||
923 | } | ||
924 | |||
925 | - bdrv_graph_wrunlock(bs); | ||
926 | + bdrv_graph_wrunlock(); | ||
927 | return job; | ||
928 | |||
929 | fail: | ||
930 | - bdrv_graph_wrunlock(bs); | ||
931 | + bdrv_graph_wrunlock(); | ||
932 | job_early_fail(&job->job); | ||
933 | return NULL; | ||
934 | } | ||
935 | diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c | ||
936 | index XXXXXXX..XXXXXXX 100644 | ||
937 | --- a/tests/unit/test-bdrv-drain.c | ||
938 | +++ b/tests/unit/test-bdrv-drain.c | ||
939 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
940 | tjob->bs = src; | ||
941 | job = &tjob->common; | ||
942 | |||
943 | - bdrv_graph_wrlock(target); | ||
944 | + bdrv_graph_wrlock(); | ||
945 | block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); | ||
946 | - bdrv_graph_wrunlock(target); | ||
947 | + bdrv_graph_wrunlock(); | ||
948 | |||
949 | switch (result) { | ||
950 | case TEST_JOB_SUCCESS: | ||
951 | @@ -XXX,XX +XXX,XX @@ static void bdrv_test_top_close(BlockDriverState *bs) | ||
952 | { | ||
953 | BdrvChild *c, *next_c; | ||
954 | |||
955 | - bdrv_graph_wrlock(NULL); | ||
956 | + bdrv_graph_wrlock(); | ||
957 | QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { | ||
958 | bdrv_unref_child(bs, c); | ||
959 | } | ||
960 | - bdrv_graph_wrunlock(NULL); | ||
961 | + bdrv_graph_wrunlock(); | ||
962 | } | ||
963 | |||
964 | static int coroutine_fn GRAPH_RDLOCK | ||
965 | @@ -XXX,XX +XXX,XX @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, | ||
966 | |||
967 | null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, | ||
968 | &error_abort); | ||
969 | - bdrv_graph_wrlock(NULL); | ||
970 | + bdrv_graph_wrlock(); | ||
971 | bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, | ||
972 | BDRV_CHILD_DATA, &error_abort); | ||
973 | - bdrv_graph_wrunlock(NULL); | ||
974 | + bdrv_graph_wrunlock(); | ||
975 | |||
976 | /* This child will be the one to pass to requests through to, and | ||
977 | * it will stall until a drain occurs */ | ||
978 | @@ -XXX,XX +XXX,XX @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, | ||
979 | &error_abort); | ||
980 | child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; | ||
981 | /* Takes our reference to child_bs */ | ||
982 | - bdrv_graph_wrlock(NULL); | ||
983 | + bdrv_graph_wrlock(); | ||
984 | tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", | ||
985 | &child_of_bds, | ||
986 | BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, | ||
987 | &error_abort); | ||
988 | - bdrv_graph_wrunlock(NULL); | ||
989 | + bdrv_graph_wrunlock(); | ||
990 | |||
991 | /* This child is just there to be deleted | ||
992 | * (for detach_instead_of_delete == true) */ | ||
993 | null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, | ||
994 | &error_abort); | ||
995 | - bdrv_graph_wrlock(NULL); | ||
996 | + bdrv_graph_wrlock(); | ||
997 | bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, | ||
998 | &error_abort); | ||
999 | - bdrv_graph_wrunlock(NULL); | ||
1000 | + bdrv_graph_wrunlock(); | ||
1001 | |||
1002 | blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); | ||
1003 | blk_insert_bs(blk, bs, &error_abort); | ||
1004 | @@ -XXX,XX +XXX,XX @@ static void no_coroutine_fn detach_indirect_bh(void *opaque) | ||
1005 | |||
1006 | bdrv_dec_in_flight(data->child_b->bs); | ||
1007 | |||
1008 | - bdrv_graph_wrlock(NULL); | ||
1009 | + bdrv_graph_wrlock(); | ||
1010 | bdrv_unref_child(data->parent_b, data->child_b); | ||
1011 | |||
1012 | bdrv_ref(data->c); | ||
1013 | data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", | ||
1014 | &child_of_bds, BDRV_CHILD_DATA, | ||
1015 | &error_abort); | ||
1016 | - bdrv_graph_wrunlock(NULL); | ||
1017 | + bdrv_graph_wrunlock(); | ||
1018 | } | ||
1019 | |||
1020 | static void coroutine_mixed_fn detach_by_parent_aio_cb(void *opaque, int ret) | ||
1021 | @@ -XXX,XX +XXX,XX @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) | ||
1022 | /* Set child relationships */ | ||
1023 | bdrv_ref(b); | ||
1024 | bdrv_ref(a); | ||
1025 | - bdrv_graph_wrlock(NULL); | ||
1026 | + bdrv_graph_wrlock(); | ||
1027 | child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, | ||
1028 | BDRV_CHILD_DATA, &error_abort); | ||
1029 | child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_of_bds, | ||
1030 | @@ -XXX,XX +XXX,XX @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) | ||
1031 | bdrv_attach_child(parent_a, a, "PA-A", | ||
1032 | by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, | ||
1033 | BDRV_CHILD_DATA, &error_abort); | ||
1034 | - bdrv_graph_wrunlock(NULL); | ||
1035 | + bdrv_graph_wrunlock(); | ||
1036 | |||
1037 | g_assert_cmpint(parent_a->refcnt, ==, 1); | ||
1038 | g_assert_cmpint(parent_b->refcnt, ==, 1); | ||
1039 | @@ -XXX,XX +XXX,XX @@ static void test_drop_intermediate_poll(void) | ||
1040 | * Establish the chain last, so the chain links are the first | ||
1041 | * elements in the BDS.parents lists | ||
1042 | */ | ||
1043 | - bdrv_graph_wrlock(NULL); | ||
1044 | + bdrv_graph_wrlock(); | ||
1045 | for (i = 0; i < 3; i++) { | ||
1046 | if (i) { | ||
1047 | /* Takes the reference to chain[i - 1] */ | ||
1048 | @@ -XXX,XX +XXX,XX @@ static void test_drop_intermediate_poll(void) | ||
1049 | &chain_child_class, BDRV_CHILD_COW, &error_abort); | ||
1050 | } | ||
1051 | } | ||
1052 | - bdrv_graph_wrunlock(NULL); | ||
1053 | + bdrv_graph_wrunlock(); | ||
1054 | |||
1055 | job = block_job_create("job", &test_simple_job_driver, NULL, job_node, | ||
1056 | 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); | ||
1057 | @@ -XXX,XX +XXX,XX @@ static void do_test_replace_child_mid_drain(int old_drain_count, | ||
1058 | new_child_bs->total_sectors = 1; | ||
1059 | |||
1060 | bdrv_ref(old_child_bs); | ||
1061 | - bdrv_graph_wrlock(NULL); | ||
1062 | + bdrv_graph_wrlock(); | ||
1063 | bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds, | ||
1064 | BDRV_CHILD_COW, &error_abort); | ||
1065 | - bdrv_graph_wrunlock(NULL); | ||
1066 | + bdrv_graph_wrunlock(); | ||
1067 | parent_s->setup_completed = true; | ||
1068 | |||
1069 | for (i = 0; i < old_drain_count; i++) { | ||
1070 | @@ -XXX,XX +XXX,XX @@ static void do_test_replace_child_mid_drain(int old_drain_count, | ||
1071 | g_assert(parent_bs->quiesce_counter == old_drain_count); | ||
1072 | bdrv_drained_begin(old_child_bs); | ||
1073 | bdrv_drained_begin(new_child_bs); | ||
1074 | - bdrv_graph_wrlock(NULL); | ||
1075 | + bdrv_graph_wrlock(); | ||
1076 | bdrv_replace_node(old_child_bs, new_child_bs, &error_abort); | ||
1077 | - bdrv_graph_wrunlock(NULL); | ||
1078 | + bdrv_graph_wrunlock(); | ||
1079 | bdrv_drained_end(new_child_bs); | ||
1080 | bdrv_drained_end(old_child_bs); | ||
1081 | g_assert(parent_bs->quiesce_counter == new_drain_count); | ||
1082 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
1083 | index XXXXXXX..XXXXXXX 100644 | ||
1084 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
1085 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
1086 | @@ -XXX,XX +XXX,XX @@ static void test_update_perm_tree(void) | ||
1087 | |||
1088 | blk_insert_bs(root, bs, &error_abort); | ||
1089 | |||
1090 | - bdrv_graph_wrlock(NULL); | ||
1091 | + bdrv_graph_wrlock(); | ||
1092 | bdrv_attach_child(filter, bs, "child", &child_of_bds, | ||
1093 | BDRV_CHILD_DATA, &error_abort); | ||
1094 | - bdrv_graph_wrunlock(NULL); | ||
1095 | + bdrv_graph_wrunlock(); | ||
1096 | |||
1097 | aio_context_acquire(qemu_get_aio_context()); | ||
1098 | ret = bdrv_append(filter, bs, NULL); | ||
1099 | @@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void) | ||
1100 | |||
1101 | bdrv_set_backing_hd(target, bs, &error_abort); | ||
1102 | |||
1103 | - bdrv_graph_wrlock(NULL); | ||
1104 | + bdrv_graph_wrlock(); | ||
1105 | g_assert(target->backing->bs == bs); | ||
1106 | bdrv_attach_child(filter, target, "target", &child_of_bds, | ||
1107 | BDRV_CHILD_DATA, &error_abort); | ||
1108 | - bdrv_graph_wrunlock(NULL); | ||
1109 | + bdrv_graph_wrunlock(); | ||
1110 | aio_context_acquire(qemu_get_aio_context()); | ||
1111 | bdrv_append(filter, bs, &error_abort); | ||
1112 | aio_context_release(qemu_get_aio_context()); | ||
1113 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_exclusive_write(void) | ||
1114 | bdrv_ref(base); | ||
1115 | bdrv_ref(fl1); | ||
1116 | |||
1117 | - bdrv_graph_wrlock(NULL); | ||
1118 | + bdrv_graph_wrlock(); | ||
1119 | bdrv_attach_child(top, fl1, "backing", &child_of_bds, | ||
1120 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | ||
1121 | &error_abort); | ||
1122 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_exclusive_write(void) | ||
1123 | &error_abort); | ||
1124 | |||
1125 | bdrv_replace_node(fl1, fl2, &error_abort); | ||
1126 | - bdrv_graph_wrunlock(NULL); | ||
1127 | + bdrv_graph_wrunlock(); | ||
1128 | |||
1129 | bdrv_drained_end(fl2); | ||
1130 | bdrv_drained_end(fl1); | ||
1131 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void) | ||
1132 | */ | ||
1133 | bdrv_ref(base); | ||
1134 | |||
1135 | - bdrv_graph_wrlock(NULL); | ||
1136 | + bdrv_graph_wrlock(); | ||
1137 | bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA, | ||
1138 | &error_abort); | ||
1139 | c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds, | ||
1140 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void) | ||
1141 | bdrv_attach_child(fl2, base, "backing", &child_of_bds, | ||
1142 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | ||
1143 | &error_abort); | ||
1144 | - bdrv_graph_wrunlock(NULL); | ||
1145 | + bdrv_graph_wrunlock(); | ||
1146 | |||
1147 | /* Select fl1 as first child to be active */ | ||
1148 | s->selected = c_fl1; | ||
1149 | @@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void) | ||
1150 | BlockDriverState *base = no_perm_node("base"); | ||
1151 | BlockDriverState *fl = exclusive_writer_node("fl1"); | ||
1152 | |||
1153 | - bdrv_graph_wrlock(NULL); | ||
1154 | + bdrv_graph_wrlock(); | ||
1155 | bdrv_attach_child(top, base, "backing", &child_of_bds, | ||
1156 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | ||
1157 | &error_abort); | ||
1158 | - bdrv_graph_wrunlock(NULL); | ||
1159 | + bdrv_graph_wrunlock(); | ||
1160 | |||
1161 | aio_context_acquire(qemu_get_aio_context()); | ||
1162 | bdrv_append(fl, base, &error_abort); | ||
1163 | diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py | ||
1164 | index XXXXXXX..XXXXXXX 100644 | ||
1165 | --- a/scripts/block-coroutine-wrapper.py | ||
1166 | +++ b/scripts/block-coroutine-wrapper.py | ||
1167 | @@ -XXX,XX +XXX,XX @@ def gen_no_co_wrapper(func: FuncDecl) -> str: | ||
1168 | graph_lock=' bdrv_graph_rdlock_main_loop();' | ||
1169 | graph_unlock=' bdrv_graph_rdunlock_main_loop();' | ||
1170 | elif func.graph_wrlock: | ||
1171 | - graph_lock=' bdrv_graph_wrlock(NULL);' | ||
1172 | - graph_unlock=' bdrv_graph_wrunlock(NULL);' | ||
1173 | + graph_lock=' bdrv_graph_wrlock();' | ||
1174 | + graph_unlock=' bdrv_graph_wrunlock();' | ||
1175 | |||
1176 | return f"""\ | ||
1177 | /* | ||
1178 | -- | 50 | -- |
1179 | 2.43.0 | 51 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | Currently, block exports can't handle inactive images correctly. |
---|---|---|---|
2 | Incoming write requests would run into assertion failures. Make sure | ||
3 | that we return an error when creating an export can't activate the | ||
4 | image. | ||
2 | 5 | ||
3 | StringOutputVisitor crashes when it visits a struct because | 6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | ->start_struct() is NULL. | 7 | Acked-by: Fabiano Rosas <farosas@suse.de> |
5 | 8 | Reviewed-by: Eric Blake <eblake@redhat.com> | |
6 | Show "<omitted>" instead of crashing. This is necessary because the | 9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
7 | virtio-blk-pci iothread-vq-mapping parameter that I'd like to introduce | 10 | Message-ID: <20250204211407.381505-11-kwolf@redhat.com> |
8 | soon is a list of IOThreadMapping structs. | ||
9 | |||
10 | This patch is a quick fix to solve the crash, but the long-term solution | ||
11 | is replacing StringOutputVisitor with something that can handle the full | ||
12 | gamut of values in QEMU. | ||
13 | |||
14 | Cc: Markus Armbruster <armbru@redhat.com> | ||
15 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
16 | Message-ID: <20231212134934.500289-1-stefanha@redhat.com> | ||
17 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
18 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | ||
19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
20 | --- | 12 | --- |
21 | include/qapi/string-output-visitor.h | 6 +++--- | 13 | block/export/export.c | 6 +++++- |
22 | qapi/string-output-visitor.c | 16 ++++++++++++++++ | 14 | 1 file changed, 5 insertions(+), 1 deletion(-) |
23 | 2 files changed, 19 insertions(+), 3 deletions(-) | ||
24 | 15 | ||
25 | diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h | 16 | diff --git a/block/export/export.c b/block/export/export.c |
26 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/include/qapi/string-output-visitor.h | 18 | --- a/block/export/export.c |
28 | +++ b/include/qapi/string-output-visitor.h | 19 | +++ b/block/export/export.c |
29 | @@ -XXX,XX +XXX,XX @@ typedef struct StringOutputVisitor StringOutputVisitor; | 20 | @@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) |
30 | * If everything else succeeds, pass @result to visit_complete() to | 21 | * ctx was acquired in the caller. |
31 | * collect the result of the visit. | 22 | */ |
32 | * | 23 | bdrv_graph_rdlock_main_loop(); |
33 | - * The string output visitor does not implement support for visiting | 24 | - bdrv_activate(bs, NULL); |
34 | - * QAPI structs, alternates, null, or arbitrary QTypes. It also | 25 | + ret = bdrv_activate(bs, errp); |
35 | - * requires a non-null list argument to visit_start_list(). | 26 | + if (ret < 0) { |
36 | + * The string output visitor does not implement support for alternates, null, | 27 | + bdrv_graph_rdunlock_main_loop(); |
37 | + * or arbitrary QTypes. Struct fields are not shown. It also requires a | 28 | + goto fail; |
38 | + * non-null list argument to visit_start_list(). | 29 | + } |
39 | */ | 30 | bdrv_graph_rdunlock_main_loop(); |
40 | Visitor *string_output_visitor_new(bool human, char **result); | 31 | |
41 | 32 | perm = BLK_PERM_CONSISTENT_READ; | |
42 | diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c | ||
43 | index XXXXXXX..XXXXXXX 100644 | ||
44 | --- a/qapi/string-output-visitor.c | ||
45 | +++ b/qapi/string-output-visitor.c | ||
46 | @@ -XXX,XX +XXX,XX @@ static bool print_type_null(Visitor *v, const char *name, QNull **obj, | ||
47 | return true; | ||
48 | } | ||
49 | |||
50 | +static bool start_struct(Visitor *v, const char *name, void **obj, | ||
51 | + size_t size, Error **errp) | ||
52 | +{ | ||
53 | + return true; | ||
54 | +} | ||
55 | + | ||
56 | +static void end_struct(Visitor *v, void **obj) | ||
57 | +{ | ||
58 | + StringOutputVisitor *sov = to_sov(v); | ||
59 | + | ||
60 | + /* TODO actually print struct fields */ | ||
61 | + string_output_set(sov, g_strdup("<omitted>")); | ||
62 | +} | ||
63 | + | ||
64 | static bool | ||
65 | start_list(Visitor *v, const char *name, GenericList **list, size_t size, | ||
66 | Error **errp) | ||
67 | @@ -XXX,XX +XXX,XX @@ Visitor *string_output_visitor_new(bool human, char **result) | ||
68 | v->visitor.type_str = print_type_str; | ||
69 | v->visitor.type_number = print_type_number; | ||
70 | v->visitor.type_null = print_type_null; | ||
71 | + v->visitor.start_struct = start_struct; | ||
72 | + v->visitor.end_struct = end_struct; | ||
73 | v->visitor.start_list = start_list; | ||
74 | v->visitor.next_list = next_list; | ||
75 | v->visitor.end_list = end_list; | ||
76 | -- | 33 | -- |
77 | 2.43.0 | 34 | 2.48.1 | diff view generated by jsdifflib |
1 | Currently, the conflict between -incoming and -loadvm is only detected | 1 | So far the assumption has always been that if we try to inactivate a |
---|---|---|---|
2 | when loading the snapshot fails because the image is still inactive for | 2 | node, it is already idle. This doesn't hold true any more if we allow |
3 | the incoming migration. This results in a suboptimal error message: | 3 | inactivating exported nodes because we can't know when new external |
4 | requests come in. | ||
4 | 5 | ||
5 | $ ./qemu-system-x86_64 -hda /tmp/test.qcow2 -loadvm foo -incoming defer | 6 | Drain the node around setting BDRV_O_INACTIVE so that requests can't |
6 | qemu-system-x86_64: Device 'ide0-hd0' is writable but does not support snapshots | 7 | start operating on an active node and then in the middle it suddenly |
7 | 8 | becomes inactive. With this change, it's enough for exports to check | |
8 | Catch the situation already in qemu_validate_options() to improve the | 9 | for new requests that they operate on an active node (or, like reads, |
9 | message: | 10 | are allowed even on an inactive node). |
10 | |||
11 | $ ./qemu-system-x86_64 -hda /tmp/test.qcow2 -loadvm foo -incoming defer | ||
12 | qemu-system-x86_64: 'incoming' and 'loadvm' options are mutually exclusive | ||
13 | 11 | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
15 | Message-ID: <20231201142520.32255-3-kwolf@redhat.com> | 13 | Acked-by: Fabiano Rosas <farosas@suse.de> |
14 | Message-ID: <20250204211407.381505-12-kwolf@redhat.com> | ||
15 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
16 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
17 | --- | 18 | --- |
18 | system/vl.c | 4 ++++ | 19 | block.c | 2 ++ |
19 | 1 file changed, 4 insertions(+) | 20 | 1 file changed, 2 insertions(+) |
20 | 21 | ||
21 | diff --git a/system/vl.c b/system/vl.c | 22 | diff --git a/block.c b/block.c |
22 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/system/vl.c | 24 | --- a/block.c |
24 | +++ b/system/vl.c | 25 | +++ b/block.c |
25 | @@ -XXX,XX +XXX,XX @@ static void qemu_validate_options(const QDict *machine_opts) | 26 | @@ -XXX,XX +XXX,XX @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level) |
26 | } | 27 | return -EPERM; |
27 | } | 28 | } |
28 | 29 | ||
29 | + if (loadvm && incoming) { | 30 | + bdrv_drained_begin(bs); |
30 | + error_report("'incoming' and 'loadvm' options are mutually exclusive"); | 31 | bs->open_flags |= BDRV_O_INACTIVE; |
31 | + exit(EXIT_FAILURE); | 32 | + bdrv_drained_end(bs); |
32 | + } | 33 | |
33 | if (loadvm && preconfig_requested) { | 34 | /* |
34 | error_report("'preconfig' and 'loadvm' options are " | 35 | * Update permissions, they may differ for inactive nodes. |
35 | "mutually exclusive"); | ||
36 | -- | 36 | -- |
37 | 2.43.0 | 37 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | Add an option in BlockExportOptions to allow creating an export on an |
---|---|---|---|
2 | inactive node without activating the node. This mode needs to be | ||
3 | explicitly supported by the export type (so that it doesn't perform any | ||
4 | operations that are forbidden for inactive nodes), so this patch alone | ||
5 | doesn't allow this option to be successfully used yet. | ||
2 | 6 | ||
3 | virtio_queue_aio_attach_host_notifier() does not require the AioContext | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | lock. Stop taking the lock and add an explicit smp_wmb() because we were | 8 | Acked-by: Fabiano Rosas <farosas@suse.de> |
5 | relying on the implicit barrier in the AioContext lock before. | ||
6 | |||
7 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | 9 | Reviewed-by: Eric Blake <eblake@redhat.com> |
9 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 10 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
10 | Message-ID: <20231204164259.1515217-3-stefanha@redhat.com> | 11 | Message-ID: <20250204211407.381505-13-kwolf@redhat.com> |
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 13 | --- |
13 | hw/scsi/virtio-scsi-dataplane.c | 8 +------- | 14 | qapi/block-export.json | 10 +++++++++- |
14 | 1 file changed, 1 insertion(+), 7 deletions(-) | 15 | include/block/export.h | 3 +++ |
16 | block/export/export.c | 31 +++++++++++++++++++++---------- | ||
17 | 3 files changed, 33 insertions(+), 11 deletions(-) | ||
15 | 18 | ||
16 | diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c | 19 | diff --git a/qapi/block-export.json b/qapi/block-export.json |
17 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/hw/scsi/virtio-scsi-dataplane.c | 21 | --- a/qapi/block-export.json |
19 | +++ b/hw/scsi/virtio-scsi-dataplane.c | 22 | +++ b/qapi/block-export.json |
20 | @@ -XXX,XX +XXX,XX @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) | 23 | @@ -XXX,XX +XXX,XX @@ |
21 | 24 | # cannot be moved to the iothread. The default is false. | |
22 | memory_region_transaction_commit(); | 25 | # (since: 5.2) |
26 | # | ||
27 | +# @allow-inactive: If true, the export allows the exported node to be inactive. | ||
28 | +# If it is created for an inactive block node, the node remains inactive. If | ||
29 | +# the export type doesn't support running on an inactive node, an error is | ||
30 | +# returned. If false, inactive block nodes are automatically activated before | ||
31 | +# creating the export and trying to inactivate them later fails. | ||
32 | +# (since: 10.0; default: false) | ||
33 | +# | ||
34 | # Since: 4.2 | ||
35 | ## | ||
36 | { 'union': 'BlockExportOptions', | ||
37 | @@ -XXX,XX +XXX,XX @@ | ||
38 | '*iothread': 'str', | ||
39 | 'node-name': 'str', | ||
40 | '*writable': 'bool', | ||
41 | - '*writethrough': 'bool' }, | ||
42 | + '*writethrough': 'bool', | ||
43 | + '*allow-inactive': 'bool' }, | ||
44 | 'discriminator': 'type', | ||
45 | 'data': { | ||
46 | 'nbd': 'BlockExportOptionsNbd', | ||
47 | diff --git a/include/block/export.h b/include/block/export.h | ||
48 | index XXXXXXX..XXXXXXX 100644 | ||
49 | --- a/include/block/export.h | ||
50 | +++ b/include/block/export.h | ||
51 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockExportDriver { | ||
52 | */ | ||
53 | size_t instance_size; | ||
54 | |||
55 | + /* True if the export type supports running on an inactive node */ | ||
56 | + bool supports_inactive; | ||
57 | + | ||
58 | /* Creates and starts a new block export */ | ||
59 | int (*create)(BlockExport *, BlockExportOptions *, Error **); | ||
60 | |||
61 | diff --git a/block/export/export.c b/block/export/export.c | ||
62 | index XXXXXXX..XXXXXXX 100644 | ||
63 | --- a/block/export/export.c | ||
64 | +++ b/block/export/export.c | ||
65 | @@ -XXX,XX +XXX,XX @@ static const BlockExportDriver *blk_exp_find_driver(BlockExportType type) | ||
66 | BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) | ||
67 | { | ||
68 | bool fixed_iothread = export->has_fixed_iothread && export->fixed_iothread; | ||
69 | + bool allow_inactive = export->has_allow_inactive && export->allow_inactive; | ||
70 | const BlockExportDriver *drv; | ||
71 | BlockExport *exp = NULL; | ||
72 | BlockDriverState *bs; | ||
73 | @@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) | ||
74 | } | ||
75 | } | ||
23 | 76 | ||
24 | - /* | 77 | - /* |
25 | - * These fields are visible to the IOThread so we rely on implicit barriers | 78 | - * Block exports are used for non-shared storage migration. Make sure |
26 | - * in aio_context_acquire() on the write side and aio_notify_accept() on | 79 | - * that BDRV_O_INACTIVE is cleared and the image is ready for write |
27 | - * the read side. | 80 | - * access since the export could be available before migration handover. |
81 | - * ctx was acquired in the caller. | ||
28 | - */ | 82 | - */ |
29 | s->dataplane_starting = false; | 83 | bdrv_graph_rdlock_main_loop(); |
30 | s->dataplane_started = true; | 84 | - ret = bdrv_activate(bs, errp); |
31 | + smp_wmb(); /* paired with aio_notify_accept() */ | 85 | - if (ret < 0) { |
32 | 86 | - bdrv_graph_rdunlock_main_loop(); | |
33 | if (s->bus.drain_count == 0) { | 87 | - goto fail; |
34 | - aio_context_acquire(s->ctx); | 88 | + if (allow_inactive) { |
35 | virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx); | 89 | + if (!drv->supports_inactive) { |
36 | virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx); | 90 | + error_setg(errp, "Export type does not support inactive exports"); |
37 | 91 | + bdrv_graph_rdunlock_main_loop(); | |
38 | for (i = 0; i < vs->conf.num_queues; i++) { | 92 | + goto fail; |
39 | virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx); | 93 | + } |
40 | } | 94 | + } else { |
41 | - aio_context_release(s->ctx); | 95 | + /* |
96 | + * Block exports are used for non-shared storage migration. Make sure | ||
97 | + * that BDRV_O_INACTIVE is cleared and the image is ready for write | ||
98 | + * access since the export could be available before migration handover. | ||
99 | + */ | ||
100 | + ret = bdrv_activate(bs, errp); | ||
101 | + if (ret < 0) { | ||
102 | + bdrv_graph_rdunlock_main_loop(); | ||
103 | + goto fail; | ||
104 | + } | ||
42 | } | 105 | } |
43 | return 0; | 106 | bdrv_graph_rdunlock_main_loop(); |
44 | 107 | ||
108 | @@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) | ||
109 | if (!fixed_iothread) { | ||
110 | blk_set_allow_aio_context_change(blk, true); | ||
111 | } | ||
112 | + if (allow_inactive) { | ||
113 | + blk_set_force_allow_inactivate(blk); | ||
114 | + } | ||
115 | |||
116 | ret = blk_insert_bs(blk, bs, errp); | ||
117 | if (ret < 0) { | ||
45 | -- | 118 | -- |
46 | 2.43.0 | 119 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | In order to support running an NBD export on inactive nodes, we must |
---|---|---|---|
2 | make sure to return errors for any operations that aren't allowed on | ||
3 | inactive nodes. Reads are the only operation we know we need for | ||
4 | inactive images, so to err on the side of caution, return errors for | ||
5 | everything else, even if some operations could possibly be okay. | ||
2 | 6 | ||
3 | nbd_trip() processes a single NBD request from start to finish and holds | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | an NBDClient reference throughout. NBDRequest does not outlive the scope | 8 | Acked-by: Fabiano Rosas <farosas@suse.de> |
5 | of nbd_trip(). Therefore it is unnecessary to ref/unref NBDClient for | 9 | Message-ID: <20250204211407.381505-14-kwolf@redhat.com> |
6 | each NBDRequest. | 10 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
7 | 11 | Reviewed-by: Eric Blake <eblake@redhat.com> | |
8 | Removing these nbd_client_get()/nbd_client_put() calls will make | ||
9 | thread-safety easier in the commits that follow. | ||
10 | |||
11 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
12 | Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> | ||
13 | Message-ID: <20231221192452.1785567-5-stefanha@redhat.com> | ||
14 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
16 | --- | 13 | --- |
17 | nbd/server.c | 3 --- | 14 | nbd/server.c | 17 +++++++++++++++++ |
18 | 1 file changed, 3 deletions(-) | 15 | 1 file changed, 17 insertions(+) |
19 | 16 | ||
20 | diff --git a/nbd/server.c b/nbd/server.c | 17 | diff --git a/nbd/server.c b/nbd/server.c |
21 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
22 | --- a/nbd/server.c | 19 | --- a/nbd/server.c |
23 | +++ b/nbd/server.c | 20 | +++ b/nbd/server.c |
24 | @@ -XXX,XX +XXX,XX @@ static NBDRequestData *nbd_request_get(NBDClient *client) | 21 | @@ -XXX,XX +XXX,XX @@ static void nbd_export_delete(BlockExport *blk_exp) |
25 | client->nb_requests++; | 22 | const BlockExportDriver blk_exp_nbd = { |
26 | 23 | .type = BLOCK_EXPORT_TYPE_NBD, | |
27 | req = g_new0(NBDRequestData, 1); | 24 | .instance_size = sizeof(NBDExport), |
28 | - nbd_client_get(client); | 25 | + .supports_inactive = true, |
29 | req->client = client; | 26 | .create = nbd_export_create, |
30 | return req; | 27 | .delete = nbd_export_delete, |
31 | } | 28 | .request_shutdown = nbd_export_request_shutdown, |
32 | @@ -XXX,XX +XXX,XX @@ static void nbd_request_put(NBDRequestData *req) | 29 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client, |
33 | } | 30 | NBDExport *exp = client->exp; |
34 | 31 | char *msg; | |
35 | nbd_client_receive_next_request(client); | 32 | size_t i; |
36 | - | 33 | + bool inactive; |
37 | - nbd_client_put(client); | 34 | + |
38 | } | 35 | + WITH_GRAPH_RDLOCK_GUARD() { |
39 | 36 | + inactive = bdrv_is_inactive(blk_bs(exp->common.blk)); | |
40 | static void blk_aio_attached(AioContext *ctx, void *opaque) | 37 | + if (inactive) { |
38 | + switch (request->type) { | ||
39 | + case NBD_CMD_READ: | ||
40 | + /* These commands are allowed on inactive nodes */ | ||
41 | + break; | ||
42 | + default: | ||
43 | + /* Return an error for the rest */ | ||
44 | + return nbd_send_generic_reply(client, request, -EPERM, | ||
45 | + "export is inactive", errp); | ||
46 | + } | ||
47 | + } | ||
48 | + } | ||
49 | |||
50 | switch (request->type) { | ||
51 | case NBD_CMD_CACHE: | ||
41 | -- | 52 | -- |
42 | 2.43.0 | 53 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | The open-coded form of this filter has been copied into enough tests |
---|---|---|---|
2 | that it's better to move it into iotests.py. | ||
2 | 3 | ||
3 | Encourage the use of locking primitives and stop mentioning the | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | AioContext lock since it is being removed. | 5 | Acked-by: Fabiano Rosas <farosas@suse.de> |
5 | |||
6 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
8 | Message-ID: <20231205182011.1976568-12-stefanha@redhat.com> | 7 | Message-ID: <20250204211407.381505-15-kwolf@redhat.com> |
9 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | --- | 10 | --- |
12 | docs/devel/multiple-iothreads.txt | 47 +++++++++++-------------------- | 11 | tests/qemu-iotests/iotests.py | 4 ++++ |
13 | 1 file changed, 16 insertions(+), 31 deletions(-) | 12 | tests/qemu-iotests/041 | 4 +--- |
13 | tests/qemu-iotests/165 | 4 +--- | ||
14 | tests/qemu-iotests/tests/copy-before-write | 3 +-- | ||
15 | tests/qemu-iotests/tests/migrate-bitmaps-test | 7 +++---- | ||
16 | 5 files changed, 10 insertions(+), 12 deletions(-) | ||
14 | 17 | ||
15 | diff --git a/docs/devel/multiple-iothreads.txt b/docs/devel/multiple-iothreads.txt | 18 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py |
16 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/docs/devel/multiple-iothreads.txt | 20 | --- a/tests/qemu-iotests/iotests.py |
18 | +++ b/docs/devel/multiple-iothreads.txt | 21 | +++ b/tests/qemu-iotests/iotests.py |
19 | @@ -XXX,XX +XXX,XX @@ loop, depending on which AioContext instance the caller passes in. | 22 | @@ -XXX,XX +XXX,XX @@ def _filter(_key, value): |
20 | 23 | def filter_nbd_exports(output: str) -> str: | |
21 | How to synchronize with an IOThread | 24 | return re.sub(r'((min|opt|max) block): [0-9]+', r'\1: XXX', output) |
22 | ----------------------------------- | 25 | |
23 | -AioContext is not thread-safe so some rules must be followed when using file | 26 | +def filter_qtest(output: str) -> str: |
24 | -descriptors, event notifiers, timers, or BHs across threads: | 27 | + output = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', output) |
25 | +Variables that can be accessed by multiple threads require some form of | 28 | + output = re.sub(r'\n?\[I \+\d+\.\d+\] CLOSED\n?$', '', output) |
26 | +synchronization such as qemu_mutex_lock(), rcu_read_lock(), etc. | 29 | + return output |
27 | 30 | ||
28 | -1. AioContext functions can always be called safely. They handle their | 31 | Msg = TypeVar('Msg', Dict[str, Any], List[Any], str) |
29 | -own locking internally. | 32 | |
30 | - | 33 | diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 |
31 | -2. Other threads wishing to access the AioContext must use | 34 | index XXXXXXX..XXXXXXX 100755 |
32 | -aio_context_acquire()/aio_context_release() for mutual exclusion. Once the | 35 | --- a/tests/qemu-iotests/041 |
33 | -context is acquired no other thread can access it or run event loop iterations | 36 | +++ b/tests/qemu-iotests/041 |
34 | -in this AioContext. | 37 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): |
35 | - | 38 | |
36 | -Legacy code sometimes nests aio_context_acquire()/aio_context_release() calls. | 39 | # Check the full error message now |
37 | -Do not use nesting anymore, it is incompatible with the BDRV_POLL_WHILE() macro | 40 | self.vm.shutdown() |
38 | -used in the block layer and can lead to hangs. | 41 | - log = self.vm.get_log() |
39 | - | 42 | - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) |
40 | -There is currently no lock ordering rule if a thread needs to acquire multiple | 43 | + log = iotests.filter_qtest(self.vm.get_log()) |
41 | -AioContexts simultaneously. Therefore, it is only safe for code holding the | 44 | log = re.sub(r'^Formatting.*\n', '', log) |
42 | -QEMU global mutex to acquire other AioContexts. | 45 | - log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log) |
43 | +AioContext functions like aio_set_fd_handler(), aio_set_event_notifier(), | 46 | log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log) |
44 | +aio_bh_new(), and aio_timer_new() are thread-safe. They can be used to trigger | 47 | |
45 | +activity in an IOThread. | 48 | self.assertEqual(log, |
46 | 49 | diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 | |
47 | Side note: the best way to schedule a function call across threads is to call | 50 | index XXXXXXX..XXXXXXX 100755 |
48 | -aio_bh_schedule_oneshot(). No acquire/release or locking is needed. | 51 | --- a/tests/qemu-iotests/165 |
49 | +aio_bh_schedule_oneshot(). | 52 | +++ b/tests/qemu-iotests/165 |
50 | + | 53 | @@ -XXX,XX +XXX,XX @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): |
51 | +The main loop thread can wait synchronously for a condition using | 54 | self.vm.shutdown() |
52 | +AIO_WAIT_WHILE(). | 55 | |
53 | 56 | #catch 'Persistent bitmaps are lost' possible error | |
54 | AioContext and the block layer | 57 | - log = self.vm.get_log() |
55 | ------------------------------ | 58 | - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) |
56 | @@ -XXX,XX +XXX,XX @@ Block layer code must therefore expect to run in an IOThread and avoid using | 59 | - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) |
57 | old APIs that implicitly use the main loop. See the "How to program for | 60 | + log = iotests.filter_qtest(self.vm.get_log()) |
58 | IOThreads" above for information on how to do that. | 61 | if log: |
59 | 62 | print(log) | |
60 | -If main loop code such as a QMP function wishes to access a BlockDriverState | 63 | |
61 | -it must first call aio_context_acquire(bdrv_get_aio_context(bs)) to ensure | 64 | diff --git a/tests/qemu-iotests/tests/copy-before-write b/tests/qemu-iotests/tests/copy-before-write |
62 | -that callbacks in the IOThread do not run in parallel. | 65 | index XXXXXXX..XXXXXXX 100755 |
63 | - | 66 | --- a/tests/qemu-iotests/tests/copy-before-write |
64 | Code running in the monitor typically needs to ensure that past | 67 | +++ b/tests/qemu-iotests/tests/copy-before-write |
65 | requests from the guest are completed. When a block device is running | 68 | @@ -XXX,XX +XXX,XX @@ class TestCbwError(iotests.QMPTestCase): |
66 | in an IOThread, the IOThread can also process requests from the guest | 69 | |
67 | (via ioeventfd). To achieve both objects, wrap the code between | 70 | self.vm.shutdown() |
68 | bdrv_drained_begin() and bdrv_drained_end(), thus creating a "drained | 71 | log = self.vm.get_log() |
69 | -section". The functions must be called between aio_context_acquire() | 72 | - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) |
70 | -and aio_context_release(). You can freely release and re-acquire the | 73 | - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) |
71 | -AioContext within a drained section. | 74 | + log = iotests.filter_qtest(log) |
72 | - | 75 | log = iotests.filter_qemu_io(log) |
73 | -Long-running jobs (usually in the form of coroutines) are best scheduled in | 76 | return log |
74 | -the BlockDriverState's AioContext to avoid the need to acquire/release around | 77 | |
75 | -each bdrv_*() call. The functions bdrv_add/remove_aio_context_notifier, | 78 | diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test |
76 | -or alternatively blk_add/remove_aio_context_notifier if you use BlockBackends, | 79 | index XXXXXXX..XXXXXXX 100755 |
77 | -can be used to get a notification whenever bdrv_try_change_aio_context() moves a | 80 | --- a/tests/qemu-iotests/tests/migrate-bitmaps-test |
78 | +section". | 81 | +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test |
79 | + | 82 | @@ -XXX,XX +XXX,XX @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): |
80 | +Long-running jobs (usually in the form of coroutines) are often scheduled in | 83 | |
81 | +the BlockDriverState's AioContext. The functions | 84 | # catch 'Could not reopen qcow2 layer: Bitmap already exists' |
82 | +bdrv_add/remove_aio_context_notifier, or alternatively | 85 | # possible error |
83 | +blk_add/remove_aio_context_notifier if you use BlockBackends, can be used to | 86 | - log = self.vm_a.get_log() |
84 | +get a notification whenever bdrv_try_change_aio_context() moves a | 87 | - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) |
85 | BlockDriverState to a different AioContext. | 88 | - log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}', |
89 | + log = iotests.filter_qtest(self.vm_a.get_log()) | ||
90 | + log = re.sub(r'^(wrote .* bytes at offset .*\n' | ||
91 | + r'.*KiB.*ops.*sec.*\n?){3}', | ||
92 | '', log) | ||
93 | - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) | ||
94 | self.assertEqual(log, '') | ||
95 | |||
96 | # test that bitmap is still persistent | ||
86 | -- | 97 | -- |
87 | 2.43.0 | 98 | 2.48.1 | diff view generated by jsdifflib |
1 | We have a few test cases that include tests for corner case aspects of | 1 | Test that it's possible to migrate a VM that uses an image on shared |
---|---|---|---|
2 | internal snapshots, but nothing that tests that they actually function | 2 | storage through qemu-storage-daemon. |
3 | as snapshots or that involves deleting a snapshot. Add a test for this | ||
4 | kind of basic internal snapshot functionality. | ||
5 | |||
6 | The error cases include a regression test for the crash we just fixed | ||
7 | with snapshot operations on inactive images. | ||
8 | 3 | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | Message-ID: <20231201142520.32255-4-kwolf@redhat.com> | 5 | Acked-by: Fabiano Rosas <farosas@suse.de> |
6 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
7 | Message-ID: <20250204211407.381505-16-kwolf@redhat.com> | ||
8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 10 | --- |
13 | .../tests/qcow2-internal-snapshots | 170 ++++++++++++++++++ | 11 | tests/qemu-iotests/tests/qsd-migrate | 140 +++++++++++++++++++++++ |
14 | .../tests/qcow2-internal-snapshots.out | 107 +++++++++++ | 12 | tests/qemu-iotests/tests/qsd-migrate.out | 59 ++++++++++ |
15 | 2 files changed, 277 insertions(+) | 13 | 2 files changed, 199 insertions(+) |
16 | create mode 100755 tests/qemu-iotests/tests/qcow2-internal-snapshots | 14 | create mode 100755 tests/qemu-iotests/tests/qsd-migrate |
17 | create mode 100644 tests/qemu-iotests/tests/qcow2-internal-snapshots.out | 15 | create mode 100644 tests/qemu-iotests/tests/qsd-migrate.out |
18 | 16 | ||
19 | diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots b/tests/qemu-iotests/tests/qcow2-internal-snapshots | 17 | diff --git a/tests/qemu-iotests/tests/qsd-migrate b/tests/qemu-iotests/tests/qsd-migrate |
20 | new file mode 100755 | 18 | new file mode 100755 |
21 | index XXXXXXX..XXXXXXX | 19 | index XXXXXXX..XXXXXXX |
22 | --- /dev/null | 20 | --- /dev/null |
23 | +++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots | 21 | +++ b/tests/qemu-iotests/tests/qsd-migrate |
24 | @@ -XXX,XX +XXX,XX @@ | 22 | @@ -XXX,XX +XXX,XX @@ |
25 | +#!/usr/bin/env bash | 23 | +#!/usr/bin/env python3 |
26 | +# group: rw quick | 24 | +# group: rw quick |
27 | +# | 25 | +# |
28 | +# Test case for internal snapshots in qcow2 | 26 | +# Copyright (C) Red Hat, Inc. |
29 | +# | ||
30 | +# Copyright (C) 2023 Red Hat, Inc. | ||
31 | +# | 27 | +# |
32 | +# This program is free software; you can redistribute it and/or modify | 28 | +# This program is free software; you can redistribute it and/or modify |
33 | +# it under the terms of the GNU General Public License as published by | 29 | +# it under the terms of the GNU General Public License as published by |
34 | +# the Free Software Foundation; either version 2 of the License, or | 30 | +# the Free Software Foundation; either version 2 of the License, or |
35 | +# (at your option) any later version. | 31 | +# (at your option) any later version. |
... | ... | ||
40 | +# GNU General Public License for more details. | 36 | +# GNU General Public License for more details. |
41 | +# | 37 | +# |
42 | +# You should have received a copy of the GNU General Public License | 38 | +# You should have received a copy of the GNU General Public License |
43 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | 39 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
44 | +# | 40 | +# |
45 | + | 41 | +# Creator/Owner: Kevin Wolf <kwolf@redhat.com> |
46 | +# creator | 42 | + |
47 | +owner=kwolf@redhat.com | 43 | +import iotests |
48 | + | 44 | + |
49 | +seq="$(basename $0)" | 45 | +from iotests import filter_qemu_io, filter_qtest |
50 | +echo "QA output created by $seq" | 46 | + |
51 | + | 47 | +iotests.script_initialize(supported_fmts=['generic'], |
52 | +status=1 # failure is the default! | 48 | + supported_protocols=['file'], |
53 | + | 49 | + supported_platforms=['linux']) |
54 | +_cleanup() | 50 | + |
55 | +{ | 51 | +with iotests.FilePath('disk.img') as path, \ |
56 | + _cleanup_test_img | 52 | + iotests.FilePath('nbd-src.sock', base_dir=iotests.sock_dir) as nbd_src, \ |
57 | +} | 53 | + iotests.FilePath('nbd-dst.sock', base_dir=iotests.sock_dir) as nbd_dst, \ |
58 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | 54 | + iotests.FilePath('migrate.sock', base_dir=iotests.sock_dir) as mig_sock, \ |
59 | + | 55 | + iotests.VM(path_suffix="-src") as vm_src, \ |
60 | +# get standard environment, filters and checks | 56 | + iotests.VM(path_suffix="-dst") as vm_dst: |
61 | +. ../common.rc | 57 | + |
62 | +. ../common.filter | 58 | + img_size = '10M' |
63 | + | 59 | + |
64 | +# This tests qcow2-specific low-level functionality | 60 | + iotests.log('Preparing disk...') |
65 | +_supported_fmt qcow2 | 61 | + iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size) |
66 | +_supported_proto generic | 62 | + |
67 | +# Internal snapshots are (currently) impossible with refcount_bits=1, | 63 | + iotests.log('Launching source QSD...') |
68 | +# and generally impossible with external data files | 64 | + qsd_src = iotests.QemuStorageDaemon( |
69 | +_unsupported_imgopts 'compat=0.10' 'refcount_bits=1[^0-9]' data_file | 65 | + '--blockdev', f'file,node-name=disk-file,filename={path}', |
70 | + | 66 | + '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt', |
71 | +IMG_SIZE=64M | 67 | + '--nbd-server', f'addr.type=unix,addr.path={nbd_src}', |
72 | + | 68 | + '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,' |
73 | +_qemu() | 69 | + 'allow-inactive=true', |
74 | +{ | 70 | + qmp=True, |
75 | + $QEMU -no-shutdown -nographic -monitor stdio -serial none \ | 71 | + ) |
76 | + -blockdev file,filename="$TEST_IMG",node-name=disk0-file \ | 72 | + |
77 | + -blockdev "$IMGFMT",file=disk0-file,node-name=disk0 \ | 73 | + iotests.log('Launching source VM...') |
78 | + -object iothread,id=iothread0 \ | 74 | + vm_src.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,' |
79 | + -device virtio-scsi,iothread=iothread0 \ | 75 | + f'server.path={nbd_src},export=disk-fmt') |
80 | + -device scsi-hd,drive=disk0,share-rw=on \ | 76 | + vm_src.add_args('-device', 'virtio-blk,drive=disk,id=virtio0') |
81 | + "$@" 2>&1 |\ | 77 | + vm_src.launch() |
82 | + _filter_qemu | _filter_hmp | _filter_qemu_io | 78 | + |
83 | +} | 79 | + iotests.log('Launching destination QSD...') |
84 | + | 80 | + qsd_dst = iotests.QemuStorageDaemon( |
85 | +_make_test_img $IMG_SIZE | 81 | + '--blockdev', f'file,node-name=disk-file,filename={path},active=off', |
86 | + | 82 | + '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,' |
87 | +echo | 83 | + f'active=off', |
88 | +echo "=== Write some data, take a snapshot and overwrite part of it ===" | 84 | + '--nbd-server', f'addr.type=unix,addr.path={nbd_dst}', |
89 | +echo | 85 | + '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,' |
90 | + | 86 | + 'allow-inactive=true', |
91 | +{ | 87 | + qmp=True, |
92 | + echo 'qemu-io disk0 "write -P0x11 0 1M"' | 88 | + instance_id='b', |
93 | + # Give qemu some time to boot before saving the VM state | 89 | + ) |
94 | + sleep 0.5 | 90 | + |
95 | + echo "savevm snap0" | 91 | + iotests.log('Launching destination VM...') |
96 | + echo 'qemu-io disk0 "write -P0x22 0 512k"' | 92 | + vm_dst.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,' |
97 | + echo "quit" | 93 | + f'server.path={nbd_dst},export=disk-fmt') |
98 | +} | _qemu | 94 | + vm_dst.add_args('-device', 'virtio-blk,drive=disk,id=virtio0') |
99 | + | 95 | + vm_dst.add_args('-incoming', f'unix:{mig_sock}') |
100 | +echo | 96 | + vm_dst.launch() |
101 | +$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size | 97 | + |
102 | +_check_test_img | 98 | + iotests.log('\nTest I/O on the source') |
103 | + | 99 | + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x11 0 4k', |
104 | +echo | 100 | + use_log=True, qdev=True) |
105 | +echo "=== Verify that loading the snapshot reverts to the old content ===" | 101 | + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', |
106 | +echo | 102 | + use_log=True, qdev=True) |
107 | + | 103 | + |
108 | +{ | 104 | + iotests.log('\nStarting migration...') |
109 | + # -loadvm reverted the write from the previous QEMU instance | 105 | + |
110 | + echo 'qemu-io disk0 "read -P0x11 0 1M"' | 106 | + mig_caps = [ |
111 | + | 107 | + {'capability': 'events', 'state': True}, |
112 | + # Verify that it works without restarting QEMU, too | 108 | + {'capability': 'pause-before-switchover', 'state': True}, |
113 | + echo 'qemu-io disk0 "write -P0x33 512k 512k"' | 109 | + ] |
114 | + echo "loadvm snap0" | 110 | + vm_src.qmp_log('migrate-set-capabilities', capabilities=mig_caps) |
115 | + echo 'qemu-io disk0 "read -P0x11 0 1M"' | 111 | + vm_dst.qmp_log('migrate-set-capabilities', capabilities=mig_caps) |
116 | + | 112 | + vm_src.qmp_log('migrate', uri=f'unix:{mig_sock}', |
117 | + # Verify COW by writing a partial cluster | 113 | + filters=[iotests.filter_qmp_testfiles]) |
118 | + echo 'qemu-io disk0 "write -P0x33 63k 2k"' | 114 | + |
119 | + echo 'qemu-io disk0 "read -P0x11 0 63k"' | 115 | + vm_src.event_wait('MIGRATION', |
120 | + echo 'qemu-io disk0 "read -P0x33 63k 2k"' | 116 | + match={'data': {'status': 'pre-switchover'}}) |
121 | + echo 'qemu-io disk0 "read -P0x11 65k 63k"' | 117 | + |
122 | + | 118 | + iotests.log('\nPre-switchover: Reconfigure QSD instances') |
123 | + # Take a second snapshot | 119 | + |
124 | + echo "savevm snap1" | 120 | + iotests.log(qsd_src.qmp('blockdev-set-active', {'active': False})) |
125 | + | 121 | + |
126 | + echo "quit" | 122 | + # Reading is okay from both sides while the image is inactive. Note that |
127 | +} | _qemu -loadvm snap0 | 123 | + # the destination may have stale data until it activates the image, though. |
128 | + | 124 | + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', |
129 | +echo | 125 | + use_log=True, qdev=True) |
130 | +$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size | 126 | + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read 0 4k', |
131 | +_check_test_img | 127 | + use_log=True, qdev=True) |
132 | + | 128 | + |
133 | +echo | 129 | + iotests.log(qsd_dst.qmp('blockdev-set-active', {'active': True})) |
134 | +echo "=== qemu-img snapshot can revert to snapshots ===" | 130 | + |
135 | +echo | 131 | + iotests.log('\nCompleting migration...') |
136 | + | 132 | + |
137 | +$QEMU_IMG snapshot -a snap0 "$TEST_IMG" | 133 | + vm_src.qmp_log('migrate-continue', state='pre-switchover') |
138 | +$QEMU_IO -c "read -P0x11 0 1M" "$TEST_IMG" | _filter_qemu_io | 134 | + vm_dst.event_wait('MIGRATION', match={'data': {'status': 'completed'}}) |
139 | +$QEMU_IMG snapshot -a snap1 "$TEST_IMG" | 135 | + |
140 | +$QEMU_IO \ | 136 | + iotests.log('\nTest I/O on the destination') |
141 | + -c "read -P0x11 0 63k" \ | 137 | + |
142 | + -c "read -P0x33 63k 2k" \ | 138 | + # Now the destination must see what the source wrote |
143 | + -c "read -P0x11 65k 63k" \ | 139 | + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', |
144 | + "$TEST_IMG" | _filter_qemu_io | 140 | + use_log=True, qdev=True) |
145 | + | 141 | + |
146 | +echo | 142 | + # And be able to overwrite it |
147 | +echo "=== Deleting snapshots ===" | 143 | + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x22 0 4k', |
148 | +echo | 144 | + use_log=True, qdev=True) |
149 | +{ | 145 | + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x22 0 4k', |
150 | + # The active layer stays unaffected by deleting the snapshot | 146 | + use_log=True, qdev=True) |
151 | + echo "delvm snap1" | 147 | + |
152 | + echo 'qemu-io disk0 "read -P0x11 0 63k"' | 148 | + iotests.log('\nDone') |
153 | + echo 'qemu-io disk0 "read -P0x33 63k 2k"' | 149 | + |
154 | + echo 'qemu-io disk0 "read -P0x11 65k 63k"' | 150 | + vm_src.shutdown() |
155 | + | 151 | + iotests.log('\n--- vm_src log ---') |
156 | + echo "quit" | 152 | + log = vm_src.get_log() |
157 | +} | _qemu | 153 | + if log: |
158 | + | 154 | + iotests.log(log, [filter_qtest, filter_qemu_io]) |
159 | + | 155 | + qsd_src.stop() |
160 | +echo | 156 | + |
161 | +$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size | 157 | + vm_dst.shutdown() |
162 | +_check_test_img | 158 | + iotests.log('\n--- vm_dst log ---') |
163 | + | 159 | + log = vm_dst.get_log() |
164 | +echo | 160 | + if log: |
165 | +echo "=== Error cases ===" | 161 | + iotests.log(log, [filter_qtest, filter_qemu_io]) |
166 | +echo | 162 | + qsd_dst.stop() |
167 | + | 163 | diff --git a/tests/qemu-iotests/tests/qsd-migrate.out b/tests/qemu-iotests/tests/qsd-migrate.out |
168 | +# snap1 should not exist any more | ||
169 | +_qemu -loadvm snap1 | ||
170 | + | ||
171 | +echo | ||
172 | +{ | ||
173 | + echo "loadvm snap1" | ||
174 | + echo "quit" | ||
175 | +} | _qemu | ||
176 | + | ||
177 | +# Snapshot operations and inactive images are incompatible | ||
178 | +echo | ||
179 | +_qemu -loadvm snap0 -incoming defer | ||
180 | +{ | ||
181 | + echo "loadvm snap0" | ||
182 | + echo "delvm snap0" | ||
183 | + echo "savevm snap1" | ||
184 | + echo "quit" | ||
185 | +} | _qemu -incoming defer | ||
186 | + | ||
187 | +# -loadvm and -preconfig are incompatible | ||
188 | +echo | ||
189 | +_qemu -loadvm snap0 -preconfig | ||
190 | + | ||
191 | +# success, all done | ||
192 | +echo "*** done" | ||
193 | +rm -f $seq.full | ||
194 | +status=0 | ||
195 | diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots.out b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out | ||
196 | new file mode 100644 | 164 | new file mode 100644 |
197 | index XXXXXXX..XXXXXXX | 165 | index XXXXXXX..XXXXXXX |
198 | --- /dev/null | 166 | --- /dev/null |
199 | +++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out | 167 | +++ b/tests/qemu-iotests/tests/qsd-migrate.out |
200 | @@ -XXX,XX +XXX,XX @@ | 168 | @@ -XXX,XX +XXX,XX @@ |
201 | +QA output created by qcow2-internal-snapshots | 169 | +Preparing disk... |
202 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 | 170 | +Launching source QSD... |
203 | + | 171 | +Launching source VM... |
204 | +=== Write some data, take a snapshot and overwrite part of it === | 172 | +Launching destination QSD... |
205 | + | 173 | +Launching destination VM... |
206 | +QEMU X.Y.Z monitor - type 'help' for more information | 174 | + |
207 | +(qemu) qemu-io disk0 "write -P0x11 0 1M" | 175 | +Test I/O on the source |
208 | +wrote 1048576/1048576 bytes at offset 0 | 176 | +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x11 0 4k\""}} |
209 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 177 | +{"return": ""} |
210 | +(qemu) savevm snap0 | 178 | +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} |
211 | +(qemu) qemu-io disk0 "write -P0x22 0 512k" | 179 | +{"return": ""} |
212 | +wrote 524288/524288 bytes at offset 0 | 180 | + |
213 | +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 181 | +Starting migration... |
214 | +(qemu) quit | 182 | +{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}} |
215 | + | 183 | +{"return": {}} |
216 | +Snapshot list: | 184 | +{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}} |
217 | +ID TAG VM SIZE DATE VM CLOCK ICOUNT | 185 | +{"return": {}} |
218 | +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 | 186 | +{"execute": "migrate", "arguments": {"uri": "unix:SOCK_DIR/PID-migrate.sock"}} |
219 | +No errors were found on the image. | 187 | +{"return": {}} |
220 | + | 188 | + |
221 | +=== Verify that loading the snapshot reverts to the old content === | 189 | +Pre-switchover: Reconfigure QSD instances |
222 | + | 190 | +{"return": {}} |
223 | +QEMU X.Y.Z monitor - type 'help' for more information | 191 | +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} |
224 | +(qemu) qemu-io disk0 "read -P0x11 0 1M" | 192 | +{"return": ""} |
225 | +read 1048576/1048576 bytes at offset 0 | 193 | +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read 0 4k\""}} |
226 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 194 | +{"return": ""} |
227 | +(qemu) qemu-io disk0 "write -P0x33 512k 512k" | 195 | +{"return": {}} |
228 | +wrote 524288/524288 bytes at offset 524288 | 196 | + |
229 | +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 197 | +Completing migration... |
230 | +(qemu) loadvm snap0 | 198 | +{"execute": "migrate-continue", "arguments": {"state": "pre-switchover"}} |
231 | +(qemu) qemu-io disk0 "read -P0x11 0 1M" | 199 | +{"return": {}} |
232 | +read 1048576/1048576 bytes at offset 0 | 200 | + |
233 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 201 | +Test I/O on the destination |
234 | +(qemu) qemu-io disk0 "write -P0x33 63k 2k" | 202 | +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} |
235 | +wrote 2048/2048 bytes at offset 64512 | 203 | +{"return": ""} |
236 | +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 204 | +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x22 0 4k\""}} |
237 | +(qemu) qemu-io disk0 "read -P0x11 0 63k" | 205 | +{"return": ""} |
238 | +read 64512/64512 bytes at offset 0 | 206 | +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x22 0 4k\""}} |
239 | +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 207 | +{"return": ""} |
240 | +(qemu) qemu-io disk0 "read -P0x33 63k 2k" | 208 | + |
241 | +read 2048/2048 bytes at offset 64512 | 209 | +Done |
242 | +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 210 | + |
243 | +(qemu) qemu-io disk0 "read -P0x11 65k 63k" | 211 | +--- vm_src log --- |
244 | +read 64512/64512 bytes at offset 66560 | 212 | +wrote 4096/4096 bytes at offset 0 |
245 | +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 213 | +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
246 | +(qemu) savevm snap1 | 214 | +read 4096/4096 bytes at offset 0 |
247 | +(qemu) quit | 215 | +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
248 | + | 216 | +read 4096/4096 bytes at offset 0 |
249 | +Snapshot list: | 217 | +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
250 | +ID TAG VM SIZE DATE VM CLOCK ICOUNT | 218 | + |
251 | +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 | 219 | +--- vm_dst log --- |
252 | +2 snap1 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 | 220 | +read 4096/4096 bytes at offset 0 |
253 | +No errors were found on the image. | 221 | +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
254 | + | 222 | +read 4096/4096 bytes at offset 0 |
255 | +=== qemu-img snapshot can revert to snapshots === | 223 | +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
256 | + | 224 | +wrote 4096/4096 bytes at offset 0 |
257 | +read 1048576/1048576 bytes at offset 0 | 225 | +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
258 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 226 | +read 4096/4096 bytes at offset 0 |
259 | +read 64512/64512 bytes at offset 0 | 227 | +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
260 | +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
261 | +read 2048/2048 bytes at offset 64512 | ||
262 | +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
263 | +read 64512/64512 bytes at offset 66560 | ||
264 | +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
265 | + | ||
266 | +=== Deleting snapshots === | ||
267 | + | ||
268 | +QEMU X.Y.Z monitor - type 'help' for more information | ||
269 | +(qemu) delvm snap1 | ||
270 | +(qemu) qemu-io disk0 "read -P0x11 0 63k" | ||
271 | +read 64512/64512 bytes at offset 0 | ||
272 | +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
273 | +(qemu) qemu-io disk0 "read -P0x33 63k 2k" | ||
274 | +read 2048/2048 bytes at offset 64512 | ||
275 | +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
276 | +(qemu) qemu-io disk0 "read -P0x11 65k 63k" | ||
277 | +read 64512/64512 bytes at offset 66560 | ||
278 | +63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
279 | +(qemu) quit | ||
280 | + | ||
281 | +Snapshot list: | ||
282 | +ID TAG VM SIZE DATE VM CLOCK ICOUNT | ||
283 | +1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 | ||
284 | +No errors were found on the image. | ||
285 | + | ||
286 | +=== Error cases === | ||
287 | + | ||
288 | +QEMU X.Y.Z monitor - type 'help' for more information | ||
289 | +(qemu) QEMU_PROG: Snapshot 'snap1' does not exist in one or more devices | ||
290 | + | ||
291 | +QEMU X.Y.Z monitor - type 'help' for more information | ||
292 | +(qemu) loadvm snap1 | ||
293 | +Error: Snapshot 'snap1' does not exist in one or more devices | ||
294 | +(qemu) quit | ||
295 | + | ||
296 | +QEMU_PROG: 'incoming' and 'loadvm' options are mutually exclusive | ||
297 | +QEMU X.Y.Z monitor - type 'help' for more information | ||
298 | +(qemu) loadvm snap0 | ||
299 | +Error: Device 'disk0' is writable but does not support snapshots | ||
300 | +(qemu) delvm snap0 | ||
301 | +Error: Device 'disk0' is writable but does not support snapshots | ||
302 | +(qemu) savevm snap1 | ||
303 | +Error: Device 'disk0' is writable but does not support snapshots | ||
304 | +(qemu) quit | ||
305 | + | ||
306 | +QEMU_PROG: 'preconfig' and 'loadvm' options are mutually exclusive | ||
307 | +*** done | ||
308 | -- | 228 | -- |
309 | 2.43.0 | 229 | 2.48.1 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | This tests different types of operations on inactive block nodes |
---|---|---|---|
2 | (including graph changes, block jobs and NBD exports) to make sure that | ||
3 | users manually activating and inactivating nodes doesn't break things. | ||
2 | 4 | ||
3 | The file-posix block driver currently only sets up Linux AIO and | 5 | Support for inactive nodes in other export types will have to come with |
4 | io_uring in the BDS's AioContext. In the multi-queue block layer we must | 6 | separate test cases because they have different dependencies like blkio |
5 | be able to submit I/O requests in AioContexts that do not have Linux AIO | 7 | or root permissions and we don't want to disable this basic test when |
6 | and io_uring set up yet since any thread can call into the block driver. | 8 | they are not fulfilled. |
7 | 9 | ||
8 | Set up Linux AIO and io_uring for the current AioContext during request | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | submission. We lose the ability to return an error from | 11 | Acked-by: Fabiano Rosas <farosas@suse.de> |
10 | .bdrv_file_open() when Linux AIO and io_uring setup fails (e.g. due to | 12 | Message-ID: <20250204211407.381505-17-kwolf@redhat.com> |
11 | resource limits). Instead the user only gets warnings and we fall back | ||
12 | to aio=threads. This is still better than a fatal error after startup. | ||
13 | |||
14 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
15 | Message-ID: <20230914140101.1065008-2-stefanha@redhat.com> | ||
16 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
17 | Reviewed-by: Eric Blake <eblake@redhat.com> | 13 | Reviewed-by: Eric Blake <eblake@redhat.com> |
14 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
19 | --- | 16 | --- |
20 | block/file-posix.c | 99 ++++++++++++++++++++++------------------------ | 17 | tests/qemu-iotests/iotests.py | 4 + |
21 | 1 file changed, 47 insertions(+), 52 deletions(-) | 18 | tests/qemu-iotests/tests/inactive-node-nbd | 303 ++++++++++++++++++ |
19 | .../qemu-iotests/tests/inactive-node-nbd.out | 239 ++++++++++++++ | ||
20 | 3 files changed, 546 insertions(+) | ||
21 | create mode 100755 tests/qemu-iotests/tests/inactive-node-nbd | ||
22 | create mode 100644 tests/qemu-iotests/tests/inactive-node-nbd.out | ||
22 | 23 | ||
23 | diff --git a/block/file-posix.c b/block/file-posix.c | 24 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py |
24 | index XXXXXXX..XXXXXXX 100644 | 25 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/block/file-posix.c | 26 | --- a/tests/qemu-iotests/iotests.py |
26 | +++ b/block/file-posix.c | 27 | +++ b/tests/qemu-iotests/iotests.py |
27 | @@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options, | 28 | @@ -XXX,XX +XXX,XX @@ def add_incoming(self, addr): |
28 | 29 | self._args.append(addr) | |
29 | #ifdef CONFIG_LINUX_AIO | 30 | return self |
30 | /* Currently Linux does AIO only for files opened with O_DIRECT */ | 31 | |
31 | - if (s->use_linux_aio) { | 32 | + def add_paused(self): |
32 | - if (!(s->open_flags & O_DIRECT)) { | 33 | + self._args.append('-S') |
33 | - error_setg(errp, "aio=native was specified, but it requires " | 34 | + return self |
34 | - "cache.direct=on, which was not specified."); | 35 | + |
35 | - ret = -EINVAL; | 36 | def hmp(self, command_line: str, use_log: bool = False) -> QMPMessage: |
36 | - goto fail; | 37 | cmd = 'human-monitor-command' |
37 | - } | 38 | kwargs: Dict[str, Any] = {'command-line': command_line} |
38 | - if (!aio_setup_linux_aio(bdrv_get_aio_context(bs), errp)) { | 39 | diff --git a/tests/qemu-iotests/tests/inactive-node-nbd b/tests/qemu-iotests/tests/inactive-node-nbd |
39 | - error_prepend(errp, "Unable to use native AIO: "); | 40 | new file mode 100755 |
40 | - goto fail; | 41 | index XXXXXXX..XXXXXXX |
41 | - } | 42 | --- /dev/null |
42 | + if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) { | 43 | +++ b/tests/qemu-iotests/tests/inactive-node-nbd |
43 | + error_setg(errp, "aio=native was specified, but it requires " | 44 | @@ -XXX,XX +XXX,XX @@ |
44 | + "cache.direct=on, which was not specified."); | 45 | +#!/usr/bin/env python3 |
45 | + ret = -EINVAL; | 46 | +# group: rw quick |
46 | + goto fail; | 47 | +# |
47 | } | 48 | +# Copyright (C) Red Hat, Inc. |
48 | #else | 49 | +# |
49 | if (s->use_linux_aio) { | 50 | +# This program is free software; you can redistribute it and/or modify |
50 | @@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options, | 51 | +# it under the terms of the GNU General Public License as published by |
51 | } | 52 | +# the Free Software Foundation; either version 2 of the License, or |
52 | #endif /* !defined(CONFIG_LINUX_AIO) */ | 53 | +# (at your option) any later version. |
53 | 54 | +# | |
54 | -#ifdef CONFIG_LINUX_IO_URING | 55 | +# This program is distributed in the hope that it will be useful, |
55 | - if (s->use_linux_io_uring) { | 56 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
56 | - if (!aio_setup_linux_io_uring(bdrv_get_aio_context(bs), errp)) { | 57 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
57 | - error_prepend(errp, "Unable to use io_uring: "); | 58 | +# GNU General Public License for more details. |
58 | - goto fail; | 59 | +# |
59 | - } | 60 | +# You should have received a copy of the GNU General Public License |
60 | - } | 61 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
61 | -#else | 62 | +# |
62 | +#ifndef CONFIG_LINUX_IO_URING | 63 | +# Creator/Owner: Kevin Wolf <kwolf@redhat.com> |
63 | if (s->use_linux_io_uring) { | 64 | + |
64 | error_setg(errp, "aio=io_uring was specified, but is not supported " | 65 | +import iotests |
65 | "in this build."); | 66 | + |
66 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) | 67 | +from iotests import QemuIoInteractive |
67 | return true; | 68 | +from iotests import filter_qemu_io, filter_qtest, filter_qmp_testfiles |
68 | } | 69 | + |
69 | 70 | +iotests.script_initialize(supported_fmts=['generic'], | |
70 | +static inline bool raw_check_linux_io_uring(BDRVRawState *s) | 71 | + supported_protocols=['file'], |
71 | +{ | 72 | + supported_platforms=['linux']) |
72 | + Error *local_err = NULL; | 73 | + |
73 | + AioContext *ctx; | 74 | +def get_export(node_name='disk-fmt', allow_inactive=None): |
74 | + | 75 | + exp = { |
75 | + if (!s->use_linux_io_uring) { | 76 | + 'id': 'exp0', |
76 | + return false; | 77 | + 'type': 'nbd', |
78 | + 'node-name': node_name, | ||
79 | + 'writable': True, | ||
77 | + } | 80 | + } |
78 | + | 81 | + |
79 | + ctx = qemu_get_current_aio_context(); | 82 | + if allow_inactive is not None: |
80 | + if (unlikely(!aio_setup_linux_io_uring(ctx, &local_err))) { | 83 | + exp['allow-inactive'] = allow_inactive |
81 | + error_reportf_err(local_err, "Unable to use linux io_uring, " | 84 | + |
82 | + "falling back to thread pool: "); | 85 | + return exp |
83 | + s->use_linux_io_uring = false; | 86 | + |
84 | + return false; | 87 | +def node_is_active(_vm, node_name): |
85 | + } | 88 | + nodes = _vm.cmd('query-named-block-nodes', flat=True) |
86 | + return true; | 89 | + node = next(n for n in nodes if n['node-name'] == node_name) |
87 | +} | 90 | + return node['active'] |
88 | + | 91 | + |
89 | +static inline bool raw_check_linux_aio(BDRVRawState *s) | 92 | +with iotests.FilePath('disk.img') as path, \ |
90 | +{ | 93 | + iotests.FilePath('snap.qcow2') as snap_path, \ |
91 | + Error *local_err = NULL; | 94 | + iotests.FilePath('snap2.qcow2') as snap2_path, \ |
92 | + AioContext *ctx; | 95 | + iotests.FilePath('target.img') as target_path, \ |
93 | + | 96 | + iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock, \ |
94 | + if (!s->use_linux_aio) { | 97 | + iotests.VM() as vm: |
95 | + return false; | 98 | + |
96 | + } | 99 | + img_size = '10M' |
97 | + | 100 | + |
98 | + ctx = qemu_get_current_aio_context(); | 101 | + iotests.log('Preparing disk...') |
99 | + if (unlikely(!aio_setup_linux_aio(ctx, &local_err))) { | 102 | + iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size) |
100 | + error_reportf_err(local_err, "Unable to use Linux AIO, " | 103 | + iotests.qemu_img_create('-f', iotests.imgfmt, target_path, img_size) |
101 | + "falling back to thread pool: "); | 104 | + |
102 | + s->use_linux_aio = false; | 105 | + iotests.qemu_img_create('-f', 'qcow2', '-b', path, '-F', iotests.imgfmt, |
103 | + return false; | 106 | + snap_path) |
104 | + } | 107 | + iotests.qemu_img_create('-f', 'qcow2', '-b', snap_path, '-F', 'qcow2', |
105 | + return true; | 108 | + snap2_path) |
106 | +} | 109 | + |
107 | + | 110 | + iotests.log('Launching VM...') |
108 | static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, | 111 | + vm.add_blockdev(f'file,node-name=disk-file,filename={path}') |
109 | uint64_t bytes, QEMUIOVector *qiov, int type) | 112 | + vm.add_blockdev(f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,' |
110 | { | 113 | + 'active=off') |
111 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, | 114 | + vm.add_blockdev(f'file,node-name=target-file,filename={target_path}') |
112 | if (s->needs_alignment && !bdrv_qiov_is_aligned(bs, qiov)) { | 115 | + vm.add_blockdev(f'{iotests.imgfmt},file=target-file,node-name=target-fmt') |
113 | type |= QEMU_AIO_MISALIGNED; | 116 | + vm.add_blockdev(f'file,node-name=snap-file,filename={snap_path}') |
114 | #ifdef CONFIG_LINUX_IO_URING | 117 | + vm.add_blockdev(f'file,node-name=snap2-file,filename={snap2_path}') |
115 | - } else if (s->use_linux_io_uring) { | 118 | + |
116 | + } else if (raw_check_linux_io_uring(s)) { | 119 | + # Actually running the VM activates all images |
117 | assert(qiov->size == bytes); | 120 | + vm.add_paused() |
118 | ret = luring_co_submit(bs, s->fd, offset, qiov, type); | 121 | + |
119 | goto out; | 122 | + vm.launch() |
120 | #endif | 123 | + vm.qmp_log('nbd-server-start', |
121 | #ifdef CONFIG_LINUX_AIO | 124 | + addr={'type': 'unix', 'data':{'path': nbd_sock}}, |
122 | - } else if (s->use_linux_aio) { | 125 | + filters=[filter_qmp_testfiles]) |
123 | + } else if (raw_check_linux_aio(s)) { | 126 | + |
124 | assert(qiov->size == bytes); | 127 | + iotests.log('\n=== Creating export of inactive node ===') |
125 | ret = laio_co_submit(s->fd, offset, qiov, type, | 128 | + |
126 | s->aio_max_batch); | 129 | + iotests.log('\nExports activate nodes without allow-inactive') |
127 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) | 130 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
128 | }; | 131 | + vm.qmp_log('block-export-add', **get_export()) |
129 | 132 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | |
130 | #ifdef CONFIG_LINUX_IO_URING | 133 | + vm.qmp_log('query-block-exports') |
131 | - if (s->use_linux_io_uring) { | 134 | + vm.qmp_log('block-export-del', id='exp0') |
132 | + if (raw_check_linux_io_uring(s)) { | 135 | + vm.event_wait('BLOCK_EXPORT_DELETED') |
133 | return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH); | 136 | + vm.qmp_log('query-block-exports') |
134 | } | 137 | + |
135 | #endif | 138 | + iotests.log('\nExports activate nodes with allow-inactive=false') |
136 | return raw_thread_pool_submit(handle_aiocb_flush, &acb); | 139 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) |
137 | } | 140 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
138 | 141 | + vm.qmp_log('block-export-add', **get_export(allow_inactive=False)) | |
139 | -static void raw_aio_attach_aio_context(BlockDriverState *bs, | 142 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
140 | - AioContext *new_context) | 143 | + vm.qmp_log('query-block-exports') |
141 | -{ | 144 | + vm.qmp_log('block-export-del', id='exp0') |
142 | - BDRVRawState __attribute__((unused)) *s = bs->opaque; | 145 | + vm.event_wait('BLOCK_EXPORT_DELETED') |
143 | -#ifdef CONFIG_LINUX_AIO | 146 | + vm.qmp_log('query-block-exports') |
144 | - if (s->use_linux_aio) { | 147 | + |
145 | - Error *local_err = NULL; | 148 | + iotests.log('\nExport leaves nodes inactive with allow-inactive=true') |
146 | - if (!aio_setup_linux_aio(new_context, &local_err)) { | 149 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) |
147 | - error_reportf_err(local_err, "Unable to use native AIO, " | 150 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
148 | - "falling back to thread pool: "); | 151 | + vm.qmp_log('block-export-add', **get_export(allow_inactive=True)) |
149 | - s->use_linux_aio = false; | 152 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
150 | - } | 153 | + vm.qmp_log('query-block-exports') |
151 | - } | 154 | + vm.qmp_log('block-export-del', id='exp0') |
152 | -#endif | 155 | + vm.event_wait('BLOCK_EXPORT_DELETED') |
153 | -#ifdef CONFIG_LINUX_IO_URING | 156 | + vm.qmp_log('query-block-exports') |
154 | - if (s->use_linux_io_uring) { | 157 | + |
155 | - Error *local_err = NULL; | 158 | + iotests.log('\n=== Inactivating node with existing export ===') |
156 | - if (!aio_setup_linux_io_uring(new_context, &local_err)) { | 159 | + |
157 | - error_reportf_err(local_err, "Unable to use linux io_uring, " | 160 | + iotests.log('\nInactivating nodes with an export fails without ' |
158 | - "falling back to thread pool: "); | 161 | + 'allow-inactive') |
159 | - s->use_linux_io_uring = false; | 162 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) |
160 | - } | 163 | + vm.qmp_log('block-export-add', **get_export(node_name='disk-fmt')) |
161 | - } | 164 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) |
162 | -#endif | 165 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
163 | -} | 166 | + vm.qmp_log('query-block-exports') |
164 | - | 167 | + vm.qmp_log('block-export-del', id='exp0') |
165 | static void raw_close(BlockDriverState *bs) | 168 | + vm.event_wait('BLOCK_EXPORT_DELETED') |
166 | { | 169 | + vm.qmp_log('query-block-exports') |
167 | BDRVRawState *s = bs->opaque; | 170 | + |
168 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = { | 171 | + iotests.log('\nInactivating nodes with an export fails with ' |
169 | .bdrv_co_copy_range_from = raw_co_copy_range_from, | 172 | + 'allow-inactive=false') |
170 | .bdrv_co_copy_range_to = raw_co_copy_range_to, | 173 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) |
171 | .bdrv_refresh_limits = raw_refresh_limits, | 174 | + vm.qmp_log('block-export-add', |
172 | - .bdrv_attach_aio_context = raw_aio_attach_aio_context, | 175 | + **get_export(node_name='disk-fmt', allow_inactive=False)) |
173 | 176 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) | |
174 | .bdrv_co_truncate = raw_co_truncate, | 177 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
175 | .bdrv_co_getlength = raw_co_getlength, | 178 | + vm.qmp_log('query-block-exports') |
176 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = { | 179 | + vm.qmp_log('block-export-del', id='exp0') |
177 | .bdrv_co_copy_range_from = raw_co_copy_range_from, | 180 | + vm.event_wait('BLOCK_EXPORT_DELETED') |
178 | .bdrv_co_copy_range_to = raw_co_copy_range_to, | 181 | + vm.qmp_log('query-block-exports') |
179 | .bdrv_refresh_limits = raw_refresh_limits, | 182 | + |
180 | - .bdrv_attach_aio_context = raw_aio_attach_aio_context, | 183 | + iotests.log('\nInactivating nodes with an export works with ' |
181 | 184 | + 'allow-inactive=true') | |
182 | .bdrv_co_truncate = raw_co_truncate, | 185 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) |
183 | .bdrv_co_getlength = raw_co_getlength, | 186 | + vm.qmp_log('block-export-add', |
184 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = { | 187 | + **get_export(node_name='disk-fmt', allow_inactive=True)) |
185 | .bdrv_co_pwritev = raw_co_pwritev, | 188 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) |
186 | .bdrv_co_flush_to_disk = raw_co_flush_to_disk, | 189 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
187 | .bdrv_refresh_limits = cdrom_refresh_limits, | 190 | + vm.qmp_log('query-block-exports') |
188 | - .bdrv_attach_aio_context = raw_aio_attach_aio_context, | 191 | + vm.qmp_log('block-export-del', id='exp0') |
189 | 192 | + vm.event_wait('BLOCK_EXPORT_DELETED') | |
190 | .bdrv_co_truncate = raw_co_truncate, | 193 | + vm.qmp_log('query-block-exports') |
191 | .bdrv_co_getlength = raw_co_getlength, | 194 | + |
192 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = { | 195 | + iotests.log('\n=== Inactive nodes with parent ===') |
193 | .bdrv_co_pwritev = raw_co_pwritev, | 196 | + |
194 | .bdrv_co_flush_to_disk = raw_co_flush_to_disk, | 197 | + iotests.log('\nInactivating nodes with an active parent fails') |
195 | .bdrv_refresh_limits = cdrom_refresh_limits, | 198 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) |
196 | - .bdrv_attach_aio_context = raw_aio_attach_aio_context, | 199 | + vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False) |
197 | 200 | + iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file')) | |
198 | .bdrv_co_truncate = raw_co_truncate, | 201 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) |
199 | .bdrv_co_getlength = raw_co_getlength, | 202 | + |
203 | + iotests.log('\nInactivating nodes with an inactive parent works') | ||
204 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) | ||
205 | + vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False) | ||
206 | + iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file')) | ||
207 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | ||
208 | + | ||
209 | + iotests.log('\nCreating active parent node with an inactive child fails') | ||
210 | + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', | ||
211 | + node_name='disk-filter') | ||
212 | + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', | ||
213 | + node_name='disk-filter', active=True) | ||
214 | + | ||
215 | + iotests.log('\nCreating inactive parent node with an inactive child works') | ||
216 | + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', | ||
217 | + node_name='disk-filter', active=False) | ||
218 | + vm.qmp_log('blockdev-del', node_name='disk-filter') | ||
219 | + | ||
220 | + iotests.log('\n=== Resizing an inactive node ===') | ||
221 | + vm.qmp_log('block_resize', node_name='disk-fmt', size=16*1024*1024) | ||
222 | + | ||
223 | + iotests.log('\n=== Taking a snapshot of an inactive node ===') | ||
224 | + | ||
225 | + iotests.log('\nActive overlay over inactive backing file automatically ' | ||
226 | + 'makes both inactive for compatibility') | ||
227 | + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt', | ||
228 | + file='snap-file', backing=None) | ||
229 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | ||
230 | + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) | ||
231 | + vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt') | ||
232 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | ||
233 | + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) | ||
234 | + vm.qmp_log('blockdev-del', node_name='snap-fmt') | ||
235 | + | ||
236 | + iotests.log('\nInactive overlay over inactive backing file just works') | ||
237 | + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt', | ||
238 | + file='snap-file', backing=None, active=False) | ||
239 | + vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt') | ||
240 | + | ||
241 | + iotests.log('\n=== Block jobs with inactive nodes ===') | ||
242 | + | ||
243 | + iotests.log('\nStreaming into an inactive node') | ||
244 | + vm.qmp_log('block-stream', device='snap-fmt', | ||
245 | + filters=[iotests.filter_qmp_generated_node_ids]) | ||
246 | + | ||
247 | + iotests.log('\nCommitting an inactive root node (active commit)') | ||
248 | + vm.qmp_log('block-commit', job_id='job0', device='snap-fmt', | ||
249 | + filters=[iotests.filter_qmp_generated_node_ids]) | ||
250 | + | ||
251 | + iotests.log('\nCommitting an inactive intermediate node to inactive base') | ||
252 | + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap2-fmt', | ||
253 | + file='snap2-file', backing='snap-fmt', active=False) | ||
254 | + | ||
255 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | ||
256 | + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) | ||
257 | + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) | ||
258 | + | ||
259 | + vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt', | ||
260 | + top_node='snap-fmt', | ||
261 | + filters=[iotests.filter_qmp_generated_node_ids]) | ||
262 | + | ||
263 | + iotests.log('\nCommitting an inactive intermediate node to active base') | ||
264 | + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) | ||
265 | + vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt', | ||
266 | + top_node='snap-fmt', | ||
267 | + filters=[iotests.filter_qmp_generated_node_ids]) | ||
268 | + | ||
269 | + iotests.log('\nMirror from inactive source to active target') | ||
270 | + vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt', | ||
271 | + target='target-fmt', sync='full', | ||
272 | + filters=[iotests.filter_qmp_generated_node_ids]) | ||
273 | + | ||
274 | + iotests.log('\nMirror from active source to inactive target') | ||
275 | + | ||
276 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | ||
277 | + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) | ||
278 | + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) | ||
279 | + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) | ||
280 | + | ||
281 | + # Activating snap2-fmt recursively activates the whole backing chain | ||
282 | + vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=True) | ||
283 | + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False) | ||
284 | + | ||
285 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | ||
286 | + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) | ||
287 | + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) | ||
288 | + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) | ||
289 | + | ||
290 | + vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt', | ||
291 | + target='target-fmt', sync='full', | ||
292 | + filters=[iotests.filter_qmp_generated_node_ids]) | ||
293 | + | ||
294 | + iotests.log('\nBackup from active source to inactive target') | ||
295 | + | ||
296 | + vm.qmp_log('blockdev-backup', job_id='job0', device='snap2-fmt', | ||
297 | + target='target-fmt', sync='full', | ||
298 | + filters=[iotests.filter_qmp_generated_node_ids]) | ||
299 | + | ||
300 | + iotests.log('\nBackup from inactive source to active target') | ||
301 | + | ||
302 | + # Inactivating snap2-fmt recursively inactivates the whole backing chain | ||
303 | + vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=False) | ||
304 | + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=True) | ||
305 | + | ||
306 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | ||
307 | + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) | ||
308 | + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) | ||
309 | + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) | ||
310 | + | ||
311 | + vm.qmp_log('blockdev-backup', job_id='job0', device='snap2-fmt', | ||
312 | + target='target-fmt', sync='full', | ||
313 | + filters=[iotests.filter_qmp_generated_node_ids]) | ||
314 | + | ||
315 | + iotests.log('\n=== Accessing export on inactive node ===') | ||
316 | + | ||
317 | + # Use the target node because it has the right image format and isn't the | ||
318 | + # (read-only) backing file of a qcow2 node | ||
319 | + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False) | ||
320 | + vm.qmp_log('block-export-add', | ||
321 | + **get_export(node_name='target-fmt', allow_inactive=True)) | ||
322 | + | ||
323 | + # The read should succeed, everything else should fail gracefully | ||
324 | + qemu_io = QemuIoInteractive('-f', 'raw', | ||
325 | + f'nbd+unix:///target-fmt?socket={nbd_sock}') | ||
326 | + iotests.log(qemu_io.cmd('read 0 64k'), filters=[filter_qemu_io]) | ||
327 | + iotests.log(qemu_io.cmd('write 0 64k'), filters=[filter_qemu_io]) | ||
328 | + iotests.log(qemu_io.cmd('write -z 0 64k'), filters=[filter_qemu_io]) | ||
329 | + iotests.log(qemu_io.cmd('write -zu 0 64k'), filters=[filter_qemu_io]) | ||
330 | + iotests.log(qemu_io.cmd('discard 0 64k'), filters=[filter_qemu_io]) | ||
331 | + iotests.log(qemu_io.cmd('flush'), filters=[filter_qemu_io]) | ||
332 | + iotests.log(qemu_io.cmd('map'), filters=[filter_qemu_io]) | ||
333 | + qemu_io.close() | ||
334 | + | ||
335 | + iotests.log('\n=== Resuming VM activates all images ===') | ||
336 | + vm.qmp_log('cont') | ||
337 | + | ||
338 | + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) | ||
339 | + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) | ||
340 | + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) | ||
341 | + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) | ||
342 | + | ||
343 | + iotests.log('\nShutting down...') | ||
344 | + vm.shutdown() | ||
345 | + log = vm.get_log() | ||
346 | + if log: | ||
347 | + iotests.log(log, [filter_qtest, filter_qemu_io]) | ||
348 | diff --git a/tests/qemu-iotests/tests/inactive-node-nbd.out b/tests/qemu-iotests/tests/inactive-node-nbd.out | ||
349 | new file mode 100644 | ||
350 | index XXXXXXX..XXXXXXX | ||
351 | --- /dev/null | ||
352 | +++ b/tests/qemu-iotests/tests/inactive-node-nbd.out | ||
353 | @@ -XXX,XX +XXX,XX @@ | ||
354 | +Preparing disk... | ||
355 | +Launching VM... | ||
356 | +{"execute": "nbd-server-start", "arguments": {"addr": {"data": {"path": "SOCK_DIR/PID-nbd.sock"}, "type": "unix"}}} | ||
357 | +{"return": {}} | ||
358 | + | ||
359 | +=== Creating export of inactive node === | ||
360 | + | ||
361 | +Exports activate nodes without allow-inactive | ||
362 | +disk-fmt active: False | ||
363 | +{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} | ||
364 | +{"return": {}} | ||
365 | +disk-fmt active: True | ||
366 | +{"execute": "query-block-exports", "arguments": {}} | ||
367 | +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} | ||
368 | +{"execute": "block-export-del", "arguments": {"id": "exp0"}} | ||
369 | +{"return": {}} | ||
370 | +{"execute": "query-block-exports", "arguments": {}} | ||
371 | +{"return": []} | ||
372 | + | ||
373 | +Exports activate nodes with allow-inactive=false | ||
374 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} | ||
375 | +{"return": {}} | ||
376 | +disk-fmt active: False | ||
377 | +{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} | ||
378 | +{"return": {}} | ||
379 | +disk-fmt active: True | ||
380 | +{"execute": "query-block-exports", "arguments": {}} | ||
381 | +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} | ||
382 | +{"execute": "block-export-del", "arguments": {"id": "exp0"}} | ||
383 | +{"return": {}} | ||
384 | +{"execute": "query-block-exports", "arguments": {}} | ||
385 | +{"return": []} | ||
386 | + | ||
387 | +Export leaves nodes inactive with allow-inactive=true | ||
388 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} | ||
389 | +{"return": {}} | ||
390 | +disk-fmt active: False | ||
391 | +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} | ||
392 | +{"return": {}} | ||
393 | +disk-fmt active: False | ||
394 | +{"execute": "query-block-exports", "arguments": {}} | ||
395 | +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} | ||
396 | +{"execute": "block-export-del", "arguments": {"id": "exp0"}} | ||
397 | +{"return": {}} | ||
398 | +{"execute": "query-block-exports", "arguments": {}} | ||
399 | +{"return": []} | ||
400 | + | ||
401 | +=== Inactivating node with existing export === | ||
402 | + | ||
403 | +Inactivating nodes with an export fails without allow-inactive | ||
404 | +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} | ||
405 | +{"return": {}} | ||
406 | +{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} | ||
407 | +{"return": {}} | ||
408 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} | ||
409 | +{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}} | ||
410 | +disk-fmt active: True | ||
411 | +{"execute": "query-block-exports", "arguments": {}} | ||
412 | +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} | ||
413 | +{"execute": "block-export-del", "arguments": {"id": "exp0"}} | ||
414 | +{"return": {}} | ||
415 | +{"execute": "query-block-exports", "arguments": {}} | ||
416 | +{"return": []} | ||
417 | + | ||
418 | +Inactivating nodes with an export fails with allow-inactive=false | ||
419 | +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} | ||
420 | +{"return": {}} | ||
421 | +{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} | ||
422 | +{"return": {}} | ||
423 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} | ||
424 | +{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}} | ||
425 | +disk-fmt active: True | ||
426 | +{"execute": "query-block-exports", "arguments": {}} | ||
427 | +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} | ||
428 | +{"execute": "block-export-del", "arguments": {"id": "exp0"}} | ||
429 | +{"return": {}} | ||
430 | +{"execute": "query-block-exports", "arguments": {}} | ||
431 | +{"return": []} | ||
432 | + | ||
433 | +Inactivating nodes with an export works with allow-inactive=true | ||
434 | +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} | ||
435 | +{"return": {}} | ||
436 | +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} | ||
437 | +{"return": {}} | ||
438 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} | ||
439 | +{"return": {}} | ||
440 | +disk-fmt active: False | ||
441 | +{"execute": "query-block-exports", "arguments": {}} | ||
442 | +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} | ||
443 | +{"execute": "block-export-del", "arguments": {"id": "exp0"}} | ||
444 | +{"return": {}} | ||
445 | +{"execute": "query-block-exports", "arguments": {}} | ||
446 | +{"return": []} | ||
447 | + | ||
448 | +=== Inactive nodes with parent === | ||
449 | + | ||
450 | +Inactivating nodes with an active parent fails | ||
451 | +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} | ||
452 | +{"return": {}} | ||
453 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}} | ||
454 | +{"error": {"class": "GenericError", "desc": "Node has active parent node"}} | ||
455 | +disk-file active: True | ||
456 | +disk-fmt active: True | ||
457 | + | ||
458 | +Inactivating nodes with an inactive parent works | ||
459 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} | ||
460 | +{"return": {}} | ||
461 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}} | ||
462 | +{"return": {}} | ||
463 | +disk-file active: False | ||
464 | +disk-fmt active: False | ||
465 | + | ||
466 | +Creating active parent node with an inactive child fails | ||
467 | +{"execute": "blockdev-add", "arguments": {"driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} | ||
468 | +{"error": {"class": "GenericError", "desc": "Inactive 'disk-fmt' can't be a file child of active 'disk-filter'"}} | ||
469 | +{"execute": "blockdev-add", "arguments": {"active": true, "driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} | ||
470 | +{"error": {"class": "GenericError", "desc": "Inactive 'disk-fmt' can't be a file child of active 'disk-filter'"}} | ||
471 | + | ||
472 | +Creating inactive parent node with an inactive child works | ||
473 | +{"execute": "blockdev-add", "arguments": {"active": false, "driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} | ||
474 | +{"return": {}} | ||
475 | +{"execute": "blockdev-del", "arguments": {"node-name": "disk-filter"}} | ||
476 | +{"return": {}} | ||
477 | + | ||
478 | +=== Resizing an inactive node === | ||
479 | +{"execute": "block_resize", "arguments": {"node-name": "disk-fmt", "size": 16777216}} | ||
480 | +{"error": {"class": "GenericError", "desc": "Permission 'resize' unavailable on inactive node"}} | ||
481 | + | ||
482 | +=== Taking a snapshot of an inactive node === | ||
483 | + | ||
484 | +Active overlay over inactive backing file automatically makes both inactive for compatibility | ||
485 | +{"execute": "blockdev-add", "arguments": {"backing": null, "driver": "qcow2", "file": "snap-file", "node-name": "snap-fmt"}} | ||
486 | +{"return": {}} | ||
487 | +disk-fmt active: False | ||
488 | +snap-fmt active: True | ||
489 | +{"execute": "blockdev-snapshot", "arguments": {"node": "disk-fmt", "overlay": "snap-fmt"}} | ||
490 | +{"return": {}} | ||
491 | +disk-fmt active: False | ||
492 | +snap-fmt active: False | ||
493 | +{"execute": "blockdev-del", "arguments": {"node-name": "snap-fmt"}} | ||
494 | +{"return": {}} | ||
495 | + | ||
496 | +Inactive overlay over inactive backing file just works | ||
497 | +{"execute": "blockdev-add", "arguments": {"active": false, "backing": null, "driver": "qcow2", "file": "snap-file", "node-name": "snap-fmt"}} | ||
498 | +{"return": {}} | ||
499 | +{"execute": "blockdev-snapshot", "arguments": {"node": "disk-fmt", "overlay": "snap-fmt"}} | ||
500 | +{"return": {}} | ||
501 | + | ||
502 | +=== Block jobs with inactive nodes === | ||
503 | + | ||
504 | +Streaming into an inactive node | ||
505 | +{"execute": "block-stream", "arguments": {"device": "snap-fmt"}} | ||
506 | +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'snap-fmt' can't be a file child of active 'NODE_NAME'"}} | ||
507 | + | ||
508 | +Committing an inactive root node (active commit) | ||
509 | +{"execute": "block-commit", "arguments": {"device": "snap-fmt", "job-id": "job0"}} | ||
510 | +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} | ||
511 | + | ||
512 | +Committing an inactive intermediate node to inactive base | ||
513 | +{"execute": "blockdev-add", "arguments": {"active": false, "backing": "snap-fmt", "driver": "qcow2", "file": "snap2-file", "node-name": "snap2-fmt"}} | ||
514 | +{"return": {}} | ||
515 | +disk-fmt active: False | ||
516 | +snap-fmt active: False | ||
517 | +snap2-fmt active: False | ||
518 | +{"execute": "block-commit", "arguments": {"device": "snap2-fmt", "job-id": "job0", "top-node": "snap-fmt"}} | ||
519 | +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} | ||
520 | + | ||
521 | +Committing an inactive intermediate node to active base | ||
522 | +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} | ||
523 | +{"return": {}} | ||
524 | +{"execute": "block-commit", "arguments": {"device": "snap2-fmt", "job-id": "job0", "top-node": "snap-fmt"}} | ||
525 | +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} | ||
526 | + | ||
527 | +Mirror from inactive source to active target | ||
528 | +{"execute": "blockdev-mirror", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} | ||
529 | +{"error": {"class": "GenericError", "desc": "Inactive 'snap2-fmt' can't be a backing child of active 'NODE_NAME'"}} | ||
530 | + | ||
531 | +Mirror from active source to inactive target | ||
532 | +disk-fmt active: True | ||
533 | +snap-fmt active: False | ||
534 | +snap2-fmt active: False | ||
535 | +target-fmt active: True | ||
536 | +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "snap2-fmt"}} | ||
537 | +{"return": {}} | ||
538 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "target-fmt"}} | ||
539 | +{"return": {}} | ||
540 | +disk-fmt active: True | ||
541 | +snap-fmt active: True | ||
542 | +snap2-fmt active: True | ||
543 | +target-fmt active: False | ||
544 | +{"execute": "blockdev-mirror", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} | ||
545 | +{"error": {"class": "GenericError", "desc": "Permission 'write' unavailable on inactive node"}} | ||
546 | + | ||
547 | +Backup from active source to inactive target | ||
548 | +{"execute": "blockdev-backup", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} | ||
549 | +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'target-fmt' can't be a target child of active 'NODE_NAME'"}} | ||
550 | + | ||
551 | +Backup from inactive source to active target | ||
552 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "snap2-fmt"}} | ||
553 | +{"return": {}} | ||
554 | +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "target-fmt"}} | ||
555 | +{"return": {}} | ||
556 | +disk-fmt active: False | ||
557 | +snap-fmt active: False | ||
558 | +snap2-fmt active: False | ||
559 | +target-fmt active: True | ||
560 | +{"execute": "blockdev-backup", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} | ||
561 | +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'snap2-fmt' can't be a file child of active 'NODE_NAME'"}} | ||
562 | + | ||
563 | +=== Accessing export on inactive node === | ||
564 | +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "target-fmt"}} | ||
565 | +{"return": {}} | ||
566 | +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "target-fmt", "type": "nbd", "writable": true}} | ||
567 | +{"return": {}} | ||
568 | +read 65536/65536 bytes at offset 0 | ||
569 | +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
570 | + | ||
571 | +write failed: Operation not permitted | ||
572 | + | ||
573 | +write failed: Operation not permitted | ||
574 | + | ||
575 | +write failed: Operation not permitted | ||
576 | + | ||
577 | +discard failed: Operation not permitted | ||
578 | + | ||
579 | + | ||
580 | +qemu-io: Failed to get allocation status: Operation not permitted | ||
581 | + | ||
582 | + | ||
583 | +=== Resuming VM activates all images === | ||
584 | +{"execute": "cont", "arguments": {}} | ||
585 | +{"return": {}} | ||
586 | +disk-fmt active: True | ||
587 | +snap-fmt active: True | ||
588 | +snap2-fmt active: True | ||
589 | +target-fmt active: True | ||
590 | + | ||
591 | +Shutting down... | ||
592 | + | ||
200 | -- | 593 | -- |
201 | 2.43.0 | 594 | 2.48.1 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | Nothing in the completion code path relies on the AioContext lock | ||
4 | anymore. Virtqueues are only accessed from one thread at any moment and | ||
5 | the s->rq global state is protected by its own lock now. | ||
6 | |||
7 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
8 | Message-ID: <20230914140101.1065008-4-stefanha@redhat.com> | ||
9 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Reviewed-by: Michael S. Tsirkin <mst@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | hw/block/virtio-blk.c | 34 ++++------------------------------ | ||
15 | 1 file changed, 4 insertions(+), 30 deletions(-) | ||
16 | |||
17 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/hw/block/virtio-blk.c | ||
20 | +++ b/hw/block/virtio-blk.c | ||
21 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_rw_complete(void *opaque, int ret) | ||
22 | VirtIOBlock *s = next->dev; | ||
23 | VirtIODevice *vdev = VIRTIO_DEVICE(s); | ||
24 | |||
25 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
26 | while (next) { | ||
27 | VirtIOBlockReq *req = next; | ||
28 | next = req->mr_next; | ||
29 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_rw_complete(void *opaque, int ret) | ||
30 | block_acct_done(blk_get_stats(s->blk), &req->acct); | ||
31 | virtio_blk_free_request(req); | ||
32 | } | ||
33 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
34 | } | ||
35 | |||
36 | static void virtio_blk_flush_complete(void *opaque, int ret) | ||
37 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_flush_complete(void *opaque, int ret) | ||
38 | VirtIOBlockReq *req = opaque; | ||
39 | VirtIOBlock *s = req->dev; | ||
40 | |||
41 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
42 | - if (ret) { | ||
43 | - if (virtio_blk_handle_rw_error(req, -ret, 0, true)) { | ||
44 | - goto out; | ||
45 | - } | ||
46 | + if (ret && virtio_blk_handle_rw_error(req, -ret, 0, true)) { | ||
47 | + return; | ||
48 | } | ||
49 | |||
50 | virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); | ||
51 | block_acct_done(blk_get_stats(s->blk), &req->acct); | ||
52 | virtio_blk_free_request(req); | ||
53 | - | ||
54 | -out: | ||
55 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
56 | } | ||
57 | |||
58 | static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) | ||
59 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) | ||
60 | bool is_write_zeroes = (virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type) & | ||
61 | ~VIRTIO_BLK_T_BARRIER) == VIRTIO_BLK_T_WRITE_ZEROES; | ||
62 | |||
63 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
64 | - if (ret) { | ||
65 | - if (virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) { | ||
66 | - goto out; | ||
67 | - } | ||
68 | + if (ret && virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) { | ||
69 | + return; | ||
70 | } | ||
71 | |||
72 | virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); | ||
73 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) | ||
74 | block_acct_done(blk_get_stats(s->blk), &req->acct); | ||
75 | } | ||
76 | virtio_blk_free_request(req); | ||
77 | - | ||
78 | -out: | ||
79 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
80 | } | ||
81 | |||
82 | #ifdef __linux__ | ||
83 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_ioctl_complete(void *opaque, int status) | ||
84 | virtio_stl_p(vdev, &scsi->data_len, hdr->dxfer_len); | ||
85 | |||
86 | out: | ||
87 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
88 | virtio_blk_req_complete(req, status); | ||
89 | virtio_blk_free_request(req); | ||
90 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
91 | g_free(ioctl_req); | ||
92 | } | ||
93 | |||
94 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_zone_report_complete(void *opaque, int ret) | ||
95 | { | ||
96 | ZoneCmdData *data = opaque; | ||
97 | VirtIOBlockReq *req = data->req; | ||
98 | - VirtIOBlock *s = req->dev; | ||
99 | VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); | ||
100 | struct iovec *in_iov = data->in_iov; | ||
101 | unsigned in_num = data->in_num; | ||
102 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_zone_report_complete(void *opaque, int ret) | ||
103 | } | ||
104 | |||
105 | out: | ||
106 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
107 | virtio_blk_req_complete(req, err_status); | ||
108 | virtio_blk_free_request(req); | ||
109 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
110 | g_free(data->zone_report_data.zones); | ||
111 | g_free(data); | ||
112 | } | ||
113 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_zone_mgmt_complete(void *opaque, int ret) | ||
114 | err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD; | ||
115 | } | ||
116 | |||
117 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
118 | virtio_blk_req_complete(req, err_status); | ||
119 | virtio_blk_free_request(req); | ||
120 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
121 | } | ||
122 | |||
123 | static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op) | ||
124 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_zone_append_complete(void *opaque, int ret) | ||
125 | { | ||
126 | ZoneCmdData *data = opaque; | ||
127 | VirtIOBlockReq *req = data->req; | ||
128 | - VirtIOBlock *s = req->dev; | ||
129 | VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); | ||
130 | int64_t append_sector, n; | ||
131 | uint8_t err_status = VIRTIO_BLK_S_OK; | ||
132 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_zone_append_complete(void *opaque, int ret) | ||
133 | trace_virtio_blk_zone_append_complete(vdev, req, append_sector, ret); | ||
134 | |||
135 | out: | ||
136 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
137 | virtio_blk_req_complete(req, err_status); | ||
138 | virtio_blk_free_request(req); | ||
139 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
140 | g_free(data); | ||
141 | } | ||
142 | |||
143 | @@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_zone_append(VirtIOBlockReq *req, | ||
144 | return 0; | ||
145 | |||
146 | out: | ||
147 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
148 | virtio_blk_req_complete(req, err_status); | ||
149 | virtio_blk_free_request(req); | ||
150 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
151 | return err_status; | ||
152 | } | ||
153 | |||
154 | -- | ||
155 | 2.43.0 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | There is no need to acquire the AioContext lock around blk_aio_*() or | ||
4 | blk_get_geometry() anymore. I/O plugging (defer_call()) also does not | ||
5 | require the AioContext lock anymore. | ||
6 | |||
7 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
8 | Message-ID: <20230914140101.1065008-5-stefanha@redhat.com> | ||
9 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Reviewed-by: Michael S. Tsirkin <mst@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | hw/block/virtio-blk.c | 5 ----- | ||
15 | 1 file changed, 5 deletions(-) | ||
16 | |||
17 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/hw/block/virtio-blk.c | ||
20 | +++ b/hw/block/virtio-blk.c | ||
21 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) | ||
22 | MultiReqBuffer mrb = {}; | ||
23 | bool suppress_notifications = virtio_queue_get_notification(vq); | ||
24 | |||
25 | - aio_context_acquire(blk_get_aio_context(s->blk)); | ||
26 | defer_call_begin(); | ||
27 | |||
28 | do { | ||
29 | @@ -XXX,XX +XXX,XX @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) | ||
30 | } | ||
31 | |||
32 | defer_call_end(); | ||
33 | - aio_context_release(blk_get_aio_context(s->blk)); | ||
34 | } | ||
35 | |||
36 | static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) | ||
37 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_dma_restart_bh(void *opaque) | ||
38 | s->rq = NULL; | ||
39 | } | ||
40 | |||
41 | - aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); | ||
42 | while (req) { | ||
43 | VirtIOBlockReq *next = req->next; | ||
44 | if (virtio_blk_handle_request(req, &mrb)) { | ||
45 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_dma_restart_bh(void *opaque) | ||
46 | |||
47 | /* Paired with inc in virtio_blk_dma_restart_cb() */ | ||
48 | blk_dec_in_flight(s->conf.conf.blk); | ||
49 | - | ||
50 | - aio_context_release(blk_get_aio_context(s->conf.conf.blk)); | ||
51 | } | ||
52 | |||
53 | static void virtio_blk_dma_restart_cb(void *opaque, bool running, | ||
54 | -- | ||
55 | 2.43.0 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | blk_aio_*() doesn't require the AioContext lock and the SCSI subsystem's | ||
4 | internal state also does not anymore. | ||
5 | |||
6 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | Acked-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | Message-ID: <20231204164259.1515217-4-stefanha@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | hw/scsi/scsi-disk.c | 23 ----------------------- | ||
13 | hw/scsi/scsi-generic.c | 20 +++----------------- | ||
14 | 2 files changed, 3 insertions(+), 40 deletions(-) | ||
15 | |||
16 | diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/hw/scsi/scsi-disk.c | ||
19 | +++ b/hw/scsi/scsi-disk.c | ||
20 | @@ -XXX,XX +XXX,XX @@ static void scsi_aio_complete(void *opaque, int ret) | ||
21 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
22 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
23 | |||
24 | - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); | ||
25 | - | ||
26 | assert(r->req.aiocb != NULL); | ||
27 | r->req.aiocb = NULL; | ||
28 | |||
29 | @@ -XXX,XX +XXX,XX @@ static void scsi_aio_complete(void *opaque, int ret) | ||
30 | scsi_req_complete(&r->req, GOOD); | ||
31 | |||
32 | done: | ||
33 | - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); | ||
34 | scsi_req_unref(&r->req); | ||
35 | } | ||
36 | |||
37 | @@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void *opaque, int ret) | ||
38 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
39 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
40 | |||
41 | - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); | ||
42 | - | ||
43 | assert(r->req.aiocb != NULL); | ||
44 | r->req.aiocb = NULL; | ||
45 | |||
46 | @@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void *opaque, int ret) | ||
47 | trace_scsi_disk_read_complete(r->req.tag, r->qiov.size); | ||
48 | } | ||
49 | scsi_read_complete_noio(r, ret); | ||
50 | - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); | ||
51 | } | ||
52 | |||
53 | /* Actually issue a read to the block device. */ | ||
54 | @@ -XXX,XX +XXX,XX @@ static void scsi_do_read_cb(void *opaque, int ret) | ||
55 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
56 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
57 | |||
58 | - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); | ||
59 | - | ||
60 | assert (r->req.aiocb != NULL); | ||
61 | r->req.aiocb = NULL; | ||
62 | |||
63 | @@ -XXX,XX +XXX,XX @@ static void scsi_do_read_cb(void *opaque, int ret) | ||
64 | block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); | ||
65 | } | ||
66 | scsi_do_read(opaque, ret); | ||
67 | - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); | ||
68 | } | ||
69 | |||
70 | /* Read more data from scsi device into buffer. */ | ||
71 | @@ -XXX,XX +XXX,XX @@ static void scsi_write_complete(void * opaque, int ret) | ||
72 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
73 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
74 | |||
75 | - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); | ||
76 | - | ||
77 | assert (r->req.aiocb != NULL); | ||
78 | r->req.aiocb = NULL; | ||
79 | |||
80 | @@ -XXX,XX +XXX,XX @@ static void scsi_write_complete(void * opaque, int ret) | ||
81 | block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); | ||
82 | } | ||
83 | scsi_write_complete_noio(r, ret); | ||
84 | - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); | ||
85 | } | ||
86 | |||
87 | static void scsi_write_data(SCSIRequest *req) | ||
88 | @@ -XXX,XX +XXX,XX @@ static void scsi_unmap_complete(void *opaque, int ret) | ||
89 | SCSIDiskReq *r = data->r; | ||
90 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
91 | |||
92 | - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); | ||
93 | - | ||
94 | assert(r->req.aiocb != NULL); | ||
95 | r->req.aiocb = NULL; | ||
96 | |||
97 | @@ -XXX,XX +XXX,XX @@ static void scsi_unmap_complete(void *opaque, int ret) | ||
98 | block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); | ||
99 | scsi_unmap_complete_noio(data, ret); | ||
100 | } | ||
101 | - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); | ||
102 | } | ||
103 | |||
104 | static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) | ||
105 | @@ -XXX,XX +XXX,XX @@ static void scsi_write_same_complete(void *opaque, int ret) | ||
106 | SCSIDiskReq *r = data->r; | ||
107 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
108 | |||
109 | - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); | ||
110 | - | ||
111 | assert(r->req.aiocb != NULL); | ||
112 | r->req.aiocb = NULL; | ||
113 | |||
114 | @@ -XXX,XX +XXX,XX @@ static void scsi_write_same_complete(void *opaque, int ret) | ||
115 | data->sector << BDRV_SECTOR_BITS, | ||
116 | &data->qiov, 0, | ||
117 | scsi_write_same_complete, data); | ||
118 | - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); | ||
119 | return; | ||
120 | } | ||
121 | |||
122 | @@ -XXX,XX +XXX,XX @@ done: | ||
123 | scsi_req_unref(&r->req); | ||
124 | qemu_vfree(data->iov.iov_base); | ||
125 | g_free(data); | ||
126 | - aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); | ||
127 | } | ||
128 | |||
129 | static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) | ||
130 | @@ -XXX,XX +XXX,XX @@ static void scsi_block_sgio_complete(void *opaque, int ret) | ||
131 | { | ||
132 | SCSIBlockReq *req = (SCSIBlockReq *)opaque; | ||
133 | SCSIDiskReq *r = &req->req; | ||
134 | - SCSIDevice *s = r->req.dev; | ||
135 | sg_io_hdr_t *io_hdr = &req->io_header; | ||
136 | |||
137 | if (ret == 0) { | ||
138 | @@ -XXX,XX +XXX,XX @@ static void scsi_block_sgio_complete(void *opaque, int ret) | ||
139 | } | ||
140 | |||
141 | if (ret > 0) { | ||
142 | - aio_context_acquire(blk_get_aio_context(s->conf.blk)); | ||
143 | if (scsi_handle_rw_error(r, ret, true)) { | ||
144 | - aio_context_release(blk_get_aio_context(s->conf.blk)); | ||
145 | scsi_req_unref(&r->req); | ||
146 | return; | ||
147 | } | ||
148 | - aio_context_release(blk_get_aio_context(s->conf.blk)); | ||
149 | |||
150 | /* Ignore error. */ | ||
151 | ret = 0; | ||
152 | diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c | ||
153 | index XXXXXXX..XXXXXXX 100644 | ||
154 | --- a/hw/scsi/scsi-generic.c | ||
155 | +++ b/hw/scsi/scsi-generic.c | ||
156 | @@ -XXX,XX +XXX,XX @@ done: | ||
157 | static void scsi_command_complete(void *opaque, int ret) | ||
158 | { | ||
159 | SCSIGenericReq *r = (SCSIGenericReq *)opaque; | ||
160 | - SCSIDevice *s = r->req.dev; | ||
161 | - | ||
162 | - aio_context_acquire(blk_get_aio_context(s->conf.blk)); | ||
163 | |||
164 | assert(r->req.aiocb != NULL); | ||
165 | r->req.aiocb = NULL; | ||
166 | |||
167 | scsi_command_complete_noio(r, ret); | ||
168 | - aio_context_release(blk_get_aio_context(s->conf.blk)); | ||
169 | } | ||
170 | |||
171 | static int execute_command(BlockBackend *blk, | ||
172 | @@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret) | ||
173 | SCSIDevice *s = r->req.dev; | ||
174 | int len; | ||
175 | |||
176 | - aio_context_acquire(blk_get_aio_context(s->conf.blk)); | ||
177 | - | ||
178 | assert(r->req.aiocb != NULL); | ||
179 | r->req.aiocb = NULL; | ||
180 | |||
181 | if (ret || r->req.io_canceled) { | ||
182 | scsi_command_complete_noio(r, ret); | ||
183 | - goto done; | ||
184 | + return; | ||
185 | } | ||
186 | |||
187 | len = r->io_header.dxfer_len - r->io_header.resid; | ||
188 | @@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret) | ||
189 | r->io_header.status != GOOD || | ||
190 | len == 0) { | ||
191 | scsi_command_complete_noio(r, 0); | ||
192 | - goto done; | ||
193 | + return; | ||
194 | } | ||
195 | |||
196 | /* Snoop READ CAPACITY output to set the blocksize. */ | ||
197 | @@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret) | ||
198 | req_complete: | ||
199 | scsi_req_data(&r->req, len); | ||
200 | scsi_req_unref(&r->req); | ||
201 | - | ||
202 | -done: | ||
203 | - aio_context_release(blk_get_aio_context(s->conf.blk)); | ||
204 | } | ||
205 | |||
206 | /* Read more data from scsi device into buffer. */ | ||
207 | @@ -XXX,XX +XXX,XX @@ static void scsi_write_complete(void * opaque, int ret) | ||
208 | |||
209 | trace_scsi_generic_write_complete(ret); | ||
210 | |||
211 | - aio_context_acquire(blk_get_aio_context(s->conf.blk)); | ||
212 | - | ||
213 | assert(r->req.aiocb != NULL); | ||
214 | r->req.aiocb = NULL; | ||
215 | |||
216 | if (ret || r->req.io_canceled) { | ||
217 | scsi_command_complete_noio(r, ret); | ||
218 | - goto done; | ||
219 | + return; | ||
220 | } | ||
221 | |||
222 | if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && | ||
223 | @@ -XXX,XX +XXX,XX @@ static void scsi_write_complete(void * opaque, int ret) | ||
224 | } | ||
225 | |||
226 | scsi_command_complete_noio(r, ret); | ||
227 | - | ||
228 | -done: | ||
229 | - aio_context_release(blk_get_aio_context(s->conf.blk)); | ||
230 | } | ||
231 | |||
232 | /* Write data to a scsi device. Returns nonzero on failure. | ||
233 | -- | ||
234 | 2.43.0 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | Commit abfcd2760b3e ("dma-helpers: prevent dma_blk_cb() vs | ||
4 | dma_aio_cancel() race") acquired the AioContext lock inside dma_blk_cb() | ||
5 | to avoid a race with scsi_device_purge_requests() running in the main | ||
6 | loop thread. | ||
7 | |||
8 | The SCSI code no longer calls dma_aio_cancel() from the main loop thread | ||
9 | while I/O is running in the IOThread AioContext. Therefore it is no | ||
10 | longer necessary to take this lock to protect DMAAIOCB fields. The | ||
11 | ->cb() function also does not require the lock because blk_aio_*() and | ||
12 | friends do not need the AioContext lock. | ||
13 | |||
14 | Both hw/ide/core.c and hw/ide/macio.c also call dma_blk_io() but don't | ||
15 | rely on it taking the AioContext lock, so this change is safe. | ||
16 | |||
17 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
18 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
19 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
20 | Message-ID: <20231204164259.1515217-5-stefanha@redhat.com> | ||
21 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
22 | --- | ||
23 | system/dma-helpers.c | 7 ++----- | ||
24 | 1 file changed, 2 insertions(+), 5 deletions(-) | ||
25 | |||
26 | diff --git a/system/dma-helpers.c b/system/dma-helpers.c | ||
27 | index XXXXXXX..XXXXXXX 100644 | ||
28 | --- a/system/dma-helpers.c | ||
29 | +++ b/system/dma-helpers.c | ||
30 | @@ -XXX,XX +XXX,XX @@ static void dma_blk_cb(void *opaque, int ret) | ||
31 | |||
32 | trace_dma_blk_cb(dbs, ret); | ||
33 | |||
34 | - aio_context_acquire(ctx); | ||
35 | dbs->acb = NULL; | ||
36 | dbs->offset += dbs->iov.size; | ||
37 | |||
38 | if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) { | ||
39 | dma_complete(dbs, ret); | ||
40 | - goto out; | ||
41 | + return; | ||
42 | } | ||
43 | dma_blk_unmap(dbs); | ||
44 | |||
45 | @@ -XXX,XX +XXX,XX @@ static void dma_blk_cb(void *opaque, int ret) | ||
46 | trace_dma_map_wait(dbs); | ||
47 | dbs->bh = aio_bh_new(ctx, reschedule_dma, dbs); | ||
48 | cpu_register_map_client(dbs->bh); | ||
49 | - goto out; | ||
50 | + return; | ||
51 | } | ||
52 | |||
53 | if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) { | ||
54 | @@ -XXX,XX +XXX,XX @@ static void dma_blk_cb(void *opaque, int ret) | ||
55 | dbs->acb = dbs->io_func(dbs->offset, &dbs->iov, | ||
56 | dma_blk_cb, dbs, dbs->io_func_opaque); | ||
57 | assert(dbs->acb); | ||
58 | -out: | ||
59 | - aio_context_release(ctx); | ||
60 | } | ||
61 | |||
62 | static void dma_aio_cancel(BlockAIOCB *acb) | ||
63 | -- | ||
64 | 2.43.0 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | Since the removal of AioContext locking, the correctness of the code | ||
4 | relies on running requests from a single AioContext at any given time. | ||
5 | |||
6 | Add assertions that verify that callbacks are invoked in the correct | ||
7 | AioContext. | ||
8 | |||
9 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
10 | Message-ID: <20231205182011.1976568-3-stefanha@redhat.com> | ||
11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | hw/scsi/scsi-disk.c | 14 ++++++++++++++ | ||
15 | system/dma-helpers.c | 3 +++ | ||
16 | 2 files changed, 17 insertions(+) | ||
17 | |||
18 | diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/hw/scsi/scsi-disk.c | ||
21 | +++ b/hw/scsi/scsi-disk.c | ||
22 | @@ -XXX,XX +XXX,XX @@ static void scsi_aio_complete(void *opaque, int ret) | ||
23 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
24 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
25 | |||
26 | + /* The request must only run in the BlockBackend's AioContext */ | ||
27 | + assert(blk_get_aio_context(s->qdev.conf.blk) == | ||
28 | + qemu_get_current_aio_context()); | ||
29 | + | ||
30 | assert(r->req.aiocb != NULL); | ||
31 | r->req.aiocb = NULL; | ||
32 | |||
33 | @@ -XXX,XX +XXX,XX @@ static void scsi_dma_complete(void *opaque, int ret) | ||
34 | |||
35 | static void scsi_read_complete_noio(SCSIDiskReq *r, int ret) | ||
36 | { | ||
37 | + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
38 | uint32_t n; | ||
39 | |||
40 | + /* The request must only run in the BlockBackend's AioContext */ | ||
41 | + assert(blk_get_aio_context(s->qdev.conf.blk) == | ||
42 | + qemu_get_current_aio_context()); | ||
43 | + | ||
44 | assert(r->req.aiocb == NULL); | ||
45 | if (scsi_disk_req_check_error(r, ret, false)) { | ||
46 | goto done; | ||
47 | @@ -XXX,XX +XXX,XX @@ static void scsi_read_data(SCSIRequest *req) | ||
48 | |||
49 | static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) | ||
50 | { | ||
51 | + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | ||
52 | uint32_t n; | ||
53 | |||
54 | + /* The request must only run in the BlockBackend's AioContext */ | ||
55 | + assert(blk_get_aio_context(s->qdev.conf.blk) == | ||
56 | + qemu_get_current_aio_context()); | ||
57 | + | ||
58 | assert (r->req.aiocb == NULL); | ||
59 | if (scsi_disk_req_check_error(r, ret, false)) { | ||
60 | goto done; | ||
61 | diff --git a/system/dma-helpers.c b/system/dma-helpers.c | ||
62 | index XXXXXXX..XXXXXXX 100644 | ||
63 | --- a/system/dma-helpers.c | ||
64 | +++ b/system/dma-helpers.c | ||
65 | @@ -XXX,XX +XXX,XX @@ static void dma_blk_cb(void *opaque, int ret) | ||
66 | |||
67 | trace_dma_blk_cb(dbs, ret); | ||
68 | |||
69 | + /* DMAAIOCB is not thread-safe and must be accessed only from dbs->ctx */ | ||
70 | + assert(ctx == qemu_get_current_aio_context()); | ||
71 | + | ||
72 | dbs->acb = NULL; | ||
73 | dbs->offset += dbs->iov.size; | ||
74 | |||
75 | -- | ||
76 | 2.43.0 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | The aio_context_acquire() API is being removed. Drop the test case that | ||
4 | calls the API. | ||
5 | |||
6 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | Message-ID: <20231205182011.1976568-4-stefanha@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | tests/unit/test-aio.c | 67 +------------------------------------------ | ||
13 | 1 file changed, 1 insertion(+), 66 deletions(-) | ||
14 | |||
15 | diff --git a/tests/unit/test-aio.c b/tests/unit/test-aio.c | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/tests/unit/test-aio.c | ||
18 | +++ b/tests/unit/test-aio.c | ||
19 | @@ -XXX,XX +XXX,XX @@ static void event_ready_cb(EventNotifier *e) | ||
20 | |||
21 | /* Tests using aio_*. */ | ||
22 | |||
23 | -typedef struct { | ||
24 | - QemuMutex start_lock; | ||
25 | - EventNotifier notifier; | ||
26 | - bool thread_acquired; | ||
27 | -} AcquireTestData; | ||
28 | - | ||
29 | -static void *test_acquire_thread(void *opaque) | ||
30 | -{ | ||
31 | - AcquireTestData *data = opaque; | ||
32 | - | ||
33 | - /* Wait for other thread to let us start */ | ||
34 | - qemu_mutex_lock(&data->start_lock); | ||
35 | - qemu_mutex_unlock(&data->start_lock); | ||
36 | - | ||
37 | - /* event_notifier_set might be called either before or after | ||
38 | - * the main thread's call to poll(). The test case's outcome | ||
39 | - * should be the same in either case. | ||
40 | - */ | ||
41 | - event_notifier_set(&data->notifier); | ||
42 | - aio_context_acquire(ctx); | ||
43 | - aio_context_release(ctx); | ||
44 | - | ||
45 | - data->thread_acquired = true; /* success, we got here */ | ||
46 | - | ||
47 | - return NULL; | ||
48 | -} | ||
49 | - | ||
50 | static void set_event_notifier(AioContext *nctx, EventNotifier *notifier, | ||
51 | EventNotifierHandler *handler) | ||
52 | { | ||
53 | aio_set_event_notifier(nctx, notifier, handler, NULL, NULL); | ||
54 | } | ||
55 | |||
56 | -static void dummy_notifier_read(EventNotifier *n) | ||
57 | -{ | ||
58 | - event_notifier_test_and_clear(n); | ||
59 | -} | ||
60 | - | ||
61 | -static void test_acquire(void) | ||
62 | -{ | ||
63 | - QemuThread thread; | ||
64 | - AcquireTestData data; | ||
65 | - | ||
66 | - /* Dummy event notifier ensures aio_poll() will block */ | ||
67 | - event_notifier_init(&data.notifier, false); | ||
68 | - set_event_notifier(ctx, &data.notifier, dummy_notifier_read); | ||
69 | - g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */ | ||
70 | - | ||
71 | - qemu_mutex_init(&data.start_lock); | ||
72 | - qemu_mutex_lock(&data.start_lock); | ||
73 | - data.thread_acquired = false; | ||
74 | - | ||
75 | - qemu_thread_create(&thread, "test_acquire_thread", | ||
76 | - test_acquire_thread, | ||
77 | - &data, QEMU_THREAD_JOINABLE); | ||
78 | - | ||
79 | - /* Block in aio_poll(), let other thread kick us and acquire context */ | ||
80 | - aio_context_acquire(ctx); | ||
81 | - qemu_mutex_unlock(&data.start_lock); /* let the thread run */ | ||
82 | - g_assert(aio_poll(ctx, true)); | ||
83 | - g_assert(!data.thread_acquired); | ||
84 | - aio_context_release(ctx); | ||
85 | - | ||
86 | - qemu_thread_join(&thread); | ||
87 | - set_event_notifier(ctx, &data.notifier, NULL); | ||
88 | - event_notifier_cleanup(&data.notifier); | ||
89 | - | ||
90 | - g_assert(data.thread_acquired); | ||
91 | -} | ||
92 | - | ||
93 | static void test_bh_schedule(void) | ||
94 | { | ||
95 | BHTestData data = { .n = 0 }; | ||
96 | @@ -XXX,XX +XXX,XX @@ static void test_worker_thread_co_enter(void) | ||
97 | qemu_thread_get_self(&this_thread); | ||
98 | co = qemu_coroutine_create(co_check_current_thread, &this_thread); | ||
99 | |||
100 | - qemu_thread_create(&worker_thread, "test_acquire_thread", | ||
101 | + qemu_thread_create(&worker_thread, "test_aio_co_enter", | ||
102 | test_aio_co_enter, | ||
103 | co, QEMU_THREAD_JOINABLE); | ||
104 | |||
105 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
106 | while (g_main_context_iteration(NULL, false)); | ||
107 | |||
108 | g_test_init(&argc, &argv, NULL); | ||
109 | - g_test_add_func("/aio/acquire", test_acquire); | ||
110 | g_test_add_func("/aio/bh/schedule", test_bh_schedule); | ||
111 | g_test_add_func("/aio/bh/schedule10", test_bh_schedule10); | ||
112 | g_test_add_func("/aio/bh/cancel", test_bh_cancel); | ||
113 | -- | ||
114 | 2.43.0 | diff view generated by jsdifflib |
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The AioContext lock no longer has any effect. Remove it. | 3 | BLOCK_OP_TYPE_DATAPLANE prevents BlockDriverState from being used by |
4 | virtio-blk/virtio-scsi with IOThread. Commit b112a65c52aa ("block: | ||
5 | declare blockjobs and dataplane friends!") eliminated the main reason | ||
6 | for this blocker in 2014. | ||
7 | |||
8 | Nowadays the block layer supports I/O from multiple AioContexts, so | ||
9 | there is even less reason to block IOThread users. Any legitimate | ||
10 | reasons related to interference would probably also apply to | ||
11 | non-IOThread users. | ||
12 | |||
13 | The only remaining users are bdrv_op_unblock(BLOCK_OP_TYPE_DATAPLANE) | ||
14 | calls after bdrv_op_block_all(). If we remove BLOCK_OP_TYPE_DATAPLANE | ||
15 | their behavior doesn't change. | ||
16 | |||
17 | Existing bdrv_op_block_all() callers that don't explicitly unblock | ||
18 | BLOCK_OP_TYPE_DATAPLANE seem to do so simply because no one bothered to | ||
19 | rather than because it is necessary to keep BLOCK_OP_TYPE_DATAPLANE | ||
20 | blocked. | ||
4 | 21 | ||
5 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 22 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
23 | Message-ID: <20250203182529.269066-1-stefanha@redhat.com> | ||
6 | Reviewed-by: Eric Blake <eblake@redhat.com> | 24 | Reviewed-by: Eric Blake <eblake@redhat.com> |
7 | Message-ID: <20231205182011.1976568-9-stefanha@redhat.com> | ||
8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 25 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 26 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 27 | --- |
11 | include/hw/virtio/virtio-scsi.h | 14 -------------- | 28 | include/block/block-common.h | 1 - |
12 | hw/scsi/scsi-bus.c | 2 -- | 29 | block/replication.c | 1 - |
13 | hw/scsi/scsi-disk.c | 31 +++++-------------------------- | 30 | blockjob.c | 2 -- |
14 | hw/scsi/virtio-scsi.c | 18 ------------------ | 31 | hw/block/virtio-blk.c | 9 --------- |
15 | 4 files changed, 5 insertions(+), 60 deletions(-) | 32 | hw/scsi/virtio-scsi.c | 3 --- |
33 | 5 files changed, 16 deletions(-) | ||
16 | 34 | ||
17 | diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h | 35 | diff --git a/include/block/block-common.h b/include/block/block-common.h |
18 | index XXXXXXX..XXXXXXX 100644 | 36 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/include/hw/virtio/virtio-scsi.h | 37 | --- a/include/block/block-common.h |
20 | +++ b/include/hw/virtio/virtio-scsi.h | 38 | +++ b/include/block/block-common.h |
21 | @@ -XXX,XX +XXX,XX @@ struct VirtIOSCSI { | 39 | @@ -XXX,XX +XXX,XX @@ typedef enum BlockOpType { |
22 | uint32_t host_features; | 40 | BLOCK_OP_TYPE_CHANGE, |
23 | }; | 41 | BLOCK_OP_TYPE_COMMIT_SOURCE, |
24 | 42 | BLOCK_OP_TYPE_COMMIT_TARGET, | |
25 | -static inline void virtio_scsi_acquire(VirtIOSCSI *s) | 43 | - BLOCK_OP_TYPE_DATAPLANE, |
26 | -{ | 44 | BLOCK_OP_TYPE_DRIVE_DEL, |
27 | - if (s->ctx) { | 45 | BLOCK_OP_TYPE_EJECT, |
28 | - aio_context_acquire(s->ctx); | 46 | BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, |
29 | - } | 47 | diff --git a/block/replication.c b/block/replication.c |
30 | -} | 48 | index XXXXXXX..XXXXXXX 100644 |
49 | --- a/block/replication.c | ||
50 | +++ b/block/replication.c | ||
51 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
52 | return; | ||
53 | } | ||
54 | bdrv_op_block_all(top_bs, s->blocker); | ||
55 | - bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker); | ||
56 | |||
57 | bdrv_graph_wrunlock(); | ||
58 | |||
59 | diff --git a/blockjob.c b/blockjob.c | ||
60 | index XXXXXXX..XXXXXXX 100644 | ||
61 | --- a/blockjob.c | ||
62 | +++ b/blockjob.c | ||
63 | @@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, | ||
64 | goto fail; | ||
65 | } | ||
66 | |||
67 | - bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); | ||
31 | - | 68 | - |
32 | -static inline void virtio_scsi_release(VirtIOSCSI *s) | 69 | if (!block_job_set_speed(job, speed, errp)) { |
33 | -{ | 70 | goto fail; |
34 | - if (s->ctx) { | 71 | } |
35 | - aio_context_release(s->ctx); | 72 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c |
36 | - } | 73 | index XXXXXXX..XXXXXXX 100644 |
37 | -} | 74 | --- a/hw/block/virtio-blk.c |
75 | +++ b/hw/block/virtio-blk.c | ||
76 | @@ -XXX,XX +XXX,XX @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp) | ||
77 | error_setg(errp, "ioeventfd is required for iothread"); | ||
78 | return false; | ||
79 | } | ||
38 | - | 80 | - |
39 | void virtio_scsi_common_realize(DeviceState *dev, | 81 | - /* |
40 | VirtIOHandleOutput ctrl, | 82 | - * If ioeventfd is (re-)enabled while the guest is running there could |
41 | VirtIOHandleOutput evt, | 83 | - * be block jobs that can conflict. |
42 | diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c | 84 | - */ |
43 | index XXXXXXX..XXXXXXX 100644 | 85 | - if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { |
44 | --- a/hw/scsi/scsi-bus.c | 86 | - error_prepend(errp, "cannot start virtio-blk ioeventfd: "); |
45 | +++ b/hw/scsi/scsi-bus.c | 87 | - return false; |
46 | @@ -XXX,XX +XXX,XX @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) | 88 | - } |
47 | { | ||
48 | scsi_device_for_each_req_async(sdev, scsi_device_purge_one_req, NULL); | ||
49 | |||
50 | - aio_context_acquire(blk_get_aio_context(sdev->conf.blk)); | ||
51 | blk_drain(sdev->conf.blk); | ||
52 | - aio_context_release(blk_get_aio_context(sdev->conf.blk)); | ||
53 | scsi_device_set_ua(sdev, sense); | ||
54 | } | ||
55 | |||
56 | diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c | ||
57 | index XXXXXXX..XXXXXXX 100644 | ||
58 | --- a/hw/scsi/scsi-disk.c | ||
59 | +++ b/hw/scsi/scsi-disk.c | ||
60 | @@ -XXX,XX +XXX,XX @@ static void scsi_disk_reset(DeviceState *dev) | ||
61 | { | ||
62 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); | ||
63 | uint64_t nb_sectors; | ||
64 | - AioContext *ctx; | ||
65 | |||
66 | scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); | ||
67 | |||
68 | - ctx = blk_get_aio_context(s->qdev.conf.blk); | ||
69 | - aio_context_acquire(ctx); | ||
70 | blk_get_geometry(s->qdev.conf.blk, &nb_sectors); | ||
71 | - aio_context_release(ctx); | ||
72 | |||
73 | nb_sectors /= s->qdev.blocksize / BDRV_SECTOR_SIZE; | ||
74 | if (nb_sectors) { | ||
75 | @@ -XXX,XX +XXX,XX @@ static void scsi_unrealize(SCSIDevice *dev) | ||
76 | static void scsi_hd_realize(SCSIDevice *dev, Error **errp) | ||
77 | { | ||
78 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); | ||
79 | - AioContext *ctx = NULL; | ||
80 | + | ||
81 | /* can happen for devices without drive. The error message for missing | ||
82 | * backend will be issued in scsi_realize | ||
83 | */ | ||
84 | if (s->qdev.conf.blk) { | ||
85 | - ctx = blk_get_aio_context(s->qdev.conf.blk); | ||
86 | - aio_context_acquire(ctx); | ||
87 | if (!blkconf_blocksizes(&s->qdev.conf, errp)) { | ||
88 | - goto out; | ||
89 | + return; | ||
90 | } | ||
91 | } | 89 | } |
92 | s->qdev.blocksize = s->qdev.conf.logical_block_size; | 90 | |
93 | @@ -XXX,XX +XXX,XX @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp) | 91 | s->vq_aio_context = g_new(AioContext *, conf->num_queues); |
94 | s->product = g_strdup("QEMU HARDDISK"); | ||
95 | } | ||
96 | scsi_realize(&s->qdev, errp); | ||
97 | -out: | ||
98 | - if (ctx) { | ||
99 | - aio_context_release(ctx); | ||
100 | - } | ||
101 | } | ||
102 | |||
103 | static void scsi_cd_realize(SCSIDevice *dev, Error **errp) | ||
104 | { | ||
105 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); | ||
106 | - AioContext *ctx; | ||
107 | int ret; | ||
108 | uint32_t blocksize = 2048; | ||
109 | |||
110 | @@ -XXX,XX +XXX,XX @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) | ||
111 | blocksize = dev->conf.physical_block_size; | ||
112 | } | ||
113 | |||
114 | - ctx = blk_get_aio_context(dev->conf.blk); | ||
115 | - aio_context_acquire(ctx); | ||
116 | s->qdev.blocksize = blocksize; | ||
117 | s->qdev.type = TYPE_ROM; | ||
118 | s->features |= 1 << SCSI_DISK_F_REMOVABLE; | ||
119 | @@ -XXX,XX +XXX,XX @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) | ||
120 | s->product = g_strdup("QEMU CD-ROM"); | ||
121 | } | ||
122 | scsi_realize(&s->qdev, errp); | ||
123 | - aio_context_release(ctx); | ||
124 | } | ||
125 | |||
126 | |||
127 | @@ -XXX,XX +XXX,XX @@ static int get_device_type(SCSIDiskState *s) | ||
128 | static void scsi_block_realize(SCSIDevice *dev, Error **errp) | ||
129 | { | ||
130 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); | ||
131 | - AioContext *ctx; | ||
132 | int sg_version; | ||
133 | int rc; | ||
134 | |||
135 | @@ -XXX,XX +XXX,XX @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) | ||
136 | "be removed in a future version"); | ||
137 | } | ||
138 | |||
139 | - ctx = blk_get_aio_context(s->qdev.conf.blk); | ||
140 | - aio_context_acquire(ctx); | ||
141 | - | ||
142 | /* check we are using a driver managing SG_IO (version 3 and after) */ | ||
143 | rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version); | ||
144 | if (rc < 0) { | ||
145 | @@ -XXX,XX +XXX,XX @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) | ||
146 | if (rc != -EPERM) { | ||
147 | error_append_hint(errp, "Is this a SCSI device?\n"); | ||
148 | } | ||
149 | - goto out; | ||
150 | + return; | ||
151 | } | ||
152 | if (sg_version < 30000) { | ||
153 | error_setg(errp, "scsi generic interface too old"); | ||
154 | - goto out; | ||
155 | + return; | ||
156 | } | ||
157 | |||
158 | /* get device type from INQUIRY data */ | ||
159 | rc = get_device_type(s); | ||
160 | if (rc < 0) { | ||
161 | error_setg(errp, "INQUIRY failed"); | ||
162 | - goto out; | ||
163 | + return; | ||
164 | } | ||
165 | |||
166 | /* Make a guess for the block size, we'll fix it when the guest sends. | ||
167 | @@ -XXX,XX +XXX,XX @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) | ||
168 | |||
169 | scsi_realize(&s->qdev, errp); | ||
170 | scsi_generic_read_device_inquiry(&s->qdev); | ||
171 | - | ||
172 | -out: | ||
173 | - aio_context_release(ctx); | ||
174 | } | ||
175 | |||
176 | typedef struct SCSIBlockReq { | ||
177 | diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c | 92 | diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c |
178 | index XXXXXXX..XXXXXXX 100644 | 93 | index XXXXXXX..XXXXXXX 100644 |
179 | --- a/hw/scsi/virtio-scsi.c | 94 | --- a/hw/scsi/virtio-scsi.c |
180 | +++ b/hw/scsi/virtio-scsi.c | 95 | +++ b/hw/scsi/virtio-scsi.c |
181 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) | ||
182 | return; | ||
183 | } | ||
184 | |||
185 | - virtio_scsi_acquire(s); | ||
186 | virtio_scsi_handle_ctrl_vq(s, vq); | ||
187 | - virtio_scsi_release(s); | ||
188 | } | ||
189 | |||
190 | static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) | ||
191 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | - virtio_scsi_acquire(s); | ||
196 | virtio_scsi_handle_cmd_vq(s, vq); | ||
197 | - virtio_scsi_release(s); | ||
198 | } | ||
199 | |||
200 | static void virtio_scsi_get_config(VirtIODevice *vdev, | ||
201 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) | ||
202 | return; | ||
203 | } | ||
204 | |||
205 | - virtio_scsi_acquire(s); | ||
206 | virtio_scsi_handle_event_vq(s, vq); | ||
207 | - virtio_scsi_release(s); | ||
208 | } | ||
209 | |||
210 | static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) | ||
211 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) | ||
212 | }, | ||
213 | }; | ||
214 | |||
215 | - virtio_scsi_acquire(s); | ||
216 | virtio_scsi_push_event(s, &info); | ||
217 | - virtio_scsi_release(s); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, | 96 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, |
222 | VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); | ||
223 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | ||
224 | SCSIDevice *sd = SCSI_DEVICE(dev); | ||
225 | - AioContext *old_context; | ||
226 | int ret; | 97 | int ret; |
227 | 98 | ||
228 | if (s->ctx && !s->dataplane_fenced) { | 99 | if (s->ctx && !s->dataplane_fenced) { |
229 | if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { | 100 | - if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { |
230 | return; | 101 | - return; |
231 | } | 102 | - } |
232 | - old_context = blk_get_aio_context(sd->conf.blk); | ||
233 | - aio_context_acquire(old_context); | ||
234 | ret = blk_set_aio_context(sd->conf.blk, s->ctx, errp); | 103 | ret = blk_set_aio_context(sd->conf.blk, s->ctx, errp); |
235 | - aio_context_release(old_context); | ||
236 | if (ret < 0) { | 104 | if (ret < 0) { |
237 | return; | 105 | return; |
238 | } | ||
239 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, | ||
240 | }, | ||
241 | }; | ||
242 | |||
243 | - virtio_scsi_acquire(s); | ||
244 | virtio_scsi_push_event(s, &info); | ||
245 | scsi_bus_set_ua(&s->bus, SENSE_CODE(REPORTED_LUNS_CHANGED)); | ||
246 | - virtio_scsi_release(s); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | @@ -XXX,XX +XXX,XX @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, | ||
251 | qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); | ||
252 | |||
253 | if (s->ctx) { | ||
254 | - virtio_scsi_acquire(s); | ||
255 | /* If other users keep the BlockBackend in the iothread, that's ok */ | ||
256 | blk_set_aio_context(sd->conf.blk, qemu_get_aio_context(), NULL); | ||
257 | - virtio_scsi_release(s); | ||
258 | } | ||
259 | |||
260 | if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { | ||
261 | - virtio_scsi_acquire(s); | ||
262 | virtio_scsi_push_event(s, &info); | ||
263 | scsi_bus_set_ua(&s->bus, SENSE_CODE(REPORTED_LUNS_CHANGED)); | ||
264 | - virtio_scsi_release(s); | ||
265 | } | ||
266 | } | ||
267 | |||
268 | -- | 106 | -- |
269 | 2.43.0 | 107 | 2.48.1 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | Now that the AioContext lock no longer exists, AIO_WAIT_WHILE() and | ||
4 | AIO_WAIT_WHILE_UNLOCKED() are equivalent. | ||
5 | |||
6 | A future patch will get rid of AIO_WAIT_WHILE_UNLOCKED(). | ||
7 | |||
8 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
9 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
10 | Message-ID: <20231205182011.1976568-10-stefanha@redhat.com> | ||
11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | include/block/aio-wait.h | 16 ++++------------ | ||
15 | 1 file changed, 4 insertions(+), 12 deletions(-) | ||
16 | |||
17 | diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/include/block/aio-wait.h | ||
20 | +++ b/include/block/aio-wait.h | ||
21 | @@ -XXX,XX +XXX,XX @@ extern AioWait global_aio_wait; | ||
22 | * @ctx: the aio context, or NULL if multiple aio contexts (for which the | ||
23 | * caller does not hold a lock) are involved in the polling condition. | ||
24 | * @cond: wait while this conditional expression is true | ||
25 | - * @unlock: whether to unlock and then lock again @ctx. This applies | ||
26 | - * only when waiting for another AioContext from the main loop. | ||
27 | - * Otherwise it's ignored. | ||
28 | * | ||
29 | * Wait while a condition is true. Use this to implement synchronous | ||
30 | * operations that require event loop activity. | ||
31 | @@ -XXX,XX +XXX,XX @@ extern AioWait global_aio_wait; | ||
32 | * wait on conditions between two IOThreads since that could lead to deadlock, | ||
33 | * go via the main loop instead. | ||
34 | */ | ||
35 | -#define AIO_WAIT_WHILE_INTERNAL(ctx, cond, unlock) ({ \ | ||
36 | +#define AIO_WAIT_WHILE_INTERNAL(ctx, cond) ({ \ | ||
37 | bool waited_ = false; \ | ||
38 | AioWait *wait_ = &global_aio_wait; \ | ||
39 | AioContext *ctx_ = (ctx); \ | ||
40 | @@ -XXX,XX +XXX,XX @@ extern AioWait global_aio_wait; | ||
41 | assert(qemu_get_current_aio_context() == \ | ||
42 | qemu_get_aio_context()); \ | ||
43 | while ((cond)) { \ | ||
44 | - if (unlock && ctx_) { \ | ||
45 | - aio_context_release(ctx_); \ | ||
46 | - } \ | ||
47 | aio_poll(qemu_get_aio_context(), true); \ | ||
48 | - if (unlock && ctx_) { \ | ||
49 | - aio_context_acquire(ctx_); \ | ||
50 | - } \ | ||
51 | waited_ = true; \ | ||
52 | } \ | ||
53 | } \ | ||
54 | @@ -XXX,XX +XXX,XX @@ extern AioWait global_aio_wait; | ||
55 | waited_; }) | ||
56 | |||
57 | #define AIO_WAIT_WHILE(ctx, cond) \ | ||
58 | - AIO_WAIT_WHILE_INTERNAL(ctx, cond, true) | ||
59 | + AIO_WAIT_WHILE_INTERNAL(ctx, cond) | ||
60 | |||
61 | +/* TODO replace this with AIO_WAIT_WHILE() in a future patch */ | ||
62 | #define AIO_WAIT_WHILE_UNLOCKED(ctx, cond) \ | ||
63 | - AIO_WAIT_WHILE_INTERNAL(ctx, cond, false) | ||
64 | + AIO_WAIT_WHILE_INTERNAL(ctx, cond) | ||
65 | |||
66 | /** | ||
67 | * aio_wait_kick: | ||
68 | -- | ||
69 | 2.43.0 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | Delete these functions because nothing calls these functions anymore. | ||
4 | |||
5 | I introduced these APIs in commit 98563fc3ec44 ("aio: add | ||
6 | aio_context_acquire() and aio_context_release()") in 2014. It's with a | ||
7 | sigh of relief that I delete these APIs almost 10 years later. | ||
8 | |||
9 | Thanks to Paolo Bonzini's vision for multi-queue QEMU, we got an | ||
10 | understanding of where the code needed to go in order to remove the | ||
11 | limitations that the original dataplane and the IOThread/AioContext | ||
12 | approach that followed it. | ||
13 | |||
14 | Emanuele Giuseppe Esposito had the splendid determination to convert | ||
15 | large parts of the codebase so that they no longer needed the AioContext | ||
16 | lock. This was a painstaking process, both in the actual code changes | ||
17 | required and the iterations of code review that Emanuele eked out of | ||
18 | Kevin and me over many months. | ||
19 | |||
20 | Kevin Wolf tackled multitudes of graph locking conversions to protect | ||
21 | in-flight I/O from run-time changes to the block graph as well as the | ||
22 | clang Thread Safety Analysis annotations that allow the compiler to | ||
23 | check whether the graph lock is being used correctly. | ||
24 | |||
25 | And me, well, I'm just here to add some pizzazz to the QEMU multi-queue | ||
26 | block layer :). Thank you to everyone who helped with this effort, | ||
27 | including Eric Blake, code reviewer extraordinaire, and others who I've | ||
28 | forgotten to mention. | ||
29 | |||
30 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
31 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
32 | Message-ID: <20231205182011.1976568-11-stefanha@redhat.com> | ||
33 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
34 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
35 | --- | ||
36 | include/block/aio.h | 17 ----------------- | ||
37 | util/async.c | 10 ---------- | ||
38 | 2 files changed, 27 deletions(-) | ||
39 | |||
40 | diff --git a/include/block/aio.h b/include/block/aio.h | ||
41 | index XXXXXXX..XXXXXXX 100644 | ||
42 | --- a/include/block/aio.h | ||
43 | +++ b/include/block/aio.h | ||
44 | @@ -XXX,XX +XXX,XX @@ void aio_context_ref(AioContext *ctx); | ||
45 | */ | ||
46 | void aio_context_unref(AioContext *ctx); | ||
47 | |||
48 | -/* Take ownership of the AioContext. If the AioContext will be shared between | ||
49 | - * threads, and a thread does not want to be interrupted, it will have to | ||
50 | - * take ownership around calls to aio_poll(). Otherwise, aio_poll() | ||
51 | - * automatically takes care of calling aio_context_acquire and | ||
52 | - * aio_context_release. | ||
53 | - * | ||
54 | - * Note that this is separate from bdrv_drained_begin/bdrv_drained_end. A | ||
55 | - * thread still has to call those to avoid being interrupted by the guest. | ||
56 | - * | ||
57 | - * Bottom halves, timers and callbacks can be created or removed without | ||
58 | - * acquiring the AioContext. | ||
59 | - */ | ||
60 | -void aio_context_acquire(AioContext *ctx); | ||
61 | - | ||
62 | -/* Relinquish ownership of the AioContext. */ | ||
63 | -void aio_context_release(AioContext *ctx); | ||
64 | - | ||
65 | /** | ||
66 | * aio_bh_schedule_oneshot_full: Allocate a new bottom half structure that will | ||
67 | * run only once and as soon as possible. | ||
68 | diff --git a/util/async.c b/util/async.c | ||
69 | index XXXXXXX..XXXXXXX 100644 | ||
70 | --- a/util/async.c | ||
71 | +++ b/util/async.c | ||
72 | @@ -XXX,XX +XXX,XX @@ void aio_context_unref(AioContext *ctx) | ||
73 | g_source_unref(&ctx->source); | ||
74 | } | ||
75 | |||
76 | -void aio_context_acquire(AioContext *ctx) | ||
77 | -{ | ||
78 | - /* TODO remove this function */ | ||
79 | -} | ||
80 | - | ||
81 | -void aio_context_release(AioContext *ctx) | ||
82 | -{ | ||
83 | - /* TODO remove this function */ | ||
84 | -} | ||
85 | - | ||
86 | QEMU_DEFINE_STATIC_CO_TLS(AioContext *, my_aiocontext) | ||
87 | |||
88 | AioContext *qemu_get_current_aio_context(void) | ||
89 | -- | ||
90 | 2.43.0 | diff view generated by jsdifflib |