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