1
The following changes since commit 6972ef1440a9d685482d78672620a7482f2bd09a:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
Merge tag 'pull-tcg-20230516-3' of https://gitlab.com/rth7680/qemu into staging (2023-05-16 21:30:27 -0700)
3
Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.12-pull-request' into staging (2017-12-22 00:11:36 +0000)
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
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to 75b2591bbce5dc9f3da89f140b7bdc00e92fa8ec:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
tested: add test for nested aio_poll() in poll handlers (2023-05-17 18:01:22 +0200)
11
block: Keep nodes drained between reopen_queue/multiple (2017-12-22 15:05:32 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches
14
Block layer patches
15
15
16
- qcow2 spec: Rename "zlib" compression to "deflate"
16
----------------------------------------------------------------
17
- Honour graph read lock even in the main thread + prerequisite fixes
17
Doug Gale (1):
18
- aio-posix: do not nest poll handlers (fixes infinite recursion)
18
nvme: Add tracing
19
- Refactor QMP blockdev transactions
20
- iotests/245: Check if 'compress' driver is available
21
19
22
----------------------------------------------------------------
20
Edgar Kaziakhmedov (1):
23
Akihiro Suda (1):
21
qcow2: get rid of qcow2_backing_read1 routine
24
docs/interop/qcow2.txt: fix description about "zlib" clusters
25
22
26
Kevin Wolf (9):
23
Fam Zheng (2):
27
block: Call .bdrv_co_create(_opts) unlocked
24
block: Open backing image in force share mode for size probe
28
block/export: Fix null pointer dereference in error path
25
block: Remove unused bdrv_requests_pending
29
qcow2: Unlock the graph in qcow2_do_open() where necessary
30
qemu-img: Take graph lock more selectively
31
test-bdrv-drain: Take graph lock more selectively
32
test-bdrv-drain: Call bdrv_co_unref() in coroutine context
33
blockjob: Adhere to rate limit even when reentered early
34
graph-lock: Honour read locks even in the main thread
35
iotests/245: Check if 'compress' driver is available
36
26
37
Stefan Hajnoczi (2):
27
John Snow (1):
38
aio-posix: do not nest poll handlers
28
iotests: fix 197 for vpc
39
tested: add test for nested aio_poll() in poll handlers
40
29
41
Vladimir Sementsov-Ogievskiy (6):
30
Kevin Wolf (27):
42
blockdev: refactor transaction to use Transaction API
31
block: Formats don't need CONSISTENT_READ with NO_IO
43
blockdev: transactions: rename some things
32
block: Make bdrv_drain_invoke() recursive
44
blockdev: qmp_transaction: refactor loop to classic for
33
block: Call .drain_begin only once in bdrv_drain_all_begin()
45
blockdev: transaction: refactor handling transaction properties
34
test-bdrv-drain: Test BlockDriver callbacks for drain
46
blockdev: use state.bitmap in block-dirty-bitmap-add action
35
block: bdrv_drain_recurse(): Remove unused begin parameter
47
blockdev: qmp_transaction: drop extra generic layer
36
block: Don't wait for requests in bdrv_drain*_end()
37
block: Unify order in drain functions
38
block: Don't acquire AioContext in hmp_qemu_io()
39
block: Document that x-blockdev-change breaks quorum children list
40
block: Assert drain_all is only called from main AioContext
41
block: Make bdrv_drain() driver callbacks non-recursive
42
test-bdrv-drain: Test callback for bdrv_drain
43
test-bdrv-drain: Test bs->quiesce_counter
44
blockjob: Pause job on draining any job BDS
45
test-bdrv-drain: Test drain vs. block jobs
46
block: Don't block_job_pause_all() in bdrv_drain_all()
47
block: Nested drain_end must still call callbacks
48
test-bdrv-drain: Test nested drain sections
49
block: Don't notify parents in drain call chain
50
block: Add bdrv_subtree_drained_begin/end()
51
test-bdrv-drain: Tests for bdrv_subtree_drain
52
test-bdrv-drain: Test behaviour in coroutine context
53
test-bdrv-drain: Recursive draining with multiple parents
54
block: Allow graph changes in subtree drained section
55
test-bdrv-drain: Test graph changes in drained section
56
commit: Simplify reopen of base
57
block: Keep nodes drained between reopen_queue/multiple
48
58
49
docs/interop/qcow2.txt | 10 +-
59
Thomas Huth (3):
50
include/block/block-global-state.h | 8 +-
60
block: Remove the obsolete -drive boot=on|off parameter
51
include/block/block_int-common.h | 4 +-
61
block: Remove the deprecated -hdachs option
52
include/block/blockjob_int.h | 14 +-
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
53
block.c | 1 -
63
54
block/commit.c | 7 +-
64
qapi/block-core.json | 4 +
55
block/create.c | 1 -
65
block/qcow2.h | 3 -
56
block/crypto.c | 25 +-
66
include/block/block.h | 15 +-
57
block/export/export.c | 6 +-
67
include/block/block_int.h | 6 +-
58
block/graph-lock.c | 10 -
68
block.c | 75 ++++-
59
block/mirror.c | 23 +-
69
block/commit.c | 8 +-
60
block/parallels.c | 6 +-
70
block/io.c | 164 +++++++---
61
block/qcow.c | 6 +-
71
block/qcow2.c | 51 +--
62
block/qcow2.c | 43 ++-
72
block/replication.c | 6 +
63
block/qed.c | 6 +-
73
blockdev.c | 11 -
64
block/raw-format.c | 2 +-
74
blockjob.c | 22 +-
65
block/stream.c | 7 +-
75
hmp.c | 6 -
66
block/vdi.c | 11 +-
76
hw/block/nvme.c | 349 +++++++++++++++++----
67
block/vhdx.c | 8 +-
77
qemu-io-cmds.c | 3 +
68
block/vmdk.c | 27 +-
78
tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++
69
block/vpc.c | 6 +-
79
vl.c | 86 +-----
70
blockdev.c | 606 +++++++++++++++----------------------
80
hw/block/trace-events | 93 ++++++
71
blockjob.c | 22 +-
81
qemu-doc.texi | 29 +-
72
qemu-img.c | 5 +-
82
qemu-options.hx | 19 +-
73
tests/unit/test-bdrv-drain.c | 6 +-
83
tests/Makefile.include | 2 +
74
tests/unit/test-nested-aio-poll.c | 130 ++++++++
84
tests/qemu-iotests/197 | 4 +
75
util/aio-posix.c | 11 +
85
tests/qemu-iotests/common.filter | 3 +-
76
tests/qemu-iotests/245 | 7 +-
86
22 files changed, 1294 insertions(+), 316 deletions(-)
77
tests/qemu-iotests/245.out | 9 +-
87
create mode 100644 tests/test-bdrv-drain.c
78
tests/unit/meson.build | 1 +
88
79
30 files changed, 541 insertions(+), 487 deletions(-)
80
create mode 100644 tests/unit/test-nested-aio-poll.c
diff view generated by jsdifflib
New patch
1
Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently
2
in use as a mirror target. It is not enough for image formats, though,
3
as these still unconditionally request BLK_PERM_CONSISTENT_READ.
1
4
5
As this permission is geared towards whether the guest-visible data is
6
consistent, and has no impact on whether the metadata is sane, and
7
'qemu-img info' does not read guest-visible data (except for the raw
8
format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there
9
is not going to be any guest I/O performed, regardless of image format.
10
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block.c | 6 +++++-
14
1 file changed, 5 insertions(+), 1 deletion(-)
15
16
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
19
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
21
assert(role == &child_backing || role == &child_file);
22
23
if (!backing) {
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
25
+
26
/* Apart from the modifications below, the same permissions are
27
* forwarded and left alone as for filters */
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
30
31
/* bs->file always needs to be consistent because of the metadata. We
32
* can never allow other users to resize or write to it. */
33
- perm |= BLK_PERM_CONSISTENT_READ;
34
+ if (!(flags & BDRV_O_NO_IO)) {
35
+ perm |= BLK_PERM_CONSISTENT_READ;
36
+ }
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
38
} else {
39
/* We want consistent read from backing files if the parent needs it.
40
--
41
2.13.6
42
43
diff view generated by jsdifflib
New patch
1
From: John Snow <jsnow@redhat.com>
1
2
3
VPC has some difficulty creating geometries of particular size.
4
However, we can indeed force it to use a literal one, so let's
5
do that for the sake of test 197, which is testing some specific
6
offsets.
7
8
Signed-off-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
13
---
14
tests/qemu-iotests/197 | 4 ++++
15
tests/qemu-iotests/common.filter | 3 ++-
16
2 files changed, 6 insertions(+), 1 deletion(-)
17
18
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/197
21
+++ b/tests/qemu-iotests/197
22
@@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ==='
23
echo
24
25
# Prep the images
26
+# VPC rounds image sizes to a specific geometry, force a specific size.
27
+if [ "$IMGFMT" = "vpc" ]; then
28
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
29
+fi
30
_make_test_img 4G
31
$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io
32
IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \
33
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
34
index XXXXXXX..XXXXXXX 100644
35
--- a/tests/qemu-iotests/common.filter
36
+++ b/tests/qemu-iotests/common.filter
37
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
38
-e "s# log_size=[0-9]\\+##g" \
39
-e "s# refcount_bits=[0-9]\\+##g" \
40
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
41
- -e "s# iter-time=[0-9]\\+##g"
42
+ -e "s# iter-time=[0-9]\\+##g" \
43
+ -e "s# force_size=\\(on\\|off\\)##g"
44
}
45
46
_filter_img_info()
47
--
48
2.13.6
49
50
diff view generated by jsdifflib
New patch
1
This change separates bdrv_drain_invoke(), which calls the BlockDriver
2
drain callbacks, from bdrv_drain_recurse(). Instead, the function
3
performs its own recursion now.
1
4
5
One reason for this is that bdrv_drain_recurse() can be called multiple
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
7
once. The separation is necessary to fix this bug.
8
9
The other reason is that we intend to go to a model where we call all
10
driver callbacks first, and only then start polling. This is not fully
11
achieved yet with this patch, as bdrv_drain_invoke() contains a
12
BDRV_POLL_WHILE() loop for the block driver callbacks, which can still
13
call callbacks for any unrelated event. It's a step in this direction
14
anyway.
15
16
Cc: qemu-stable@nongnu.org
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
---
20
block/io.c | 14 +++++++++++---
21
1 file changed, 11 insertions(+), 3 deletions(-)
22
23
diff --git a/block/io.c b/block/io.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/block/io.c
26
+++ b/block/io.c
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
28
bdrv_wakeup(bs);
29
}
30
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
33
{
34
+ BdrvChild *child, *tmp;
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
36
37
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
39
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
40
bdrv_coroutine_enter(bs, data.co);
41
BDRV_POLL_WHILE(bs, !data.done);
42
+
43
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
44
+ bdrv_drain_invoke(child->bs, begin);
45
+ }
46
}
47
48
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
49
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
50
BdrvChild *child, *tmp;
51
bool waited;
52
53
- /* Ensure any pending metadata writes are submitted to bs->file. */
54
- bdrv_drain_invoke(bs, begin);
55
-
56
/* Wait for drained requests to finish */
57
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
58
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
60
bdrv_parent_drained_begin(bs);
61
}
62
63
+ bdrv_drain_invoke(bs, true);
64
bdrv_drain_recurse(bs, true);
65
}
66
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
68
}
69
70
bdrv_parent_drained_end(bs);
71
+ bdrv_drain_invoke(bs, false);
72
bdrv_drain_recurse(bs, false);
73
aio_enable_external(bdrv_get_aio_context(bs));
74
}
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
76
aio_context_acquire(aio_context);
77
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
78
if (aio_context == bdrv_get_aio_context(bs)) {
79
+ /* FIXME Calling this multiple times is wrong */
80
+ bdrv_drain_invoke(bs, true);
81
waited |= bdrv_drain_recurse(bs, true);
82
}
83
}
84
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
85
aio_context_acquire(aio_context);
86
aio_enable_external(aio_context);
87
bdrv_parent_drained_end(bs);
88
+ bdrv_drain_invoke(bs, false);
89
bdrv_drain_recurse(bs, false);
90
aio_context_release(aio_context);
91
}
92
--
93
2.13.6
94
95
diff view generated by jsdifflib
New patch
1
bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver
2
callback inside its polling loop. This means that how many times it got
3
called for each node depended on long it had to poll the event loop.
1
4
5
This is obviously not right and results in nodes that stay drained even
6
after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per
7
node.
8
9
Fix bdrv_drain_all_begin() to call the callback only once, too.
10
11
Cc: qemu-stable@nongnu.org
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
---
15
block/io.c | 3 +--
16
1 file changed, 1 insertion(+), 2 deletions(-)
17
18
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/io.c
21
+++ b/block/io.c
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
23
aio_context_acquire(aio_context);
24
bdrv_parent_drained_begin(bs);
25
aio_disable_external(aio_context);
26
+ bdrv_drain_invoke(bs, true);
27
aio_context_release(aio_context);
28
29
if (!g_slist_find(aio_ctxs, aio_context)) {
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
31
aio_context_acquire(aio_context);
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
33
if (aio_context == bdrv_get_aio_context(bs)) {
34
- /* FIXME Calling this multiple times is wrong */
35
- bdrv_drain_invoke(bs, true);
36
waited |= bdrv_drain_recurse(bs, true);
37
}
38
}
39
--
40
2.13.6
41
42
diff view generated by jsdifflib
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
This adds a test case that the BlockDriver callbacks for drain are
2
called in bdrv_drained_all_begin/end(), and that both of them are called
3
exactly once.
2
4
3
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Message-Id: <20230502184134.534703-3-stefanha@redhat.com>
5
Tested-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
7
---
8
---
8
tests/unit/test-nested-aio-poll.c | 130 ++++++++++++++++++++++++++++++
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
9
tests/unit/meson.build | 1 +
10
tests/Makefile.include | 2 +
10
2 files changed, 131 insertions(+)
11
2 files changed, 139 insertions(+)
11
create mode 100644 tests/unit/test-nested-aio-poll.c
12
create mode 100644 tests/test-bdrv-drain.c
12
13
13
diff --git a/tests/unit/test-nested-aio-poll.c b/tests/unit/test-nested-aio-poll.c
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
14
new file mode 100644
15
new file mode 100644
15
index XXXXXXX..XXXXXXX
16
index XXXXXXX..XXXXXXX
16
--- /dev/null
17
--- /dev/null
17
+++ b/tests/unit/test-nested-aio-poll.c
18
+++ b/tests/test-bdrv-drain.c
18
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
19
+/* SPDX-License-Identifier: GPL-2.0-or-later */
20
+/*
20
+/*
21
+ * Test that poll handlers are not re-entrant in nested aio_poll()
21
+ * Block node draining tests
22
+ *
22
+ *
23
+ * Copyright Red Hat
23
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
24
+ *
24
+ *
25
+ * Poll handlers are usually level-triggered. That means they continue firing
25
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
26
+ * until the condition is reset (e.g. a virtqueue becomes empty). If a poll
26
+ * of this software and associated documentation files (the "Software"), to deal
27
+ * handler calls nested aio_poll() before the condition is reset, then infinite
27
+ * in the Software without restriction, including without limitation the rights
28
+ * recursion occurs.
28
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29
+ * copies of the Software, and to permit persons to whom the Software is
30
+ * furnished to do so, subject to the following conditions:
29
+ *
31
+ *
30
+ * aio_poll() is supposed to prevent this by disabling poll handlers in nested
32
+ * The above copyright notice and this permission notice shall be included in
31
+ * aio_poll() calls. This test case checks that this is indeed what happens.
33
+ * all copies or substantial portions of the Software.
34
+ *
35
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41
+ * THE SOFTWARE.
32
+ */
42
+ */
43
+
33
+#include "qemu/osdep.h"
44
+#include "qemu/osdep.h"
34
+#include "block/aio.h"
45
+#include "block/block.h"
46
+#include "sysemu/block-backend.h"
35
+#include "qapi/error.h"
47
+#include "qapi/error.h"
36
+
48
+
37
+typedef struct {
49
+typedef struct BDRVTestState {
38
+ AioContext *ctx;
50
+ int drain_count;
51
+} BDRVTestState;
39
+
52
+
40
+ /* This is the EventNotifier that drives the test */
53
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
41
+ EventNotifier poll_notifier;
42
+
43
+ /* This EventNotifier is only used to wake aio_poll() */
44
+ EventNotifier dummy_notifier;
45
+
46
+ bool nested;
47
+} TestData;
48
+
49
+static void io_read(EventNotifier *notifier)
50
+{
54
+{
51
+ fprintf(stderr, "%s %p\n", __func__, notifier);
55
+ BDRVTestState *s = bs->opaque;
52
+ event_notifier_test_and_clear(notifier);
56
+ s->drain_count++;
53
+}
57
+}
54
+
58
+
55
+static bool io_poll_true(void *opaque)
59
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
56
+{
60
+{
57
+ fprintf(stderr, "%s %p\n", __func__, opaque);
61
+ BDRVTestState *s = bs->opaque;
58
+ return true;
62
+ s->drain_count--;
59
+}
63
+}
60
+
64
+
61
+static bool io_poll_false(void *opaque)
65
+static void bdrv_test_close(BlockDriverState *bs)
62
+{
66
+{
63
+ fprintf(stderr, "%s %p\n", __func__, opaque);
67
+ BDRVTestState *s = bs->opaque;
64
+ return false;
68
+ g_assert_cmpint(s->drain_count, >, 0);
65
+}
69
+}
66
+
70
+
67
+static void io_poll_ready(EventNotifier *notifier)
71
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
72
+ uint64_t offset, uint64_t bytes,
73
+ QEMUIOVector *qiov, int flags)
68
+{
74
+{
69
+ TestData *td = container_of(notifier, TestData, poll_notifier);
75
+ /* We want this request to stay until the polling loop in drain waits for
76
+ * it to complete. We need to sleep a while as bdrv_drain_invoke() comes
77
+ * first and polls its result, too, but it shouldn't accidentally complete
78
+ * this request yet. */
79
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
70
+
80
+
71
+ fprintf(stderr, "> %s\n", __func__);
81
+ return 0;
72
+
73
+ g_assert(!td->nested);
74
+ td->nested = true;
75
+
76
+ /* Wake the following nested aio_poll() call */
77
+ event_notifier_set(&td->dummy_notifier);
78
+
79
+ /* This nested event loop must not call io_poll()/io_poll_ready() */
80
+ g_assert(aio_poll(td->ctx, true));
81
+
82
+ td->nested = false;
83
+
84
+ fprintf(stderr, "< %s\n", __func__);
85
+}
82
+}
86
+
83
+
87
+/* dummy_notifier never triggers */
84
+static BlockDriver bdrv_test = {
88
+static void io_poll_never_ready(EventNotifier *notifier)
85
+ .format_name = "test",
86
+ .instance_size = sizeof(BDRVTestState),
87
+
88
+ .bdrv_close = bdrv_test_close,
89
+ .bdrv_co_preadv = bdrv_test_co_preadv,
90
+
91
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
92
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
93
+};
94
+
95
+static void aio_ret_cb(void *opaque, int ret)
89
+{
96
+{
90
+ g_assert_not_reached();
97
+ int *aio_ret = opaque;
98
+ *aio_ret = ret;
91
+}
99
+}
92
+
100
+
93
+static void test(void)
101
+static void test_drv_cb_drain_all(void)
94
+{
102
+{
95
+ TestData td = {
103
+ BlockBackend *blk;
96
+ .ctx = aio_context_new(&error_abort),
104
+ BlockDriverState *bs;
105
+ BDRVTestState *s;
106
+ BlockAIOCB *acb;
107
+ int aio_ret;
108
+
109
+ QEMUIOVector qiov;
110
+ struct iovec iov = {
111
+ .iov_base = NULL,
112
+ .iov_len = 0,
97
+ };
113
+ };
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
98
+
115
+
99
+ qemu_set_current_aio_context(td.ctx);
116
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
117
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
118
+ &error_abort);
119
+ s = bs->opaque;
120
+ blk_insert_bs(blk, bs, &error_abort);
100
+
121
+
101
+ /* Enable polling */
122
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
102
+ aio_context_set_poll_params(td.ctx, 1000000, 2, 2, &error_abort);
123
+ g_assert_cmpint(s->drain_count, ==, 0);
124
+ bdrv_drain_all_begin();
125
+ g_assert_cmpint(s->drain_count, ==, 1);
126
+ bdrv_drain_all_end();
127
+ g_assert_cmpint(s->drain_count, ==, 0);
103
+
128
+
104
+ /*
129
+ /* Now do the same while a request is pending */
105
+ * The GSource is unused but this has the side-effect of changing the fdmon
130
+ aio_ret = -EINPROGRESS;
106
+ * that AioContext uses.
131
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
107
+ */
132
+ g_assert(acb != NULL);
108
+ aio_get_g_source(td.ctx);
133
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
109
+
134
+
110
+ /* Make the event notifier active (set) right away */
135
+ g_assert_cmpint(s->drain_count, ==, 0);
111
+ event_notifier_init(&td.poll_notifier, 1);
136
+ bdrv_drain_all_begin();
112
+ aio_set_event_notifier(td.ctx, &td.poll_notifier, false,
137
+ g_assert_cmpint(aio_ret, ==, 0);
113
+ io_read, io_poll_true, io_poll_ready);
138
+ g_assert_cmpint(s->drain_count, ==, 1);
139
+ bdrv_drain_all_end();
140
+ g_assert_cmpint(s->drain_count, ==, 0);
114
+
141
+
115
+ /* This event notifier will be used later */
142
+ bdrv_unref(bs);
116
+ event_notifier_init(&td.dummy_notifier, 0);
143
+ blk_unref(blk);
117
+ aio_set_event_notifier(td.ctx, &td.dummy_notifier, false,
118
+ io_read, io_poll_false, io_poll_never_ready);
119
+
120
+ /* Consume aio_notify() */
121
+ g_assert(!aio_poll(td.ctx, false));
122
+
123
+ /*
124
+ * Run the io_read() handler. This has the side-effect of activating
125
+ * polling in future aio_poll() calls.
126
+ */
127
+ g_assert(aio_poll(td.ctx, true));
128
+
129
+ /* The second time around the io_poll()/io_poll_ready() handler runs */
130
+ g_assert(aio_poll(td.ctx, true));
131
+
132
+ /* Run io_poll()/io_poll_ready() one more time to show it keeps working */
133
+ g_assert(aio_poll(td.ctx, true));
134
+
135
+ aio_set_event_notifier(td.ctx, &td.dummy_notifier, false,
136
+ NULL, NULL, NULL);
137
+ aio_set_event_notifier(td.ctx, &td.poll_notifier, false, NULL, NULL, NULL);
138
+ event_notifier_cleanup(&td.dummy_notifier);
139
+ event_notifier_cleanup(&td.poll_notifier);
140
+ aio_context_unref(td.ctx);
141
+}
144
+}
142
+
145
+
143
+int main(int argc, char **argv)
146
+int main(int argc, char **argv)
144
+{
147
+{
148
+ bdrv_init();
149
+ qemu_init_main_loop(&error_abort);
150
+
145
+ g_test_init(&argc, &argv, NULL);
151
+ g_test_init(&argc, &argv, NULL);
146
+ g_test_add_func("/nested-aio-poll", test);
152
+
153
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
154
+
147
+ return g_test_run();
155
+ return g_test_run();
148
+}
156
+}
149
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
157
diff --git a/tests/Makefile.include b/tests/Makefile.include
150
index XXXXXXX..XXXXXXX 100644
158
index XXXXXXX..XXXXXXX 100644
151
--- a/tests/unit/meson.build
159
--- a/tests/Makefile.include
152
+++ b/tests/unit/meson.build
160
+++ b/tests/Makefile.include
153
@@ -XXX,XX +XXX,XX @@ if have_block
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
154
'test-coroutine': [testblock],
162
gcov-files-test-hbitmap-y = util/hbitmap.c
155
'test-aio': [testblock],
163
check-unit-y += tests/test-hbitmap$(EXESUF)
156
'test-aio-multithread': [testblock],
164
gcov-files-test-hbitmap-y = blockjob.c
157
+ 'test-nested-aio-poll': [testblock],
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
158
'test-throttle': [testblock],
166
check-unit-y += tests/test-blockjob$(EXESUF)
159
'test-thread-pool': [testblock],
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
160
'test-hbitmap': [testblock],
168
check-unit-y += tests/test-x86-cpuid$(EXESUF)
169
@@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
170
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
171
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
172
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
173
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
174
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
175
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
176
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
161
--
177
--
162
2.40.1
178
2.13.6
179
180
diff view generated by jsdifflib
New patch
1
Now that the bdrv_drain_invoke() calls are pulled up to the callers of
2
bdrv_drain_recurse(), the 'begin' parameter isn't needed any more.
1
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
---
7
block/io.c | 12 ++++++------
8
1 file changed, 6 insertions(+), 6 deletions(-)
9
10
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/block/io.c
13
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
15
}
16
}
17
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
19
+static bool bdrv_drain_recurse(BlockDriverState *bs)
20
{
21
BdrvChild *child, *tmp;
22
bool waited;
23
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
24
*/
25
bdrv_ref(bs);
26
}
27
- waited |= bdrv_drain_recurse(bs, begin);
28
+ waited |= bdrv_drain_recurse(bs);
29
if (in_main_loop) {
30
bdrv_unref(bs);
31
}
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
33
}
34
35
bdrv_drain_invoke(bs, true);
36
- bdrv_drain_recurse(bs, true);
37
+ bdrv_drain_recurse(bs);
38
}
39
40
void bdrv_drained_end(BlockDriverState *bs)
41
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
42
43
bdrv_parent_drained_end(bs);
44
bdrv_drain_invoke(bs, false);
45
- bdrv_drain_recurse(bs, false);
46
+ bdrv_drain_recurse(bs);
47
aio_enable_external(bdrv_get_aio_context(bs));
48
}
49
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
51
aio_context_acquire(aio_context);
52
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
53
if (aio_context == bdrv_get_aio_context(bs)) {
54
- waited |= bdrv_drain_recurse(bs, true);
55
+ waited |= bdrv_drain_recurse(bs);
56
}
57
}
58
aio_context_release(aio_context);
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
60
aio_enable_external(aio_context);
61
bdrv_parent_drained_end(bs);
62
bdrv_drain_invoke(bs, false);
63
- bdrv_drain_recurse(bs, false);
64
+ bdrv_drain_recurse(bs);
65
aio_context_release(aio_context);
66
}
67
68
--
69
2.13.6
70
71
diff view generated by jsdifflib
1
Skip TestBlockdevReopen.test_insert_compress_filter() if the 'compress'
1
The device is drained, so there is no point in waiting for requests at
2
driver isn't available.
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
there.
3
4
4
In order to make the test succeed when the case is skipped, we also need
5
The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e
5
to remove any output from it (which would be missing in the case where
6
in order to call the .bdrv_co_drain_end() driver callback. This is now
6
we skip it). This is done by replacing qemu_io_log() with qemu_io(). In
7
done by a separate bdrv_drain_invoke() call.
7
case of failure, qemu_io() raises an exception with the output of the
8
qemu-io binary in its message, so we don't actually lose anything.
9
8
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20230511143801.255021-1-kwolf@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
12
---
14
tests/qemu-iotests/245 | 7 ++++---
13
block/io.c | 2 --
15
tests/qemu-iotests/245.out | 9 +--------
14
1 file changed, 2 deletions(-)
16
2 files changed, 5 insertions(+), 11 deletions(-)
17
15
18
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
16
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/245
21
+++ b/tests/qemu-iotests/245
22
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
23
self.reopen(hd0_opts, {'file': 'hd0-file'})
24
25
# Insert (and remove) a compress filter
26
+ @iotests.skip_if_unsupported(['compress'])
27
def test_insert_compress_filter(self):
28
# Add an image to the VM: hd (raw) -> hd0 (qcow2) -> hd0-file (file)
29
opts = {'driver': 'raw', 'node-name': 'hd', 'file': hd_opts(0)}
30
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
31
32
# Check the first byte of the first three L2 entries and verify that
33
# the second one is compressed (0x40) while the others are not (0x80)
34
- iotests.qemu_io_log('-f', 'raw', '-c', 'read -P 0x80 0x40000 1',
35
- '-c', 'read -P 0x40 0x40008 1',
36
- '-c', 'read -P 0x80 0x40010 1', hd_path[0])
37
+ iotests.qemu_io('-f', 'raw', '-c', 'read -P 0x80 0x40000 1',
38
+ '-c', 'read -P 0x40 0x40008 1',
39
+ '-c', 'read -P 0x80 0x40010 1', hd_path[0])
40
41
# Swap the disk images of two active block devices
42
def test_swap_files(self):
43
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
44
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
45
--- a/tests/qemu-iotests/245.out
18
--- a/block/io.c
46
+++ b/tests/qemu-iotests/245.out
19
+++ b/block/io.c
47
@@ -XXX,XX +XXX,XX @@
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
48
{"return": {}}
21
49
{"data": {"id": "stream0", "type": "stream"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
22
bdrv_parent_drained_end(bs);
50
{"data": {"device": "stream0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "stream"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
23
bdrv_drain_invoke(bs, false);
51
-....read 1/1 bytes at offset 262144
24
- bdrv_drain_recurse(bs);
52
-1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
25
aio_enable_external(bdrv_get_aio_context(bs));
53
-read 1/1 bytes at offset 262152
26
}
54
-1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
27
55
-read 1/1 bytes at offset 262160
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
56
-1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
29
aio_enable_external(aio_context);
57
-
30
bdrv_parent_drained_end(bs);
58
-................
31
bdrv_drain_invoke(bs, false);
59
+....................
32
- bdrv_drain_recurse(bs);
60
----------------------------------------------------------------------
33
aio_context_release(aio_context);
61
Ran 26 tests
34
}
62
35
63
--
36
--
64
2.40.1
37
2.13.6
38
39
diff view generated by jsdifflib
1
If we take a reader lock, we can't call any functions that take a writer
1
Drain requests are propagated to child nodes, parent nodes and directly
2
lock internally without causing deadlocks once the reader lock is
2
to the AioContext. The order in which this happened was different
3
actually enforced in the main thread, too. Take the reader lock only
3
between all combinations of drain/drain_all and begin/end.
4
where it is actually needed.
4
5
The correct order is to keep children only drained when their parents
6
are also drained. This means that at the start of a drained section, the
7
AioContext needs to be drained first, the parents second and only then
8
the children. The correct order for the end of a drained section is the
9
opposite.
10
11
This patch changes the three other functions to follow the example of
12
bdrv_drained_begin(), which is the only one that got it right.
5
13
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20230510203601.418015-6-kwolf@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
16
---
11
tests/unit/test-bdrv-drain.c | 4 ++--
17
block/io.c | 12 ++++++++----
12
1 file changed, 2 insertions(+), 2 deletions(-)
18
1 file changed, 8 insertions(+), 4 deletions(-)
13
19
14
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
20
diff --git a/block/io.c b/block/io.c
15
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
16
--- a/tests/unit/test-bdrv-drain.c
22
--- a/block/io.c
17
+++ b/tests/unit/test-bdrv-drain.c
23
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_co_delete_by_drain(void *opaque)
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
19
void *buffer = g_malloc(65536);
25
return;
20
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buffer, 65536);
26
}
21
27
22
- GRAPH_RDLOCK_GUARD();
28
+ /* Stop things in parent-to-child order */
23
-
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
24
/* Pretend some internal write operation from parent to child.
30
aio_disable_external(bdrv_get_aio_context(bs));
25
* Important: We have to read from the child, not from the parent!
31
bdrv_parent_drained_begin(bs);
26
* Draining works by first propagating it all up the tree to the
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_co_delete_by_drain(void *opaque)
33
return;
28
* everything will be drained before we go back down the tree, but
34
}
29
* we do not want that. We want to be in the middle of draining
35
30
* when this following requests returns. */
36
- bdrv_parent_drained_end(bs);
31
+ bdrv_graph_co_rdlock();
37
+ /* Re-enable things in child-to-parent order */
32
bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0);
38
bdrv_drain_invoke(bs, false);
33
+ bdrv_graph_co_rdunlock();
39
+ bdrv_parent_drained_end(bs);
34
40
aio_enable_external(bdrv_get_aio_context(bs));
35
g_assert_cmpint(bs->refcnt, ==, 1);
41
}
42
43
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
44
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
45
AioContext *aio_context = bdrv_get_aio_context(bs);
46
47
+ /* Stop things in parent-to-child order */
48
aio_context_acquire(aio_context);
49
- bdrv_parent_drained_begin(bs);
50
aio_disable_external(aio_context);
51
+ bdrv_parent_drained_begin(bs);
52
bdrv_drain_invoke(bs, true);
53
aio_context_release(aio_context);
54
55
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
56
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
57
AioContext *aio_context = bdrv_get_aio_context(bs);
58
59
+ /* Re-enable things in child-to-parent order */
60
aio_context_acquire(aio_context);
61
- aio_enable_external(aio_context);
62
- bdrv_parent_drained_end(bs);
63
bdrv_drain_invoke(bs, false);
64
+ bdrv_parent_drained_end(bs);
65
+ aio_enable_external(aio_context);
66
aio_context_release(aio_context);
67
}
36
68
37
--
69
--
38
2.40.1
70
2.13.6
71
72
diff view generated by jsdifflib
New patch
1
Commit 15afd94a047 added code to acquire and release the AioContext in
2
qemuio_command(). This means that the lock is taken twice now in the
3
call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for
4
any requests issued to nodes in a non-mainloop AioContext.
1
5
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
hmp.c | 6 ------
12
1 file changed, 6 deletions(-)
13
14
diff --git a/hmp.c b/hmp.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/hmp.c
17
+++ b/hmp.c
18
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
19
{
20
BlockBackend *blk;
21
BlockBackend *local_blk = NULL;
22
- AioContext *aio_context;
23
const char* device = qdict_get_str(qdict, "device");
24
const char* command = qdict_get_str(qdict, "command");
25
Error *err = NULL;
26
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
27
}
28
}
29
30
- aio_context = blk_get_aio_context(blk);
31
- aio_context_acquire(aio_context);
32
-
33
/*
34
* Notably absent: Proper permission management. This is sad, but it seems
35
* almost impossible to achieve without changing the semantics and thereby
36
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
37
*/
38
qemuio_command(blk, command);
39
40
- aio_context_release(aio_context);
41
-
42
fail:
43
blk_unref(local_blk);
44
hmp_handle_error(mon, &err);
45
--
46
2.13.6
47
48
diff view generated by jsdifflib
1
qcow2_do_open() calls a few no_co_wrappers that wrap functions taking
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
2
the graph lock internally as a writer. Therefore, it can't hold the
3
reader lock across these calls, it causes deadlocks. Drop the lock
4
temporarily around the calls.
5
2
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Since bdrv_co_preadv does all neccessary checks including
7
Message-Id: <20230510203601.418015-4-kwolf@redhat.com>
4
reading after the end of the backing file, avoid duplication
5
of verification before bdrv_co_preadv call.
6
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
---
11
block/qcow2.c | 6 ++++++
12
block/qcow2.h | 3 ---
12
1 file changed, 6 insertions(+)
13
block/qcow2.c | 51 ++++++++-------------------------------------------
14
2 files changed, 8 insertions(+), 46 deletions(-)
13
15
16
diff --git a/block/qcow2.h b/block/qcow2.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2.h
19
+++ b/block/qcow2.h
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
21
}
22
23
/* qcow2.c functions */
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
25
- int64_t sector_num, int nb_sectors);
26
-
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
28
int refcount_order, bool generous_increase,
29
uint64_t *refblock_count);
14
diff --git a/block/qcow2.c b/block/qcow2.c
30
diff --git a/block/qcow2.c b/block/qcow2.c
15
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
16
--- a/block/qcow2.c
32
--- a/block/qcow2.c
17
+++ b/block/qcow2.c
33
+++ b/block/qcow2.c
18
@@ -XXX,XX +XXX,XX @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
19
35
return status;
20
if (open_data_file) {
36
}
21
/* Open external data file */
37
22
+ bdrv_graph_co_rdunlock();
38
-/* handle reading after the end of the backing file */
23
s->data_file = bdrv_co_open_child(NULL, options, "data-file", bs,
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
24
&child_of_bds, BDRV_CHILD_DATA,
40
- int64_t offset, int bytes)
25
true, errp);
41
-{
26
+ bdrv_graph_co_rdlock();
42
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
27
if (*errp) {
43
- int n1;
28
ret = -EINVAL;
44
-
29
goto fail;
45
- if ((offset + bytes) <= bs_size) {
30
@@ -XXX,XX +XXX,XX @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
46
- return bytes;
31
47
- }
32
if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
48
-
33
if (!s->data_file && s->image_data_file) {
49
- if (offset >= bs_size) {
34
+ bdrv_graph_co_rdunlock();
50
- n1 = 0;
35
s->data_file = bdrv_co_open_child(s->image_data_file, options,
51
- } else {
36
"data-file", bs,
52
- n1 = bs_size - offset;
37
&child_of_bds,
53
- }
38
BDRV_CHILD_DATA, false, errp);
54
-
39
+ bdrv_graph_co_rdlock();
55
- qemu_iovec_memset(qiov, n1, 0, bytes - n1);
40
if (!s->data_file) {
56
-
41
ret = -EINVAL;
57
- return n1;
42
goto fail;
58
-}
43
@@ -XXX,XX +XXX,XX @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
59
-
44
fail:
60
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
45
g_free(s->image_data_file);
61
uint64_t bytes, QEMUIOVector *qiov,
46
if (open_data_file && has_data_file(bs)) {
62
int flags)
47
+ bdrv_graph_co_rdunlock();
63
{
48
bdrv_unref_child(bs, s->data_file);
64
BDRVQcow2State *s = bs->opaque;
49
+ bdrv_graph_co_rdlock();
65
- int offset_in_cluster, n1;
50
s->data_file = NULL;
66
+ int offset_in_cluster;
51
}
67
int ret;
52
g_free(s->unknown_header_fields);
68
unsigned int cur_bytes; /* number of bytes in current iteration */
69
uint64_t cluster_offset = 0;
70
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
71
case QCOW2_CLUSTER_UNALLOCATED:
72
73
if (bs->backing) {
74
- /* read from the base image */
75
- n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
76
- offset, cur_bytes);
77
- if (n1 > 0) {
78
- QEMUIOVector local_qiov;
79
-
80
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
81
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
82
-
83
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
84
- qemu_co_mutex_unlock(&s->lock);
85
- ret = bdrv_co_preadv(bs->backing, offset, n1,
86
- &local_qiov, 0);
87
- qemu_co_mutex_lock(&s->lock);
88
-
89
- qemu_iovec_destroy(&local_qiov);
90
-
91
- if (ret < 0) {
92
- goto fail;
93
- }
94
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
95
+ qemu_co_mutex_unlock(&s->lock);
96
+ ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
97
+ &hd_qiov, 0);
98
+ qemu_co_mutex_lock(&s->lock);
99
+ if (ret < 0) {
100
+ goto fail;
101
}
102
} else {
103
/* Note: in this case, no need to wait */
53
--
104
--
54
2.40.1
105
2.13.6
106
107
diff view generated by jsdifflib
New patch
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
driver state that cannot be recreated with create options because it
3
would require a list with gaps. This causes trouble in at least
4
.bdrv_refresh_filename().
1
5
6
Document this problem so that we won't accidentally mark the command
7
stable without having addressed it.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
11
---
12
qapi/block-core.json | 4 ++++
13
1 file changed, 4 insertions(+)
14
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
index XXXXXXX..XXXXXXX 100644
17
--- a/qapi/block-core.json
18
+++ b/qapi/block-core.json
19
@@ -XXX,XX +XXX,XX @@
20
# does not support all kinds of operations, all kinds of children, nor
21
# all block drivers.
22
#
23
+# FIXME Removing children from a quorum node means introducing gaps in the
24
+# child indices. This cannot be represented in the 'children' list of
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
26
+#
27
# Warning: The data in a new quorum child MUST be consistent with that of
28
# the rest of the array.
29
#
30
--
31
2.13.6
32
33
diff view generated by jsdifflib
New patch
1
From: Doug Gale <doug16k@gmail.com>
1
2
3
Add trace output for commands, errors, and undefined behavior.
4
Add guest error log output for undefined behavior.
5
Report invalid undefined accesses to MMIO.
6
Annotate unlikely error checks with unlikely.
7
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
14
hw/block/trace-events | 93 ++++++++++++++
15
2 files changed, 390 insertions(+), 52 deletions(-)
16
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/hw/block/nvme.c
20
+++ b/hw/block/nvme.c
21
@@ -XXX,XX +XXX,XX @@
22
#include "qapi/visitor.h"
23
#include "sysemu/block-backend.h"
24
25
+#include "qemu/log.h"
26
+#include "trace.h"
27
#include "nvme.h"
28
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
30
+ do { \
31
+ (trace_##trace)(__VA_ARGS__); \
32
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
33
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
34
+ } while (0)
35
+
36
static void nvme_process_sq(void *opaque);
37
38
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
39
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
40
{
41
if (cq->irq_enabled) {
42
if (msix_enabled(&(n->parent_obj))) {
43
+ trace_nvme_irq_msix(cq->vector);
44
msix_notify(&(n->parent_obj), cq->vector);
45
} else {
46
+ trace_nvme_irq_pin();
47
pci_irq_pulse(&n->parent_obj);
48
}
49
+ } else {
50
+ trace_nvme_irq_masked();
51
}
52
}
53
54
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
55
trans_len = MIN(len, trans_len);
56
int num_prps = (len >> n->page_bits) + 1;
57
58
- if (!prp1) {
59
+ if (unlikely(!prp1)) {
60
+ trace_nvme_err_invalid_prp();
61
return NVME_INVALID_FIELD | NVME_DNR;
62
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
63
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
64
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
65
}
66
len -= trans_len;
67
if (len) {
68
- if (!prp2) {
69
+ if (unlikely(!prp2)) {
70
+ trace_nvme_err_invalid_prp2_missing();
71
goto unmap;
72
}
73
if (len > n->page_size) {
74
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
76
77
if (i == n->max_prp_ents - 1 && len > n->page_size) {
78
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
79
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
80
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
81
goto unmap;
82
}
83
84
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
85
prp_ent = le64_to_cpu(prp_list[i]);
86
}
87
88
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
89
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
90
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
91
goto unmap;
92
}
93
94
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
95
i++;
96
}
97
} else {
98
- if (prp2 & (n->page_size - 1)) {
99
+ if (unlikely(prp2 & (n->page_size - 1))) {
100
+ trace_nvme_err_invalid_prp2_align(prp2);
101
goto unmap;
102
}
103
if (qsg->nsg) {
104
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
105
QEMUIOVector iov;
106
uint16_t status = NVME_SUCCESS;
107
108
+ trace_nvme_dma_read(prp1, prp2);
109
+
110
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
111
return NVME_INVALID_FIELD | NVME_DNR;
112
}
113
if (qsg.nsg > 0) {
114
- if (dma_buf_read(ptr, len, &qsg)) {
115
+ if (unlikely(dma_buf_read(ptr, len, &qsg))) {
116
+ trace_nvme_err_invalid_dma();
117
status = NVME_INVALID_FIELD | NVME_DNR;
118
}
119
qemu_sglist_destroy(&qsg);
120
} else {
121
- if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
122
+ if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
123
+ trace_nvme_err_invalid_dma();
124
status = NVME_INVALID_FIELD | NVME_DNR;
125
}
126
qemu_iovec_destroy(&iov);
127
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
128
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
129
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
130
131
- if (slba + nlb > ns->id_ns.nsze) {
132
+ if (unlikely(slba + nlb > ns->id_ns.nsze)) {
133
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
134
return NVME_LBA_RANGE | NVME_DNR;
135
}
136
137
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
138
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
139
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
140
141
- if ((slba + nlb) > ns->id_ns.nsze) {
142
+ trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
143
+
144
+ if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
145
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
146
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
147
return NVME_LBA_RANGE | NVME_DNR;
148
}
149
150
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
151
NvmeNamespace *ns;
152
uint32_t nsid = le32_to_cpu(cmd->nsid);
153
154
- if (nsid == 0 || nsid > n->num_namespaces) {
155
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
156
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
157
return NVME_INVALID_NSID | NVME_DNR;
158
}
159
160
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
161
case NVME_CMD_READ:
162
return nvme_rw(n, ns, cmd, req);
163
default:
164
+ trace_nvme_err_invalid_opc(cmd->opcode);
165
return NVME_INVALID_OPCODE | NVME_DNR;
166
}
167
}
168
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
169
NvmeCQueue *cq;
170
uint16_t qid = le16_to_cpu(c->qid);
171
172
- if (!qid || nvme_check_sqid(n, qid)) {
173
+ if (unlikely(!qid || nvme_check_sqid(n, qid))) {
174
+ trace_nvme_err_invalid_del_sq(qid);
175
return NVME_INVALID_QID | NVME_DNR;
176
}
177
178
+ trace_nvme_del_sq(qid);
179
+
180
sq = n->sq[qid];
181
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
182
req = QTAILQ_FIRST(&sq->out_req_list);
183
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
184
uint16_t qflags = le16_to_cpu(c->sq_flags);
185
uint64_t prp1 = le64_to_cpu(c->prp1);
186
187
- if (!cqid || nvme_check_cqid(n, cqid)) {
188
+ trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags);
189
+
190
+ if (unlikely(!cqid || nvme_check_cqid(n, cqid))) {
191
+ trace_nvme_err_invalid_create_sq_cqid(cqid);
192
return NVME_INVALID_CQID | NVME_DNR;
193
}
194
- if (!sqid || !nvme_check_sqid(n, sqid)) {
195
+ if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) {
196
+ trace_nvme_err_invalid_create_sq_sqid(sqid);
197
return NVME_INVALID_QID | NVME_DNR;
198
}
199
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
200
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
201
+ trace_nvme_err_invalid_create_sq_size(qsize);
202
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
203
}
204
- if (!prp1 || prp1 & (n->page_size - 1)) {
205
+ if (unlikely(!prp1 || prp1 & (n->page_size - 1))) {
206
+ trace_nvme_err_invalid_create_sq_addr(prp1);
207
return NVME_INVALID_FIELD | NVME_DNR;
208
}
209
- if (!(NVME_SQ_FLAGS_PC(qflags))) {
210
+ if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) {
211
+ trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags));
212
return NVME_INVALID_FIELD | NVME_DNR;
213
}
214
sq = g_malloc0(sizeof(*sq));
215
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
216
NvmeCQueue *cq;
217
uint16_t qid = le16_to_cpu(c->qid);
218
219
- if (!qid || nvme_check_cqid(n, qid)) {
220
+ if (unlikely(!qid || nvme_check_cqid(n, qid))) {
221
+ trace_nvme_err_invalid_del_cq_cqid(qid);
222
return NVME_INVALID_CQID | NVME_DNR;
223
}
224
225
cq = n->cq[qid];
226
- if (!QTAILQ_EMPTY(&cq->sq_list)) {
227
+ if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) {
228
+ trace_nvme_err_invalid_del_cq_notempty(qid);
229
return NVME_INVALID_QUEUE_DEL;
230
}
231
+ trace_nvme_del_cq(qid);
232
nvme_free_cq(cq, n);
233
return NVME_SUCCESS;
234
}
235
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
236
uint16_t qflags = le16_to_cpu(c->cq_flags);
237
uint64_t prp1 = le64_to_cpu(c->prp1);
238
239
- if (!cqid || !nvme_check_cqid(n, cqid)) {
240
+ trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags,
241
+ NVME_CQ_FLAGS_IEN(qflags) != 0);
242
+
243
+ if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) {
244
+ trace_nvme_err_invalid_create_cq_cqid(cqid);
245
return NVME_INVALID_CQID | NVME_DNR;
246
}
247
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
248
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
249
+ trace_nvme_err_invalid_create_cq_size(qsize);
250
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
251
}
252
- if (!prp1) {
253
+ if (unlikely(!prp1)) {
254
+ trace_nvme_err_invalid_create_cq_addr(prp1);
255
return NVME_INVALID_FIELD | NVME_DNR;
256
}
257
- if (vector > n->num_queues) {
258
+ if (unlikely(vector > n->num_queues)) {
259
+ trace_nvme_err_invalid_create_cq_vector(vector);
260
return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
261
}
262
- if (!(NVME_CQ_FLAGS_PC(qflags))) {
263
+ if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) {
264
+ trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags));
265
return NVME_INVALID_FIELD | NVME_DNR;
266
}
267
268
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c)
269
uint64_t prp1 = le64_to_cpu(c->prp1);
270
uint64_t prp2 = le64_to_cpu(c->prp2);
271
272
+ trace_nvme_identify_ctrl();
273
+
274
return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
275
prp1, prp2);
276
}
277
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c)
278
uint64_t prp1 = le64_to_cpu(c->prp1);
279
uint64_t prp2 = le64_to_cpu(c->prp2);
280
281
- if (nsid == 0 || nsid > n->num_namespaces) {
282
+ trace_nvme_identify_ns(nsid);
283
+
284
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
285
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
286
return NVME_INVALID_NSID | NVME_DNR;
287
}
288
289
ns = &n->namespaces[nsid - 1];
290
+
291
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
292
prp1, prp2);
293
}
294
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
295
uint16_t ret;
296
int i, j = 0;
297
298
+ trace_nvme_identify_nslist(min_nsid);
299
+
300
list = g_malloc0(data_len);
301
for (i = 0; i < n->num_namespaces; i++) {
302
if (i < min_nsid) {
303
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
304
case 0x02:
305
return nvme_identify_nslist(n, c);
306
default:
307
+ trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
308
return NVME_INVALID_FIELD | NVME_DNR;
309
}
310
}
311
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
312
switch (dw10) {
313
case NVME_VOLATILE_WRITE_CACHE:
314
result = blk_enable_write_cache(n->conf.blk);
315
+ trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
316
break;
317
case NVME_NUMBER_OF_QUEUES:
318
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
319
+ trace_nvme_getfeat_numq(result);
320
break;
321
default:
322
+ trace_nvme_err_invalid_getfeat(dw10);
323
return NVME_INVALID_FIELD | NVME_DNR;
324
}
325
326
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
327
blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
328
break;
329
case NVME_NUMBER_OF_QUEUES:
330
+ trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1,
331
+ ((dw11 >> 16) & 0xFFFF) + 1,
332
+ n->num_queues - 1, n->num_queues - 1);
333
req->cqe.result =
334
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
335
break;
336
default:
337
+ trace_nvme_err_invalid_setfeat(dw10);
338
return NVME_INVALID_FIELD | NVME_DNR;
339
}
340
return NVME_SUCCESS;
341
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
342
case NVME_ADM_CMD_GET_FEATURES:
343
return nvme_get_feature(n, cmd, req);
344
default:
345
+ trace_nvme_err_invalid_admin_opc(cmd->opcode);
346
return NVME_INVALID_OPCODE | NVME_DNR;
347
}
348
}
349
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
350
uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
351
uint32_t page_size = 1 << page_bits;
352
353
- if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
354
- n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
355
- NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
356
- NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
357
- NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
358
- NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
359
- NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
360
- NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
361
- !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) {
362
+ if (unlikely(n->cq[0])) {
363
+ trace_nvme_err_startfail_cq();
364
+ return -1;
365
+ }
366
+ if (unlikely(n->sq[0])) {
367
+ trace_nvme_err_startfail_sq();
368
+ return -1;
369
+ }
370
+ if (unlikely(!n->bar.asq)) {
371
+ trace_nvme_err_startfail_nbarasq();
372
+ return -1;
373
+ }
374
+ if (unlikely(!n->bar.acq)) {
375
+ trace_nvme_err_startfail_nbaracq();
376
+ return -1;
377
+ }
378
+ if (unlikely(n->bar.asq & (page_size - 1))) {
379
+ trace_nvme_err_startfail_asq_misaligned(n->bar.asq);
380
+ return -1;
381
+ }
382
+ if (unlikely(n->bar.acq & (page_size - 1))) {
383
+ trace_nvme_err_startfail_acq_misaligned(n->bar.acq);
384
+ return -1;
385
+ }
386
+ if (unlikely(NVME_CC_MPS(n->bar.cc) <
387
+ NVME_CAP_MPSMIN(n->bar.cap))) {
388
+ trace_nvme_err_startfail_page_too_small(
389
+ NVME_CC_MPS(n->bar.cc),
390
+ NVME_CAP_MPSMIN(n->bar.cap));
391
+ return -1;
392
+ }
393
+ if (unlikely(NVME_CC_MPS(n->bar.cc) >
394
+ NVME_CAP_MPSMAX(n->bar.cap))) {
395
+ trace_nvme_err_startfail_page_too_large(
396
+ NVME_CC_MPS(n->bar.cc),
397
+ NVME_CAP_MPSMAX(n->bar.cap));
398
+ return -1;
399
+ }
400
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) <
401
+ NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) {
402
+ trace_nvme_err_startfail_cqent_too_small(
403
+ NVME_CC_IOCQES(n->bar.cc),
404
+ NVME_CTRL_CQES_MIN(n->bar.cap));
405
+ return -1;
406
+ }
407
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) >
408
+ NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) {
409
+ trace_nvme_err_startfail_cqent_too_large(
410
+ NVME_CC_IOCQES(n->bar.cc),
411
+ NVME_CTRL_CQES_MAX(n->bar.cap));
412
+ return -1;
413
+ }
414
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) <
415
+ NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) {
416
+ trace_nvme_err_startfail_sqent_too_small(
417
+ NVME_CC_IOSQES(n->bar.cc),
418
+ NVME_CTRL_SQES_MIN(n->bar.cap));
419
+ return -1;
420
+ }
421
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) >
422
+ NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) {
423
+ trace_nvme_err_startfail_sqent_too_large(
424
+ NVME_CC_IOSQES(n->bar.cc),
425
+ NVME_CTRL_SQES_MAX(n->bar.cap));
426
+ return -1;
427
+ }
428
+ if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) {
429
+ trace_nvme_err_startfail_asqent_sz_zero();
430
+ return -1;
431
+ }
432
+ if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) {
433
+ trace_nvme_err_startfail_acqent_sz_zero();
434
return -1;
435
}
436
437
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
438
static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
439
unsigned size)
440
{
441
+ if (unlikely(offset & (sizeof(uint32_t) - 1))) {
442
+ NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32,
443
+ "MMIO write not 32-bit aligned,"
444
+ " offset=0x%"PRIx64"", offset);
445
+ /* should be ignored, fall through for now */
446
+ }
447
+
448
+ if (unlikely(size < sizeof(uint32_t))) {
449
+ NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall,
450
+ "MMIO write smaller than 32-bits,"
451
+ " offset=0x%"PRIx64", size=%u",
452
+ offset, size);
453
+ /* should be ignored, fall through for now */
454
+ }
455
+
456
switch (offset) {
457
- case 0xc:
458
+ case 0xc: /* INTMS */
459
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
460
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
461
+ "undefined access to interrupt mask set"
462
+ " when MSI-X is enabled");
463
+ /* should be ignored, fall through for now */
464
+ }
465
n->bar.intms |= data & 0xffffffff;
466
n->bar.intmc = n->bar.intms;
467
+ trace_nvme_mmio_intm_set(data & 0xffffffff,
468
+ n->bar.intmc);
469
break;
470
- case 0x10:
471
+ case 0x10: /* INTMC */
472
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
473
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
474
+ "undefined access to interrupt mask clr"
475
+ " when MSI-X is enabled");
476
+ /* should be ignored, fall through for now */
477
+ }
478
n->bar.intms &= ~(data & 0xffffffff);
479
n->bar.intmc = n->bar.intms;
480
+ trace_nvme_mmio_intm_clr(data & 0xffffffff,
481
+ n->bar.intmc);
482
break;
483
- case 0x14:
484
+ case 0x14: /* CC */
485
+ trace_nvme_mmio_cfg(data & 0xffffffff);
486
/* Windows first sends data, then sends enable bit */
487
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
488
!NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
489
@@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
490
491
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
492
n->bar.cc = data;
493
- if (nvme_start_ctrl(n)) {
494
+ if (unlikely(nvme_start_ctrl(n))) {
495
+ trace_nvme_err_startfail();
496
n->bar.csts = NVME_CSTS_FAILED;
497
} else {
498
+ trace_nvme_mmio_start_success();
499
n->bar.csts = NVME_CSTS_READY;
500
}
501
} else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
502
+ trace_nvme_mmio_stopped();
503
nvme_clear_ctrl(n);
504
n->bar.csts &= ~NVME_CSTS_READY;
505
}
506
if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
507
- nvme_clear_ctrl(n);
508
- n->bar.cc = data;
509
- n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
510
+ trace_nvme_mmio_shutdown_set();
511
+ nvme_clear_ctrl(n);
512
+ n->bar.cc = data;
513
+ n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
514
} else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
515
- n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
516
- n->bar.cc = data;
517
+ trace_nvme_mmio_shutdown_cleared();
518
+ n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
519
+ n->bar.cc = data;
520
+ }
521
+ break;
522
+ case 0x1C: /* CSTS */
523
+ if (data & (1 << 4)) {
524
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported,
525
+ "attempted to W1C CSTS.NSSRO"
526
+ " but CAP.NSSRS is zero (not supported)");
527
+ } else if (data != 0) {
528
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts,
529
+ "attempted to set a read only bit"
530
+ " of controller status");
531
+ }
532
+ break;
533
+ case 0x20: /* NSSR */
534
+ if (data == 0x4E564D65) {
535
+ trace_nvme_ub_mmiowr_ssreset_unsupported();
536
+ } else {
537
+ /* The spec says that writes of other values have no effect */
538
+ return;
539
}
540
break;
541
- case 0x24:
542
+ case 0x24: /* AQA */
543
n->bar.aqa = data & 0xffffffff;
544
+ trace_nvme_mmio_aqattr(data & 0xffffffff);
545
break;
546
- case 0x28:
547
+ case 0x28: /* ASQ */
548
n->bar.asq = data;
549
+ trace_nvme_mmio_asqaddr(data);
550
break;
551
- case 0x2c:
552
+ case 0x2c: /* ASQ hi */
553
n->bar.asq |= data << 32;
554
+ trace_nvme_mmio_asqaddr_hi(data, n->bar.asq);
555
break;
556
- case 0x30:
557
+ case 0x30: /* ACQ */
558
+ trace_nvme_mmio_acqaddr(data);
559
n->bar.acq = data;
560
break;
561
- case 0x34:
562
+ case 0x34: /* ACQ hi */
563
n->bar.acq |= data << 32;
564
+ trace_nvme_mmio_acqaddr_hi(data, n->bar.acq);
565
break;
566
+ case 0x38: /* CMBLOC */
567
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved,
568
+ "invalid write to reserved CMBLOC"
569
+ " when CMBSZ is zero, ignored");
570
+ return;
571
+ case 0x3C: /* CMBSZ */
572
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly,
573
+ "invalid write to read only CMBSZ, ignored");
574
+ return;
575
default:
576
+ NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
577
+ "invalid MMIO write,"
578
+ " offset=0x%"PRIx64", data=%"PRIx64"",
579
+ offset, data);
580
break;
581
}
582
}
583
@@ -XXX,XX +XXX,XX @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
584
uint8_t *ptr = (uint8_t *)&n->bar;
585
uint64_t val = 0;
586
587
+ if (unlikely(addr & (sizeof(uint32_t) - 1))) {
588
+ NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32,
589
+ "MMIO read not 32-bit aligned,"
590
+ " offset=0x%"PRIx64"", addr);
591
+ /* should RAZ, fall through for now */
592
+ } else if (unlikely(size < sizeof(uint32_t))) {
593
+ NVME_GUEST_ERR(nvme_ub_mmiord_toosmall,
594
+ "MMIO read smaller than 32-bits,"
595
+ " offset=0x%"PRIx64"", addr);
596
+ /* should RAZ, fall through for now */
597
+ }
598
+
599
if (addr < sizeof(n->bar)) {
600
memcpy(&val, ptr + addr, size);
601
+ } else {
602
+ NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs,
603
+ "MMIO read beyond last register,"
604
+ " offset=0x%"PRIx64", returning 0", addr);
605
}
606
+
607
return val;
608
}
609
610
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
611
{
612
uint32_t qid;
613
614
- if (addr & ((1 << 2) - 1)) {
615
+ if (unlikely(addr & ((1 << 2) - 1))) {
616
+ NVME_GUEST_ERR(nvme_ub_db_wr_misaligned,
617
+ "doorbell write not 32-bit aligned,"
618
+ " offset=0x%"PRIx64", ignoring", addr);
619
return;
620
}
621
622
if (((addr - 0x1000) >> 2) & 1) {
623
+ /* Completion queue doorbell write */
624
+
625
uint16_t new_head = val & 0xffff;
626
int start_sqs;
627
NvmeCQueue *cq;
628
629
qid = (addr - (0x1000 + (1 << 2))) >> 3;
630
- if (nvme_check_cqid(n, qid)) {
631
+ if (unlikely(nvme_check_cqid(n, qid))) {
632
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq,
633
+ "completion queue doorbell write"
634
+ " for nonexistent queue,"
635
+ " sqid=%"PRIu32", ignoring", qid);
636
return;
637
}
638
639
cq = n->cq[qid];
640
- if (new_head >= cq->size) {
641
+ if (unlikely(new_head >= cq->size)) {
642
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead,
643
+ "completion queue doorbell write value"
644
+ " beyond queue size, sqid=%"PRIu32","
645
+ " new_head=%"PRIu16", ignoring",
646
+ qid, new_head);
647
return;
648
}
649
650
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
651
nvme_isr_notify(n, cq);
652
}
653
} else {
654
+ /* Submission queue doorbell write */
655
+
656
uint16_t new_tail = val & 0xffff;
657
NvmeSQueue *sq;
658
659
qid = (addr - 0x1000) >> 3;
660
- if (nvme_check_sqid(n, qid)) {
661
+ if (unlikely(nvme_check_sqid(n, qid))) {
662
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq,
663
+ "submission queue doorbell write"
664
+ " for nonexistent queue,"
665
+ " sqid=%"PRIu32", ignoring", qid);
666
return;
667
}
668
669
sq = n->sq[qid];
670
- if (new_tail >= sq->size) {
671
+ if (unlikely(new_tail >= sq->size)) {
672
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail,
673
+ "submission queue doorbell write value"
674
+ " beyond queue size, sqid=%"PRIu32","
675
+ " new_tail=%"PRIu16", ignoring",
676
+ qid, new_tail);
677
return;
678
}
679
680
diff --git a/hw/block/trace-events b/hw/block/trace-events
681
index XXXXXXX..XXXXXXX 100644
682
--- a/hw/block/trace-events
683
+++ b/hw/block/trace-events
684
@@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
685
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
686
hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
687
688
+# hw/block/nvme.c
689
+# nvme traces for successful events
690
+nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
691
+nvme_irq_pin(void) "pulsing IRQ pin"
692
+nvme_irq_masked(void) "IRQ is masked"
693
+nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
694
+nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64""
695
+nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16""
696
+nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d"
697
+nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
698
+nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
699
+nvme_identify_ctrl(void) "identify controller"
700
+nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
701
+nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
702
+nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
703
+nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
704
+nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d"
705
+nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
706
+nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
707
+nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
708
+nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
709
+nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
710
+nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
711
+nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
712
+nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
713
+nvme_mmio_start_success(void) "setting controller enable bit succeeded"
714
+nvme_mmio_stopped(void) "cleared controller enable bit"
715
+nvme_mmio_shutdown_set(void) "shutdown bit set"
716
+nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
717
+
718
+# nvme traces for error conditions
719
+nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
720
+nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
721
+nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
722
+nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
723
+nvme_err_invalid_field(void) "invalid field"
724
+nvme_err_invalid_prp(void) "invalid PRP"
725
+nvme_err_invalid_sgl(void) "invalid SGL"
726
+nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
727
+nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
728
+nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
729
+nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
730
+nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
731
+nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
732
+nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
733
+nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
734
+nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
735
+nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
736
+nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
737
+nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
738
+nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
739
+nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
740
+nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
741
+nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
742
+nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
743
+nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
744
+nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
745
+nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
746
+nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
747
+nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
748
+nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
749
+nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
750
+nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
751
+nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
752
+nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u"
753
+nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u"
754
+nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u"
755
+nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u"
756
+nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u"
757
+nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u"
758
+nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero"
759
+nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero"
760
+nvme_err_startfail(void) "setting controller enable bit failed"
761
+
762
+# Traces for undefined behavior
763
+nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64""
764
+nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u"
765
+nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled"
766
+nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status"
767
+nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)"
768
+nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
769
+nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
770
+nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
771
+nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
772
+nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
773
+nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
774
+nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0"
775
+nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring"
776
+nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring"
777
+nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring"
778
+nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring"
779
+nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
780
+
781
# hw/block/xen_disk.c
782
xen_disk_alloc(char *name) "%s"
783
xen_disk_init(char *name) "%s"
784
--
785
2.13.6
786
787
diff view generated by jsdifflib
1
bdrv_unref() is a no_coroutine_fn, so calling it from coroutine context
1
From: Fam Zheng <famz@redhat.com>
2
is invalid. Use bdrv_co_unref() instead.
3
2
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Management tools create overlays of running guests with qemu-img:
5
Message-Id: <20230510203601.418015-7-kwolf@redhat.com>
4
5
$ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2
6
7
but this doesn't work anymore due to image locking:
8
9
qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock
10
Is another process using the image?
11
Could not open backing image to determine size.
12
Use the force share option to allow this use case again.
13
14
Cc: qemu-stable@nongnu.org
15
Signed-off-by: Fam Zheng <famz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
18
---
9
tests/unit/test-bdrv-drain.c | 2 +-
19
block.c | 3 ++-
10
1 file changed, 1 insertion(+), 1 deletion(-)
20
1 file changed, 2 insertions(+), 1 deletion(-)
11
21
12
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
22
diff --git a/block.c b/block.c
13
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/unit/test-bdrv-drain.c
24
--- a/block.c
15
+++ b/tests/unit/test-bdrv-drain.c
25
+++ b/block.c
16
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_co_delete_by_drain(void *opaque)
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
17
g_assert_cmpint(bs->refcnt, ==, 1);
27
back_flags = flags;
18
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
19
if (!dbdd->detach_instead_of_delete) {
29
20
- blk_unref(blk);
30
+ backing_options = qdict_new();
21
+ blk_co_unref(blk);
31
if (backing_fmt) {
22
} else {
32
- backing_options = qdict_new();
23
BdrvChild *c, *next_c;
33
qdict_put_str(backing_options, "driver", backing_fmt);
24
QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) {
34
}
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
36
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
38
&local_err);
25
--
39
--
26
2.40.1
40
2.13.6
41
42
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
5
Message-Id: <20230510150624.310640-4-vsementsov@yandex-team.ru>
5
Signed-off-by: Thomas Huth <thuth@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
9
---
8
blockdev.c | 9 +++------
10
blockdev.c | 11 -----------
9
1 file changed, 3 insertions(+), 6 deletions(-)
11
qemu-doc.texi | 6 ------
12
2 files changed, 17 deletions(-)
10
13
11
diff --git a/blockdev.c b/blockdev.c
14
diff --git a/blockdev.c b/blockdev.c
12
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
13
--- a/blockdev.c
16
--- a/blockdev.c
14
+++ b/blockdev.c
17
+++ b/blockdev.c
15
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *actions,
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
16
struct TransactionProperties *properties,
19
.type = QEMU_OPT_STRING,
17
Error **errp)
20
.help = "chs translation (auto, lba, none)",
18
{
21
},{
19
- TransactionActionList *act = actions;
22
- .name = "boot",
20
+ TransactionActionList *act;
23
- .type = QEMU_OPT_BOOL,
21
bool has_properties = !!properties;
24
- .help = "(deprecated, ignored)",
22
JobTxn *block_job_txn = NULL;
25
- },{
23
Error *local_err = NULL;
26
.name = "addr",
24
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *actions,
27
.type = QEMU_OPT_STRING,
25
bdrv_drain_all();
28
.help = "pci address (virtio only)",
26
29
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
27
/* We don't do anything in this loop that commits us to the operations */
30
goto fail;
28
- while (NULL != act) {
31
}
29
- TransactionAction *dev_info = NULL;
32
30
+ for (act = actions; act; act = act->next) {
33
- /* Deprecated option boot=[on|off] */
31
+ TransactionAction *dev_info = act->value;
34
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
32
const BlkActionOps *ops;
35
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
33
BlkActionState *state;
36
- "ignored. Future versions will reject this parameter. Please "
34
37
- "update your scripts.\n");
35
- dev_info = act->value;
38
- }
36
- act = act->next;
37
-
39
-
38
assert(dev_info->type < ARRAY_SIZE(actions_map));
40
/* Other deprecated options */
39
41
if (!qtest_enabled()) {
40
ops = &actions_map[dev_info->type];
42
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
43
diff --git a/qemu-doc.texi b/qemu-doc.texi
44
index XXXXXXX..XXXXXXX 100644
45
--- a/qemu-doc.texi
46
+++ b/qemu-doc.texi
47
@@ -XXX,XX +XXX,XX @@ deprecated.
48
49
@section System emulator command line arguments
50
51
-@subsection -drive boot=on|off (since 1.3.0)
52
-
53
-The ``boot=on|off'' option to the ``-drive'' argument is
54
-ignored. Applications should use the ``bootindex=N'' parameter
55
-to set an absolute ordering between devices instead.
56
-
57
@subsection -tdf (since 1.3.0)
58
59
The ``-tdf'' argument is ignored. The behaviour implemented
41
--
60
--
42
2.40.1
61
2.13.6
62
63
diff view generated by jsdifflib
New patch
1
1
From: Thomas Huth <thuth@redhat.com>
2
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
complained that we should keep it, so let's remove this legacy option
5
now to simplify the code quite a bit.
6
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
vl.c | 86 ++-------------------------------------------------------
13
qemu-doc.texi | 8 ------
14
qemu-options.hx | 19 ++-----------
15
3 files changed, 4 insertions(+), 109 deletions(-)
16
17
diff --git a/vl.c b/vl.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/vl.c
20
+++ b/vl.c
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
22
const char *boot_order = NULL;
23
const char *boot_once = NULL;
24
DisplayState *ds;
25
- int cyls, heads, secs, translation;
26
QemuOpts *opts, *machine_opts;
27
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
28
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
29
QemuOptsList *olist;
30
int optind;
31
const char *optarg;
32
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
33
34
cpu_model = NULL;
35
snapshot = 0;
36
- cyls = heads = secs = 0;
37
- translation = BIOS_ATA_TRANSLATION_AUTO;
38
39
nb_nics = 0;
40
41
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
42
if (optind >= argc)
43
break;
44
if (argv[optind][0] != '-') {
45
- hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
46
+ drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
47
} else {
48
const QEMUOption *popt;
49
50
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
51
cpu_model = optarg;
52
break;
53
case QEMU_OPTION_hda:
54
- {
55
- char buf[256];
56
- if (cyls == 0)
57
- snprintf(buf, sizeof(buf), "%s", HD_OPTS);
58
- else
59
- snprintf(buf, sizeof(buf),
60
- "%s,cyls=%d,heads=%d,secs=%d%s",
61
- HD_OPTS , cyls, heads, secs,
62
- translation == BIOS_ATA_TRANSLATION_LBA ?
63
- ",trans=lba" :
64
- translation == BIOS_ATA_TRANSLATION_NONE ?
65
- ",trans=none" : "");
66
- drive_add(IF_DEFAULT, 0, optarg, buf);
67
- break;
68
- }
69
case QEMU_OPTION_hdb:
70
case QEMU_OPTION_hdc:
71
case QEMU_OPTION_hdd:
72
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
73
case QEMU_OPTION_snapshot:
74
snapshot = 1;
75
break;
76
- case QEMU_OPTION_hdachs:
77
- {
78
- const char *p;
79
- p = optarg;
80
- cyls = strtol(p, (char **)&p, 0);
81
- if (cyls < 1 || cyls > 16383)
82
- goto chs_fail;
83
- if (*p != ',')
84
- goto chs_fail;
85
- p++;
86
- heads = strtol(p, (char **)&p, 0);
87
- if (heads < 1 || heads > 16)
88
- goto chs_fail;
89
- if (*p != ',')
90
- goto chs_fail;
91
- p++;
92
- secs = strtol(p, (char **)&p, 0);
93
- if (secs < 1 || secs > 63)
94
- goto chs_fail;
95
- if (*p == ',') {
96
- p++;
97
- if (!strcmp(p, "large")) {
98
- translation = BIOS_ATA_TRANSLATION_LARGE;
99
- } else if (!strcmp(p, "rechs")) {
100
- translation = BIOS_ATA_TRANSLATION_RECHS;
101
- } else if (!strcmp(p, "none")) {
102
- translation = BIOS_ATA_TRANSLATION_NONE;
103
- } else if (!strcmp(p, "lba")) {
104
- translation = BIOS_ATA_TRANSLATION_LBA;
105
- } else if (!strcmp(p, "auto")) {
106
- translation = BIOS_ATA_TRANSLATION_AUTO;
107
- } else {
108
- goto chs_fail;
109
- }
110
- } else if (*p != '\0') {
111
- chs_fail:
112
- error_report("invalid physical CHS format");
113
- exit(1);
114
- }
115
- if (hda_opts != NULL) {
116
- qemu_opt_set_number(hda_opts, "cyls", cyls,
117
- &error_abort);
118
- qemu_opt_set_number(hda_opts, "heads", heads,
119
- &error_abort);
120
- qemu_opt_set_number(hda_opts, "secs", secs,
121
- &error_abort);
122
- if (translation == BIOS_ATA_TRANSLATION_LARGE) {
123
- qemu_opt_set(hda_opts, "trans", "large",
124
- &error_abort);
125
- } else if (translation == BIOS_ATA_TRANSLATION_RECHS) {
126
- qemu_opt_set(hda_opts, "trans", "rechs",
127
- &error_abort);
128
- } else if (translation == BIOS_ATA_TRANSLATION_LBA) {
129
- qemu_opt_set(hda_opts, "trans", "lba",
130
- &error_abort);
131
- } else if (translation == BIOS_ATA_TRANSLATION_NONE) {
132
- qemu_opt_set(hda_opts, "trans", "none",
133
- &error_abort);
134
- }
135
- }
136
- }
137
- error_report("'-hdachs' is deprecated, please use '-device"
138
- " ide-hd,cyls=c,heads=h,secs=s,...' instead");
139
- break;
140
case QEMU_OPTION_numa:
141
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
142
optarg, true);
143
diff --git a/qemu-doc.texi b/qemu-doc.texi
144
index XXXXXXX..XXXXXXX 100644
145
--- a/qemu-doc.texi
146
+++ b/qemu-doc.texi
147
@@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the
148
``-object filter-dump'' argument which works in combination
149
with the modern ``-netdev`` backends instead.
150
151
-@subsection -hdachs (since 2.10.0)
152
-
153
-The ``-hdachs'' argument is now a synonym for setting
154
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
155
-on the ``ide-hd'' device using the ``-device'' argument.
156
-The new syntax allows different settings to be provided
157
-per disk.
158
-
159
@subsection -usbdevice (since 2.10.0)
160
161
The ``-usbdevice DEV'' argument is now a synonym for setting
162
diff --git a/qemu-options.hx b/qemu-options.hx
163
index XXXXXXX..XXXXXXX 100644
164
--- a/qemu-options.hx
165
+++ b/qemu-options.hx
166
@@ -XXX,XX +XXX,XX @@ of available connectors of a given interface type.
167
@item media=@var{media}
168
This option defines the type of the media: disk or cdrom.
169
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
170
-These options have the same definition as they have in @option{-hdachs}.
171
-These parameters are deprecated, use the corresponding parameters
172
+Force disk physical geometry and the optional BIOS translation (trans=none or
173
+lba). These parameters are deprecated, use the corresponding parameters
174
of @code{-device} instead.
175
@item snapshot=@var{snapshot}
176
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
177
@@ -XXX,XX +XXX,XX @@ the raw disk image you use is not written back. You can however force
178
the write back by pressing @key{C-a s} (@pxref{disk_images}).
179
ETEXI
180
181
-DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
182
- "-hdachs c,h,s[,t]\n" \
183
- " force hard disk 0 physical geometry and the optional BIOS\n" \
184
- " translation (t=none or lba) (usually QEMU can guess them)\n",
185
- QEMU_ARCH_ALL)
186
-STEXI
187
-@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
188
-@findex -hdachs
189
-Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
190
-@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
191
-translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
192
-all those parameters. This option is deprecated, please use
193
-@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead.
194
-ETEXI
195
-
196
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
197
"-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n"
198
" [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n"
199
--
200
2.13.6
201
202
diff view generated by jsdifflib
New patch
1
From: Thomas Huth <thuth@redhat.com>
1
2
3
Looks like we forgot to announce the deprecation of these options in
4
the corresponding chapter of the qemu-doc text, so let's do that now.
5
6
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
qemu-doc.texi | 15 +++++++++++++++
12
1 file changed, 15 insertions(+)
13
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qemu-doc.texi
17
+++ b/qemu-doc.texi
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
19
The ``-drive if=scsi'' argument is replaced by the the
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
21
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
23
+
24
+The drive geometry arguments are replaced by the the geometry arguments
25
+that can be specified with the ``-device'' parameter.
26
+
27
+@subsection -drive serial=... (since 2.10.0)
28
+
29
+The drive serial argument is replaced by the the serial argument
30
+that can be specified with the ``-device'' parameter.
31
+
32
+@subsection -drive addr=... (since 2.10.0)
33
+
34
+The drive addr argument is replaced by the the addr argument
35
+that can be specified with the ``-device'' parameter.
36
+
37
@subsection -net dump (since 2.10.0)
38
39
The ``--net dump'' argument is now replaced with the
40
--
41
2.13.6
42
43
diff view generated by jsdifflib
1
There are some conditions under which we don't actually need to do
1
From: Fam Zheng <famz@redhat.com>
2
anything for taking a reader lock: Writing the graph is only possible
3
from the main context while holding the BQL. So if a reader is running
4
in the main context under the BQL and knows that it won't be interrupted
5
until the next writer runs, we don't actually need to do anything.
6
2
7
This is the case if the reader code neither has a nested event loop
3
Signed-off-by: Fam Zheng <famz@redhat.com>
8
(this is forbidden anyway while you hold the lock) nor is a coroutine
9
(because a writer could run when the coroutine has yielded).
10
11
These conditions are exactly what bdrv_graph_rdlock_main_loop() asserts.
12
They are not fulfilled in bdrv_graph_co_rdlock(), which always runs in a
13
coroutine.
14
15
This deletes the shortcuts in bdrv_graph_co_rdlock() that skip taking
16
the reader lock in the main thread.
17
18
Reported-by: Fiona Ebner <f.ebner@proxmox.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
Message-Id: <20230510203601.418015-9-kwolf@redhat.com>
21
Reviewed-by: Eric Blake <eblake@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
5
---
24
block/graph-lock.c | 10 ----------
6
include/block/block_int.h | 1 -
25
1 file changed, 10 deletions(-)
7
block/io.c | 18 ------------------
8
2 files changed, 19 deletions(-)
26
9
27
diff --git a/block/graph-lock.c b/block/graph-lock.c
10
diff --git a/include/block/block_int.h b/include/block/block_int.h
28
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
29
--- a/block/graph-lock.c
12
--- a/include/block/block_int.h
30
+++ b/block/graph-lock.c
13
+++ b/include/block/block_int.h
31
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_graph_co_rdlock(void)
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
32
BdrvGraphRWlock *bdrv_graph;
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
33
bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
16
34
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
35
- /* Do not lock if in main thread */
18
-bool bdrv_requests_pending(BlockDriverState *bs);
36
- if (qemu_in_main_thread()) {
19
37
- return;
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
22
diff --git a/block/io.c b/block/io.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/block/io.c
25
+++ b/block/io.c
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
27
assert(old >= 1);
28
}
29
30
-/* Check if any requests are in-flight (including throttled requests) */
31
-bool bdrv_requests_pending(BlockDriverState *bs)
32
-{
33
- BdrvChild *child;
34
-
35
- if (atomic_read(&bs->in_flight)) {
36
- return true;
38
- }
37
- }
39
-
38
-
40
for (;;) {
39
- QLIST_FOREACH(child, &bs->children, next) {
41
qatomic_set(&bdrv_graph->reader_count,
40
- if (bdrv_requests_pending(child->bs)) {
42
bdrv_graph->reader_count + 1);
41
- return true;
43
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_graph_co_rdunlock(void)
42
- }
44
BdrvGraphRWlock *bdrv_graph;
45
bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
46
47
- /* Do not lock if in main thread */
48
- if (qemu_in_main_thread()) {
49
- return;
50
- }
43
- }
51
-
44
-
52
qatomic_store_release(&bdrv_graph->reader_count,
45
- return false;
53
bdrv_graph->reader_count - 1);
46
-}
54
/* make sure writer sees reader_count before we check has_writer */
47
-
48
typedef struct {
49
Coroutine *co;
50
BlockDriverState *bs;
55
--
51
--
56
2.40.1
52
2.13.6
53
54
diff view generated by jsdifflib
New patch
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Fam Zheng <famz@redhat.com>
3
---
4
block/io.c | 6 ++++++
5
1 file changed, 6 insertions(+)
1
6
7
diff --git a/block/io.c b/block/io.c
8
index XXXXXXX..XXXXXXX 100644
9
--- a/block/io.c
10
+++ b/block/io.c
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
12
BdrvNextIterator it;
13
GSList *aio_ctxs = NULL, *ctx;
14
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
17
+ * nodes in several different AioContexts, so make sure we're in the main
18
+ * context. */
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
20
+
21
block_job_pause_all();
22
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
24
--
25
2.13.6
26
27
diff view generated by jsdifflib
New patch
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
and also doesn't notify other parent nodes of children, which both means
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
is providing useful functionality only on a single node.
1
5
6
To keep things consistent, we also shouldn't call the block driver
7
callbacks recursively.
8
9
A proper recursive drain version that provides an actually working
10
drained section for child nodes will be introduced later.
11
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
14
---
15
block/io.c | 16 +++++++++-------
16
1 file changed, 9 insertions(+), 7 deletions(-)
17
18
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/io.c
21
+++ b/block/io.c
22
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
23
}
24
25
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
26
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
27
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
28
{
29
BdrvChild *child, *tmp;
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
32
bdrv_coroutine_enter(bs, data.co);
33
BDRV_POLL_WHILE(bs, !data.done);
34
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
36
- bdrv_drain_invoke(child->bs, begin);
37
+ if (recursive) {
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
39
+ bdrv_drain_invoke(child->bs, begin, true);
40
+ }
41
}
42
}
43
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
45
bdrv_parent_drained_begin(bs);
46
}
47
48
- bdrv_drain_invoke(bs, true);
49
+ bdrv_drain_invoke(bs, true, false);
50
bdrv_drain_recurse(bs);
51
}
52
53
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
54
}
55
56
/* Re-enable things in child-to-parent order */
57
- bdrv_drain_invoke(bs, false);
58
+ bdrv_drain_invoke(bs, false, false);
59
bdrv_parent_drained_end(bs);
60
aio_enable_external(bdrv_get_aio_context(bs));
61
}
62
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
63
aio_context_acquire(aio_context);
64
aio_disable_external(aio_context);
65
bdrv_parent_drained_begin(bs);
66
- bdrv_drain_invoke(bs, true);
67
+ bdrv_drain_invoke(bs, true, true);
68
aio_context_release(aio_context);
69
70
if (!g_slist_find(aio_ctxs, aio_context)) {
71
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
72
73
/* Re-enable things in child-to-parent order */
74
aio_context_acquire(aio_context);
75
- bdrv_drain_invoke(bs, false);
76
+ bdrv_drain_invoke(bs, false, true);
77
bdrv_parent_drained_end(bs);
78
aio_enable_external(aio_context);
79
aio_context_release(aio_context);
80
--
81
2.13.6
82
83
diff view generated by jsdifflib
New patch
1
The existing test is for bdrv_drain_all_begin/end() only. Generalise the
2
test case so that it can be run for the other variants as well. At the
3
moment this is only bdrv_drain_begin/end(), but in a while, we'll add
4
another one.
1
5
6
Also, add a backing file to the test node to test whether the operations
7
work recursively.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
12
1 file changed, 62 insertions(+), 7 deletions(-)
13
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/tests/test-bdrv-drain.c
17
+++ b/tests/test-bdrv-drain.c
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
19
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
22
+
23
+ .bdrv_child_perm = bdrv_format_default_perms,
24
};
25
26
static void aio_ret_cb(void *opaque, int ret)
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
28
*aio_ret = ret;
29
}
30
31
-static void test_drv_cb_drain_all(void)
32
+enum drain_type {
33
+ BDRV_DRAIN_ALL,
34
+ BDRV_DRAIN,
35
+};
36
+
37
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
38
+{
39
+ switch (drain_type) {
40
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
41
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
42
+ default: g_assert_not_reached();
43
+ }
44
+}
45
+
46
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
47
+{
48
+ switch (drain_type) {
49
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
50
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
51
+ default: g_assert_not_reached();
52
+ }
53
+}
54
+
55
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
56
{
57
BlockBackend *blk;
58
- BlockDriverState *bs;
59
- BDRVTestState *s;
60
+ BlockDriverState *bs, *backing;
61
+ BDRVTestState *s, *backing_s;
62
BlockAIOCB *acb;
63
int aio_ret;
64
65
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
66
s = bs->opaque;
67
blk_insert_bs(blk, bs, &error_abort);
68
69
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
70
+ backing_s = backing->opaque;
71
+ bdrv_set_backing_hd(bs, backing, &error_abort);
72
+
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
74
g_assert_cmpint(s->drain_count, ==, 0);
75
- bdrv_drain_all_begin();
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
77
+
78
+ do_drain_begin(drain_type, bs);
79
+
80
g_assert_cmpint(s->drain_count, ==, 1);
81
- bdrv_drain_all_end();
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
83
+
84
+ do_drain_end(drain_type, bs);
85
+
86
g_assert_cmpint(s->drain_count, ==, 0);
87
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
88
89
/* Now do the same while a request is pending */
90
aio_ret = -EINPROGRESS;
91
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
92
g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
93
94
g_assert_cmpint(s->drain_count, ==, 0);
95
- bdrv_drain_all_begin();
96
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
97
+
98
+ do_drain_begin(drain_type, bs);
99
+
100
g_assert_cmpint(aio_ret, ==, 0);
101
g_assert_cmpint(s->drain_count, ==, 1);
102
- bdrv_drain_all_end();
103
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
104
+
105
+ do_drain_end(drain_type, bs);
106
+
107
g_assert_cmpint(s->drain_count, ==, 0);
108
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
109
110
+ bdrv_unref(backing);
111
bdrv_unref(bs);
112
blk_unref(blk);
113
}
114
115
+static void test_drv_cb_drain_all(void)
116
+{
117
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
118
+}
119
+
120
+static void test_drv_cb_drain(void)
121
+{
122
+ test_drv_cb_common(BDRV_DRAIN, false);
123
+}
124
+
125
int main(int argc, char **argv)
126
{
127
bdrv_init();
128
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
129
g_test_init(&argc, &argv, NULL);
130
131
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
132
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
133
134
return g_test_run();
135
}
136
--
137
2.13.6
138
139
diff view generated by jsdifflib
New patch
1
This is currently only working correctly for bdrv_drain(), not for
2
bdrv_drain_all(). Leave a comment for the drain_all case, we'll address
3
it later.
1
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
8
1 file changed, 45 insertions(+)
9
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/test-bdrv-drain.c
13
+++ b/tests/test-bdrv-drain.c
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
15
test_drv_cb_common(BDRV_DRAIN, false);
16
}
17
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
19
+{
20
+ BlockBackend *blk;
21
+ BlockDriverState *bs, *backing;
22
+
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
25
+ &error_abort);
26
+ blk_insert_bs(blk, bs, &error_abort);
27
+
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
29
+ bdrv_set_backing_hd(bs, backing, &error_abort);
30
+
31
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
32
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
33
+
34
+ do_drain_begin(drain_type, bs);
35
+
36
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
37
+ g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
38
+
39
+ do_drain_end(drain_type, bs);
40
+
41
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
43
+
44
+ bdrv_unref(backing);
45
+ bdrv_unref(bs);
46
+ blk_unref(blk);
47
+}
48
+
49
+static void test_quiesce_drain_all(void)
50
+{
51
+ // XXX drain_all doesn't quiesce
52
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
53
+}
54
+
55
+static void test_quiesce_drain(void)
56
+{
57
+ test_quiesce_common(BDRV_DRAIN, false);
58
+}
59
+
60
int main(int argc, char **argv)
61
{
62
bdrv_init();
63
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
64
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
65
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
66
67
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
68
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
69
+
70
return g_test_run();
71
}
72
--
73
2.13.6
74
75
diff view generated by jsdifflib
1
When jobs are sleeping, for example to enforce a given rate limit, they
1
Block jobs already paused themselves when their main BlockBackend
2
can be reentered early, in particular in order to get paused, to update
2
entered a drained section. This is not good enough: We also want to
3
the rate limit or to get cancelled.
3
pause a block job and may not submit new requests if, for example, the
4
mirror target node should be drained.
4
5
5
Before this patch, they behave in this case as if they had fully
6
This implements .drained_begin/end callbacks in child_job in order to
6
completed their rate limiting delay. This means that requests are sped
7
consider all block nodes related to the job, and removes the
7
up beyond their limit, violating the constraints that the user gave us.
8
BlockBackend callbacks which are unnecessary now because the root of the
8
9
job main BlockBackend is always referenced with a child_job, too.
9
Change the block jobs to sleep in a loop until the necessary delay is
10
completed, while still allowing cancelling them immediately as well
11
pausing (handled by the pause point in job_sleep_ns()) and updating the
12
rate limit.
13
14
This change is also motivated by iotests cases being prone to fail
15
because drain operations pause and unpause them so often that block jobs
16
complete earlier than they are supposed to. In particular, the next
17
commit would fail iotests 030 without this change.
18
10
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
Message-Id: <20230510203601.418015-8-kwolf@redhat.com>
21
Reviewed-by: Eric Blake <eblake@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
12
---
24
include/block/blockjob_int.h | 14 ++++++++++----
13
blockjob.c | 22 +++++++++-------------
25
block/commit.c | 7 ++-----
14
1 file changed, 9 insertions(+), 13 deletions(-)
26
block/mirror.c | 23 ++++++++++-------------
27
block/stream.c | 7 ++-----
28
blockjob.c | 22 ++++++++++++++++++++--
29
5 files changed, 44 insertions(+), 29 deletions(-)
30
15
31
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
32
index XXXXXXX..XXXXXXX 100644
33
--- a/include/block/blockjob_int.h
34
+++ b/include/block/blockjob_int.h
35
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job);
36
*/
37
38
/**
39
- * block_job_ratelimit_get_delay:
40
+ * block_job_ratelimit_processed_bytes:
41
*
42
- * Calculate and return delay for the next request in ns. See the documentation
43
- * of ratelimit_calculate_delay() for details.
44
+ * To be called after some work has been done. Adjusts the delay for the next
45
+ * request. See the documentation of ratelimit_calculate_delay() for details.
46
*/
47
-int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
48
+void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n);
49
+
50
+/**
51
+ * Put the job to sleep (assuming that it wasn't canceled) to throttle it to the
52
+ * right speed according to its rate limiting.
53
+ */
54
+void block_job_ratelimit_sleep(BlockJob *job);
55
56
/**
57
* block_job_error_action:
58
diff --git a/block/commit.c b/block/commit.c
59
index XXXXXXX..XXXXXXX 100644
60
--- a/block/commit.c
61
+++ b/block/commit.c
62
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
63
{
64
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
65
int64_t offset;
66
- uint64_t delay_ns = 0;
67
int ret = 0;
68
int64_t n = 0; /* bytes */
69
QEMU_AUTO_VFREE void *buf = NULL;
70
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
71
/* Note that even when no rate limit is applied we need to yield
72
* with no pending I/O here so that bdrv_drain_all() returns.
73
*/
74
- job_sleep_ns(&s->common.job, delay_ns);
75
+ block_job_ratelimit_sleep(&s->common);
76
if (job_is_cancelled(&s->common.job)) {
77
break;
78
}
79
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
80
job_progress_update(&s->common.job, n);
81
82
if (copy) {
83
- delay_ns = block_job_ratelimit_get_delay(&s->common, n);
84
- } else {
85
- delay_ns = 0;
86
+ block_job_ratelimit_processed_bytes(&s->common, n);
87
}
88
}
89
90
diff --git a/block/mirror.c b/block/mirror.c
91
index XXXXXXX..XXXXXXX 100644
92
--- a/block/mirror.c
93
+++ b/block/mirror.c
94
@@ -XXX,XX +XXX,XX @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
95
return bytes_handled;
96
}
97
98
-static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
99
+static void coroutine_fn mirror_iteration(MirrorBlockJob *s)
100
{
101
BlockDriverState *source = s->mirror_top_bs->backing->bs;
102
MirrorOp *pseudo_op;
103
int64_t offset;
104
- uint64_t delay_ns = 0, ret = 0;
105
/* At least the first dirty chunk is mirrored in one iteration. */
106
int nb_chunks = 1;
107
bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target));
108
@@ -XXX,XX +XXX,XX @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
109
assert(io_bytes);
110
offset += io_bytes;
111
nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity);
112
- delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct);
113
+ block_job_ratelimit_processed_bytes(&s->common, io_bytes_acct);
114
}
115
116
- ret = delay_ns;
117
fail:
118
QTAILQ_REMOVE(&s->ops_in_flight, pseudo_op, next);
119
qemu_co_queue_restart_all(&pseudo_op->waiting_requests);
120
g_free(pseudo_op);
121
-
122
- return ret;
123
}
124
125
static void mirror_free_init(MirrorBlockJob *s)
126
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
127
assert(!s->dbi);
128
s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap);
129
for (;;) {
130
- uint64_t delay_ns = 0;
131
int64_t cnt, delta;
132
bool should_complete;
133
134
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
135
mirror_wait_for_free_in_flight_slot(s);
136
continue;
137
} else if (cnt != 0) {
138
- delay_ns = mirror_iteration(s);
139
+ mirror_iteration(s);
140
}
141
}
142
143
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
144
}
145
146
if (job_is_ready(&s->common.job) && !should_complete) {
147
- delay_ns = (s->in_flight == 0 &&
148
- cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0);
149
+ if (s->in_flight == 0 && cnt == 0) {
150
+ trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job),
151
+ BLOCK_JOB_SLICE_TIME);
152
+ job_sleep_ns(&s->common.job, BLOCK_JOB_SLICE_TIME);
153
+ }
154
+ } else {
155
+ block_job_ratelimit_sleep(&s->common);
156
}
157
- trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job),
158
- delay_ns);
159
- job_sleep_ns(&s->common.job, delay_ns);
160
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
161
}
162
163
diff --git a/block/stream.c b/block/stream.c
164
index XXXXXXX..XXXXXXX 100644
165
--- a/block/stream.c
166
+++ b/block/stream.c
167
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
168
BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs);
169
int64_t len;
170
int64_t offset = 0;
171
- uint64_t delay_ns = 0;
172
int error = 0;
173
int64_t n = 0; /* bytes */
174
175
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
176
/* Note that even when no rate limit is applied we need to yield
177
* with no pending I/O here so that bdrv_drain_all() returns.
178
*/
179
- job_sleep_ns(&s->common.job, delay_ns);
180
+ block_job_ratelimit_sleep(&s->common);
181
if (job_is_cancelled(&s->common.job)) {
182
break;
183
}
184
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
185
/* Publish progress */
186
job_progress_update(&s->common.job, n);
187
if (copy) {
188
- delay_ns = block_job_ratelimit_get_delay(&s->common, n);
189
- } else {
190
- delay_ns = 0;
191
+ block_job_ratelimit_processed_bytes(&s->common, n);
192
}
193
}
194
195
diff --git a/blockjob.c b/blockjob.c
16
diff --git a/blockjob.c b/blockjob.c
196
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
197
--- a/blockjob.c
18
--- a/blockjob.c
198
+++ b/blockjob.c
19
+++ b/blockjob.c
199
@@ -XXX,XX +XXX,XX @@ static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
200
return block_job_set_speed_locked(job, speed, errp);
21
job->id);
201
}
22
}
202
23
203
-int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
24
-static const BdrvChildRole child_job = {
204
+void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n)
25
- .get_parent_desc = child_job_get_parent_desc,
26
- .stay_at_node = true,
27
-};
28
-
29
-static void block_job_drained_begin(void *opaque)
30
+static void child_job_drained_begin(BdrvChild *c)
205
{
31
{
206
IO_CODE();
32
- BlockJob *job = opaque;
207
- return ratelimit_calculate_delay(&job->limit, n);
33
+ BlockJob *job = c->opaque;
208
+ ratelimit_calculate_delay(&job->limit, n);
34
block_job_pause(job);
209
+}
210
+
211
+void block_job_ratelimit_sleep(BlockJob *job)
212
+{
213
+ uint64_t delay_ns;
214
+
215
+ /*
216
+ * Sleep at least once. If the job is reentered early, keep waiting until
217
+ * we've waited for the full time that is necessary to keep the job at the
218
+ * right speed.
219
+ *
220
+ * Make sure to recalculate the delay after each (possibly interrupted)
221
+ * sleep because the speed can change while the job has yielded.
222
+ */
223
+ do {
224
+ delay_ns = ratelimit_calculate_delay(&job->limit, 0);
225
+ job_sleep_ns(&job->job, delay_ns);
226
+ } while (delay_ns && !job_is_cancelled(&job->job));
227
}
35
}
228
36
229
BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp)
37
-static void block_job_drained_end(void *opaque)
38
+static void child_job_drained_end(BdrvChild *c)
39
{
40
- BlockJob *job = opaque;
41
+ BlockJob *job = c->opaque;
42
block_job_resume(job);
43
}
44
45
-static const BlockDevOps block_job_dev_ops = {
46
- .drained_begin = block_job_drained_begin,
47
- .drained_end = block_job_drained_end,
48
+static const BdrvChildRole child_job = {
49
+ .get_parent_desc = child_job_get_parent_desc,
50
+ .drained_begin = child_job_drained_begin,
51
+ .drained_end = child_job_drained_end,
52
+ .stay_at_node = true,
53
};
54
55
void block_job_remove_all_bdrv(BlockJob *job)
56
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
57
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
58
bs->job = job;
59
60
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
61
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
62
63
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
230
--
64
--
231
2.40.1
65
2.13.6
66
67
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
1
Block jobs must be paused if any of the involved nodes are drained.
2
2
3
Look at qmp_transaction(): dev_list is not obvious name for list of
4
actions. Let's look at qapi spec, this argument is "actions". Let's
5
follow the common practice of using same argument names in qapi scheme
6
and code.
7
8
To be honest, rename props to properties for same reason.
9
10
Next, we have to rename global map of actions, to not conflict with new
11
name for function argument.
12
13
Rename also dev_entry loop variable accordingly to new name of the
14
list.
15
16
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
17
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
18
Message-Id: <20230510150624.310640-3-vsementsov@yandex-team.ru>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
4
---
21
blockdev.c | 30 +++++++++++++++---------------
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
22
1 file changed, 15 insertions(+), 15 deletions(-)
6
1 file changed, 121 insertions(+)
23
7
24
diff --git a/blockdev.c b/blockdev.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
25
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
26
--- a/blockdev.c
10
--- a/tests/test-bdrv-drain.c
27
+++ b/blockdev.c
11
+++ b/tests/test-bdrv-drain.c
28
@@ -XXX,XX +XXX,XX @@ static void abort_commit(void *opaque)
12
@@ -XXX,XX +XXX,XX @@
29
g_assert_not_reached(); /* this action never succeeds */
13
14
#include "qemu/osdep.h"
15
#include "block/block.h"
16
+#include "block/blockjob_int.h"
17
#include "sysemu/block-backend.h"
18
#include "qapi/error.h"
19
20
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
21
test_quiesce_common(BDRV_DRAIN, false);
30
}
22
}
31
23
32
-static const BlkActionOps actions[] = {
24
+
33
+static const BlkActionOps actions_map[] = {
25
+typedef struct TestBlockJob {
34
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = {
26
+ BlockJob common;
35
.instance_size = sizeof(ExternalSnapshotState),
27
+ bool should_complete;
36
.action = external_snapshot_action,
28
+} TestBlockJob;
37
@@ -XXX,XX +XXX,XX @@ static TransactionProperties *get_transaction_properties(
29
+
38
*
30
+static void test_job_completed(BlockJob *job, void *opaque)
39
* Always run under BQL.
31
+{
40
*/
32
+ block_job_completed(job, 0);
41
-void qmp_transaction(TransactionActionList *dev_list,
33
+}
42
- struct TransactionProperties *props,
34
+
43
+void qmp_transaction(TransactionActionList *actions,
35
+static void coroutine_fn test_job_start(void *opaque)
44
+ struct TransactionProperties *properties,
36
+{
45
Error **errp)
37
+ TestBlockJob *s = opaque;
38
+
39
+ while (!s->should_complete) {
40
+ block_job_sleep_ns(&s->common, 100000);
41
+ }
42
+
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
44
+}
45
+
46
+static void test_job_complete(BlockJob *job, Error **errp)
47
+{
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
49
+ s->should_complete = true;
50
+}
51
+
52
+BlockJobDriver test_job_driver = {
53
+ .instance_size = sizeof(TestBlockJob),
54
+ .start = test_job_start,
55
+ .complete = test_job_complete,
56
+};
57
+
58
+static void test_blockjob_common(enum drain_type drain_type)
59
+{
60
+ BlockBackend *blk_src, *blk_target;
61
+ BlockDriverState *src, *target;
62
+ BlockJob *job;
63
+ int ret;
64
+
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
66
+ &error_abort);
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
68
+ blk_insert_bs(blk_src, src, &error_abort);
69
+
70
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
71
+ &error_abort);
72
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
73
+ blk_insert_bs(blk_target, target, &error_abort);
74
+
75
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
76
+ 0, NULL, NULL, &error_abort);
77
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
78
+ block_job_start(job);
79
+
80
+ g_assert_cmpint(job->pause_count, ==, 0);
81
+ g_assert_false(job->paused);
82
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
83
+
84
+ do_drain_begin(drain_type, src);
85
+
86
+ if (drain_type == BDRV_DRAIN_ALL) {
87
+ /* bdrv_drain_all() drains both src and target, and involves an
88
+ * additional block_job_pause_all() */
89
+ g_assert_cmpint(job->pause_count, ==, 3);
90
+ } else {
91
+ g_assert_cmpint(job->pause_count, ==, 1);
92
+ }
93
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
94
+ /* g_assert_true(job->paused); */
95
+ g_assert_false(job->busy); /* The job is paused */
96
+
97
+ do_drain_end(drain_type, src);
98
+
99
+ g_assert_cmpint(job->pause_count, ==, 0);
100
+ g_assert_false(job->paused);
101
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
102
+
103
+ do_drain_begin(drain_type, target);
104
+
105
+ if (drain_type == BDRV_DRAIN_ALL) {
106
+ /* bdrv_drain_all() drains both src and target, and involves an
107
+ * additional block_job_pause_all() */
108
+ g_assert_cmpint(job->pause_count, ==, 3);
109
+ } else {
110
+ g_assert_cmpint(job->pause_count, ==, 1);
111
+ }
112
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
113
+ /* g_assert_true(job->paused); */
114
+ g_assert_false(job->busy); /* The job is paused */
115
+
116
+ do_drain_end(drain_type, target);
117
+
118
+ g_assert_cmpint(job->pause_count, ==, 0);
119
+ g_assert_false(job->paused);
120
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
121
+
122
+ ret = block_job_complete_sync(job, &error_abort);
123
+ g_assert_cmpint(ret, ==, 0);
124
+
125
+ blk_unref(blk_src);
126
+ blk_unref(blk_target);
127
+ bdrv_unref(src);
128
+ bdrv_unref(target);
129
+}
130
+
131
+static void test_blockjob_drain_all(void)
132
+{
133
+ test_blockjob_common(BDRV_DRAIN_ALL);
134
+}
135
+
136
+static void test_blockjob_drain(void)
137
+{
138
+ test_blockjob_common(BDRV_DRAIN);
139
+}
140
+
141
int main(int argc, char **argv)
46
{
142
{
47
- TransactionActionList *dev_entry = dev_list;
143
bdrv_init();
48
- bool has_props = !!props;
144
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
49
+ TransactionActionList *act = actions;
145
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
50
+ bool has_properties = !!properties;
146
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
51
JobTxn *block_job_txn = NULL;
147
52
Error *local_err = NULL;
148
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
53
Transaction *tran = tran_new();
149
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
54
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
150
+
55
/* Does this transaction get canceled as a group on failure?
151
return g_test_run();
56
* If not, we don't really need to make a JobTxn.
57
*/
58
- props = get_transaction_properties(props);
59
- if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
60
+ properties = get_transaction_properties(properties);
61
+ if (properties->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
62
block_job_txn = job_txn_new();
63
}
64
65
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
66
bdrv_drain_all();
67
68
/* We don't do anything in this loop that commits us to the operations */
69
- while (NULL != dev_entry) {
70
+ while (NULL != act) {
71
TransactionAction *dev_info = NULL;
72
const BlkActionOps *ops;
73
BlkActionState *state;
74
75
- dev_info = dev_entry->value;
76
- dev_entry = dev_entry->next;
77
+ dev_info = act->value;
78
+ act = act->next;
79
80
- assert(dev_info->type < ARRAY_SIZE(actions));
81
+ assert(dev_info->type < ARRAY_SIZE(actions_map));
82
83
- ops = &actions[dev_info->type];
84
+ ops = &actions_map[dev_info->type];
85
assert(ops->instance_size > 0);
86
87
state = g_malloc0(ops->instance_size);
88
state->ops = ops;
89
state->action = dev_info;
90
state->block_job_txn = block_job_txn;
91
- state->txn_props = props;
92
+ state->txn_props = properties;
93
94
state->ops->action(state, tran, &local_err);
95
if (local_err) {
96
@@ -XXX,XX +XXX,XX @@ delete_and_fail:
97
/* failure, and it is all-or-none; roll back all operations */
98
tran_abort(tran);
99
exit:
100
- if (!has_props) {
101
- qapi_free_TransactionProperties(props);
102
+ if (!has_properties) {
103
+ qapi_free_TransactionProperties(properties);
104
}
105
job_txn_unref(block_job_txn);
106
}
152
}
107
--
153
--
108
2.40.1
154
2.13.6
155
156
diff view generated by jsdifflib
New patch
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
so we don't need an additional block_job_pause_all() call.
1
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
6
block/io.c | 4 ----
7
tests/test-bdrv-drain.c | 10 ++++------
8
2 files changed, 4 insertions(+), 10 deletions(-)
9
10
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/block/io.c
13
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
15
* context. */
16
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
17
18
- block_job_pause_all();
19
-
20
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
21
AioContext *aio_context = bdrv_get_aio_context(bs);
22
23
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
24
aio_enable_external(aio_context);
25
aio_context_release(aio_context);
26
}
27
-
28
- block_job_resume_all();
29
}
30
31
void bdrv_drain_all(void)
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/tests/test-bdrv-drain.c
35
+++ b/tests/test-bdrv-drain.c
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
37
do_drain_begin(drain_type, src);
38
39
if (drain_type == BDRV_DRAIN_ALL) {
40
- /* bdrv_drain_all() drains both src and target, and involves an
41
- * additional block_job_pause_all() */
42
- g_assert_cmpint(job->pause_count, ==, 3);
43
+ /* bdrv_drain_all() drains both src and target */
44
+ g_assert_cmpint(job->pause_count, ==, 2);
45
} else {
46
g_assert_cmpint(job->pause_count, ==, 1);
47
}
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
49
do_drain_begin(drain_type, target);
50
51
if (drain_type == BDRV_DRAIN_ALL) {
52
- /* bdrv_drain_all() drains both src and target, and involves an
53
- * additional block_job_pause_all() */
54
- g_assert_cmpint(job->pause_count, ==, 3);
55
+ /* bdrv_drain_all() drains both src and target */
56
+ g_assert_cmpint(job->pause_count, ==, 2);
57
} else {
58
g_assert_cmpint(job->pause_count, ==, 1);
59
}
60
--
61
2.13.6
62
63
diff view generated by jsdifflib
New patch
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
aio_disable_external() to the outermost drain section, but the block
3
driver callbacks are always called. bdrv_do_drained_end() must match
4
this behaviour, otherwise nodes stay drained even if begin/end calls
5
were balanced.
1
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
block/io.c | 12 +++++++-----
10
1 file changed, 7 insertions(+), 5 deletions(-)
11
12
diff --git a/block/io.c b/block/io.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/block/io.c
15
+++ b/block/io.c
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
17
18
void bdrv_drained_end(BlockDriverState *bs)
19
{
20
+ int old_quiesce_counter;
21
+
22
if (qemu_in_coroutine()) {
23
bdrv_co_yield_to_drain(bs, false);
24
return;
25
}
26
assert(bs->quiesce_counter > 0);
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
28
- return;
29
- }
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
31
32
/* Re-enable things in child-to-parent order */
33
bdrv_drain_invoke(bs, false, false);
34
- bdrv_parent_drained_end(bs);
35
- aio_enable_external(bdrv_get_aio_context(bs));
36
+ if (old_quiesce_counter == 1) {
37
+ bdrv_parent_drained_end(bs);
38
+ aio_enable_external(bdrv_get_aio_context(bs));
39
+ }
40
}
41
42
/*
43
--
44
2.13.6
45
46
diff view generated by jsdifflib
1
From: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2
3
"zlib" clusters are actually raw deflate (RFC1951) clusters without
4
zlib headers.
5
6
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
7
Message-Id: <168424874322.11954.1340942046351859521-0@git.sr.ht>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
2
---
12
docs/interop/qcow2.txt | 10 +++++++---
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
13
1 file changed, 7 insertions(+), 3 deletions(-)
4
1 file changed, 57 insertions(+)
14
5
15
diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
16
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
17
--- a/docs/interop/qcow2.txt
8
--- a/tests/test-bdrv-drain.c
18
+++ b/docs/interop/qcow2.txt
9
+++ b/tests/test-bdrv-drain.c
19
@@ -XXX,XX +XXX,XX @@ version 2.
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
20
type.
11
enum drain_type {
21
12
BDRV_DRAIN_ALL,
22
If the incompatible bit "Compression type" is set: the field
13
BDRV_DRAIN,
23
- must be present and non-zero (which means non-zlib
14
+ DRAIN_TYPE_MAX,
24
+ must be present and non-zero (which means non-deflate
15
};
25
compression type). Otherwise, this field must not be present
16
26
- or must be zero (which means zlib).
17
static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
27
+ or must be zero (which means deflate).
18
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
28
19
test_quiesce_common(BDRV_DRAIN, false);
29
Available compression type values:
20
}
30
- 0: zlib <https://www.zlib.net/>
21
31
+ 0: deflate <https://www.ietf.org/rfc/rfc1951.txt>
22
+static void test_nested(void)
32
1: zstd <http://github.com/facebook/zstd>
23
+{
33
24
+ BlockBackend *blk;
34
+ The deflate compression type is called "zlib"
25
+ BlockDriverState *bs, *backing;
35
+ <https://www.zlib.net/> in QEMU. However, clusters with the
26
+ BDRVTestState *s, *backing_s;
36
+ deflate compression type do not have zlib headers.
27
+ enum drain_type outer, inner;
37
+
28
+
38
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
39
=== Header padding ===
30
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
31
+ &error_abort);
32
+ s = bs->opaque;
33
+ blk_insert_bs(blk, bs, &error_abort);
34
+
35
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
36
+ backing_s = backing->opaque;
37
+ bdrv_set_backing_hd(bs, backing, &error_abort);
38
+
39
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
40
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
41
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
42
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
43
+ (inner != BDRV_DRAIN_ALL);
44
+ int backing_quiesce = 0;
45
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
46
+ (inner != BDRV_DRAIN);
47
+
48
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
49
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
50
+ g_assert_cmpint(s->drain_count, ==, 0);
51
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
52
+
53
+ do_drain_begin(outer, bs);
54
+ do_drain_begin(inner, bs);
55
+
56
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
57
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
58
+ g_assert_cmpint(s->drain_count, ==, 2);
59
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
60
+
61
+ do_drain_end(inner, bs);
62
+ do_drain_end(outer, bs);
63
+
64
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
65
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
66
+ g_assert_cmpint(s->drain_count, ==, 0);
67
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
68
+ }
69
+ }
70
+
71
+ bdrv_unref(backing);
72
+ bdrv_unref(bs);
73
+ blk_unref(blk);
74
+}
75
+
76
77
typedef struct TestBlockJob {
78
BlockJob common;
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
82
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
84
+
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
40
87
41
--
88
--
42
2.40.1
89
2.13.6
90
91
diff view generated by jsdifflib
New patch
1
1
This is in preparation for subtree drains, i.e. drained sections that
2
affect not only a single node, but recursively all child nodes, too.
3
4
Calling the parent callbacks for drain is pointless when we just came
5
from that parent node recursively and leads to multiple increases of
6
bs->quiesce_counter in a single drain call. Don't do it.
7
8
In order for this to work correctly, the parent callback must be called
9
for every bdrv_drain_begin/end() call, not only for the outermost one:
10
11
If we have a node N with two parents A and B, recursive draining of A
12
should cause the quiesce_counter of B to increase because its child N is
13
drained independently of B. If now B is recursively drained, too, A must
14
increase its quiesce_counter because N is drained independently of A
15
only now, even if N is going from quiesce_counter 1 to 2.
16
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
19
include/block/block.h | 4 ++--
20
block.c | 13 +++++++++----
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
22
3 files changed, 45 insertions(+), 19 deletions(-)
23
24
diff --git a/include/block/block.h b/include/block/block.h
25
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/block.h
27
+++ b/include/block/block.h
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
29
* Begin a quiesced section of all users of @bs. This is part of
30
* bdrv_drained_begin.
31
*/
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
34
35
/**
36
* bdrv_parent_drained_end:
37
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs);
38
* End a quiesced section of all users of @bs. This is part of
39
* bdrv_drained_end.
40
*/
41
-void bdrv_parent_drained_end(BlockDriverState *bs);
42
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
43
44
/**
45
* bdrv_drained_begin:
46
diff --git a/block.c b/block.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/block.c
49
+++ b/block.c
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
51
BlockDriverState *new_bs)
52
{
53
BlockDriverState *old_bs = child->bs;
54
+ int i;
55
56
if (old_bs && new_bs) {
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
58
}
59
if (old_bs) {
60
if (old_bs->quiesce_counter && child->role->drained_end) {
61
- child->role->drained_end(child);
62
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
63
+ child->role->drained_end(child);
64
+ }
65
}
66
if (child->role->detach) {
67
child->role->detach(child);
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
69
if (new_bs) {
70
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
71
if (new_bs->quiesce_counter && child->role->drained_begin) {
72
- child->role->drained_begin(child);
73
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
74
+ child->role->drained_begin(child);
75
+ }
76
}
77
78
if (child->role->attach) {
79
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
80
AioContext *ctx = bdrv_get_aio_context(bs);
81
82
aio_disable_external(ctx);
83
- bdrv_parent_drained_begin(bs);
84
+ bdrv_parent_drained_begin(bs, NULL);
85
bdrv_drain(bs); /* ensure there are no in-flight requests */
86
87
while (aio_poll(ctx, false)) {
88
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
89
*/
90
aio_context_acquire(new_context);
91
bdrv_attach_aio_context(bs, new_context);
92
- bdrv_parent_drained_end(bs);
93
+ bdrv_parent_drained_end(bs, NULL);
94
aio_enable_external(ctx);
95
aio_context_release(new_context);
96
}
97
diff --git a/block/io.c b/block/io.c
98
index XXXXXXX..XXXXXXX 100644
99
--- a/block/io.c
100
+++ b/block/io.c
101
@@ -XXX,XX +XXX,XX @@
102
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
103
int64_t offset, int bytes, BdrvRequestFlags flags);
104
105
-void bdrv_parent_drained_begin(BlockDriverState *bs)
106
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
107
{
108
BdrvChild *c, *next;
109
110
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
111
+ if (c == ignore) {
112
+ continue;
113
+ }
114
if (c->role->drained_begin) {
115
c->role->drained_begin(c);
116
}
117
}
118
}
119
120
-void bdrv_parent_drained_end(BlockDriverState *bs)
121
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
122
{
123
BdrvChild *c, *next;
124
125
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
126
+ if (c == ignore) {
127
+ continue;
128
+ }
129
if (c->role->drained_end) {
130
c->role->drained_end(c);
131
}
132
@@ -XXX,XX +XXX,XX @@ typedef struct {
133
BlockDriverState *bs;
134
bool done;
135
bool begin;
136
+ BdrvChild *parent;
137
} BdrvCoDrainData;
138
139
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
140
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
141
return waited;
142
}
143
144
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
145
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
146
+
147
static void bdrv_co_drain_bh_cb(void *opaque)
148
{
149
BdrvCoDrainData *data = opaque;
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
151
152
bdrv_dec_in_flight(bs);
153
if (data->begin) {
154
- bdrv_drained_begin(bs);
155
+ bdrv_do_drained_begin(bs, data->parent);
156
} else {
157
- bdrv_drained_end(bs);
158
+ bdrv_do_drained_end(bs, data->parent);
159
}
160
161
data->done = true;
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
163
}
164
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
166
- bool begin)
167
+ bool begin, BdrvChild *parent)
168
{
169
BdrvCoDrainData data;
170
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
172
.bs = bs,
173
.done = false,
174
.begin = begin,
175
+ .parent = parent,
176
};
177
bdrv_inc_in_flight(bs);
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
180
assert(data.done);
181
}
182
183
-void bdrv_drained_begin(BlockDriverState *bs)
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
185
{
186
if (qemu_in_coroutine()) {
187
- bdrv_co_yield_to_drain(bs, true);
188
+ bdrv_co_yield_to_drain(bs, true, parent);
189
return;
190
}
191
192
/* Stop things in parent-to-child order */
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
194
aio_disable_external(bdrv_get_aio_context(bs));
195
- bdrv_parent_drained_begin(bs);
196
}
197
198
+ bdrv_parent_drained_begin(bs, parent);
199
bdrv_drain_invoke(bs, true, false);
200
bdrv_drain_recurse(bs);
201
}
202
203
-void bdrv_drained_end(BlockDriverState *bs)
204
+void bdrv_drained_begin(BlockDriverState *bs)
205
+{
206
+ bdrv_do_drained_begin(bs, NULL);
207
+}
208
+
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
210
{
211
int old_quiesce_counter;
212
213
if (qemu_in_coroutine()) {
214
- bdrv_co_yield_to_drain(bs, false);
215
+ bdrv_co_yield_to_drain(bs, false, parent);
216
return;
217
}
218
assert(bs->quiesce_counter > 0);
219
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
220
221
/* Re-enable things in child-to-parent order */
222
bdrv_drain_invoke(bs, false, false);
223
+ bdrv_parent_drained_end(bs, parent);
224
if (old_quiesce_counter == 1) {
225
- bdrv_parent_drained_end(bs);
226
aio_enable_external(bdrv_get_aio_context(bs));
227
}
228
}
229
230
+void bdrv_drained_end(BlockDriverState *bs)
231
+{
232
+ bdrv_do_drained_end(bs, NULL);
233
+}
234
+
235
/*
236
* Wait for pending requests to complete on a single BlockDriverState subtree,
237
* and suspend block driver's internal I/O until next request arrives.
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
239
/* Stop things in parent-to-child order */
240
aio_context_acquire(aio_context);
241
aio_disable_external(aio_context);
242
- bdrv_parent_drained_begin(bs);
243
+ bdrv_parent_drained_begin(bs, NULL);
244
bdrv_drain_invoke(bs, true, true);
245
aio_context_release(aio_context);
246
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
248
/* Re-enable things in child-to-parent order */
249
aio_context_acquire(aio_context);
250
bdrv_drain_invoke(bs, false, true);
251
- bdrv_parent_drained_end(bs);
252
+ bdrv_parent_drained_end(bs, NULL);
253
aio_enable_external(aio_context);
254
aio_context_release(aio_context);
255
}
256
--
257
2.13.6
258
259
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
until bdrv_drained_end().
2
4
3
We are going to add more block-graph modifying transaction actions,
5
Add a version that keeps the whole subtree drained. As of this commit,
4
and block-graph modifying functions are already based on Transaction
6
graph changes cannot be allowed during a subtree drained section, but
5
API.
7
this will be fixed soon.
6
8
7
Next, we'll need to separately update permissions after several
8
graph-modifying actions, and this would be simple with help of
9
Transaction API.
10
11
So, now let's just transform what we have into new-style transaction
12
actions.
13
14
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Message-Id: <20230510150624.310640-2-vsementsov@yandex-team.ru>
16
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
10
---
19
blockdev.c | 309 ++++++++++++++++++++++++++++++-----------------------
11
include/block/block.h | 13 +++++++++++++
20
1 file changed, 178 insertions(+), 131 deletions(-)
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
13
2 files changed, 56 insertions(+), 11 deletions(-)
21
14
22
diff --git a/blockdev.c b/blockdev.c
15
diff --git a/include/block/block.h b/include/block/block.h
23
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
24
--- a/blockdev.c
17
--- a/include/block/block.h
25
+++ b/blockdev.c
18
+++ b/include/block/block.h
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlkActionState BlkActionState;
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
20
void bdrv_drained_begin(BlockDriverState *bs);
21
22
/**
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
24
+ * exclusive access to all child nodes as well.
25
+ *
26
+ * Graph changes are not allowed during a subtree drain section.
27
+ */
28
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
29
+
30
+/**
31
* bdrv_drained_end:
32
*
33
* End a quiescent section started by bdrv_drained_begin().
27
*/
34
*/
28
typedef struct BlkActionOps {
35
void bdrv_drained_end(BlockDriverState *bs);
29
size_t instance_size;
36
30
- void (*prepare)(BlkActionState *common, Error **errp);
37
+/**
31
- void (*commit)(BlkActionState *common);
38
+ * End a quiescent section started by bdrv_subtree_drained_begin().
32
- void (*abort)(BlkActionState *common);
39
+ */
33
- void (*clean)(BlkActionState *common);
40
+void bdrv_subtree_drained_end(BlockDriverState *bs);
34
+ void (*action)(BlkActionState *common, Transaction *tran, Error **errp);
41
+
35
} BlkActionOps;
42
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
36
43
Error **errp);
37
/**
44
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
38
@@ -XXX,XX +XXX,XX @@ typedef struct InternalSnapshotState {
45
diff --git a/block/io.c b/block/io.c
39
bool created;
46
index XXXXXXX..XXXXXXX 100644
40
} InternalSnapshotState;
47
--- a/block/io.c
41
48
+++ b/block/io.c
42
+static void internal_snapshot_abort(void *opaque);
49
@@ -XXX,XX +XXX,XX @@ typedef struct {
43
+static void internal_snapshot_clean(void *opaque);
50
BlockDriverState *bs;
44
+TransactionActionDrv internal_snapshot_drv = {
51
bool done;
45
+ .abort = internal_snapshot_abort,
52
bool begin;
46
+ .clean = internal_snapshot_clean,
53
+ bool recursive;
47
+};
54
BdrvChild *parent;
48
55
} BdrvCoDrainData;
49
static int action_check_completion_mode(BlkActionState *s, Error **errp)
56
57
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
58
return waited;
59
}
60
61
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
62
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
63
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
64
+ BdrvChild *parent);
65
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
66
+ BdrvChild *parent);
67
68
static void bdrv_co_drain_bh_cb(void *opaque)
50
{
69
{
51
@@ -XXX,XX +XXX,XX @@ static int action_check_completion_mode(BlkActionState *s, Error **errp)
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
52
return 0;
71
72
bdrv_dec_in_flight(bs);
73
if (data->begin) {
74
- bdrv_do_drained_begin(bs, data->parent);
75
+ bdrv_do_drained_begin(bs, data->recursive, data->parent);
76
} else {
77
- bdrv_do_drained_end(bs, data->parent);
78
+ bdrv_do_drained_end(bs, data->recursive, data->parent);
79
}
80
81
data->done = true;
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
53
}
83
}
54
84
55
-static void internal_snapshot_prepare(BlkActionState *common,
85
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
56
- Error **errp)
86
- bool begin, BdrvChild *parent)
57
+static void internal_snapshot_action(BlkActionState *common,
87
+ bool begin, bool recursive,
58
+ Transaction *tran, Error **errp)
88
+ BdrvChild *parent)
59
{
89
{
60
Error *local_err = NULL;
90
BdrvCoDrainData data;
61
const char *device;
91
62
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_prepare(BlkActionState *common,
92
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
63
internal = common->action->u.blockdev_snapshot_internal_sync.data;
93
.bs = bs,
64
state = DO_UPCAST(InternalSnapshotState, common, common);
94
.done = false,
65
95
.begin = begin,
66
+ tran_add(tran, &internal_snapshot_drv, state);
96
+ .recursive = recursive,
97
.parent = parent,
98
};
99
bdrv_inc_in_flight(bs);
100
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
101
assert(data.done);
102
}
103
104
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
105
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
106
+ BdrvChild *parent)
107
{
108
+ BdrvChild *child, *next;
67
+
109
+
68
/* 1. parse input */
110
if (qemu_in_coroutine()) {
69
device = internal->device;
111
- bdrv_co_yield_to_drain(bs, true, parent);
70
name = internal->name;
112
+ bdrv_co_yield_to_drain(bs, true, recursive, parent);
71
@@ -XXX,XX +XXX,XX @@ out:
72
aio_context_release(aio_context);
73
}
74
75
-static void internal_snapshot_abort(BlkActionState *common)
76
+static void internal_snapshot_abort(void *opaque)
77
{
78
- InternalSnapshotState *state =
79
- DO_UPCAST(InternalSnapshotState, common, common);
80
+ InternalSnapshotState *state = opaque;
81
BlockDriverState *bs = state->bs;
82
QEMUSnapshotInfo *sn = &state->sn;
83
AioContext *aio_context;
84
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_abort(BlkActionState *common)
85
aio_context_release(aio_context);
86
}
87
88
-static void internal_snapshot_clean(BlkActionState *common)
89
+static void internal_snapshot_clean(void *opaque)
90
{
91
- InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
92
- common, common);
93
+ g_autofree InternalSnapshotState *state = opaque;
94
AioContext *aio_context;
95
96
if (!state->bs) {
97
@@ -XXX,XX +XXX,XX @@ typedef struct ExternalSnapshotState {
98
bool overlay_appended;
99
} ExternalSnapshotState;
100
101
-static void external_snapshot_prepare(BlkActionState *common,
102
- Error **errp)
103
+static void external_snapshot_commit(void *opaque);
104
+static void external_snapshot_abort(void *opaque);
105
+static void external_snapshot_clean(void *opaque);
106
+TransactionActionDrv external_snapshot_drv = {
107
+ .commit = external_snapshot_commit,
108
+ .abort = external_snapshot_abort,
109
+ .clean = external_snapshot_clean,
110
+};
111
+
112
+static void external_snapshot_action(BlkActionState *common, Transaction *tran,
113
+ Error **errp)
114
{
115
int ret;
116
int flags = 0;
117
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
118
AioContext *aio_context;
119
uint64_t perm, shared;
120
121
+ tran_add(tran, &external_snapshot_drv, state);
122
+
123
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
124
* purpose but a different set of parameters */
125
switch (action->type) {
126
@@ -XXX,XX +XXX,XX @@ out:
127
aio_context_release(aio_context);
128
}
129
130
-static void external_snapshot_commit(BlkActionState *common)
131
+static void external_snapshot_commit(void *opaque)
132
{
133
- ExternalSnapshotState *state =
134
- DO_UPCAST(ExternalSnapshotState, common, common);
135
+ ExternalSnapshotState *state = opaque;
136
AioContext *aio_context;
137
138
aio_context = bdrv_get_aio_context(state->old_bs);
139
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_commit(BlkActionState *common)
140
aio_context_release(aio_context);
141
}
142
143
-static void external_snapshot_abort(BlkActionState *common)
144
+static void external_snapshot_abort(void *opaque)
145
{
146
- ExternalSnapshotState *state =
147
- DO_UPCAST(ExternalSnapshotState, common, common);
148
+ ExternalSnapshotState *state = opaque;
149
if (state->new_bs) {
150
if (state->overlay_appended) {
151
AioContext *aio_context;
152
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(BlkActionState *common)
153
}
154
}
155
156
-static void external_snapshot_clean(BlkActionState *common)
157
+static void external_snapshot_clean(void *opaque)
158
{
159
- ExternalSnapshotState *state =
160
- DO_UPCAST(ExternalSnapshotState, common, common);
161
+ g_autofree ExternalSnapshotState *state = opaque;
162
AioContext *aio_context;
163
164
if (!state->old_bs) {
165
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_backup_common(BackupCommon *backup,
166
AioContext *aio_context,
167
JobTxn *txn, Error **errp);
168
169
-static void drive_backup_prepare(BlkActionState *common, Error **errp)
170
+static void drive_backup_commit(void *opaque);
171
+static void drive_backup_abort(void *opaque);
172
+static void drive_backup_clean(void *opaque);
173
+TransactionActionDrv drive_backup_drv = {
174
+ .commit = drive_backup_commit,
175
+ .abort = drive_backup_abort,
176
+ .clean = drive_backup_clean,
177
+};
178
+
179
+static void drive_backup_action(BlkActionState *common, Transaction *tran,
180
+ Error **errp)
181
{
182
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
183
DriveBackup *backup;
184
@@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
185
bool set_backing_hd = false;
186
int ret;
187
188
+ tran_add(tran, &drive_backup_drv, state);
189
+
190
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
191
backup = common->action->u.drive_backup.data;
192
193
@@ -XXX,XX +XXX,XX @@ out:
194
aio_context_release(aio_context);
195
}
196
197
-static void drive_backup_commit(BlkActionState *common)
198
+static void drive_backup_commit(void *opaque)
199
{
200
- DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
201
+ DriveBackupState *state = opaque;
202
AioContext *aio_context;
203
204
aio_context = bdrv_get_aio_context(state->bs);
205
@@ -XXX,XX +XXX,XX @@ static void drive_backup_commit(BlkActionState *common)
206
aio_context_release(aio_context);
207
}
208
209
-static void drive_backup_abort(BlkActionState *common)
210
+static void drive_backup_abort(void *opaque)
211
{
212
- DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
213
+ DriveBackupState *state = opaque;
214
215
if (state->job) {
216
job_cancel_sync(&state->job->job, true);
217
}
218
}
219
220
-static void drive_backup_clean(BlkActionState *common)
221
+static void drive_backup_clean(void *opaque)
222
{
223
- DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
224
+ g_autofree DriveBackupState *state = opaque;
225
AioContext *aio_context;
226
227
if (!state->bs) {
228
@@ -XXX,XX +XXX,XX @@ typedef struct BlockdevBackupState {
229
BlockJob *job;
230
} BlockdevBackupState;
231
232
-static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
233
+static void blockdev_backup_commit(void *opaque);
234
+static void blockdev_backup_abort(void *opaque);
235
+static void blockdev_backup_clean(void *opaque);
236
+TransactionActionDrv blockdev_backup_drv = {
237
+ .commit = blockdev_backup_commit,
238
+ .abort = blockdev_backup_abort,
239
+ .clean = blockdev_backup_clean,
240
+};
241
+
242
+static void blockdev_backup_action(BlkActionState *common, Transaction *tran,
243
+ Error **errp)
244
{
245
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
246
BlockdevBackup *backup;
247
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
248
AioContext *old_context;
249
int ret;
250
251
+ tran_add(tran, &blockdev_backup_drv, state);
252
+
253
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
254
backup = common->action->u.blockdev_backup.data;
255
256
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
257
aio_context_release(aio_context);
258
}
259
260
-static void blockdev_backup_commit(BlkActionState *common)
261
+static void blockdev_backup_commit(void *opaque)
262
{
263
- BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
264
+ BlockdevBackupState *state = opaque;
265
AioContext *aio_context;
266
267
aio_context = bdrv_get_aio_context(state->bs);
268
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_commit(BlkActionState *common)
269
aio_context_release(aio_context);
270
}
271
272
-static void blockdev_backup_abort(BlkActionState *common)
273
+static void blockdev_backup_abort(void *opaque)
274
{
275
- BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
276
+ BlockdevBackupState *state = opaque;
277
278
if (state->job) {
279
job_cancel_sync(&state->job->job, true);
280
}
281
}
282
283
-static void blockdev_backup_clean(BlkActionState *common)
284
+static void blockdev_backup_clean(void *opaque)
285
{
286
- BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
287
+ g_autofree BlockdevBackupState *state = opaque;
288
AioContext *aio_context;
289
290
if (!state->bs) {
291
@@ -XXX,XX +XXX,XX @@ typedef struct BlockDirtyBitmapState {
292
bool was_enabled;
293
} BlockDirtyBitmapState;
294
295
-static void block_dirty_bitmap_add_prepare(BlkActionState *common,
296
- Error **errp)
297
+static void block_dirty_bitmap_add_abort(void *opaque);
298
+TransactionActionDrv block_dirty_bitmap_add_drv = {
299
+ .abort = block_dirty_bitmap_add_abort,
300
+ .clean = g_free,
301
+};
302
+
303
+static void block_dirty_bitmap_add_action(BlkActionState *common,
304
+ Transaction *tran, Error **errp)
305
{
306
Error *local_err = NULL;
307
BlockDirtyBitmapAdd *action;
308
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
309
common, common);
310
311
+ tran_add(tran, &block_dirty_bitmap_add_drv, state);
312
+
313
if (action_check_completion_mode(common, errp) < 0) {
314
return;
113
return;
315
}
114
}
316
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common,
115
317
}
116
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
117
bdrv_parent_drained_begin(bs, parent);
118
bdrv_drain_invoke(bs, true, false);
119
bdrv_drain_recurse(bs);
120
+
121
+ if (recursive) {
122
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
123
+ bdrv_do_drained_begin(child->bs, true, child);
124
+ }
125
+ }
318
}
126
}
319
127
320
-static void block_dirty_bitmap_add_abort(BlkActionState *common)
128
void bdrv_drained_begin(BlockDriverState *bs)
321
+static void block_dirty_bitmap_add_abort(void *opaque)
322
{
129
{
323
BlockDirtyBitmapAdd *action;
130
- bdrv_do_drained_begin(bs, NULL);
324
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
131
+ bdrv_do_drained_begin(bs, false, NULL);
325
- common, common);
132
+}
326
+ BlockDirtyBitmapState *state = opaque;
133
+
327
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
328
- action = common->action->u.block_dirty_bitmap_add.data;
135
+{
329
+ action = state->common.action->u.block_dirty_bitmap_add.data;
136
+ bdrv_do_drained_begin(bs, true, NULL);
330
/* Should not be able to fail: IF the bitmap was added via .prepare(),
331
* then the node reference and bitmap name must have been valid.
332
*/
333
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_add_abort(BlkActionState *common)
334
}
335
}
137
}
336
138
337
-static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
338
- Error **errp)
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
339
+static void block_dirty_bitmap_restore(void *opaque);
141
+ BdrvChild *parent)
340
+static void block_dirty_bitmap_free_backup(void *opaque);
341
+TransactionActionDrv block_dirty_bitmap_clear_drv = {
342
+ .abort = block_dirty_bitmap_restore,
343
+ .commit = block_dirty_bitmap_free_backup,
344
+ .clean = g_free,
345
+};
346
+
347
+static void block_dirty_bitmap_clear_action(BlkActionState *common,
348
+ Transaction *tran, Error **errp)
349
{
142
{
350
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
143
+ BdrvChild *child, *next;
351
common, common);
144
int old_quiesce_counter;
352
BlockDirtyBitmap *action;
145
353
146
if (qemu_in_coroutine()) {
354
+ tran_add(tran, &block_dirty_bitmap_clear_drv, state);
147
- bdrv_co_yield_to_drain(bs, false, parent);
355
+
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
356
if (action_check_completion_mode(common, errp) < 0) {
357
return;
149
return;
358
}
150
}
359
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
151
assert(bs->quiesce_counter > 0);
360
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
152
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
153
if (old_quiesce_counter == 1) {
154
aio_enable_external(bdrv_get_aio_context(bs));
155
}
156
+
157
+ if (recursive) {
158
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
159
+ bdrv_do_drained_end(child->bs, true, child);
160
+ }
161
+ }
361
}
162
}
362
163
363
-static void block_dirty_bitmap_restore(BlkActionState *common)
164
void bdrv_drained_end(BlockDriverState *bs)
364
+static void block_dirty_bitmap_restore(void *opaque)
365
{
165
{
366
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
166
- bdrv_do_drained_end(bs, NULL);
367
- common, common);
167
+ bdrv_do_drained_end(bs, false, NULL);
368
+ BlockDirtyBitmapState *state = opaque;
168
+}
369
169
+
370
if (state->backup) {
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
371
bdrv_restore_dirty_bitmap(state->bitmap, state->backup);
171
+{
372
}
172
+ bdrv_do_drained_end(bs, true, NULL);
373
}
173
}
374
174
375
-static void block_dirty_bitmap_free_backup(BlkActionState *common)
175
/*
376
+static void block_dirty_bitmap_free_backup(void *opaque)
377
{
378
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
379
- common, common);
380
+ BlockDirtyBitmapState *state = opaque;
381
382
hbitmap_free(state->backup);
383
}
384
385
-static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
386
- Error **errp)
387
+static void block_dirty_bitmap_enable_abort(void *opaque);
388
+TransactionActionDrv block_dirty_bitmap_enable_drv = {
389
+ .abort = block_dirty_bitmap_enable_abort,
390
+ .clean = g_free,
391
+};
392
+
393
+static void block_dirty_bitmap_enable_action(BlkActionState *common,
394
+ Transaction *tran, Error **errp)
395
{
396
BlockDirtyBitmap *action;
397
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
398
common, common);
399
400
+ tran_add(tran, &block_dirty_bitmap_enable_drv, state);
401
+
402
if (action_check_completion_mode(common, errp) < 0) {
403
return;
404
}
405
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
406
bdrv_enable_dirty_bitmap(state->bitmap);
407
}
408
409
-static void block_dirty_bitmap_enable_abort(BlkActionState *common)
410
+static void block_dirty_bitmap_enable_abort(void *opaque)
411
{
412
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
413
- common, common);
414
+ BlockDirtyBitmapState *state = opaque;
415
416
if (!state->was_enabled) {
417
bdrv_disable_dirty_bitmap(state->bitmap);
418
}
419
}
420
421
-static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
422
- Error **errp)
423
+static void block_dirty_bitmap_disable_abort(void *opaque);
424
+TransactionActionDrv block_dirty_bitmap_disable_drv = {
425
+ .abort = block_dirty_bitmap_disable_abort,
426
+ .clean = g_free,
427
+};
428
+
429
+static void block_dirty_bitmap_disable_action(BlkActionState *common,
430
+ Transaction *tran, Error **errp)
431
{
432
BlockDirtyBitmap *action;
433
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
434
common, common);
435
436
+ tran_add(tran, &block_dirty_bitmap_disable_drv, state);
437
+
438
if (action_check_completion_mode(common, errp) < 0) {
439
return;
440
}
441
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
442
bdrv_disable_dirty_bitmap(state->bitmap);
443
}
444
445
-static void block_dirty_bitmap_disable_abort(BlkActionState *common)
446
+static void block_dirty_bitmap_disable_abort(void *opaque)
447
{
448
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
449
- common, common);
450
+ BlockDirtyBitmapState *state = opaque;
451
452
if (state->was_enabled) {
453
bdrv_enable_dirty_bitmap(state->bitmap);
454
}
455
}
456
457
-static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
458
- Error **errp)
459
+TransactionActionDrv block_dirty_bitmap_merge_drv = {
460
+ .commit = block_dirty_bitmap_free_backup,
461
+ .abort = block_dirty_bitmap_restore,
462
+ .clean = g_free,
463
+};
464
+
465
+static void block_dirty_bitmap_merge_action(BlkActionState *common,
466
+ Transaction *tran, Error **errp)
467
{
468
BlockDirtyBitmapMerge *action;
469
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
470
common, common);
471
472
+ tran_add(tran, &block_dirty_bitmap_merge_drv, state);
473
+
474
if (action_check_completion_mode(common, errp) < 0) {
475
return;
476
}
477
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
478
errp);
479
}
480
481
-static void block_dirty_bitmap_remove_prepare(BlkActionState *common,
482
- Error **errp)
483
+static void block_dirty_bitmap_remove_commit(void *opaque);
484
+static void block_dirty_bitmap_remove_abort(void *opaque);
485
+TransactionActionDrv block_dirty_bitmap_remove_drv = {
486
+ .commit = block_dirty_bitmap_remove_commit,
487
+ .abort = block_dirty_bitmap_remove_abort,
488
+ .clean = g_free,
489
+};
490
+
491
+static void block_dirty_bitmap_remove_action(BlkActionState *common,
492
+ Transaction *tran, Error **errp)
493
{
494
BlockDirtyBitmap *action;
495
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
496
common, common);
497
498
+ tran_add(tran, &block_dirty_bitmap_remove_drv, state);
499
+
500
if (action_check_completion_mode(common, errp) < 0) {
501
return;
502
}
503
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_remove_prepare(BlkActionState *common,
504
}
505
}
506
507
-static void block_dirty_bitmap_remove_abort(BlkActionState *common)
508
+static void block_dirty_bitmap_remove_abort(void *opaque)
509
{
510
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
511
- common, common);
512
+ BlockDirtyBitmapState *state = opaque;
513
514
if (state->bitmap) {
515
bdrv_dirty_bitmap_skip_store(state->bitmap, false);
516
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_remove_abort(BlkActionState *common)
517
}
518
}
519
520
-static void block_dirty_bitmap_remove_commit(BlkActionState *common)
521
+static void block_dirty_bitmap_remove_commit(void *opaque)
522
{
523
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
524
- common, common);
525
+ BlockDirtyBitmapState *state = opaque;
526
527
bdrv_dirty_bitmap_set_busy(state->bitmap, false);
528
bdrv_release_dirty_bitmap(state->bitmap);
529
}
530
531
-static void abort_prepare(BlkActionState *common, Error **errp)
532
+static void abort_commit(void *opaque);
533
+TransactionActionDrv abort_drv = {
534
+ .commit = abort_commit,
535
+ .clean = g_free,
536
+};
537
+
538
+static void abort_action(BlkActionState *common, Transaction *tran,
539
+ Error **errp)
540
{
541
+ tran_add(tran, &abort_drv, common);
542
error_setg(errp, "Transaction aborted using Abort action");
543
}
544
545
-static void abort_commit(BlkActionState *common)
546
+static void abort_commit(void *opaque)
547
{
548
g_assert_not_reached(); /* this action never succeeds */
549
}
550
@@ -XXX,XX +XXX,XX @@ static void abort_commit(BlkActionState *common)
551
static const BlkActionOps actions[] = {
552
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = {
553
.instance_size = sizeof(ExternalSnapshotState),
554
- .prepare = external_snapshot_prepare,
555
- .commit = external_snapshot_commit,
556
- .abort = external_snapshot_abort,
557
- .clean = external_snapshot_clean,
558
+ .action = external_snapshot_action,
559
},
560
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
561
.instance_size = sizeof(ExternalSnapshotState),
562
- .prepare = external_snapshot_prepare,
563
- .commit = external_snapshot_commit,
564
- .abort = external_snapshot_abort,
565
- .clean = external_snapshot_clean,
566
+ .action = external_snapshot_action,
567
},
568
[TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
569
.instance_size = sizeof(DriveBackupState),
570
- .prepare = drive_backup_prepare,
571
- .commit = drive_backup_commit,
572
- .abort = drive_backup_abort,
573
- .clean = drive_backup_clean,
574
+ .action = drive_backup_action,
575
},
576
[TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
577
.instance_size = sizeof(BlockdevBackupState),
578
- .prepare = blockdev_backup_prepare,
579
- .commit = blockdev_backup_commit,
580
- .abort = blockdev_backup_abort,
581
- .clean = blockdev_backup_clean,
582
+ .action = blockdev_backup_action,
583
},
584
[TRANSACTION_ACTION_KIND_ABORT] = {
585
.instance_size = sizeof(BlkActionState),
586
- .prepare = abort_prepare,
587
- .commit = abort_commit,
588
+ .action = abort_action,
589
},
590
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
591
.instance_size = sizeof(InternalSnapshotState),
592
- .prepare = internal_snapshot_prepare,
593
- .abort = internal_snapshot_abort,
594
- .clean = internal_snapshot_clean,
595
+ .action = internal_snapshot_action,
596
},
597
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD] = {
598
.instance_size = sizeof(BlockDirtyBitmapState),
599
- .prepare = block_dirty_bitmap_add_prepare,
600
- .abort = block_dirty_bitmap_add_abort,
601
+ .action = block_dirty_bitmap_add_action,
602
},
603
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = {
604
.instance_size = sizeof(BlockDirtyBitmapState),
605
- .prepare = block_dirty_bitmap_clear_prepare,
606
- .commit = block_dirty_bitmap_free_backup,
607
- .abort = block_dirty_bitmap_restore,
608
+ .action = block_dirty_bitmap_clear_action,
609
},
610
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = {
611
.instance_size = sizeof(BlockDirtyBitmapState),
612
- .prepare = block_dirty_bitmap_enable_prepare,
613
- .abort = block_dirty_bitmap_enable_abort,
614
+ .action = block_dirty_bitmap_enable_action,
615
},
616
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = {
617
.instance_size = sizeof(BlockDirtyBitmapState),
618
- .prepare = block_dirty_bitmap_disable_prepare,
619
- .abort = block_dirty_bitmap_disable_abort,
620
+ .action = block_dirty_bitmap_disable_action,
621
},
622
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE] = {
623
.instance_size = sizeof(BlockDirtyBitmapState),
624
- .prepare = block_dirty_bitmap_merge_prepare,
625
- .commit = block_dirty_bitmap_free_backup,
626
- .abort = block_dirty_bitmap_restore,
627
+ .action = block_dirty_bitmap_merge_action,
628
},
629
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE] = {
630
.instance_size = sizeof(BlockDirtyBitmapState),
631
- .prepare = block_dirty_bitmap_remove_prepare,
632
- .commit = block_dirty_bitmap_remove_commit,
633
- .abort = block_dirty_bitmap_remove_abort,
634
+ .action = block_dirty_bitmap_remove_action,
635
},
636
/* Where are transactions for MIRROR, COMMIT and STREAM?
637
* Although these blockjobs use transaction callbacks like the backup job,
638
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
639
TransactionActionList *dev_entry = dev_list;
640
bool has_props = !!props;
641
JobTxn *block_job_txn = NULL;
642
- BlkActionState *state, *next;
643
Error *local_err = NULL;
644
+ Transaction *tran = tran_new();
645
646
GLOBAL_STATE_CODE();
647
648
- QTAILQ_HEAD(, BlkActionState) snap_bdrv_states;
649
- QTAILQ_INIT(&snap_bdrv_states);
650
-
651
/* Does this transaction get canceled as a group on failure?
652
* If not, we don't really need to make a JobTxn.
653
*/
654
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
655
while (NULL != dev_entry) {
656
TransactionAction *dev_info = NULL;
657
const BlkActionOps *ops;
658
+ BlkActionState *state;
659
660
dev_info = dev_entry->value;
661
dev_entry = dev_entry->next;
662
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
663
state->action = dev_info;
664
state->block_job_txn = block_job_txn;
665
state->txn_props = props;
666
- QTAILQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
667
668
- state->ops->prepare(state, &local_err);
669
+ state->ops->action(state, tran, &local_err);
670
if (local_err) {
671
error_propagate(errp, local_err);
672
goto delete_and_fail;
673
}
674
}
675
676
- QTAILQ_FOREACH(state, &snap_bdrv_states, entry) {
677
- if (state->ops->commit) {
678
- state->ops->commit(state);
679
- }
680
- }
681
+ tran_commit(tran);
682
683
/* success */
684
goto exit;
685
686
delete_and_fail:
687
/* failure, and it is all-or-none; roll back all operations */
688
- QTAILQ_FOREACH_REVERSE(state, &snap_bdrv_states, entry) {
689
- if (state->ops->abort) {
690
- state->ops->abort(state);
691
- }
692
- }
693
+ tran_abort(tran);
694
exit:
695
- QTAILQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) {
696
- if (state->ops->clean) {
697
- state->ops->clean(state);
698
- }
699
- g_free(state);
700
- }
701
if (!has_props) {
702
qapi_free_TransactionProperties(props);
703
}
704
--
176
--
705
2.40.1
177
2.13.6
178
179
diff view generated by jsdifflib
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
Add a subtree drain version to the existing test cases.
2
2
3
QEMU's event loop supports nesting, which means that event handler
4
functions may themselves call aio_poll(). The condition that triggered a
5
handler must be reset before the nested aio_poll() call, otherwise the
6
same handler will be called and immediately re-enter aio_poll. This
7
leads to an infinite loop and stack exhaustion.
8
9
Poll handlers are especially prone to this issue, because they typically
10
reset their condition by finishing the processing of pending work.
11
Unfortunately it is during the processing of pending work that nested
12
aio_poll() calls typically occur and the condition has not yet been
13
reset.
14
15
Disable a poll handler during ->io_poll_ready() so that a nested
16
aio_poll() call cannot invoke ->io_poll_ready() again. As a result, the
17
disabled poll handler and its associated fd handler do not run during
18
the nested aio_poll(). Calling aio_set_fd_handler() from inside nested
19
aio_poll() could cause it to run again. If the fd handler is pending
20
inside nested aio_poll(), then it will also run again.
21
22
In theory fd handlers can be affected by the same issue, but they are
23
more likely to reset the condition before calling nested aio_poll().
24
25
This is a special case and it's somewhat complex, but I don't see a way
26
around it as long as nested aio_poll() is supported.
27
28
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2186181
29
Fixes: c38270692593 ("block: Mark bdrv_co_io_(un)plug() and callers GRAPH_RDLOCK")
30
Cc: Kevin Wolf <kwolf@redhat.com>
31
Cc: Emanuele Giuseppe Esposito <eesposit@redhat.com>
32
Cc: Paolo Bonzini <pbonzini@redhat.com>
33
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
34
Message-Id: <20230502184134.534703-2-stefanha@redhat.com>
35
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
36
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
37
---
4
---
38
util/aio-posix.c | 11 +++++++++++
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
39
1 file changed, 11 insertions(+)
6
1 file changed, 26 insertions(+), 1 deletion(-)
40
7
41
diff --git a/util/aio-posix.c b/util/aio-posix.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
42
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
43
--- a/util/aio-posix.c
10
--- a/tests/test-bdrv-drain.c
44
+++ b/util/aio-posix.c
11
+++ b/tests/test-bdrv-drain.c
45
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node)
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
46
poll_ready && revents == 0 &&
13
enum drain_type {
47
aio_node_check(ctx, node->is_external) &&
14
BDRV_DRAIN_ALL,
48
node->io_poll_ready) {
15
BDRV_DRAIN,
49
+ /*
16
+ BDRV_SUBTREE_DRAIN,
50
+ * Remove temporarily to avoid infinite loops when ->io_poll_ready()
17
DRAIN_TYPE_MAX,
51
+ * calls aio_poll() before clearing the condition that made the poll
18
};
52
+ * handler become ready.
19
53
+ */
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
54
+ QLIST_SAFE_REMOVE(node, node_poll);
21
switch (drain_type) {
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
25
default: g_assert_not_reached();
26
}
27
}
28
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
29
switch (drain_type) {
30
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
31
case BDRV_DRAIN: bdrv_drained_end(bs); break;
32
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
33
default: g_assert_not_reached();
34
}
35
}
36
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
37
test_drv_cb_common(BDRV_DRAIN, false);
38
}
39
40
+static void test_drv_cb_drain_subtree(void)
41
+{
42
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
43
+}
55
+
44
+
56
node->io_poll_ready(node->opaque);
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
57
46
{
58
+ if (!QLIST_IS_INSERTED(node, node_poll)) {
47
BlockBackend *blk;
59
+ QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll);
48
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
60
+ }
49
test_quiesce_common(BDRV_DRAIN, false);
50
}
51
52
+static void test_quiesce_drain_subtree(void)
53
+{
54
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
55
+}
61
+
56
+
62
/*
57
static void test_nested(void)
63
* Return early since revents was zero. aio_notify() does not count as
58
{
64
* progress.
59
BlockBackend *blk;
60
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
61
/* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
62
int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
63
(inner != BDRV_DRAIN_ALL);
64
- int backing_quiesce = 0;
65
+ int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) +
66
+ (inner == BDRV_SUBTREE_DRAIN);
67
int backing_cb_cnt = (outer != BDRV_DRAIN) +
68
(inner != BDRV_DRAIN);
69
70
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_drain(void)
71
test_blockjob_common(BDRV_DRAIN);
72
}
73
74
+static void test_blockjob_drain_subtree(void)
75
+{
76
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
77
+}
78
+
79
int main(int argc, char **argv)
80
{
81
bdrv_init();
82
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
83
84
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
85
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
86
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
87
+ test_drv_cb_drain_subtree);
88
89
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
90
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
91
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
92
+ test_quiesce_drain_subtree);
93
94
g_test_add_func("/bdrv-drain/nested", test_nested);
95
96
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
97
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
98
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
99
+ test_blockjob_drain_subtree);
100
101
return g_test_run();
102
}
65
--
103
--
66
2.40.1
104
2.13.6
105
106
diff view generated by jsdifflib
1
There are some error paths in blk_exp_add() that jump to 'fail:' before
1
If bdrv_do_drained_begin/end() are called in coroutine context, they
2
'exp' is even created. So we can't just unconditionally access exp->blk.
2
first use a BH to get out of the coroutine context. Call some existing
3
tests again from a coroutine to cover this code path.
3
4
4
Add a NULL check, and switch from exp->blk to blk, which is available
5
earlier, just to be extra sure that we really cover all cases where
6
BlockDevOps could have been set for it (in practice, this only happens
7
in drv->create() today, so this part of the change isn't strictly
8
necessary).
9
10
Fixes: Coverity CID 1509238
11
Fixes: de79b52604e43fdeba6cee4f5af600b62169f2d2
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20230510203601.418015-3-kwolf@redhat.com>
14
Reviewed-by: Eric Blake <eblake@redhat.com>
15
Tested-by: Eric Blake <eblake@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
6
---
18
block/export/export.c | 6 ++++--
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
19
1 file changed, 4 insertions(+), 2 deletions(-)
8
1 file changed, 59 insertions(+)
20
9
21
diff --git a/block/export/export.c b/block/export/export.c
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
22
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
23
--- a/block/export/export.c
12
--- a/tests/test-bdrv-drain.c
24
+++ b/block/export/export.c
13
+++ b/tests/test-bdrv-drain.c
25
@@ -XXX,XX +XXX,XX @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
26
return exp;
15
*aio_ret = ret;
27
16
}
28
fail:
17
29
- blk_set_dev_ops(exp->blk, NULL, NULL);
18
+typedef struct CallInCoroutineData {
30
- blk_unref(blk);
19
+ void (*entry)(void);
31
+ if (blk) {
20
+ bool done;
32
+ blk_set_dev_ops(blk, NULL, NULL);
21
+} CallInCoroutineData;
33
+ blk_unref(blk);
22
+
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
24
+{
25
+ CallInCoroutineData *data = opaque;
26
+
27
+ data->entry();
28
+ data->done = true;
29
+}
30
+
31
+static void call_in_coroutine(void (*entry)(void))
32
+{
33
+ Coroutine *co;
34
+ CallInCoroutineData data = {
35
+ .entry = entry,
36
+ .done = false,
37
+ };
38
+
39
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
40
+ qemu_coroutine_enter(co);
41
+ while (!data.done) {
42
+ aio_poll(qemu_get_aio_context(), true);
34
+ }
43
+ }
35
aio_context_release(ctx);
44
+}
36
if (exp) {
45
+
37
g_free(exp->id);
46
enum drain_type {
47
BDRV_DRAIN_ALL,
48
BDRV_DRAIN,
49
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_subtree(void)
50
test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
51
}
52
53
+static void test_drv_cb_co_drain(void)
54
+{
55
+ call_in_coroutine(test_drv_cb_drain);
56
+}
57
+
58
+static void test_drv_cb_co_drain_subtree(void)
59
+{
60
+ call_in_coroutine(test_drv_cb_drain_subtree);
61
+}
62
+
63
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
64
{
65
BlockBackend *blk;
66
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void)
67
test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
68
}
69
70
+static void test_quiesce_co_drain(void)
71
+{
72
+ call_in_coroutine(test_quiesce_drain);
73
+}
74
+
75
+static void test_quiesce_co_drain_subtree(void)
76
+{
77
+ call_in_coroutine(test_quiesce_drain_subtree);
78
+}
79
+
80
static void test_nested(void)
81
{
82
BlockBackend *blk;
83
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
84
g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
85
test_drv_cb_drain_subtree);
86
87
+ // XXX bdrv_drain_all() doesn't work in coroutine context
88
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
89
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
90
+ test_drv_cb_co_drain_subtree);
91
+
92
+
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
94
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
95
g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
96
test_quiesce_drain_subtree);
97
98
+ // XXX bdrv_drain_all() doesn't work in coroutine context
99
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
100
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
101
+ test_quiesce_co_drain_subtree);
102
+
103
g_test_add_func("/bdrv-drain/nested", test_nested);
104
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
38
--
106
--
39
2.40.1
107
2.13.6
108
109
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
1
Test that drain sections are correctly propagated through the graph.
2
2
3
Other bitmap related actions use the .bitmap pointer in .abort action,
4
let's do same here:
5
6
1. It helps further refactoring, as bitmap-add is the only bitmap
7
action that uses state.action in .abort
8
9
2. It must be safe: transaction actions rely on the fact that on
10
.abort() the state is the same as at the end of .prepare(), so that
11
in .abort() we could precisely rollback the changes done by
12
.prepare().
13
The only way to remove the bitmap during transaction should be
14
block-dirty-bitmap-remove action, but it postpones actual removal to
15
.commit(), so we are OK on any rollback path. (Note also that
16
bitmap-remove is the only bitmap action that has .commit() phase,
17
except for simple g_free the state on .clean())
18
19
3. Again, other bitmap actions behave this way: keep the bitmap pointer
20
during the transaction.
21
22
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
23
Message-Id: <20230510150624.310640-6-vsementsov@yandex-team.ru>
24
[kwolf: Also remove the now unused BlockDirtyBitmapState.prepared]
25
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
27
---
4
---
28
blockdev.c | 13 ++++---------
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
29
1 file changed, 4 insertions(+), 9 deletions(-)
6
1 file changed, 74 insertions(+)
30
7
31
diff --git a/blockdev.c b/blockdev.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
32
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
33
--- a/blockdev.c
10
--- a/tests/test-bdrv-drain.c
34
+++ b/blockdev.c
11
+++ b/tests/test-bdrv-drain.c
35
@@ -XXX,XX +XXX,XX @@ typedef struct BlockDirtyBitmapState {
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
36
BdrvDirtyBitmap *bitmap;
13
blk_unref(blk);
37
BlockDriverState *bs;
38
HBitmap *backup;
39
- bool prepared;
40
bool was_enabled;
41
} BlockDirtyBitmapState;
42
43
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_add_action(BlkActionState *common,
44
&local_err);
45
46
if (!local_err) {
47
- state->prepared = true;
48
+ state->bitmap = block_dirty_bitmap_lookup(action->node, action->name,
49
+ NULL, &error_abort);
50
} else {
51
error_propagate(errp, local_err);
52
}
53
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_add_action(BlkActionState *common,
54
55
static void block_dirty_bitmap_add_abort(void *opaque)
56
{
57
- BlockDirtyBitmapAdd *action;
58
BlockDirtyBitmapState *state = opaque;
59
60
- action = state->common.action->u.block_dirty_bitmap_add.data;
61
- /* Should not be able to fail: IF the bitmap was added via .prepare(),
62
- * then the node reference and bitmap name must have been valid.
63
- */
64
- if (state->prepared) {
65
- qmp_block_dirty_bitmap_remove(action->node, action->name, &error_abort);
66
+ if (state->bitmap) {
67
+ bdrv_release_dirty_bitmap(state->bitmap);
68
}
69
}
14
}
70
15
16
+static void test_multiparent(void)
17
+{
18
+ BlockBackend *blk_a, *blk_b;
19
+ BlockDriverState *bs_a, *bs_b, *backing;
20
+ BDRVTestState *a_s, *b_s, *backing_s;
21
+
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
23
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
24
+ &error_abort);
25
+ a_s = bs_a->opaque;
26
+ blk_insert_bs(blk_a, bs_a, &error_abort);
27
+
28
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
29
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
30
+ &error_abort);
31
+ b_s = bs_b->opaque;
32
+ blk_insert_bs(blk_b, bs_b, &error_abort);
33
+
34
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
35
+ backing_s = backing->opaque;
36
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
37
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
38
+
39
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
40
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
41
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(a_s->drain_count, ==, 0);
43
+ g_assert_cmpint(b_s->drain_count, ==, 0);
44
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
45
+
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
47
+
48
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
49
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
50
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
51
+ g_assert_cmpint(a_s->drain_count, ==, 1);
52
+ g_assert_cmpint(b_s->drain_count, ==, 1);
53
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
54
+
55
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
56
+
57
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
58
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
59
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(a_s->drain_count, ==, 2);
61
+ g_assert_cmpint(b_s->drain_count, ==, 2);
62
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
63
+
64
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
65
+
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
69
+ g_assert_cmpint(a_s->drain_count, ==, 1);
70
+ g_assert_cmpint(b_s->drain_count, ==, 1);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
74
+
75
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
76
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
77
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
78
+ g_assert_cmpint(a_s->drain_count, ==, 0);
79
+ g_assert_cmpint(b_s->drain_count, ==, 0);
80
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
81
+
82
+ bdrv_unref(backing);
83
+ bdrv_unref(bs_a);
84
+ bdrv_unref(bs_b);
85
+ blk_unref(blk_a);
86
+ blk_unref(blk_b);
87
+}
88
+
89
90
typedef struct TestBlockJob {
91
BlockJob common;
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
93
test_quiesce_co_drain_subtree);
94
95
g_test_add_func("/bdrv-drain/nested", test_nested);
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
97
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
71
--
100
--
72
2.40.1
101
2.13.6
102
103
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
1
We need to remember how many of the drain sections in which a node is
2
2
were recursive (i.e. subtree drain rather than node drain), so that they
3
Only backup supports GROUPED mode. Make this logic more clear. And
3
can be correctly applied when children are added or removed during the
4
avoid passing extra thing to each action.
4
drained section.
5
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
6
With this change, it is safe to modify the graph even inside a
7
Message-Id: <20230510150624.310640-5-vsementsov@yandex-team.ru>
7
bdrv_subtree_drained_begin/end() section.
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
10
---
11
blockdev.c | 96 +++++++++++++-----------------------------------------
11
include/block/block.h | 2 --
12
1 file changed, 22 insertions(+), 74 deletions(-)
12
include/block/block_int.h | 5 +++++
13
13
block.c | 32 +++++++++++++++++++++++++++++---
14
diff --git a/blockdev.c b/blockdev.c
14
block/io.c | 28 ++++++++++++++++++++++++----
15
index XXXXXXX..XXXXXXX 100644
15
4 files changed, 58 insertions(+), 9 deletions(-)
16
--- a/blockdev.c
16
17
+++ b/blockdev.c
17
diff --git a/include/block/block.h b/include/block/block.h
18
@@ -XXX,XX +XXX,XX @@ struct BlkActionState {
18
index XXXXXXX..XXXXXXX 100644
19
TransactionAction *action;
19
--- a/include/block/block.h
20
const BlkActionOps *ops;
20
+++ b/include/block/block.h
21
JobTxn *block_job_txn;
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
22
- TransactionProperties *txn_props;
22
/**
23
QTAILQ_ENTRY(BlkActionState) entry;
23
* Like bdrv_drained_begin, but recursively begins a quiesced section for
24
* exclusive access to all child nodes as well.
25
- *
26
- * Graph changes are not allowed during a subtree drain section.
27
*/
28
void bdrv_subtree_drained_begin(BlockDriverState *bs);
29
30
diff --git a/include/block/block_int.h b/include/block/block_int.h
31
index XXXXXXX..XXXXXXX 100644
32
--- a/include/block/block_int.h
33
+++ b/include/block/block_int.h
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
35
36
/* Accessed with atomic ops. */
37
int quiesce_counter;
38
+ int recursive_quiesce_counter;
39
+
40
unsigned int write_gen; /* Current data generation */
41
42
/* Protected by reqs_lock. */
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
44
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
45
BdrvRequestFlags flags);
46
47
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
48
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
49
+
50
int get_tmp_filename(char *filename, int size);
51
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
52
const char *filename);
53
diff --git a/block.c b/block.c
54
index XXXXXXX..XXXXXXX 100644
55
--- a/block.c
56
+++ b/block.c
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
58
bdrv_drained_end(bs);
59
}
60
61
+static void bdrv_child_cb_attach(BdrvChild *child)
62
+{
63
+ BlockDriverState *bs = child->opaque;
64
+ bdrv_apply_subtree_drain(child, bs);
65
+}
66
+
67
+static void bdrv_child_cb_detach(BdrvChild *child)
68
+{
69
+ BlockDriverState *bs = child->opaque;
70
+ bdrv_unapply_subtree_drain(child, bs);
71
+}
72
+
73
static int bdrv_child_cb_inactivate(BdrvChild *child)
74
{
75
BlockDriverState *bs = child->opaque;
76
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_file = {
77
.inherit_options = bdrv_inherited_options,
78
.drained_begin = bdrv_child_cb_drained_begin,
79
.drained_end = bdrv_child_cb_drained_end,
80
+ .attach = bdrv_child_cb_attach,
81
+ .detach = bdrv_child_cb_detach,
82
.inactivate = bdrv_child_cb_inactivate,
24
};
83
};
25
84
26
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv internal_snapshot_drv = {
85
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = {
27
.clean = internal_snapshot_clean,
86
.inherit_options = bdrv_inherited_fmt_options,
87
.drained_begin = bdrv_child_cb_drained_begin,
88
.drained_end = bdrv_child_cb_drained_end,
89
+ .attach = bdrv_child_cb_attach,
90
+ .detach = bdrv_child_cb_detach,
91
.inactivate = bdrv_child_cb_inactivate,
28
};
92
};
29
93
30
-static int action_check_completion_mode(BlkActionState *s, Error **errp)
94
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_attach(BdrvChild *c)
31
-{
95
parent->backing_blocker);
32
- if (s->txn_props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
96
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
33
- error_setg(errp,
97
parent->backing_blocker);
34
- "Action '%s' does not support Transaction property "
98
+
35
- "completion-mode = %s",
99
+ bdrv_child_cb_attach(c);
36
- TransactionActionKind_str(s->action->type),
100
}
37
- ActionCompletionMode_str(s->txn_props->completion_mode));
101
38
- return -1;
102
static void bdrv_backing_detach(BdrvChild *c)
39
- }
103
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_detach(BdrvChild *c)
40
- return 0;
104
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
41
-}
105
error_free(parent->backing_blocker);
42
-
106
parent->backing_blocker = NULL;
43
static void internal_snapshot_action(BlkActionState *common,
107
+
44
Transaction *tran, Error **errp)
108
+ bdrv_child_cb_detach(c);
109
}
110
111
/*
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
114
}
115
if (old_bs) {
116
+ /* Detach first so that the recursive drain sections coming from @child
117
+ * are already gone and we only end the drain sections that came from
118
+ * elsewhere. */
119
+ if (child->role->detach) {
120
+ child->role->detach(child);
121
+ }
122
if (old_bs->quiesce_counter && child->role->drained_end) {
123
for (i = 0; i < old_bs->quiesce_counter; i++) {
124
child->role->drained_end(child);
125
}
126
}
127
- if (child->role->detach) {
128
- child->role->detach(child);
129
- }
130
QLIST_REMOVE(child, next_parent);
131
}
132
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
134
}
135
}
136
137
+ /* Attach only after starting new drained sections, so that recursive
138
+ * drain sections coming from @child don't get an extra .drained_begin
139
+ * callback. */
140
if (child->role->attach) {
141
child->role->attach(child);
142
}
143
diff --git a/block/io.c b/block/io.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/io.c
146
+++ b/block/io.c
147
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
148
assert(data.done);
149
}
150
151
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
152
- BdrvChild *parent)
153
+void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
154
+ BdrvChild *parent)
45
{
155
{
46
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_action(BlkActionState *common,
156
BdrvChild *child, *next;
47
157
48
tran_add(tran, &internal_snapshot_drv, state);
158
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
49
159
bdrv_drain_recurse(bs);
50
- /* 1. parse input */
160
51
device = internal->device;
161
if (recursive) {
52
name = internal->name;
162
+ bs->recursive_quiesce_counter++;
53
163
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
54
- /* 2. check for validation */
164
bdrv_do_drained_begin(child->bs, true, child);
55
- if (action_check_completion_mode(common, errp) < 0) {
165
}
56
- return;
166
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
57
- }
167
bdrv_do_drained_begin(bs, true, NULL);
58
-
168
}
59
bs = qmp_get_root_bs(device, errp);
169
60
if (!bs) {
170
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
61
return;
171
- BdrvChild *parent)
62
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(BlkActionState *common, Transaction *tran,
172
+void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
173
+ BdrvChild *parent)
174
{
175
BdrvChild *child, *next;
176
int old_quiesce_counter;
177
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
63
}
178
}
64
179
65
/* start processing */
180
if (recursive) {
66
- if (action_check_completion_mode(common, errp) < 0) {
181
+ bs->recursive_quiesce_counter--;
67
- return;
182
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
68
- }
183
bdrv_do_drained_end(child->bs, true, child);
69
184
}
70
state->old_bs = bdrv_lookup_bs(device, node_name, errp);
185
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs)
71
if (!state->old_bs) {
186
bdrv_do_drained_end(bs, true, NULL);
72
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_add_action(BlkActionState *common,
187
}
73
188
74
tran_add(tran, &block_dirty_bitmap_add_drv, state);
189
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
75
190
+{
76
- if (action_check_completion_mode(common, errp) < 0) {
191
+ int i;
77
- return;
192
+
78
- }
193
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
79
-
194
+ bdrv_do_drained_begin(child->bs, true, child);
80
action = common->action->u.block_dirty_bitmap_add.data;
195
+ }
81
/* AIO context taken and released within qmp_block_dirty_bitmap_add */
196
+}
82
qmp_block_dirty_bitmap_add(action->node, action->name,
197
+
83
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_clear_action(BlkActionState *common,
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
84
199
+{
85
tran_add(tran, &block_dirty_bitmap_clear_drv, state);
200
+ int i;
86
201
+
87
- if (action_check_completion_mode(common, errp) < 0) {
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
88
- return;
203
+ bdrv_do_drained_end(child->bs, true, child);
89
- }
204
+ }
90
-
205
+}
91
action = common->action->u.block_dirty_bitmap_clear.data;
206
+
92
state->bitmap = block_dirty_bitmap_lookup(action->node,
93
action->name,
94
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_enable_action(BlkActionState *common,
95
96
tran_add(tran, &block_dirty_bitmap_enable_drv, state);
97
98
- if (action_check_completion_mode(common, errp) < 0) {
99
- return;
100
- }
101
-
102
action = common->action->u.block_dirty_bitmap_enable.data;
103
state->bitmap = block_dirty_bitmap_lookup(action->node,
104
action->name,
105
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_disable_action(BlkActionState *common,
106
107
tran_add(tran, &block_dirty_bitmap_disable_drv, state);
108
109
- if (action_check_completion_mode(common, errp) < 0) {
110
- return;
111
- }
112
-
113
action = common->action->u.block_dirty_bitmap_disable.data;
114
state->bitmap = block_dirty_bitmap_lookup(action->node,
115
action->name,
116
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_merge_action(BlkActionState *common,
117
118
tran_add(tran, &block_dirty_bitmap_merge_drv, state);
119
120
- if (action_check_completion_mode(common, errp) < 0) {
121
- return;
122
- }
123
-
124
action = common->action->u.block_dirty_bitmap_merge.data;
125
126
state->bitmap = block_dirty_bitmap_merge(action->node, action->target,
127
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_remove_action(BlkActionState *common,
128
129
tran_add(tran, &block_dirty_bitmap_remove_drv, state);
130
131
- if (action_check_completion_mode(common, errp) < 0) {
132
- return;
133
- }
134
-
135
action = common->action->u.block_dirty_bitmap_remove.data;
136
137
state->bitmap = block_dirty_bitmap_remove(action->node, action->name,
138
@@ -XXX,XX +XXX,XX @@ static const BlkActionOps actions_map[] = {
139
*/
140
};
141
142
-/**
143
- * Allocate a TransactionProperties structure if necessary, and fill
144
- * that structure with desired defaults if they are unset.
145
- */
146
-static TransactionProperties *get_transaction_properties(
147
- TransactionProperties *props)
148
-{
149
- if (!props) {
150
- props = g_new0(TransactionProperties, 1);
151
- }
152
-
153
- if (!props->has_completion_mode) {
154
- props->has_completion_mode = true;
155
- props->completion_mode = ACTION_COMPLETION_MODE_INDIVIDUAL;
156
- }
157
-
158
- return props;
159
-}
160
-
161
/*
207
/*
162
* 'Atomic' group operations. The operations are performed as a set, and if
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
163
* any fail then we roll back all operations in the group.
209
* and suspend block driver's internal I/O until next request arrives.
164
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *actions,
165
Error **errp)
166
{
167
TransactionActionList *act;
168
- bool has_properties = !!properties;
169
JobTxn *block_job_txn = NULL;
170
Error *local_err = NULL;
171
- Transaction *tran = tran_new();
172
+ Transaction *tran;
173
+ ActionCompletionMode comp_mode =
174
+ properties ? properties->completion_mode :
175
+ ACTION_COMPLETION_MODE_INDIVIDUAL;
176
177
GLOBAL_STATE_CODE();
178
179
/* Does this transaction get canceled as a group on failure?
180
* If not, we don't really need to make a JobTxn.
181
*/
182
- properties = get_transaction_properties(properties);
183
- if (properties->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
184
+ if (comp_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
185
+ for (act = actions; act; act = act->next) {
186
+ TransactionActionKind type = act->value->type;
187
+
188
+ if (type != TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP &&
189
+ type != TRANSACTION_ACTION_KIND_DRIVE_BACKUP)
190
+ {
191
+ error_setg(errp,
192
+ "Action '%s' does not support transaction property "
193
+ "completion-mode = %s",
194
+ TransactionActionKind_str(type),
195
+ ActionCompletionMode_str(comp_mode));
196
+ return;
197
+ }
198
+ }
199
+
200
block_job_txn = job_txn_new();
201
}
202
203
/* drain all i/o before any operations */
204
bdrv_drain_all();
205
206
+ tran = tran_new();
207
+
208
/* We don't do anything in this loop that commits us to the operations */
209
for (act = actions; act; act = act->next) {
210
TransactionAction *dev_info = act->value;
211
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *actions,
212
state->ops = ops;
213
state->action = dev_info;
214
state->block_job_txn = block_job_txn;
215
- state->txn_props = properties;
216
217
state->ops->action(state, tran, &local_err);
218
if (local_err) {
219
@@ -XXX,XX +XXX,XX @@ delete_and_fail:
220
/* failure, and it is all-or-none; roll back all operations */
221
tran_abort(tran);
222
exit:
223
- if (!has_properties) {
224
- qapi_free_TransactionProperties(properties);
225
- }
226
job_txn_unref(block_job_txn);
227
}
228
229
--
210
--
230
2.40.1
211
2.13.6
212
213
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
2
3
Let's simplify things:
4
5
First, actions generally don't need access to common BlkActionState
6
structure. The only exclusion are backup actions that need
7
block_job_txn.
8
9
Next, for transaction actions of Transaction API is more native to
10
allocated state structure in the action itself.
11
12
So, do the following transformation:
13
14
1. Let all actions be represented by a function with corresponding
15
structure as arguments.
16
17
2. Instead of array-map marshaller, let's make a function, that calls
18
corresponding action directly.
19
20
3. BlkActionOps and BlkActionState structures become unused
21
22
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
23
Message-Id: <20230510150624.310640-7-vsementsov@yandex-team.ru>
24
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
---
2
---
27
blockdev.c | 265 +++++++++++++++++------------------------------------
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
28
1 file changed, 85 insertions(+), 180 deletions(-)
4
1 file changed, 80 insertions(+)
29
5
30
diff --git a/blockdev.c b/blockdev.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
31
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
32
--- a/blockdev.c
8
--- a/tests/test-bdrv-drain.c
33
+++ b/blockdev.c
9
+++ b/tests/test-bdrv-drain.c
34
@@ -XXX,XX +XXX,XX @@ out_aio_context:
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
35
return NULL;
11
blk_unref(blk_b);
36
}
12
}
37
13
38
-/* New and old BlockDriverState structs for atomic group operations */
14
+static void test_graph_change(void)
39
-
40
-typedef struct BlkActionState BlkActionState;
41
-
42
-/**
43
- * BlkActionOps:
44
- * Table of operations that define an Action.
45
- *
46
- * @instance_size: Size of state struct, in bytes.
47
- * @prepare: Prepare the work, must NOT be NULL.
48
- * @commit: Commit the changes, can be NULL.
49
- * @abort: Abort the changes on fail, can be NULL.
50
- * @clean: Clean up resources after all transaction actions have called
51
- * commit() or abort(). Can be NULL.
52
- *
53
- * Only prepare() may fail. In a single transaction, only one of commit() or
54
- * abort() will be called. clean() will always be called if it is present.
55
- *
56
- * Always run under BQL.
57
- */
58
-typedef struct BlkActionOps {
59
- size_t instance_size;
60
- void (*action)(BlkActionState *common, Transaction *tran, Error **errp);
61
-} BlkActionOps;
62
-
63
-/**
64
- * BlkActionState:
65
- * Describes one Action's state within a Transaction.
66
- *
67
- * @action: QAPI-defined enum identifying which Action to perform.
68
- * @ops: Table of ActionOps this Action can perform.
69
- * @block_job_txn: Transaction which this action belongs to.
70
- * @entry: List membership for all Actions in this Transaction.
71
- *
72
- * This structure must be arranged as first member in a subclassed type,
73
- * assuming that the compiler will also arrange it to the same offsets as the
74
- * base class.
75
- */
76
-struct BlkActionState {
77
- TransactionAction *action;
78
- const BlkActionOps *ops;
79
- JobTxn *block_job_txn;
80
- QTAILQ_ENTRY(BlkActionState) entry;
81
-};
82
-
83
/* internal snapshot private data */
84
typedef struct InternalSnapshotState {
85
- BlkActionState common;
86
BlockDriverState *bs;
87
QEMUSnapshotInfo sn;
88
bool created;
89
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv internal_snapshot_drv = {
90
.clean = internal_snapshot_clean,
91
};
92
93
-static void internal_snapshot_action(BlkActionState *common,
94
+static void internal_snapshot_action(BlockdevSnapshotInternal *internal,
95
Transaction *tran, Error **errp)
96
{
97
Error *local_err = NULL;
98
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_action(BlkActionState *common,
99
QEMUSnapshotInfo old_sn, *sn;
100
bool ret;
101
int64_t rt;
102
- BlockdevSnapshotInternal *internal;
103
- InternalSnapshotState *state;
104
+ InternalSnapshotState *state = g_new0(InternalSnapshotState, 1);
105
AioContext *aio_context;
106
int ret1;
107
108
- g_assert(common->action->type ==
109
- TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC);
110
- internal = common->action->u.blockdev_snapshot_internal_sync.data;
111
- state = DO_UPCAST(InternalSnapshotState, common, common);
112
-
113
tran_add(tran, &internal_snapshot_drv, state);
114
115
device = internal->device;
116
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_clean(void *opaque)
117
118
/* external snapshot private data */
119
typedef struct ExternalSnapshotState {
120
- BlkActionState common;
121
BlockDriverState *old_bs;
122
BlockDriverState *new_bs;
123
bool overlay_appended;
124
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv external_snapshot_drv = {
125
.clean = external_snapshot_clean,
126
};
127
128
-static void external_snapshot_action(BlkActionState *common, Transaction *tran,
129
- Error **errp)
130
+static void external_snapshot_action(TransactionAction *action,
131
+ Transaction *tran, Error **errp)
132
{
133
int ret;
134
int flags = 0;
135
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(BlkActionState *common, Transaction *tran,
136
const char *snapshot_ref;
137
/* File name of the new image (for 'blockdev-snapshot-sync') */
138
const char *new_image_file;
139
- ExternalSnapshotState *state =
140
- DO_UPCAST(ExternalSnapshotState, common, common);
141
- TransactionAction *action = common->action;
142
+ ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1);
143
AioContext *aio_context;
144
uint64_t perm, shared;
145
146
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_clean(void *opaque)
147
}
148
149
typedef struct DriveBackupState {
150
- BlkActionState common;
151
BlockDriverState *bs;
152
BlockJob *job;
153
} DriveBackupState;
154
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv drive_backup_drv = {
155
.clean = drive_backup_clean,
156
};
157
158
-static void drive_backup_action(BlkActionState *common, Transaction *tran,
159
- Error **errp)
160
+static void drive_backup_action(DriveBackup *backup,
161
+ JobTxn *block_job_txn,
162
+ Transaction *tran, Error **errp)
163
{
164
- DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
165
- DriveBackup *backup;
166
+ DriveBackupState *state = g_new0(DriveBackupState, 1);
167
BlockDriverState *bs;
168
BlockDriverState *target_bs;
169
BlockDriverState *source = NULL;
170
@@ -XXX,XX +XXX,XX @@ static void drive_backup_action(BlkActionState *common, Transaction *tran,
171
172
tran_add(tran, &drive_backup_drv, state);
173
174
- assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
175
- backup = common->action->u.drive_backup.data;
176
-
177
if (!backup->has_mode) {
178
backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
179
}
180
@@ -XXX,XX +XXX,XX @@ static void drive_backup_action(BlkActionState *common, Transaction *tran,
181
182
state->job = do_backup_common(qapi_DriveBackup_base(backup),
183
bs, target_bs, aio_context,
184
- common->block_job_txn, errp);
185
+ block_job_txn, errp);
186
187
unref:
188
bdrv_unref(target_bs);
189
@@ -XXX,XX +XXX,XX @@ static void drive_backup_clean(void *opaque)
190
}
191
192
typedef struct BlockdevBackupState {
193
- BlkActionState common;
194
BlockDriverState *bs;
195
BlockJob *job;
196
} BlockdevBackupState;
197
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv blockdev_backup_drv = {
198
.clean = blockdev_backup_clean,
199
};
200
201
-static void blockdev_backup_action(BlkActionState *common, Transaction *tran,
202
- Error **errp)
203
+static void blockdev_backup_action(BlockdevBackup *backup,
204
+ JobTxn *block_job_txn,
205
+ Transaction *tran, Error **errp)
206
{
207
- BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
208
- BlockdevBackup *backup;
209
+ BlockdevBackupState *state = g_new0(BlockdevBackupState, 1);
210
BlockDriverState *bs;
211
BlockDriverState *target_bs;
212
AioContext *aio_context;
213
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_action(BlkActionState *common, Transaction *tran,
214
215
tran_add(tran, &blockdev_backup_drv, state);
216
217
- assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
218
- backup = common->action->u.blockdev_backup.data;
219
-
220
bs = bdrv_lookup_bs(backup->device, backup->device, errp);
221
if (!bs) {
222
return;
223
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_action(BlkActionState *common, Transaction *tran,
224
225
state->job = do_backup_common(qapi_BlockdevBackup_base(backup),
226
bs, target_bs, aio_context,
227
- common->block_job_txn, errp);
228
+ block_job_txn, errp);
229
230
aio_context_release(aio_context);
231
}
232
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_clean(void *opaque)
233
}
234
235
typedef struct BlockDirtyBitmapState {
236
- BlkActionState common;
237
BdrvDirtyBitmap *bitmap;
238
BlockDriverState *bs;
239
HBitmap *backup;
240
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv block_dirty_bitmap_add_drv = {
241
.clean = g_free,
242
};
243
244
-static void block_dirty_bitmap_add_action(BlkActionState *common,
245
+static void block_dirty_bitmap_add_action(BlockDirtyBitmapAdd *action,
246
Transaction *tran, Error **errp)
247
{
248
Error *local_err = NULL;
249
- BlockDirtyBitmapAdd *action;
250
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
251
- common, common);
252
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
253
254
tran_add(tran, &block_dirty_bitmap_add_drv, state);
255
256
- action = common->action->u.block_dirty_bitmap_add.data;
257
/* AIO context taken and released within qmp_block_dirty_bitmap_add */
258
qmp_block_dirty_bitmap_add(action->node, action->name,
259
action->has_granularity, action->granularity,
260
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv block_dirty_bitmap_clear_drv = {
261
.clean = g_free,
262
};
263
264
-static void block_dirty_bitmap_clear_action(BlkActionState *common,
265
+static void block_dirty_bitmap_clear_action(BlockDirtyBitmap *action,
266
Transaction *tran, Error **errp)
267
{
268
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
269
- common, common);
270
- BlockDirtyBitmap *action;
271
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
272
273
tran_add(tran, &block_dirty_bitmap_clear_drv, state);
274
275
- action = common->action->u.block_dirty_bitmap_clear.data;
276
state->bitmap = block_dirty_bitmap_lookup(action->node,
277
action->name,
278
&state->bs,
279
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv block_dirty_bitmap_enable_drv = {
280
.clean = g_free,
281
};
282
283
-static void block_dirty_bitmap_enable_action(BlkActionState *common,
284
+static void block_dirty_bitmap_enable_action(BlockDirtyBitmap *action,
285
Transaction *tran, Error **errp)
286
{
287
- BlockDirtyBitmap *action;
288
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
289
- common, common);
290
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
291
292
tran_add(tran, &block_dirty_bitmap_enable_drv, state);
293
294
- action = common->action->u.block_dirty_bitmap_enable.data;
295
state->bitmap = block_dirty_bitmap_lookup(action->node,
296
action->name,
297
NULL,
298
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv block_dirty_bitmap_disable_drv = {
299
.clean = g_free,
300
};
301
302
-static void block_dirty_bitmap_disable_action(BlkActionState *common,
303
+static void block_dirty_bitmap_disable_action(BlockDirtyBitmap *action,
304
Transaction *tran, Error **errp)
305
{
306
- BlockDirtyBitmap *action;
307
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
308
- common, common);
309
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
310
311
tran_add(tran, &block_dirty_bitmap_disable_drv, state);
312
313
- action = common->action->u.block_dirty_bitmap_disable.data;
314
state->bitmap = block_dirty_bitmap_lookup(action->node,
315
action->name,
316
NULL,
317
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv block_dirty_bitmap_merge_drv = {
318
.clean = g_free,
319
};
320
321
-static void block_dirty_bitmap_merge_action(BlkActionState *common,
322
+static void block_dirty_bitmap_merge_action(BlockDirtyBitmapMerge *action,
323
Transaction *tran, Error **errp)
324
{
325
- BlockDirtyBitmapMerge *action;
326
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
327
- common, common);
328
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
329
330
tran_add(tran, &block_dirty_bitmap_merge_drv, state);
331
332
- action = common->action->u.block_dirty_bitmap_merge.data;
333
-
334
state->bitmap = block_dirty_bitmap_merge(action->node, action->target,
335
action->bitmaps, &state->backup,
336
errp);
337
@@ -XXX,XX +XXX,XX @@ TransactionActionDrv block_dirty_bitmap_remove_drv = {
338
.clean = g_free,
339
};
340
341
-static void block_dirty_bitmap_remove_action(BlkActionState *common,
342
+static void block_dirty_bitmap_remove_action(BlockDirtyBitmap *action,
343
Transaction *tran, Error **errp)
344
{
345
- BlockDirtyBitmap *action;
346
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
347
- common, common);
348
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
349
350
tran_add(tran, &block_dirty_bitmap_remove_drv, state);
351
352
- action = common->action->u.block_dirty_bitmap_remove.data;
353
354
state->bitmap = block_dirty_bitmap_remove(action->node, action->name,
355
false, &state->bs, errp);
356
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_remove_commit(void *opaque)
357
static void abort_commit(void *opaque);
358
TransactionActionDrv abort_drv = {
359
.commit = abort_commit,
360
- .clean = g_free,
361
};
362
363
-static void abort_action(BlkActionState *common, Transaction *tran,
364
- Error **errp)
365
+static void abort_action(Transaction *tran, Error **errp)
366
{
367
- tran_add(tran, &abort_drv, common);
368
+ tran_add(tran, &abort_drv, NULL);
369
error_setg(errp, "Transaction aborted using Abort action");
370
}
371
372
@@ -XXX,XX +XXX,XX @@ static void abort_commit(void *opaque)
373
g_assert_not_reached(); /* this action never succeeds */
374
}
375
376
-static const BlkActionOps actions_map[] = {
377
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = {
378
- .instance_size = sizeof(ExternalSnapshotState),
379
- .action = external_snapshot_action,
380
- },
381
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
382
- .instance_size = sizeof(ExternalSnapshotState),
383
- .action = external_snapshot_action,
384
- },
385
- [TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
386
- .instance_size = sizeof(DriveBackupState),
387
- .action = drive_backup_action,
388
- },
389
- [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
390
- .instance_size = sizeof(BlockdevBackupState),
391
- .action = blockdev_backup_action,
392
- },
393
- [TRANSACTION_ACTION_KIND_ABORT] = {
394
- .instance_size = sizeof(BlkActionState),
395
- .action = abort_action,
396
- },
397
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
398
- .instance_size = sizeof(InternalSnapshotState),
399
- .action = internal_snapshot_action,
400
- },
401
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD] = {
402
- .instance_size = sizeof(BlockDirtyBitmapState),
403
- .action = block_dirty_bitmap_add_action,
404
- },
405
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = {
406
- .instance_size = sizeof(BlockDirtyBitmapState),
407
- .action = block_dirty_bitmap_clear_action,
408
- },
409
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = {
410
- .instance_size = sizeof(BlockDirtyBitmapState),
411
- .action = block_dirty_bitmap_enable_action,
412
- },
413
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = {
414
- .instance_size = sizeof(BlockDirtyBitmapState),
415
- .action = block_dirty_bitmap_disable_action,
416
- },
417
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE] = {
418
- .instance_size = sizeof(BlockDirtyBitmapState),
419
- .action = block_dirty_bitmap_merge_action,
420
- },
421
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE] = {
422
- .instance_size = sizeof(BlockDirtyBitmapState),
423
- .action = block_dirty_bitmap_remove_action,
424
- },
425
- /* Where are transactions for MIRROR, COMMIT and STREAM?
426
+static void transaction_action(TransactionAction *act, JobTxn *block_job_txn,
427
+ Transaction *tran, Error **errp)
428
+{
15
+{
429
+ switch (act->type) {
16
+ BlockBackend *blk_a, *blk_b;
430
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT:
17
+ BlockDriverState *bs_a, *bs_b, *backing;
431
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
18
+ BDRVTestState *a_s, *b_s, *backing_s;
432
+ external_snapshot_action(act, tran, errp);
19
+
433
+ return;
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
434
+ case TRANSACTION_ACTION_KIND_DRIVE_BACKUP:
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
435
+ drive_backup_action(act->u.drive_backup.data,
22
+ &error_abort);
436
+ block_job_txn, tran, errp);
23
+ a_s = bs_a->opaque;
437
+ return;
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
438
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP:
25
+
439
+ blockdev_backup_action(act->u.blockdev_backup.data,
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
440
+ block_job_txn, tran, errp);
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
441
+ return;
28
+ &error_abort);
442
+ case TRANSACTION_ACTION_KIND_ABORT:
29
+ b_s = bs_b->opaque;
443
+ abort_action(tran, errp);
30
+ blk_insert_bs(blk_b, bs_b, &error_abort);
444
+ return;
31
+
445
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC:
32
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
446
+ internal_snapshot_action(act->u.blockdev_snapshot_internal_sync.data,
33
+ backing_s = backing->opaque;
447
+ tran, errp);
34
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
448
+ return;
35
+
449
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD:
36
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
450
+ block_dirty_bitmap_add_action(act->u.block_dirty_bitmap_add.data,
37
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
451
+ tran, errp);
38
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
452
+ return;
39
+ g_assert_cmpint(a_s->drain_count, ==, 0);
453
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR:
40
+ g_assert_cmpint(b_s->drain_count, ==, 0);
454
+ block_dirty_bitmap_clear_action(act->u.block_dirty_bitmap_clear.data,
41
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
455
+ tran, errp);
42
+
456
+ return;
43
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
457
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE:
44
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
458
+ block_dirty_bitmap_enable_action(act->u.block_dirty_bitmap_enable.data,
45
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
459
+ tran, errp);
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
460
+ return;
47
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
461
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE:
48
+
462
+ block_dirty_bitmap_disable_action(
49
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
463
+ act->u.block_dirty_bitmap_disable.data, tran, errp);
50
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
464
+ return;
51
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
465
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE:
52
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
466
+ block_dirty_bitmap_merge_action(act->u.block_dirty_bitmap_merge.data,
53
+ g_assert_cmpint(a_s->drain_count, ==, 5);
467
+ tran, errp);
54
+ g_assert_cmpint(b_s->drain_count, ==, 5);
468
+ return;
55
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
469
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE:
56
+
470
+ block_dirty_bitmap_remove_action(act->u.block_dirty_bitmap_remove.data,
57
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
471
+ tran, errp);
58
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
472
+ return;
59
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
473
+ /*
60
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
474
+ * Where are transactions for MIRROR, COMMIT and STREAM?
61
+ g_assert_cmpint(a_s->drain_count, ==, 3);
475
* Although these blockjobs use transaction callbacks like the backup job,
62
+ g_assert_cmpint(b_s->drain_count, ==, 2);
476
* these jobs do not necessarily adhere to transaction semantics.
63
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
477
* These jobs may not fully undo all of their actions on abort, nor do they
64
+
478
* necessarily work in transactions with more than one job in them.
65
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
479
*/
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
480
-};
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
481
+ case TRANSACTION_ACTION_KIND__MAX:
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
482
+ default:
69
+ g_assert_cmpint(a_s->drain_count, ==, 5);
483
+ g_assert_not_reached();
70
+ g_assert_cmpint(b_s->drain_count, ==, 5);
484
+ };
71
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
74
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
75
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
76
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
77
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
78
+
79
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
80
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
81
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
82
+ g_assert_cmpint(a_s->drain_count, ==, 0);
83
+ g_assert_cmpint(b_s->drain_count, ==, 0);
84
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
85
+
86
+ bdrv_unref(backing);
87
+ bdrv_unref(bs_a);
88
+ bdrv_unref(bs_b);
89
+ blk_unref(blk_a);
90
+ blk_unref(blk_b);
485
+}
91
+}
486
+
92
+
487
93
488
/*
94
typedef struct TestBlockJob {
489
* 'Atomic' group operations. The operations are performed as a set, and if
95
BlockJob common;
490
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *actions,
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
491
97
492
/* We don't do anything in this loop that commits us to the operations */
98
g_test_add_func("/bdrv-drain/nested", test_nested);
493
for (act = actions; act; act = act->next) {
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
494
- TransactionAction *dev_info = act->value;
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
495
- const BlkActionOps *ops;
101
496
- BlkActionState *state;
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
497
-
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
498
- assert(dev_info->type < ARRAY_SIZE(actions_map));
499
-
500
- ops = &actions_map[dev_info->type];
501
- assert(ops->instance_size > 0);
502
-
503
- state = g_malloc0(ops->instance_size);
504
- state->ops = ops;
505
- state->action = dev_info;
506
- state->block_job_txn = block_job_txn;
507
-
508
- state->ops->action(state, tran, &local_err);
509
+ transaction_action(act->value, block_job_txn, tran, &local_err);
510
if (local_err) {
511
error_propagate(errp, local_err);
512
goto delete_and_fail;
513
--
104
--
514
2.40.1
105
2.13.6
106
107
diff view generated by jsdifflib
1
If we take a reader lock, we can't call any functions that take a writer
1
Since commit bde70715, base is the only node that is reopened in
2
lock internally without causing deadlocks once the reader lock is
2
commit_start(). This means that the code, which still involves an
3
actually enforced in the main thread, too. Take the reader lock only
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
4
where it is actually needed.
5
4
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20230510203601.418015-5-kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
7
---
11
qemu-img.c | 5 +++--
8
block/commit.c | 8 +-------
12
1 file changed, 3 insertions(+), 2 deletions(-)
9
1 file changed, 1 insertion(+), 7 deletions(-)
13
10
14
diff --git a/qemu-img.c b/qemu-img.c
11
diff --git a/block/commit.c b/block/commit.c
15
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
16
--- a/qemu-img.c
13
--- a/block/commit.c
17
+++ b/qemu-img.c
14
+++ b/block/commit.c
18
@@ -XXX,XX +XXX,XX @@ static BlockGraphInfoList *collect_image_info_list(bool image_opts,
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
19
}
16
const char *filter_node_name, Error **errp)
20
bs = blk_bs(blk);
17
{
21
18
CommitBlockJob *s;
22
- GRAPH_RDLOCK_GUARD_MAINLOOP();
19
- BlockReopenQueue *reopen_queue = NULL;
20
int orig_base_flags;
21
BlockDriverState *iter;
22
BlockDriverState *commit_top_bs = NULL;
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
24
/* convert base to r/w, if necessary */
25
orig_base_flags = bdrv_get_flags(base);
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
28
- orig_base_flags | BDRV_O_RDWR);
29
- }
23
-
30
-
24
/*
31
- if (reopen_queue) {
25
* Note that the returned BlockGraphInfo object will not have
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
26
* information about this image's backing node, because we have opened
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
27
@@ -XXX,XX +XXX,XX @@ static BlockGraphInfoList *collect_image_info_list(bool image_opts,
34
if (local_err != NULL) {
28
* duplicate the backing chain information that we obtain by walking
35
error_propagate(errp, local_err);
29
* the chain manually here.
36
goto fail;
30
*/
31
+ bdrv_graph_rdlock_main_loop();
32
bdrv_query_block_graph_info(bs, &info, &err);
33
+ bdrv_graph_rdunlock_main_loop();
34
+
35
if (err) {
36
error_report_err(err);
37
blk_unref(blk);
38
--
37
--
39
2.40.1
38
2.13.6
39
40
diff view generated by jsdifflib
1
These are functions that modify the graph, so they must be able to take
1
The bdrv_reopen*() implementation doesn't like it if the graph is
2
a writer lock. This is impossible if they already hold the reader lock.
2
changed between queuing nodes for reopen and actually reopening them
3
If they need a reader lock for some of their operations, they should
3
(one of the reasons is that queuing can be recursive).
4
take it internally.
5
4
6
Many of them go through blk_*(), which will always take the lock itself.
5
So instead of draining the device only in bdrv_reopen_multiple(),
7
Direct calls of bdrv_*() need to take the reader lock. Note that while
6
require that callers already drained all affected nodes, and assert this
8
locking for bdrv_co_*() calls is checked by TSA, this is not the case
7
in bdrv_reopen_queue().
9
for the mixed_coroutine_fns bdrv_*(). Holding the lock is still required
10
when they are called from coroutine context like here!
11
12
This effectively reverts 4ec8df0183, but adds some internal locking
13
instead.
14
8
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Message-Id: <20230510203601.418015-2-kwolf@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
17
Reviewed-by: Eric Blake <eblake@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
11
---
20
include/block/block-global-state.h | 8 +++----
12
block.c | 23 ++++++++++++++++-------
21
include/block/block_int-common.h | 4 ++--
13
block/replication.c | 6 ++++++
22
block.c | 1 -
14
qemu-io-cmds.c | 3 +++
23
block/create.c | 1 -
15
3 files changed, 25 insertions(+), 7 deletions(-)
24
block/crypto.c | 25 ++++++++++----------
25
block/parallels.c | 6 ++---
26
block/qcow.c | 6 ++---
27
block/qcow2.c | 37 +++++++++++++++++++-----------
28
block/qed.c | 6 ++---
29
block/raw-format.c | 2 +-
30
block/vdi.c | 11 +++++----
31
block/vhdx.c | 8 ++++---
32
block/vmdk.c | 27 ++++++++++------------
33
block/vpc.c | 6 ++---
34
14 files changed, 78 insertions(+), 70 deletions(-)
35
16
36
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
37
index XXXXXXX..XXXXXXX 100644
38
--- a/include/block/block-global-state.h
39
+++ b/include/block/block-global-state.h
40
@@ -XXX,XX +XXX,XX @@ BlockDriver *bdrv_find_protocol(const char *filename,
41
Error **errp);
42
BlockDriver *bdrv_find_format(const char *format_name);
43
44
-int coroutine_fn GRAPH_RDLOCK
45
+int coroutine_fn GRAPH_UNLOCKED
46
bdrv_co_create(BlockDriver *drv, const char *filename, QemuOpts *opts,
47
Error **errp);
48
49
-int co_wrapper_bdrv_rdlock bdrv_create(BlockDriver *drv, const char *filename,
50
- QemuOpts *opts, Error **errp);
51
+int co_wrapper bdrv_create(BlockDriver *drv, const char *filename,
52
+ QemuOpts *opts, Error **errp);
53
54
-int coroutine_fn GRAPH_RDLOCK
55
+int coroutine_fn GRAPH_UNLOCKED
56
bdrv_co_create_file(const char *filename, QemuOpts *opts, Error **errp);
57
58
BlockDriverState *bdrv_new(void);
59
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
60
index XXXXXXX..XXXXXXX 100644
61
--- a/include/block/block_int-common.h
62
+++ b/include/block/block_int-common.h
63
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
64
BlockDriverState *bs, QDict *options, int flags, Error **errp);
65
void (*bdrv_close)(BlockDriverState *bs);
66
67
- int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_create)(
68
+ int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create)(
69
BlockdevCreateOptions *opts, Error **errp);
70
71
- int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_create_opts)(
72
+ int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create_opts)(
73
BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp);
74
75
int (*bdrv_amend_options)(BlockDriverState *bs,
76
diff --git a/block.c b/block.c
17
diff --git a/block.c b/block.c
77
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
78
--- a/block.c
19
--- a/block.c
79
+++ b/block.c
20
+++ b/block.c
80
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
81
int ret;
22
* returns a pointer to bs_queue, which is either the newly allocated
82
GLOBAL_STATE_CODE();
23
* bs_queue, or the existing bs_queue being used.
83
ERRP_GUARD();
24
*
84
- assert_bdrv_graph_readable();
25
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
85
26
*/
86
if (!drv->bdrv_co_create_opts) {
27
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
87
error_setg(errp, "Driver '%s' does not support image creation",
28
BlockDriverState *bs,
88
diff --git a/block/create.c b/block/create.c
29
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
89
index XXXXXXX..XXXXXXX 100644
30
BdrvChild *child;
90
--- a/block/create.c
31
QDict *old_options, *explicit_options;
91
+++ b/block/create.c
32
92
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
33
+ /* Make sure that the caller remembered to use a drained section. This is
93
int ret;
34
+ * important to avoid graph changes between the recursive queuing here and
94
35
+ * bdrv_reopen_multiple(). */
95
GLOBAL_STATE_CODE();
36
+ assert(bs->quiesce_counter > 0);
96
- GRAPH_RDLOCK_GUARD();
37
+
97
38
if (bs_queue == NULL) {
98
job_progress_set_remaining(&s->common, 1);
39
bs_queue = g_new0(BlockReopenQueue, 1);
99
ret = s->drv->bdrv_co_create(s->opts, errp);
40
QSIMPLEQ_INIT(bs_queue);
100
diff --git a/block/crypto.c b/block/crypto.c
41
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
101
index XXXXXXX..XXXXXXX 100644
42
* If all devices prepare successfully, then the changes are committed
102
--- a/block/crypto.c
43
* to all devices.
103
+++ b/block/crypto.c
44
*
104
@@ -XXX,XX +XXX,XX @@ struct BlockCryptoCreateData {
45
+ * All affected nodes must be drained between bdrv_reopen_queue() and
105
};
46
+ * bdrv_reopen_multiple().
106
47
*/
107
48
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
108
-static int block_crypto_create_write_func(QCryptoBlock *block,
109
- size_t offset,
110
- const uint8_t *buf,
111
- size_t buflen,
112
- void *opaque,
113
- Error **errp)
114
+static int coroutine_fn GRAPH_UNLOCKED
115
+block_crypto_create_write_func(QCryptoBlock *block, size_t offset,
116
+ const uint8_t *buf, size_t buflen, void *opaque,
117
+ Error **errp)
118
{
49
{
119
struct BlockCryptoCreateData *data = opaque;
50
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
120
ssize_t ret;
51
121
@@ -XXX,XX +XXX,XX @@ static int block_crypto_create_write_func(QCryptoBlock *block,
52
assert(bs_queue != NULL);
122
return 0;
53
123
}
54
- aio_context_release(ctx);
124
55
- bdrv_drain_all_begin();
125
-static int block_crypto_create_init_func(QCryptoBlock *block,
56
- aio_context_acquire(ctx);
126
- size_t headerlen,
57
-
127
- void *opaque,
58
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
128
- Error **errp)
59
+ assert(bs_entry->state.bs->quiesce_counter > 0);
129
+static int coroutine_fn GRAPH_UNLOCKED
60
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
130
+block_crypto_create_init_func(QCryptoBlock *block, size_t headerlen,
61
error_propagate(errp, local_err);
131
+ void *opaque, Error **errp)
62
goto cleanup;
132
{
63
@@ -XXX,XX +XXX,XX @@ cleanup:
133
struct BlockCryptoCreateData *data = opaque;
64
}
134
Error *local_error = NULL;
65
g_free(bs_queue);
135
@@ -XXX,XX +XXX,XX @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
66
136
}
67
- bdrv_drain_all_end();
137
68
-
138
139
-static int coroutine_fn
140
+static int coroutine_fn GRAPH_UNLOCKED
141
block_crypto_co_create_generic(BlockDriverState *bs, int64_t size,
142
QCryptoBlockCreateOptions *opts,
143
PreallocMode prealloc, Error **errp)
144
@@ -XXX,XX +XXX,XX @@ static int block_crypto_open_luks(BlockDriverState *bs,
145
bs, options, flags, errp);
146
}
147
148
-static int coroutine_fn
149
+static int coroutine_fn GRAPH_UNLOCKED
150
block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
151
{
152
BlockdevCreateOptionsLUKS *luks_opts;
153
@@ -XXX,XX +XXX,XX @@ fail:
154
return ret;
69
return ret;
155
}
70
}
156
71
157
-static int coroutine_fn GRAPH_RDLOCK
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
158
+static int coroutine_fn GRAPH_UNLOCKED
159
block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
160
QemuOpts *opts, Error **errp)
161
{
73
{
162
@@ -XXX,XX +XXX,XX @@ fail:
74
int ret = -1;
163
* beforehand, it has been truncated and corrupted in the process.
75
Error *local_err = NULL;
164
*/
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
165
if (ret) {
77
+ BlockReopenQueue *queue;
166
+ bdrv_graph_co_rdlock();
78
167
bdrv_co_delete_file_noerr(bs);
79
+ bdrv_subtree_drained_begin(bs);
168
+ bdrv_graph_co_rdunlock();
80
+
81
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
82
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
83
if (local_err != NULL) {
84
error_propagate(errp, local_err);
169
}
85
}
170
86
+
171
bdrv_co_unref(bs);
87
+ bdrv_subtree_drained_end(bs);
172
diff --git a/block/parallels.c b/block/parallels.c
88
+
173
index XXXXXXX..XXXXXXX 100644
174
--- a/block/parallels.c
175
+++ b/block/parallels.c
176
@@ -XXX,XX +XXX,XX @@ out:
177
}
178
179
180
-static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
181
- Error **errp)
182
+static int coroutine_fn GRAPH_UNLOCKED
183
+parallels_co_create(BlockdevCreateOptions* opts, Error **errp)
184
{
185
BlockdevCreateOptionsParallels *parallels_opts;
186
BlockDriverState *bs;
187
@@ -XXX,XX +XXX,XX @@ exit:
188
goto out;
189
}
190
191
-static int coroutine_fn GRAPH_RDLOCK
192
+static int coroutine_fn GRAPH_UNLOCKED
193
parallels_co_create_opts(BlockDriver *drv, const char *filename,
194
QemuOpts *opts, Error **errp)
195
{
196
diff --git a/block/qcow.c b/block/qcow.c
197
index XXXXXXX..XXXXXXX 100644
198
--- a/block/qcow.c
199
+++ b/block/qcow.c
200
@@ -XXX,XX +XXX,XX @@ static void qcow_close(BlockDriverState *bs)
201
error_free(s->migration_blocker);
202
}
203
204
-static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
205
- Error **errp)
206
+static int coroutine_fn GRAPH_UNLOCKED
207
+qcow_co_create(BlockdevCreateOptions *opts, Error **errp)
208
{
209
BlockdevCreateOptionsQcow *qcow_opts;
210
int header_size, backing_filename_len, l1_size, shift, i;
211
@@ -XXX,XX +XXX,XX @@ exit:
212
return ret;
89
return ret;
213
}
90
}
214
91
215
-static int coroutine_fn GRAPH_RDLOCK
92
diff --git a/block/replication.c b/block/replication.c
216
+static int coroutine_fn GRAPH_UNLOCKED
217
qcow_co_create_opts(BlockDriver *drv, const char *filename,
218
QemuOpts *opts, Error **errp)
219
{
220
diff --git a/block/qcow2.c b/block/qcow2.c
221
index XXXXXXX..XXXXXXX 100644
93
index XXXXXXX..XXXXXXX 100644
222
--- a/block/qcow2.c
94
--- a/block/replication.c
223
+++ b/block/qcow2.c
95
+++ b/block/replication.c
224
@@ -XXX,XX +XXX,XX @@ static int qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset,
96
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
97
new_secondary_flags = s->orig_secondary_flags;
98
}
99
100
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
101
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
102
+
103
if (orig_hidden_flags != new_hidden_flags) {
104
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
105
new_hidden_flags);
106
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
107
reopen_queue, &local_err);
108
error_propagate(errp, local_err);
109
}
110
+
111
+ bdrv_subtree_drained_end(s->hidden_disk->bs);
112
+ bdrv_subtree_drained_end(s->secondary_disk->bs);
225
}
113
}
226
114
227
115
static void backup_job_cleanup(BlockDriverState *bs)
228
-static int qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,
116
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
229
- void *opaque, Error **errp)
117
index XXXXXXX..XXXXXXX 100644
230
+static int coroutine_fn GRAPH_RDLOCK
118
--- a/qemu-io-cmds.c
231
+qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, void *opaque,
119
+++ b/qemu-io-cmds.c
232
+ Error **errp)
120
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
233
{
121
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
234
BlockDriverState *bs = opaque;
122
qemu_opts_reset(&reopen_opts);
235
BDRVQcow2State *s = bs->opaque;
123
236
@@ -XXX,XX +XXX,XX @@ static int qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,
124
+ bdrv_subtree_drained_begin(bs);
237
*/
125
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
238
clusterlen = size_to_clusters(s, headerlen) * s->cluster_size;
126
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
239
assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0);
127
+ bdrv_subtree_drained_end(bs);
240
- ret = bdrv_pwrite_zeroes(bs->file,
241
- ret,
242
- clusterlen, 0);
243
+ ret = bdrv_co_pwrite_zeroes(bs->file, ret, clusterlen, 0);
244
if (ret < 0) {
245
error_setg_errno(errp, -ret, "Could not zero fill encryption header");
246
return -1;
247
@@ -XXX,XX +XXX,XX @@ static int qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,
248
}
249
250
251
-static int qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset,
252
- const uint8_t *buf, size_t buflen,
253
- void *opaque, Error **errp)
254
+/* The graph lock must be held when called in coroutine context */
255
+static int coroutine_mixed_fn
256
+qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset,
257
+ const uint8_t *buf, size_t buflen,
258
+ void *opaque, Error **errp)
259
{
260
BlockDriverState *bs = opaque;
261
BDRVQcow2State *s = bs->opaque;
262
@@ -XXX,XX +XXX,XX @@ static int qcow2_change_backing_file(BlockDriverState *bs,
263
return qcow2_update_header(bs);
264
}
265
266
-static int qcow2_set_up_encryption(BlockDriverState *bs,
267
- QCryptoBlockCreateOptions *cryptoopts,
268
- Error **errp)
269
+static int coroutine_fn GRAPH_RDLOCK
270
+qcow2_set_up_encryption(BlockDriverState *bs,
271
+ QCryptoBlockCreateOptions *cryptoopts,
272
+ Error **errp)
273
{
274
BDRVQcow2State *s = bs->opaque;
275
QCryptoBlock *crypto = NULL;
276
@@ -XXX,XX +XXX,XX @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
277
return refcount_bits;
278
}
279
280
-static int coroutine_fn
281
+static int coroutine_fn GRAPH_UNLOCKED
282
qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
283
{
284
BlockdevCreateOptionsQcow2 *qcow2_opts;
285
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
286
goto out;
287
}
288
289
+ bdrv_graph_co_rdlock();
290
ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size);
291
if (ret < 0) {
292
+ bdrv_graph_co_rdunlock();
293
error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 "
294
"header and refcount table");
295
goto out;
296
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
297
298
/* Create a full header (including things like feature table) */
299
ret = qcow2_update_header(blk_bs(blk));
300
+ bdrv_graph_co_rdunlock();
301
+
128
+
302
if (ret < 0) {
129
if (local_err) {
303
error_setg_errno(errp, -ret, "Could not update qcow2 header");
130
error_report_err(local_err);
304
goto out;
305
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
306
307
/* Want encryption? There you go. */
308
if (qcow2_opts->encrypt) {
309
+ bdrv_graph_co_rdlock();
310
ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp);
311
+ bdrv_graph_co_rdunlock();
312
+
313
if (ret < 0) {
314
goto out;
315
}
316
@@ -XXX,XX +XXX,XX @@ out:
317
return ret;
318
}
319
320
-static int coroutine_fn GRAPH_RDLOCK
321
+static int coroutine_fn GRAPH_UNLOCKED
322
qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts,
323
Error **errp)
324
{
325
@@ -XXX,XX +XXX,XX @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts,
326
ret = qcow2_co_create(create_options, errp);
327
finish:
328
if (ret < 0) {
329
+ bdrv_graph_co_rdlock();
330
bdrv_co_delete_file_noerr(bs);
331
bdrv_co_delete_file_noerr(data_bs);
332
+ bdrv_graph_co_rdunlock();
333
} else {
131
} else {
334
ret = 0;
335
}
336
diff --git a/block/qed.c b/block/qed.c
337
index XXXXXXX..XXXXXXX 100644
338
--- a/block/qed.c
339
+++ b/block/qed.c
340
@@ -XXX,XX +XXX,XX @@ static void bdrv_qed_close(BlockDriverState *bs)
341
qemu_vfree(s->l1_table);
342
}
343
344
-static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
345
- Error **errp)
346
+static int coroutine_fn GRAPH_UNLOCKED
347
+bdrv_qed_co_create(BlockdevCreateOptions *opts, Error **errp)
348
{
349
BlockdevCreateOptionsQed *qed_opts;
350
BlockBackend *blk = NULL;
351
@@ -XXX,XX +XXX,XX @@ out:
352
return ret;
353
}
354
355
-static int coroutine_fn GRAPH_RDLOCK
356
+static int coroutine_fn GRAPH_UNLOCKED
357
bdrv_qed_co_create_opts(BlockDriver *drv, const char *filename,
358
QemuOpts *opts, Error **errp)
359
{
360
diff --git a/block/raw-format.c b/block/raw-format.c
361
index XXXXXXX..XXXXXXX 100644
362
--- a/block/raw-format.c
363
+++ b/block/raw-format.c
364
@@ -XXX,XX +XXX,XX @@ static int raw_has_zero_init(BlockDriverState *bs)
365
return bdrv_has_zero_init(bs->file->bs);
366
}
367
368
-static int coroutine_fn GRAPH_RDLOCK
369
+static int coroutine_fn GRAPH_UNLOCKED
370
raw_co_create_opts(BlockDriver *drv, const char *filename,
371
QemuOpts *opts, Error **errp)
372
{
373
diff --git a/block/vdi.c b/block/vdi.c
374
index XXXXXXX..XXXXXXX 100644
375
--- a/block/vdi.c
376
+++ b/block/vdi.c
377
@@ -XXX,XX +XXX,XX @@ nonallocating_write:
378
return ret;
379
}
380
381
-static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
382
- size_t block_size, Error **errp)
383
+static int coroutine_fn GRAPH_UNLOCKED
384
+vdi_co_do_create(BlockdevCreateOptions *create_options, size_t block_size,
385
+ Error **errp)
386
{
387
BlockdevCreateOptionsVdi *vdi_opts;
388
int ret = 0;
389
@@ -XXX,XX +XXX,XX @@ exit:
390
return ret;
391
}
392
393
-static int coroutine_fn vdi_co_create(BlockdevCreateOptions *create_options,
394
- Error **errp)
395
+static int coroutine_fn GRAPH_UNLOCKED
396
+vdi_co_create(BlockdevCreateOptions *create_options, Error **errp)
397
{
398
return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp);
399
}
400
401
-static int coroutine_fn GRAPH_RDLOCK
402
+static int coroutine_fn GRAPH_UNLOCKED
403
vdi_co_create_opts(BlockDriver *drv, const char *filename,
404
QemuOpts *opts, Error **errp)
405
{
406
diff --git a/block/vhdx.c b/block/vhdx.c
407
index XXXXXXX..XXXXXXX 100644
408
--- a/block/vhdx.c
409
+++ b/block/vhdx.c
410
@@ -XXX,XX +XXX,XX @@ exit:
411
* There are 2 headers, and the highest sequence number will represent
412
* the active header
413
*/
414
-static int coroutine_fn GRAPH_RDLOCK
415
+static int coroutine_fn GRAPH_UNLOCKED
416
vhdx_create_new_headers(BlockBackend *blk, uint64_t image_size,
417
uint32_t log_size)
418
{
419
@@ -XXX,XX +XXX,XX @@ vhdx_create_new_headers(BlockBackend *blk, uint64_t image_size,
420
int ret = 0;
421
VHDXHeader *hdr = NULL;
422
423
+ GRAPH_RDLOCK_GUARD();
424
+
425
hdr = g_new0(VHDXHeader, 1);
426
427
hdr->signature = VHDX_HEADER_SIGNATURE;
428
@@ -XXX,XX +XXX,XX @@ exit:
429
* .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
430
* 1MB
431
*/
432
-static int coroutine_fn GRAPH_RDLOCK
433
+static int coroutine_fn GRAPH_UNLOCKED
434
vhdx_co_create(BlockdevCreateOptions *opts, Error **errp)
435
{
436
BlockdevCreateOptionsVhdx *vhdx_opts;
437
@@ -XXX,XX +XXX,XX @@ delete_and_exit:
438
return ret;
439
}
440
441
-static int coroutine_fn GRAPH_RDLOCK
442
+static int coroutine_fn GRAPH_UNLOCKED
443
vhdx_co_create_opts(BlockDriver *drv, const char *filename,
444
QemuOpts *opts, Error **errp)
445
{
446
diff --git a/block/vmdk.c b/block/vmdk.c
447
index XXXXXXX..XXXXXXX 100644
448
--- a/block/vmdk.c
449
+++ b/block/vmdk.c
450
@@ -XXX,XX +XXX,XX @@ vmdk_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes,
451
return ret;
452
}
453
454
-static int vmdk_init_extent(BlockBackend *blk,
455
- int64_t filesize, bool flat,
456
- bool compress, bool zeroed_grain,
457
- Error **errp)
458
+static int GRAPH_UNLOCKED
459
+vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress,
460
+ bool zeroed_grain, Error **errp)
461
{
462
int ret, i;
463
VMDK4Header header;
464
@@ -XXX,XX +XXX,XX @@ exit:
465
return ret;
466
}
467
468
-static int coroutine_fn GRAPH_RDLOCK
469
+static int coroutine_fn GRAPH_UNLOCKED
470
vmdk_create_extent(const char *filename, int64_t filesize, bool flat,
471
bool compress, bool zeroed_grain, BlockBackend **pbb,
472
QemuOpts *opts, Error **errp)
473
@@ -XXX,XX +XXX,XX @@ static int filename_decompose(const char *filename, char *path, char *prefix,
474
* non-split format.
475
* idx >= 1: get the n-th extent if in a split subformat
476
*/
477
-typedef BlockBackend * coroutine_fn /* GRAPH_RDLOCK */
478
+typedef BlockBackend * coroutine_fn GRAPH_UNLOCKED_PTR
479
(*vmdk_create_extent_fn)(int64_t size, int idx, bool flat, bool split,
480
bool compress, bool zeroed_grain, void *opaque,
481
Error **errp);
482
@@ -XXX,XX +XXX,XX @@ static void vmdk_desc_add_extent(GString *desc,
483
g_free(basename);
484
}
485
486
-static int coroutine_fn GRAPH_RDLOCK
487
+static int coroutine_fn GRAPH_UNLOCKED
488
vmdk_co_do_create(int64_t size,
489
BlockdevVmdkSubformat subformat,
490
BlockdevVmdkAdapterType adapter_type,
491
@@ -XXX,XX +XXX,XX @@ typedef struct {
492
QemuOpts *opts;
493
} VMDKCreateOptsData;
494
495
-static BlockBackend * coroutine_fn GRAPH_RDLOCK
496
+static BlockBackend * coroutine_fn GRAPH_UNLOCKED
497
vmdk_co_create_opts_cb(int64_t size, int idx, bool flat, bool split,
498
bool compress, bool zeroed_grain, void *opaque,
499
Error **errp)
500
@@ -XXX,XX +XXX,XX @@ exit:
501
return blk;
502
}
503
504
-static int coroutine_fn GRAPH_RDLOCK
505
+static int coroutine_fn GRAPH_UNLOCKED
506
vmdk_co_create_opts(BlockDriver *drv, const char *filename,
507
QemuOpts *opts, Error **errp)
508
{
509
@@ -XXX,XX +XXX,XX @@ exit:
510
return ret;
511
}
512
513
-static BlockBackend * coroutine_fn vmdk_co_create_cb(int64_t size, int idx,
514
- bool flat, bool split,
515
- bool compress,
516
- bool zeroed_grain,
517
- void *opaque, Error **errp)
518
+static BlockBackend * coroutine_fn GRAPH_UNLOCKED
519
+vmdk_co_create_cb(int64_t size, int idx, bool flat, bool split, bool compress,
520
+ bool zeroed_grain, void *opaque, Error **errp)
521
{
522
int ret;
523
BlockDriverState *bs;
524
@@ -XXX,XX +XXX,XX @@ static BlockBackend * coroutine_fn vmdk_co_create_cb(int64_t size, int idx,
525
return blk;
526
}
527
528
-static int coroutine_fn GRAPH_RDLOCK
529
+static int coroutine_fn GRAPH_UNLOCKED
530
vmdk_co_create(BlockdevCreateOptions *create_options, Error **errp)
531
{
532
BlockdevCreateOptionsVmdk *opts;
533
diff --git a/block/vpc.c b/block/vpc.c
534
index XXXXXXX..XXXXXXX 100644
535
--- a/block/vpc.c
536
+++ b/block/vpc.c
537
@@ -XXX,XX +XXX,XX @@ static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
538
return 0;
539
}
540
541
-static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
542
- Error **errp)
543
+static int coroutine_fn GRAPH_UNLOCKED
544
+vpc_co_create(BlockdevCreateOptions *opts, Error **errp)
545
{
546
BlockdevCreateOptionsVpc *vpc_opts;
547
BlockBackend *blk = NULL;
548
@@ -XXX,XX +XXX,XX @@ out:
549
return ret;
550
}
551
552
-static int coroutine_fn GRAPH_RDLOCK
553
+static int coroutine_fn GRAPH_UNLOCKED
554
vpc_co_create_opts(BlockDriver *drv, const char *filename,
555
QemuOpts *opts, Error **errp)
556
{
557
--
132
--
558
2.40.1
133
2.13.6
134
135
diff view generated by jsdifflib