1
The following changes since commit 5204b499a6cae4dfd9fe762d5e6e82224892383b:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
mailmap: Fix Stefan Weil author email (2022-12-13 15:56:57 -0500)
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 2ad19e5dc950d4b340894846b9e71c0b20f9a1cc:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
block: GRAPH_RDLOCK for functions only called by co_wrappers (2022-12-14 13:13:07 +0100)
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
- Code cleanups around block graph modification
16
----------------------------------------------------------------
17
- Simplify drain
17
Doug Gale (1):
18
- coroutine_fn correctness fixes, including splitting generated
18
nvme: Add tracing
19
coroutine wrappers into co_wrapper (to be called only from
20
non-coroutine context) and co_wrapper_mixed (both coroutine and
21
non-coroutine context)
22
- Introduce a block graph rwlock
23
19
24
----------------------------------------------------------------
20
Edgar Kaziakhmedov (1):
25
Emanuele Giuseppe Esposito (21):
21
qcow2: get rid of qcow2_backing_read1 routine
26
block-io: introduce coroutine_fn duplicates for bdrv_common_block_status_above callers
27
block-copy: add coroutine_fn annotations
28
nbd/server.c: add coroutine_fn annotations
29
block-backend: replace bdrv_*_above with blk_*_above
30
block/vmdk: add coroutine_fn annotations
31
block: avoid duplicating filename string in bdrv_create
32
block: distinguish between bdrv_create running in coroutine and not
33
block: bdrv_create_file is a coroutine_fn
34
block: rename generated_co_wrapper in co_wrapper_mixed
35
block-coroutine-wrapper.py: introduce co_wrapper
36
block-coroutine-wrapper.py: support functions without bs arg
37
block-coroutine-wrapper.py: support also basic return types
38
block: convert bdrv_create to co_wrapper
39
block/dirty-bitmap: convert coroutine-only functions to co_wrapper
40
graph-lock: Implement guard macros
41
async: Register/unregister aiocontext in graph lock list
42
block: wrlock in bdrv_replace_child_noperm
43
block: remove unnecessary assert_bdrv_graph_writable()
44
block: assert that graph read and writes are performed correctly
45
block-coroutine-wrapper.py: introduce annotations that take the graph rdlock
46
block: use co_wrapper_mixed_bdrv_rdlock in functions taking the rdlock
47
22
48
Kevin Wolf (25):
23
Fam Zheng (2):
49
qed: Don't yield in bdrv_qed_co_drain_begin()
24
block: Open backing image in force share mode for size probe
50
test-bdrv-drain: Don't yield in .bdrv_co_drained_begin/end()
25
block: Remove unused bdrv_requests_pending
51
block: Revert .bdrv_drained_begin/end to non-coroutine_fn
52
block: Remove drained_end_counter
53
block: Inline bdrv_drain_invoke()
54
block: Fix locking for bdrv_reopen_queue_child()
55
block: Drain individual nodes during reopen
56
block: Don't use subtree drains in bdrv_drop_intermediate()
57
stream: Replace subtree drain with a single node drain
58
block: Remove subtree drains
59
block: Call drain callbacks only once
60
block: Remove ignore_bds_parents parameter from drain_begin/end.
61
block: Drop out of coroutine in bdrv_do_drained_begin_quiesce()
62
block: Don't poll in bdrv_replace_child_noperm()
63
block: Remove poll parameter from bdrv_parent_drained_begin_single()
64
block: Factor out bdrv_drain_all_begin_nopoll()
65
Import clang-tsa.h
66
clang-tsa: Add TSA_ASSERT() macro
67
clang-tsa: Add macros for shared locks
68
configure: Enable -Wthread-safety if present
69
test-bdrv-drain: Fix incorrrect drain assumptions
70
block: Fix locking in external_snapshot_prepare()
71
graph-lock: TSA annotations for lock/unlock functions
72
Mark assert_bdrv_graph_readable/writable() GRAPH_RD/WRLOCK
73
block: GRAPH_RDLOCK for functions only called by co_wrappers
74
26
75
Paolo Bonzini (1):
27
John Snow (1):
76
graph-lock: Introduce a lock to protect block graph operations
28
iotests: fix 197 for vpc
77
29
78
Vladimir Sementsov-Ogievskiy (4):
30
Kevin Wolf (27):
79
block: Inline bdrv_detach_child()
31
block: Formats don't need CONSISTENT_READ with NO_IO
80
block: drop bdrv_remove_filter_or_cow_child
32
block: Make bdrv_drain_invoke() recursive
81
block: bdrv_refresh_perms(): allow external tran
33
block: Call .drain_begin only once in bdrv_drain_all_begin()
82
block: refactor bdrv_list_refresh_perms to allow any list of nodes
34
test-bdrv-drain: Test BlockDriver callbacks for drain
35
block: bdrv_drain_recurse(): Remove unused begin parameter
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
83
58
84
docs/devel/block-coroutine-wrapper.rst | 6 +-
59
Thomas Huth (3):
85
configure | 1 +
60
block: Remove the obsolete -drive boot=on|off parameter
86
block/block-gen.h | 11 +-
61
block: Remove the deprecated -hdachs option
87
block/coroutines.h | 21 +-
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
88
include/block/aio.h | 9 +
63
89
include/block/block-common.h | 27 ++-
64
qapi/block-core.json | 4 +
90
include/block/block-copy.h | 5 +-
65
block/qcow2.h | 3 -
91
include/block/block-global-state.h | 15 +-
66
include/block/block.h | 15 +-
92
include/block/block-io.h | 136 +++++------
67
include/block/block_int.h | 6 +-
93
include/block/block_int-common.h | 49 ++--
68
block.c | 75 ++++-
94
include/block/block_int-global-state.h | 17 --
69
block/commit.c | 8 +-
95
include/block/block_int-io.h | 12 -
70
block/io.c | 164 +++++++---
96
include/block/block_int.h | 1 +
71
block/qcow2.c | 51 +--
97
include/block/dirty-bitmap.h | 10 +-
72
block/replication.c | 6 +
98
include/block/graph-lock.h | 280 +++++++++++++++++++++++
73
blockdev.c | 11 -
99
include/qemu/clang-tsa.h | 114 ++++++++++
74
blockjob.c | 22 +-
100
include/sysemu/block-backend-io.h | 77 ++++---
75
hmp.c | 6 -
101
block.c | 404 ++++++++++++++++++---------------
76
hw/block/nvme.c | 349 +++++++++++++++++----
102
block/block-backend.c | 25 +-
77
qemu-io-cmds.c | 3 +
103
block/block-copy.c | 21 +-
78
tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++
104
block/commit.c | 4 +-
79
vl.c | 86 +-----
105
block/crypto.c | 2 +-
80
hw/block/trace-events | 93 ++++++
106
block/dirty-bitmap.c | 88 +------
81
qemu-doc.texi | 29 +-
107
block/graph-lock.c | 275 ++++++++++++++++++++++
82
qemu-options.hx | 19 +-
108
block/io.c | 367 ++++++++++--------------------
83
tests/Makefile.include | 2 +
109
block/parallels.c | 2 +-
84
tests/qemu-iotests/197 | 4 +
110
block/qcow.c | 2 +-
85
tests/qemu-iotests/common.filter | 3 +-
111
block/qcow2.c | 4 +-
86
22 files changed, 1294 insertions(+), 316 deletions(-)
112
block/qed.c | 28 ++-
87
create mode 100644 tests/test-bdrv-drain.c
113
block/raw-format.c | 2 +-
88
114
block/replication.c | 6 -
115
block/stream.c | 26 ++-
116
block/throttle.c | 8 +-
117
block/vdi.c | 2 +-
118
block/vhdx.c | 2 +-
119
block/vmdk.c | 38 ++--
120
block/vpc.c | 2 +-
121
blockdev.c | 17 +-
122
blockjob.c | 2 +-
123
nbd/server.c | 47 ++--
124
stubs/graph-lock.c | 10 +
125
tests/unit/test-bdrv-drain.c | 387 +++++++++----------------------
126
util/async.c | 4 +
127
scripts/block-coroutine-wrapper.py | 133 ++++++++---
128
block/meson.build | 2 +
129
stubs/meson.build | 1 +
130
46 files changed, 1575 insertions(+), 1127 deletions(-)
131
create mode 100644 include/block/graph-lock.h
132
create mode 100644 include/qemu/clang-tsa.h
133
create mode 100644 block/graph-lock.c
134
create mode 100644 stubs/graph-lock.c
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
1
3
The only caller is bdrv_root_unref_child(), let's just do the logic
4
directly in it. It simplifies further conversion of
5
bdrv_root_unref_child() to transaction actions.
6
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
8
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
9
Message-Id: <20221107163558.618889-2-vsementsov@yandex-team.ru>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block.c | 46 +++++++++++++++++++---------------------------
14
1 file changed, 19 insertions(+), 27 deletions(-)
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 @@ static BdrvChild *bdrv_attach_child_noperm(BlockDriverState *parent_bs,
21
tran, errp);
22
}
23
24
-static void bdrv_detach_child(BdrvChild *child)
25
-{
26
- BlockDriverState *old_bs = child->bs;
27
-
28
- GLOBAL_STATE_CODE();
29
- bdrv_replace_child_noperm(child, NULL);
30
- bdrv_child_free(child);
31
-
32
- if (old_bs) {
33
- /*
34
- * Update permissions for old node. We're just taking a parent away, so
35
- * we're loosening restrictions. Errors of permission update are not
36
- * fatal in this case, ignore them.
37
- */
38
- bdrv_refresh_perms(old_bs, NULL);
39
-
40
- /*
41
- * When the parent requiring a non-default AioContext is removed, the
42
- * node moves back to the main AioContext
43
- */
44
- bdrv_try_change_aio_context(old_bs, qemu_get_aio_context(), NULL, NULL);
45
- }
46
-}
47
-
48
/*
49
* This function steals the reference to child_bs from the caller.
50
* That reference is later dropped by bdrv_root_unref_child().
51
@@ -XXX,XX +XXX,XX @@ out:
52
/* Callers must ensure that child->frozen is false. */
53
void bdrv_root_unref_child(BdrvChild *child)
54
{
55
- BlockDriverState *child_bs;
56
+ BlockDriverState *child_bs = child->bs;
57
58
GLOBAL_STATE_CODE();
59
+ bdrv_replace_child_noperm(child, NULL);
60
+ bdrv_child_free(child);
61
+
62
+ if (child_bs) {
63
+ /*
64
+ * Update permissions for old node. We're just taking a parent away, so
65
+ * we're loosening restrictions. Errors of permission update are not
66
+ * fatal in this case, ignore them.
67
+ */
68
+ bdrv_refresh_perms(child_bs, NULL);
69
+
70
+ /*
71
+ * When the parent requiring a non-default AioContext is removed, the
72
+ * node moves back to the main AioContext
73
+ */
74
+ bdrv_try_change_aio_context(child_bs, qemu_get_aio_context(), NULL,
75
+ NULL);
76
+ }
77
78
- child_bs = child->bs;
79
- bdrv_detach_child(child);
80
bdrv_unref(child_bs);
81
}
82
83
--
84
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
1
3
Drop this simple wrapper used only in one place. We have too many graph
4
modifying functions even without it.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
8
Message-Id: <20221107163558.618889-3-vsementsov@yandex-team.ru>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block.c | 15 +--------------
13
1 file changed, 1 insertion(+), 14 deletions(-)
14
15
diff --git a/block.c b/block.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block.c
18
+++ b/block.c
19
@@ -XXX,XX +XXX,XX @@ static bool bdrv_recurse_has_child(BlockDriverState *bs,
20
static void bdrv_replace_child_noperm(BdrvChild *child,
21
BlockDriverState *new_bs);
22
static void bdrv_remove_child(BdrvChild *child, Transaction *tran);
23
-static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
24
- Transaction *tran);
25
26
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
27
BlockReopenQueue *queue,
28
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_child(BdrvChild *child, Transaction *tran)
29
tran_add(tran, &bdrv_remove_child_drv, child);
30
}
31
32
-/*
33
- * A function to remove backing-chain child of @bs if exists: cow child for
34
- * format nodes (always .backing) and filter child for filters (may be .file or
35
- * .backing)
36
- */
37
-static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
38
- Transaction *tran)
39
-{
40
- bdrv_remove_child(bdrv_filter_or_cow_child(bs), tran);
41
-}
42
-
43
static int bdrv_replace_node_noperm(BlockDriverState *from,
44
BlockDriverState *to,
45
bool auto_skip, Transaction *tran,
46
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
47
}
48
49
if (detach_subchain) {
50
- bdrv_remove_filter_or_cow_child(to_cow_parent, tran);
51
+ bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran);
52
}
53
54
found = g_hash_table_new(NULL, NULL);
55
--
56
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
1
3
Allow passing external Transaction pointer, stop creating extra
4
Transaction objects.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
8
Message-Id: <20221107163558.618889-4-vsementsov@yandex-team.ru>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block.c | 31 ++++++++++++++++++++-----------
13
1 file changed, 20 insertions(+), 11 deletions(-)
14
15
diff --git a/block.c b/block.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block.c
18
+++ b/block.c
19
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
20
}
21
22
23
-static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
24
+/* @tran is allowed to be NULL. In this case no rollback is possible */
25
+static int bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran,
26
+ Error **errp)
27
{
28
int ret;
29
- Transaction *tran = tran_new();
30
+ Transaction *local_tran = NULL;
31
g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
32
GLOBAL_STATE_CODE();
33
34
+ if (!tran) {
35
+ tran = local_tran = tran_new();
36
+ }
37
+
38
ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
39
- tran_finalize(tran, ret);
40
+
41
+ if (local_tran) {
42
+ tran_finalize(local_tran, ret);
43
+ }
44
45
return ret;
46
}
47
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
48
49
bdrv_child_set_perm(c, perm, shared, tran);
50
51
- ret = bdrv_refresh_perms(c->bs, &local_err);
52
+ ret = bdrv_refresh_perms(c->bs, tran, &local_err);
53
54
tran_finalize(tran, ret);
55
56
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
57
goto out;
58
}
59
60
- ret = bdrv_refresh_perms(child_bs, errp);
61
+ ret = bdrv_refresh_perms(child_bs, tran, errp);
62
63
out:
64
tran_finalize(tran, ret);
65
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
66
goto out;
67
}
68
69
- ret = bdrv_refresh_perms(parent_bs, errp);
70
+ ret = bdrv_refresh_perms(parent_bs, tran, errp);
71
if (ret < 0) {
72
goto out;
73
}
74
@@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child)
75
* we're loosening restrictions. Errors of permission update are not
76
* fatal in this case, ignore them.
77
*/
78
- bdrv_refresh_perms(child_bs, NULL);
79
+ bdrv_refresh_perms(child_bs, NULL, NULL);
80
81
/*
82
* When the parent requiring a non-default AioContext is removed, the
83
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
84
goto out;
85
}
86
87
- ret = bdrv_refresh_perms(bs, errp);
88
+ ret = bdrv_refresh_perms(bs, tran, errp);
89
out:
90
tran_finalize(tran, ret);
91
92
@@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
93
goto out;
94
}
95
96
- ret = bdrv_refresh_perms(bs_new, errp);
97
+ ret = bdrv_refresh_perms(bs_new, tran, errp);
98
out:
99
tran_finalize(tran, ret);
100
101
@@ -XXX,XX +XXX,XX @@ int bdrv_activate(BlockDriverState *bs, Error **errp)
102
*/
103
if (bs->open_flags & BDRV_O_INACTIVE) {
104
bs->open_flags &= ~BDRV_O_INACTIVE;
105
- ret = bdrv_refresh_perms(bs, errp);
106
+ ret = bdrv_refresh_perms(bs, NULL, errp);
107
if (ret < 0) {
108
bs->open_flags |= BDRV_O_INACTIVE;
109
return ret;
110
@@ -XXX,XX +XXX,XX @@ static int bdrv_inactivate_recurse(BlockDriverState *bs)
111
* We only tried to loosen restrictions, so errors are not fatal, ignore
112
* them.
113
*/
114
- bdrv_refresh_perms(bs, NULL);
115
+ bdrv_refresh_perms(bs, NULL, NULL);
116
117
/* Recursively inactivate children */
118
QLIST_FOREACH(child, &bs->children, next) {
119
--
120
2.38.1
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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.
2
4
3
We don't protect bdrv->aio_context with the graph rwlock,
5
As this permission is geared towards whether the guest-visible data is
4
so these assertions are not needed
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.
5
10
6
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20221207131838.239125-13-kwolf@redhat.com>
9
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
12
---
13
block.c | 3 ---
13
block.c | 6 +++++-
14
1 file changed, 3 deletions(-)
14
1 file changed, 5 insertions(+), 1 deletion(-)
15
15
16
diff --git a/block.c b/block.c
16
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
18
--- a/block.c
19
+++ b/block.c
19
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ static void bdrv_detach_aio_context(BlockDriverState *bs)
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
21
if (bs->quiesce_counter) {
21
assert(role == &child_backing || role == &child_file);
22
aio_enable_external(bs->aio_context);
22
23
}
23
if (!backing) {
24
- assert_bdrv_graph_writable(bs);
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
25
bs->aio_context = NULL;
25
+
26
}
26
/* Apart from the modifications below, the same permissions are
27
27
* forwarded and left alone as for filters */
28
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_aio_context(BlockDriverState *bs,
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
29
aio_disable_external(new_context);
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
30
}
30
31
31
/* bs->file always needs to be consistent because of the metadata. We
32
- assert_bdrv_graph_writable(bs);
32
* can never allow other users to resize or write to it. */
33
bs->aio_context = new_context;
33
- perm |= BLK_PERM_CONSISTENT_READ;
34
34
+ if (!(flags & BDRV_O_NO_IO)) {
35
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
35
+ perm |= BLK_PERM_CONSISTENT_READ;
36
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_aio_context_commit(void *opaque)
36
+ }
37
BlockDriverState *bs = (BlockDriverState *) state->bs;
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
38
AioContext *new_context = state->new_ctx;
38
} else {
39
AioContext *old_context = bdrv_get_aio_context(bs);
39
/* We want consistent read from backing files if the parent needs it.
40
- assert_bdrv_graph_writable(bs);
41
42
/*
43
* Take the old AioContex when detaching it from bs.
44
--
40
--
45
2.38.1
41
2.13.6
42
43
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
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>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Message-Id: <20221207131838.239125-16-kwolf@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
3
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
13
---
6
include/block/block_int-common.h | 4 ++--
14
tests/qemu-iotests/197 | 4 ++++
7
include/block/graph-lock.h | 4 ++--
15
tests/qemu-iotests/common.filter | 3 ++-
8
block.c | 4 ++--
16
2 files changed, 6 insertions(+), 1 deletion(-)
9
3 files changed, 6 insertions(+), 6 deletions(-)
10
17
11
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
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
12
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
13
--- a/include/block/block_int-common.h
35
--- a/tests/qemu-iotests/common.filter
14
+++ b/include/block/block_int-common.h
36
+++ b/tests/qemu-iotests/common.filter
15
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
37
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
16
void (*activate)(BdrvChild *child, Error **errp);
38
-e "s# log_size=[0-9]\\+##g" \
17
int (*inactivate)(BdrvChild *child);
39
-e "s# refcount_bits=[0-9]\\+##g" \
18
40
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
19
- void (*attach)(BdrvChild *child);
41
- -e "s# iter-time=[0-9]\\+##g"
20
- void (*detach)(BdrvChild *child);
42
+ -e "s# iter-time=[0-9]\\+##g" \
21
+ void GRAPH_WRLOCK_PTR (*attach)(BdrvChild *child);
43
+ -e "s# force_size=\\(on\\|off\\)##g"
22
+ void GRAPH_WRLOCK_PTR (*detach)(BdrvChild *child);
23
24
/*
25
* Notifies the parent that the filename of its child has changed (e.g.
26
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
27
index XXXXXXX..XXXXXXX 100644
28
--- a/include/block/graph-lock.h
29
+++ b/include/block/graph-lock.h
30
@@ -XXX,XX +XXX,XX @@ bdrv_graph_rdunlock_main_loop(void);
31
* or there is at least a reader helding the rdlock.
32
* In this way an incoming writer is aware of the read and waits.
33
*/
34
-void assert_bdrv_graph_readable(void);
35
+void GRAPH_RDLOCK assert_bdrv_graph_readable(void);
36
37
/*
38
* assert_bdrv_graph_writable:
39
* Make sure that the writer is the main loop and has set @has_writer,
40
* so that incoming readers will pause.
41
*/
42
-void assert_bdrv_graph_writable(void);
43
+void GRAPH_WRLOCK assert_bdrv_graph_writable(void);
44
45
/*
46
* Calling this function tells TSA that we know that the lock is effectively
47
diff --git a/block.c b/block.c
48
index XXXXXXX..XXXXXXX 100644
49
--- a/block.c
50
+++ b/block.c
51
@@ -XXX,XX +XXX,XX @@ static void bdrv_inherited_options(BdrvChildRole role, bool parent_is_format,
52
*child_flags = flags;
53
}
44
}
54
45
55
-static void bdrv_child_cb_attach(BdrvChild *child)
46
_filter_img_info()
56
+static void GRAPH_WRLOCK bdrv_child_cb_attach(BdrvChild *child)
57
{
58
BlockDriverState *bs = child->opaque;
59
60
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child)
61
}
62
}
63
64
-static void bdrv_child_cb_detach(BdrvChild *child)
65
+static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child)
66
{
67
BlockDriverState *bs = child->opaque;
68
69
--
47
--
70
2.38.1
48
2.13.6
49
50
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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.
2
4
3
bdrv_common_block_status_above() is a g_c_w, and it is being called by
5
One reason for this is that bdrv_drain_recurse() can be called multiple
4
many "wrapper" functions like bdrv_is_allocated(),
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
5
bdrv_is_allocated_above() and bdrv_block_status_above().
7
once. The separation is necessary to fix this bug.
6
8
7
Because we want to eventually split the coroutine from non-coroutine
9
The other reason is that we intend to go to a model where we call all
8
case in g_c_w, create duplicate wrappers that take care of directly
10
driver callbacks first, and only then start polling. This is not fully
9
calling the same coroutine functions called in the g_c_w.
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.
10
15
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
16
Cc: qemu-stable@nongnu.org
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
14
Message-Id: <20221128142337.657646-2-eesposit@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
16
---
19
---
17
include/block/block-io.h | 15 +++++++++++
20
block/io.c | 14 +++++++++++---
18
block/io.c | 58 +++++++++++++++++++++++++++++++++++++---
21
1 file changed, 11 insertions(+), 3 deletions(-)
19
2 files changed, 70 insertions(+), 3 deletions(-)
20
22
21
diff --git a/include/block/block-io.h b/include/block/block-io.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block-io.h
24
+++ b/include/block/block-io.h
25
@@ -XXX,XX +XXX,XX @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
26
int bdrv_block_status(BlockDriverState *bs, int64_t offset,
27
int64_t bytes, int64_t *pnum, int64_t *map,
28
BlockDriverState **file);
29
+
30
+int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs,
31
+ BlockDriverState *base,
32
+ int64_t offset, int64_t bytes,
33
+ int64_t *pnum, int64_t *map,
34
+ BlockDriverState **file);
35
int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base,
36
int64_t offset, int64_t bytes, int64_t *pnum,
37
int64_t *map, BlockDriverState **file);
38
+
39
+int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset,
40
+ int64_t bytes, int64_t *pnum);
41
int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
42
int64_t *pnum);
43
+
44
+int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
45
+ BlockDriverState *base,
46
+ bool include_base, int64_t offset,
47
+ int64_t bytes, int64_t *pnum);
48
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
49
bool include_base, int64_t offset, int64_t bytes,
50
int64_t *pnum);
51
+
52
int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
53
int64_t bytes);
54
55
diff --git a/block/io.c b/block/io.c
23
diff --git a/block/io.c b/block/io.c
56
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
57
--- a/block/io.c
25
--- a/block/io.c
58
+++ b/block/io.c
26
+++ b/block/io.c
59
@@ -XXX,XX +XXX,XX @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
60
return ret;
28
bdrv_wakeup(bs);
61
}
29
}
62
30
63
+int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs,
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
64
+ BlockDriverState *base,
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
65
+ int64_t offset, int64_t bytes,
33
{
66
+ int64_t *pnum, int64_t *map,
34
+ BdrvChild *child, *tmp;
67
+ BlockDriverState **file)
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
68
+{
36
69
+ IO_CODE();
37
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
70
+ return bdrv_co_common_block_status_above(bs, base, false, true, offset,
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
71
+ bytes, pnum, map, file, NULL);
39
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
72
+}
40
bdrv_coroutine_enter(bs, data.co);
41
BDRV_POLL_WHILE(bs, !data.done);
73
+
42
+
74
int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base,
43
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
75
int64_t offset, int64_t bytes, int64_t *pnum,
44
+ bdrv_drain_invoke(child->bs, begin);
76
int64_t *map, BlockDriverState **file)
45
+ }
77
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
78
return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO);
79
}
46
}
80
47
81
+int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset,
48
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
82
+ int64_t bytes, int64_t *pnum)
49
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
83
+{
50
BdrvChild *child, *tmp;
84
+ int ret;
51
bool waited;
85
+ int64_t dummy;
52
86
+ IO_CODE();
53
- /* Ensure any pending metadata writes are submitted to bs->file. */
87
+
54
- bdrv_drain_invoke(bs, begin);
88
+ ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset,
55
-
89
+ bytes, pnum ? pnum : &dummy, NULL,
56
/* Wait for drained requests to finish */
90
+ NULL, NULL);
57
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
91
+ if (ret < 0) {
58
92
+ return ret;
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
93
+ }
60
bdrv_parent_drained_begin(bs);
94
+ return !!(ret & BDRV_BLOCK_ALLOCATED);
61
}
95
+}
62
96
+
63
+ bdrv_drain_invoke(bs, true);
97
int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
64
bdrv_drain_recurse(bs, true);
98
int64_t *pnum)
99
{
100
@@ -XXX,XX +XXX,XX @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
101
return !!(ret & BDRV_BLOCK_ALLOCATED);
102
}
65
}
103
66
104
+/* See bdrv_is_allocated_above for documentation */
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
105
+int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
68
}
106
+ BlockDriverState *base,
69
107
+ bool include_base, int64_t offset,
70
bdrv_parent_drained_end(bs);
108
+ int64_t bytes, int64_t *pnum)
71
+ bdrv_drain_invoke(bs, false);
109
+{
72
bdrv_drain_recurse(bs, false);
110
+ int depth;
73
aio_enable_external(bdrv_get_aio_context(bs));
111
+ int ret;
74
}
112
+ IO_CODE();
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
113
+
76
aio_context_acquire(aio_context);
114
+ ret = bdrv_co_common_block_status_above(top, base, include_base, false,
77
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
115
+ offset, bytes, pnum, NULL, NULL,
78
if (aio_context == bdrv_get_aio_context(bs)) {
116
+ &depth);
79
+ /* FIXME Calling this multiple times is wrong */
117
+ if (ret < 0) {
80
+ bdrv_drain_invoke(bs, true);
118
+ return ret;
81
waited |= bdrv_drain_recurse(bs, true);
119
+ }
82
}
120
+
83
}
121
+ if (ret & BDRV_BLOCK_ALLOCATED) {
84
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
122
+ return depth;
85
aio_context_acquire(aio_context);
123
+ }
86
aio_enable_external(aio_context);
124
+ return 0;
87
bdrv_parent_drained_end(bs);
125
+}
88
+ bdrv_drain_invoke(bs, false);
126
+
89
bdrv_drain_recurse(bs, false);
127
/*
90
aio_context_release(aio_context);
128
* Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP]
129
*
130
@@ -XXX,XX +XXX,XX @@ int bdrv_is_allocated_above(BlockDriverState *top,
131
int64_t bytes, int64_t *pnum)
132
{
133
int depth;
134
- int ret = bdrv_common_block_status_above(top, base, include_base, false,
135
- offset, bytes, pnum, NULL, NULL,
136
- &depth);
137
+ int ret;
138
IO_CODE();
139
+
140
+ ret = bdrv_common_block_status_above(top, base, include_base, false,
141
+ offset, bytes, pnum, NULL, NULL,
142
+ &depth);
143
if (ret < 0) {
144
return ret;
145
}
91
}
146
--
92
--
147
2.38.1
93
2.13.6
94
95
diff view generated by jsdifflib
1
The generated coroutine wrappers already take care to take the lock in
1
bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver
2
the non-coroutine path, and assume that the lock is already taken in the
2
callback inside its polling loop. This means that how many times it got
3
coroutine path.
3
called for each node depended on long it had to poll the event loop.
4
4
5
The only thing we need to do for the wrapped function is adding the
5
This is obviously not right and results in nodes that stay drained even
6
GRAPH_RDLOCK annotation. Doing so also allows us to mark the
6
after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per
7
corresponding callbacks in BlockDriver as GRAPH_RDLOCK_PTR.
7
node.
8
8
9
Fix bdrv_drain_all_begin() to call the callback only once, too.
10
11
Cc: qemu-stable@nongnu.org
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20221207131838.239125-19-kwolf@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
---
14
block/coroutines.h | 17 ++++++++++-------
15
block/io.c | 3 +--
15
include/block/block_int-common.h | 20 +++++++++-----------
16
1 file changed, 1 insertion(+), 2 deletions(-)
16
block.c | 2 ++
17
block/io.c | 2 ++
18
4 files changed, 23 insertions(+), 18 deletions(-)
19
17
20
diff --git a/block/coroutines.h b/block/coroutines.h
21
index XXXXXXX..XXXXXXX 100644
22
--- a/block/coroutines.h
23
+++ b/block/coroutines.h
24
@@ -XXX,XX +XXX,XX @@
25
* the I/O API.
26
*/
27
28
-int coroutine_fn bdrv_co_check(BlockDriverState *bs,
29
- BdrvCheckResult *res, BdrvCheckMode fix);
30
-int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp);
31
+int coroutine_fn GRAPH_RDLOCK
32
+bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
33
+
34
+int coroutine_fn GRAPH_RDLOCK
35
+bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp);
36
37
int coroutine_fn
38
bdrv_co_common_block_status_above(BlockDriverState *bs,
39
@@ -XXX,XX +XXX,XX @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
40
BlockDriverState **file,
41
int *depth);
42
43
-int coroutine_fn bdrv_co_readv_vmstate(BlockDriverState *bs,
44
- QEMUIOVector *qiov, int64_t pos);
45
-int coroutine_fn bdrv_co_writev_vmstate(BlockDriverState *bs,
46
- QEMUIOVector *qiov, int64_t pos);
47
+int coroutine_fn GRAPH_RDLOCK
48
+bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
49
+
50
+int coroutine_fn GRAPH_RDLOCK
51
+bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
52
53
int coroutine_fn
54
nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
55
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
56
index XXXXXXX..XXXXXXX 100644
57
--- a/include/block/block_int-common.h
58
+++ b/include/block/block_int-common.h
59
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
60
/*
61
* Invalidate any cached meta-data.
62
*/
63
- void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs,
64
- Error **errp);
65
+ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_invalidate_cache)(
66
+ BlockDriverState *bs, Error **errp);
67
68
/*
69
* Flushes all data for all layers by calling bdrv_co_flush for underlying
70
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
71
Error **errp);
72
BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs);
73
74
- int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
75
- QEMUIOVector *qiov,
76
- int64_t pos);
77
- int coroutine_fn (*bdrv_load_vmstate)(BlockDriverState *bs,
78
- QEMUIOVector *qiov,
79
- int64_t pos);
80
+ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_save_vmstate)(
81
+ BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
82
+
83
+ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_load_vmstate)(
84
+ BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
85
86
/* removable device specific */
87
bool (*bdrv_is_inserted)(BlockDriverState *bs);
88
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
89
* Returns 0 for completed check, -errno for internal errors.
90
* The check results are stored in result.
91
*/
92
- int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs,
93
- BdrvCheckResult *result,
94
- BdrvCheckMode fix);
95
+ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_check)(
96
+ BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix);
97
98
void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
99
100
diff --git a/block.c b/block.c
101
index XXXXXXX..XXXXXXX 100644
102
--- a/block.c
103
+++ b/block.c
104
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_check(BlockDriverState *bs,
105
BdrvCheckResult *res, BdrvCheckMode fix)
106
{
107
IO_CODE();
108
+ assert_bdrv_graph_readable();
109
if (bs->drv == NULL) {
110
return -ENOMEDIUM;
111
}
112
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp)
113
IO_CODE();
114
115
assert(!(bs->open_flags & BDRV_O_INACTIVE));
116
+ assert_bdrv_graph_readable();
117
118
if (bs->drv->bdrv_co_invalidate_cache) {
119
bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
120
diff --git a/block/io.c b/block/io.c
18
diff --git a/block/io.c b/block/io.c
121
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
122
--- a/block/io.c
20
--- a/block/io.c
123
+++ b/block/io.c
21
+++ b/block/io.c
124
@@ -XXX,XX +XXX,XX @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
125
BlockDriverState *child_bs = bdrv_primary_bs(bs);
23
aio_context_acquire(aio_context);
126
int ret;
24
bdrv_parent_drained_begin(bs);
127
IO_CODE();
25
aio_disable_external(aio_context);
128
+ assert_bdrv_graph_readable();
26
+ bdrv_drain_invoke(bs, true);
129
27
aio_context_release(aio_context);
130
ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL);
28
131
if (ret < 0) {
29
if (!g_slist_find(aio_ctxs, aio_context)) {
132
@@ -XXX,XX +XXX,XX @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
133
BlockDriverState *child_bs = bdrv_primary_bs(bs);
31
aio_context_acquire(aio_context);
134
int ret;
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
135
IO_CODE();
33
if (aio_context == bdrv_get_aio_context(bs)) {
136
+ assert_bdrv_graph_readable();
34
- /* FIXME Calling this multiple times is wrong */
137
35
- bdrv_drain_invoke(bs, true);
138
ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL);
36
waited |= bdrv_drain_recurse(bs, true);
139
if (ret < 0) {
37
}
38
}
140
--
39
--
141
2.38.1
40
2.13.6
41
42
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@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
Block layer graph operations are always run under BQL in the main loop.
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
This is proved by the assertion qemu_in_main_thread() and its wrapper
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
macro GLOBAL_STATE_CODE.
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
10
tests/Makefile.include | 2 +
11
2 files changed, 139 insertions(+)
12
create mode 100644 tests/test-bdrv-drain.c
6
13
7
However, there are also concurrent coroutines running in other iothreads
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
8
that always try to traverse the graph. Currently this is protected
9
(among various other things) by the AioContext lock, but once this is
10
removed, we need to make sure that reads do not happen while modifying
11
the graph.
12
13
We distinguish between writer (main loop, under BQL) that modifies the
14
graph, and readers (all other coroutines running in various AioContext),
15
that go through the graph edges, reading ->parents and->children.
16
17
The writer (main loop) has "exclusive" access, so it first waits for any
18
current read to finish, and then prevents incoming ones from entering
19
while it has the exclusive access.
20
21
The readers (coroutines in multiple AioContext) are free to access the
22
graph as long the writer is not modifying the graph. In case it is, they
23
go in a CoQueue and sleep until the writer is done.
24
25
If a coroutine changes AioContext, the counter in the original and new
26
AioContext are left intact, since the writer does not care where the
27
reader is, but only if there is one.
28
29
As a result, some AioContexts might have a negative reader count, to
30
balance the positive count of the AioContext that took the lock. This
31
also means that when an AioContext is deleted it may have a nonzero
32
reader count. In that case we transfer the count to a global shared
33
counter so that the writer is always aware of all readers.
34
35
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
36
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
37
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
38
Message-Id: <20221207131838.239125-3-kwolf@redhat.com>
39
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
40
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
41
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
42
---
43
include/block/aio.h | 9 ++
44
include/block/block_int.h | 1 +
45
include/block/graph-lock.h | 139 ++++++++++++++++++++
46
block/graph-lock.c | 261 +++++++++++++++++++++++++++++++++++++
47
block/meson.build | 1 +
48
5 files changed, 411 insertions(+)
49
create mode 100644 include/block/graph-lock.h
50
create mode 100644 block/graph-lock.c
51
52
diff --git a/include/block/aio.h b/include/block/aio.h
53
index XXXXXXX..XXXXXXX 100644
54
--- a/include/block/aio.h
55
+++ b/include/block/aio.h
56
@@ -XXX,XX +XXX,XX @@
57
#include "qemu/event_notifier.h"
58
#include "qemu/thread.h"
59
#include "qemu/timer.h"
60
+#include "block/graph-lock.h"
61
62
typedef struct BlockAIOCB BlockAIOCB;
63
typedef void BlockCompletionFunc(void *opaque, int ret);
64
@@ -XXX,XX +XXX,XX @@ struct AioContext {
65
/* Used by AioContext users to protect from multi-threaded access. */
66
QemuRecMutex lock;
67
68
+ /*
69
+ * Keep track of readers and writers of the block layer graph.
70
+ * This is essential to avoid performing additions and removal
71
+ * of nodes and edges from block graph while some
72
+ * other thread is traversing it.
73
+ */
74
+ BdrvGraphRWlock *bdrv_graph;
75
+
76
/* The list of registered AIO handlers. Protected by ctx->list_lock. */
77
AioHandlerList aio_handlers;
78
79
diff --git a/include/block/block_int.h b/include/block/block_int.h
80
index XXXXXXX..XXXXXXX 100644
81
--- a/include/block/block_int.h
82
+++ b/include/block/block_int.h
83
@@ -XXX,XX +XXX,XX @@
84
85
#include "block_int-global-state.h"
86
#include "block_int-io.h"
87
+#include "block/graph-lock.h"
88
89
/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */
90
91
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
92
new file mode 100644
15
new file mode 100644
93
index XXXXXXX..XXXXXXX
16
index XXXXXXX..XXXXXXX
94
--- /dev/null
17
--- /dev/null
95
+++ b/include/block/graph-lock.h
18
+++ b/tests/test-bdrv-drain.c
96
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
97
+/*
20
+/*
98
+ * Graph lock: rwlock to protect block layer graph manipulations (add/remove
21
+ * Block node draining tests
99
+ * edges and nodes)
100
+ *
22
+ *
101
+ * Copyright (c) 2022 Red Hat
23
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
102
+ *
24
+ *
103
+ * This library is free software; you can redistribute it and/or
25
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
104
+ * modify it under the terms of the GNU Lesser General Public
26
+ * of this software and associated documentation files (the "Software"), to deal
105
+ * License as published by the Free Software Foundation; either
27
+ * in the Software without restriction, including without limitation the rights
106
+ * version 2.1 of the License, or (at your option) any later version.
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:
107
+ *
31
+ *
108
+ * This library is distributed in the hope that it will be useful,
32
+ * The above copyright notice and this permission notice shall be included in
109
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
33
+ * all copies or substantial portions of the Software.
110
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
111
+ * Lesser General Public License for more details.
112
+ *
34
+ *
113
+ * You should have received a copy of the GNU Lesser General Public
35
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
114
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
36
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
115
+ */
37
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
116
+#ifndef GRAPH_LOCK_H
38
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
117
+#define GRAPH_LOCK_H
39
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
118
+
40
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
119
+#include "qemu/osdep.h"
41
+ * THE SOFTWARE.
120
+
121
+#include "qemu/coroutine.h"
122
+
123
+/**
124
+ * Graph Lock API
125
+ * This API provides a rwlock used to protect block layer
126
+ * graph modifications like edge (BdrvChild) and node (BlockDriverState)
127
+ * addition and removal.
128
+ * Currently we have 1 writer only, the Main loop, and many
129
+ * readers, mostly coroutines running in other AioContext thus other threads.
130
+ *
131
+ * We distinguish between writer (main loop, under BQL) that modifies the
132
+ * graph, and readers (all other coroutines running in various AioContext),
133
+ * that go through the graph edges, reading
134
+ * BlockDriverState ->parents and->children.
135
+ *
136
+ * The writer (main loop) has an "exclusive" access, so it first waits for
137
+ * current read to finish, and then prevents incoming ones from
138
+ * entering while it has the exclusive access.
139
+ *
140
+ * The readers (coroutines in multiple AioContext) are free to
141
+ * access the graph as long the writer is not modifying the graph.
142
+ * In case it is, they go in a CoQueue and sleep until the writer
143
+ * is done.
144
+ *
145
+ * If a coroutine changes AioContext, the counter in the original and new
146
+ * AioContext are left intact, since the writer does not care where is the
147
+ * reader, but only if there is one.
148
+ * As a result, some AioContexts might have a negative reader count, to
149
+ * balance the positive count of the AioContext that took the lock.
150
+ * This also means that when an AioContext is deleted it may have a nonzero
151
+ * reader count. In that case we transfer the count to a global shared counter
152
+ * so that the writer is always aware of all readers.
153
+ */
154
+typedef struct BdrvGraphRWlock BdrvGraphRWlock;
155
+
156
+/*
157
+ * register_aiocontext:
158
+ * Add AioContext @ctx to the list of AioContext.
159
+ * This list is used to obtain the total number of readers
160
+ * currently running the graph.
161
+ */
162
+void register_aiocontext(AioContext *ctx);
163
+
164
+/*
165
+ * unregister_aiocontext:
166
+ * Removes AioContext @ctx to the list of AioContext.
167
+ */
168
+void unregister_aiocontext(AioContext *ctx);
169
+
170
+/*
171
+ * bdrv_graph_wrlock:
172
+ * Start an exclusive write operation to modify the graph. This means we are
173
+ * adding or removing an edge or a node in the block layer graph. Nobody else
174
+ * is allowed to access the graph.
175
+ *
176
+ * Must only be called from outside bdrv_graph_co_rdlock.
177
+ *
178
+ * The wrlock can only be taken from the main loop, with BQL held, as only the
179
+ * main loop is allowed to modify the graph.
180
+ *
181
+ * This function polls. Callers must not hold the lock of any AioContext other
182
+ * than the current one.
183
+ */
184
+void bdrv_graph_wrlock(void);
185
+
186
+/*
187
+ * bdrv_graph_wrunlock:
188
+ * Write finished, reset global has_writer to 0 and restart
189
+ * all readers that are waiting.
190
+ */
191
+void bdrv_graph_wrunlock(void);
192
+
193
+/*
194
+ * bdrv_graph_co_rdlock:
195
+ * Read the bs graph. This usually means traversing all nodes in
196
+ * the graph, therefore it can't happen while another thread is
197
+ * modifying it.
198
+ * Increases the reader counter of the current aiocontext,
199
+ * and if has_writer is set, it means that the writer is modifying
200
+ * the graph, therefore wait in a coroutine queue.
201
+ * The writer will then wake this coroutine once it is done.
202
+ *
203
+ * This lock should be taken from Iothreads (IO_CODE() class of functions)
204
+ * because it signals the writer that there are some
205
+ * readers currently running, or waits until the current
206
+ * write is finished before continuing.
207
+ * Calling this function from the Main Loop with BQL held
208
+ * is not necessary, since the Main Loop itself is the only
209
+ * writer, thus won't be able to read and write at the same time.
210
+ * The only exception to that is when we can't take the lock in the
211
+ * function/coroutine itself, and need to delegate the caller (usually main
212
+ * loop) to take it and wait that the coroutine ends, so that
213
+ * we always signal that a reader is running.
214
+ */
215
+void coroutine_fn bdrv_graph_co_rdlock(void);
216
+
217
+/*
218
+ * bdrv_graph_rdunlock:
219
+ * Read terminated, decrease the count of readers in the current aiocontext.
220
+ * If the writer is waiting for reads to finish (has_writer == 1), signal
221
+ * the writer that we are done via aio_wait_kick() to let it continue.
222
+ */
223
+void coroutine_fn bdrv_graph_co_rdunlock(void);
224
+
225
+/*
226
+ * bdrv_graph_rd{un}lock_main_loop:
227
+ * Just a placeholder to mark where the graph rdlock should be taken
228
+ * in the main loop. It is just asserting that we are not
229
+ * in a coroutine and in GLOBAL_STATE_CODE.
230
+ */
231
+void bdrv_graph_rdlock_main_loop(void);
232
+void bdrv_graph_rdunlock_main_loop(void);
233
+
234
+#endif /* GRAPH_LOCK_H */
235
+
236
diff --git a/block/graph-lock.c b/block/graph-lock.c
237
new file mode 100644
238
index XXXXXXX..XXXXXXX
239
--- /dev/null
240
+++ b/block/graph-lock.c
241
@@ -XXX,XX +XXX,XX @@
242
+/*
243
+ * Graph lock: rwlock to protect block layer graph manipulations (add/remove
244
+ * edges and nodes)
245
+ *
246
+ * Copyright (c) 2022 Red Hat
247
+ *
248
+ * This library is free software; you can redistribute it and/or
249
+ * modify it under the terms of the GNU Lesser General Public
250
+ * License as published by the Free Software Foundation; either
251
+ * version 2.1 of the License, or (at your option) any later version.
252
+ *
253
+ * This library is distributed in the hope that it will be useful,
254
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
255
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
256
+ * Lesser General Public License for more details.
257
+ *
258
+ * You should have received a copy of the GNU Lesser General Public
259
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
260
+ */
42
+ */
261
+
43
+
262
+#include "qemu/osdep.h"
44
+#include "qemu/osdep.h"
263
+#include "qemu/main-loop.h"
264
+#include "block/graph-lock.h"
265
+#include "block/block.h"
45
+#include "block/block.h"
266
+#include "block/block_int.h"
46
+#include "sysemu/block-backend.h"
47
+#include "qapi/error.h"
267
+
48
+
268
+/* Protects the list of aiocontext and orphaned_reader_count */
49
+typedef struct BDRVTestState {
269
+static QemuMutex aio_context_list_lock;
50
+ int drain_count;
51
+} BDRVTestState;
270
+
52
+
271
+/* Written and read with atomic operations. */
53
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
272
+static int has_writer;
54
+{
55
+ BDRVTestState *s = bs->opaque;
56
+ s->drain_count++;
57
+}
273
+
58
+
274
+/*
59
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
275
+ * A reader coroutine could move from an AioContext to another.
60
+{
276
+ * If this happens, there is no problem from the point of view of
61
+ BDRVTestState *s = bs->opaque;
277
+ * counters. The problem is that the total count becomes
62
+ s->drain_count--;
278
+ * unbalanced if one of the two AioContexts gets deleted.
63
+}
279
+ * The count of readers must remain correct, so the AioContext's
280
+ * balance is transferred to this glboal variable.
281
+ * Protected by aio_context_list_lock.
282
+ */
283
+static uint32_t orphaned_reader_count;
284
+
64
+
285
+/* Queue of readers waiting for the writer to finish */
65
+static void bdrv_test_close(BlockDriverState *bs)
286
+static CoQueue reader_queue;
66
+{
67
+ BDRVTestState *s = bs->opaque;
68
+ g_assert_cmpint(s->drain_count, >, 0);
69
+}
287
+
70
+
288
+struct BdrvGraphRWlock {
71
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
289
+ /* How many readers are currently reading the graph. */
72
+ uint64_t offset, uint64_t bytes,
290
+ uint32_t reader_count;
73
+ QEMUIOVector *qiov, int flags)
74
+{
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);
291
+
80
+
292
+ /*
81
+ return 0;
293
+ * List of BdrvGraphRWlock kept in graph-lock.c
82
+}
294
+ * Protected by aio_context_list_lock
83
+
295
+ */
84
+static BlockDriver bdrv_test = {
296
+ QTAILQ_ENTRY(BdrvGraphRWlock) next_aio;
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,
297
+};
93
+};
298
+
94
+
299
+/*
95
+static void aio_ret_cb(void *opaque, int ret)
300
+ * List of BdrvGraphRWlock. This list ensures that each BdrvGraphRWlock
301
+ * can safely modify only its own counter, avoid reading/writing
302
+ * others and thus improving performances by avoiding cacheline bounces.
303
+ */
304
+static QTAILQ_HEAD(, BdrvGraphRWlock) aio_context_list =
305
+ QTAILQ_HEAD_INITIALIZER(aio_context_list);
306
+
307
+static void __attribute__((__constructor__)) bdrv_init_graph_lock(void)
308
+{
96
+{
309
+ qemu_mutex_init(&aio_context_list_lock);
97
+ int *aio_ret = opaque;
310
+ qemu_co_queue_init(&reader_queue);
98
+ *aio_ret = ret;
311
+}
99
+}
312
+
100
+
313
+void register_aiocontext(AioContext *ctx)
101
+static void test_drv_cb_drain_all(void)
314
+{
102
+{
315
+ ctx->bdrv_graph = g_new0(BdrvGraphRWlock, 1);
103
+ BlockBackend *blk;
316
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
104
+ BlockDriverState *bs;
317
+ assert(ctx->bdrv_graph->reader_count == 0);
105
+ BDRVTestState *s;
318
+ QTAILQ_INSERT_TAIL(&aio_context_list, ctx->bdrv_graph, next_aio);
106
+ BlockAIOCB *acb;
107
+ int aio_ret;
108
+
109
+ QEMUIOVector qiov;
110
+ struct iovec iov = {
111
+ .iov_base = NULL,
112
+ .iov_len = 0,
113
+ };
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
115
+
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);
121
+
122
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
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);
128
+
129
+ /* Now do the same while a request is pending */
130
+ aio_ret = -EINPROGRESS;
131
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
132
+ g_assert(acb != NULL);
133
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
134
+
135
+ g_assert_cmpint(s->drain_count, ==, 0);
136
+ bdrv_drain_all_begin();
137
+ g_assert_cmpint(aio_ret, ==, 0);
138
+ g_assert_cmpint(s->drain_count, ==, 1);
139
+ bdrv_drain_all_end();
140
+ g_assert_cmpint(s->drain_count, ==, 0);
141
+
142
+ bdrv_unref(bs);
143
+ blk_unref(blk);
319
+}
144
+}
320
+
145
+
321
+void unregister_aiocontext(AioContext *ctx)
146
+int main(int argc, char **argv)
322
+{
147
+{
323
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
148
+ bdrv_init();
324
+ orphaned_reader_count += ctx->bdrv_graph->reader_count;
149
+ qemu_init_main_loop(&error_abort);
325
+ QTAILQ_REMOVE(&aio_context_list, ctx->bdrv_graph, next_aio);
150
+
326
+ g_free(ctx->bdrv_graph);
151
+ g_test_init(&argc, &argv, NULL);
152
+
153
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
154
+
155
+ return g_test_run();
327
+}
156
+}
328
+
157
diff --git a/tests/Makefile.include b/tests/Makefile.include
329
+static uint32_t reader_count(void)
330
+{
331
+ BdrvGraphRWlock *brdv_graph;
332
+ uint32_t rd;
333
+
334
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
335
+
336
+ /* rd can temporarly be negative, but the total will *always* be >= 0 */
337
+ rd = orphaned_reader_count;
338
+ QTAILQ_FOREACH(brdv_graph, &aio_context_list, next_aio) {
339
+ rd += qatomic_read(&brdv_graph->reader_count);
340
+ }
341
+
342
+ /* shouldn't overflow unless there are 2^31 readers */
343
+ assert((int32_t)rd >= 0);
344
+ return rd;
345
+}
346
+
347
+void bdrv_graph_wrlock(void)
348
+{
349
+ GLOBAL_STATE_CODE();
350
+ assert(!qatomic_read(&has_writer));
351
+
352
+ /* Make sure that constantly arriving new I/O doesn't cause starvation */
353
+ bdrv_drain_all_begin_nopoll();
354
+
355
+ /*
356
+ * reader_count == 0: this means writer will read has_reader as 1
357
+ * reader_count >= 1: we don't know if writer read has_writer == 0 or 1,
358
+ * but we need to wait.
359
+ * Wait by allowing other coroutine (and possible readers) to continue.
360
+ */
361
+ do {
362
+ /*
363
+ * has_writer must be 0 while polling, otherwise we get a deadlock if
364
+ * any callback involved during AIO_WAIT_WHILE() tries to acquire the
365
+ * reader lock.
366
+ */
367
+ qatomic_set(&has_writer, 0);
368
+ AIO_WAIT_WHILE(qemu_get_aio_context(), reader_count() >= 1);
369
+ qatomic_set(&has_writer, 1);
370
+
371
+ /*
372
+ * We want to only check reader_count() after has_writer = 1 is visible
373
+ * to other threads. That way no more readers can sneak in after we've
374
+ * determined reader_count() == 0.
375
+ */
376
+ smp_mb();
377
+ } while (reader_count() >= 1);
378
+
379
+ bdrv_drain_all_end();
380
+}
381
+
382
+void bdrv_graph_wrunlock(void)
383
+{
384
+ GLOBAL_STATE_CODE();
385
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
386
+ assert(qatomic_read(&has_writer));
387
+
388
+ /*
389
+ * No need for memory barriers, this works in pair with
390
+ * the slow path of rdlock() and both take the lock.
391
+ */
392
+ qatomic_store_release(&has_writer, 0);
393
+
394
+ /* Wake up all coroutine that are waiting to read the graph */
395
+ qemu_co_enter_all(&reader_queue, &aio_context_list_lock);
396
+}
397
+
398
+void coroutine_fn bdrv_graph_co_rdlock(void)
399
+{
400
+ BdrvGraphRWlock *bdrv_graph;
401
+ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
402
+
403
+ /* Do not lock if in main thread */
404
+ if (qemu_in_main_thread()) {
405
+ return;
406
+ }
407
+
408
+ for (;;) {
409
+ qatomic_set(&bdrv_graph->reader_count,
410
+ bdrv_graph->reader_count + 1);
411
+ /* make sure writer sees reader_count before we check has_writer */
412
+ smp_mb();
413
+
414
+ /*
415
+ * has_writer == 0: this means writer will read reader_count as >= 1
416
+ * has_writer == 1: we don't know if writer read reader_count == 0
417
+ * or > 0, but we need to wait anyways because
418
+ * it will write.
419
+ */
420
+ if (!qatomic_read(&has_writer)) {
421
+ break;
422
+ }
423
+
424
+ /*
425
+ * Synchronize access with reader_count() in bdrv_graph_wrlock().
426
+ * Case 1:
427
+ * If this critical section gets executed first, reader_count will
428
+ * decrease and the reader will go to sleep.
429
+ * Then the writer will read reader_count that does not take into
430
+ * account this reader, and if there's no other reader it will
431
+ * enter the write section.
432
+ * Case 2:
433
+ * If reader_count() critical section gets executed first,
434
+ * then writer will read reader_count >= 1.
435
+ * It will wait in AIO_WAIT_WHILE(), but once it releases the lock
436
+ * we will enter this critical section and call aio_wait_kick().
437
+ */
438
+ WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) {
439
+ /*
440
+ * Additional check when we use the above lock to synchronize
441
+ * with bdrv_graph_wrunlock().
442
+ * Case 1:
443
+ * If this gets executed first, has_writer is still 1, so we reduce
444
+ * reader_count and go to sleep.
445
+ * Then the writer will set has_writer to 0 and wake up all readers,
446
+ * us included.
447
+ * Case 2:
448
+ * If bdrv_graph_wrunlock() critical section gets executed first,
449
+ * then it will set has_writer to 0 and wake up all other readers.
450
+ * Then we execute this critical section, and therefore must check
451
+ * again for has_writer, otherwise we sleep without any writer
452
+ * actually running.
453
+ */
454
+ if (!qatomic_read(&has_writer)) {
455
+ return;
456
+ }
457
+
458
+ /* slow path where reader sleeps */
459
+ bdrv_graph->reader_count--;
460
+ aio_wait_kick();
461
+ qemu_co_queue_wait(&reader_queue, &aio_context_list_lock);
462
+ }
463
+ }
464
+}
465
+
466
+void coroutine_fn bdrv_graph_co_rdunlock(void)
467
+{
468
+ BdrvGraphRWlock *bdrv_graph;
469
+ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
470
+
471
+ /* Do not lock if in main thread */
472
+ if (qemu_in_main_thread()) {
473
+ return;
474
+ }
475
+
476
+ qatomic_store_release(&bdrv_graph->reader_count,
477
+ bdrv_graph->reader_count - 1);
478
+ /* make sure writer sees reader_count before we check has_writer */
479
+ smp_mb();
480
+
481
+ /*
482
+ * has_writer == 0: this means reader will read reader_count decreased
483
+ * has_writer == 1: we don't know if writer read reader_count old or
484
+ * new. Therefore, kick again so on next iteration
485
+ * writer will for sure read the updated value.
486
+ */
487
+ if (qatomic_read(&has_writer)) {
488
+ aio_wait_kick();
489
+ }
490
+}
491
+
492
+void bdrv_graph_rdlock_main_loop(void)
493
+{
494
+ GLOBAL_STATE_CODE();
495
+ assert(!qemu_in_coroutine());
496
+}
497
+
498
+void bdrv_graph_rdunlock_main_loop(void)
499
+{
500
+ GLOBAL_STATE_CODE();
501
+ assert(!qemu_in_coroutine());
502
+}
503
diff --git a/block/meson.build b/block/meson.build
504
index XXXXXXX..XXXXXXX 100644
158
index XXXXXXX..XXXXXXX 100644
505
--- a/block/meson.build
159
--- a/tests/Makefile.include
506
+++ b/block/meson.build
160
+++ b/tests/Makefile.include
507
@@ -XXX,XX +XXX,XX @@ block_ss.add(files(
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
508
'blkverify.c',
162
gcov-files-test-hbitmap-y = util/hbitmap.c
509
'block-backend.c',
163
check-unit-y += tests/test-hbitmap$(EXESUF)
510
'block-copy.c',
164
gcov-files-test-hbitmap-y = blockjob.c
511
+ 'graph-lock.c',
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
512
'commit.c',
166
check-unit-y += tests/test-blockjob$(EXESUF)
513
'copy-on-read.c',
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
514
'preallocate.c',
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)
515
--
177
--
516
2.38.1
178
2.13.6
179
180
diff view generated by jsdifflib
1
All callers of bdrv_parent_drained_begin_single() pass poll=false now,
1
Now that the bdrv_drain_invoke() calls are pulled up to the callers of
2
so we don't need the parameter any more.
2
bdrv_drain_recurse(), the 'begin' parameter isn't needed any more.
3
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Message-Id: <20221118174110.55183-16-kwolf@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
6
---
10
include/block/block-io.h | 5 ++---
7
block/io.c | 12 ++++++------
11
block.c | 4 ++--
8
1 file changed, 6 insertions(+), 6 deletions(-)
12
block/io.c | 8 ++------
13
3 files changed, 6 insertions(+), 11 deletions(-)
14
9
15
diff --git a/include/block/block-io.h b/include/block/block-io.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/block-io.h
18
+++ b/include/block/block-io.h
19
@@ -XXX,XX +XXX,XX @@ bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
20
/**
21
* bdrv_parent_drained_begin_single:
22
*
23
- * Begin a quiesced section for the parent of @c. If @poll is true, wait for
24
- * any pending activity to cease.
25
+ * Begin a quiesced section for the parent of @c.
26
*/
27
-void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
28
+void bdrv_parent_drained_begin_single(BdrvChild *c);
29
30
/**
31
* bdrv_parent_drained_poll_single:
32
diff --git a/block.c b/block.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/block.c
35
+++ b/block.c
36
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque)
37
* new_bs drained when calling bdrv_replace_child_tran() is not a
38
* requirement any more.
39
*/
40
- bdrv_parent_drained_begin_single(s->child, false);
41
+ bdrv_parent_drained_begin_single(s->child);
42
assert(!bdrv_parent_drained_poll_single(s->child));
43
}
44
assert(s->child->quiesced_parent);
45
@@ -XXX,XX +XXX,XX @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs,
46
* a problem, we already did this), but it will still poll until the parent
47
* is fully quiesced, so it will not be negatively affected either.
48
*/
49
- bdrv_parent_drained_begin_single(new_child, false);
50
+ bdrv_parent_drained_begin_single(new_child);
51
bdrv_replace_child_noperm(new_child, child_bs);
52
53
BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1);
54
diff --git a/block/io.c b/block/io.c
10
diff --git a/block/io.c b/block/io.c
55
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
56
--- a/block/io.c
12
--- a/block/io.c
57
+++ b/block/io.c
13
+++ b/block/io.c
58
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
59
if (c == ignore) {
60
continue;
61
}
62
- bdrv_parent_drained_begin_single(c, false);
63
+ bdrv_parent_drained_begin_single(c);
64
}
15
}
65
}
16
}
66
17
67
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore,
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
68
return busy;
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);
69
}
38
}
70
39
71
-void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
40
void bdrv_drained_end(BlockDriverState *bs)
72
+void bdrv_parent_drained_begin_single(BdrvChild *c)
41
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
73
{
42
74
- AioContext *ctx = bdrv_child_get_parent_aio_context(c);
43
bdrv_parent_drained_end(bs);
75
IO_OR_GS_CODE();
44
bdrv_drain_invoke(bs, false);
76
45
- bdrv_drain_recurse(bs, false);
77
assert(!c->quiesced_parent);
46
+ bdrv_drain_recurse(bs);
78
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
47
aio_enable_external(bdrv_get_aio_context(bs));
79
if (c->klass->drained_begin) {
48
}
80
c->klass->drained_begin(c);
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);
81
}
66
}
82
- if (poll) {
67
83
- AIO_WAIT_WHILE(ctx, bdrv_parent_drained_poll_single(c));
84
- }
85
}
86
87
static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
88
--
68
--
89
2.38.1
69
2.13.6
70
71
diff view generated by jsdifflib
1
Provide a separate function that just quiesces the users of a node to
1
The device is drained, so there is no point in waiting for requests at
2
prevent new requests from coming in, but without waiting for the already
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
in-flight I/O to complete.
3
there.
4
4
5
This function can be used in contexts where polling is not allowed.
5
The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e
6
in order to call the .bdrv_co_drain_end() driver callback. This is now
7
done by a separate bdrv_drain_invoke() call.
6
8
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20221207131838.239125-2-kwolf@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
9
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
---
12
include/block/block-global-state.h | 1 +
13
block/io.c | 2 --
13
block/io.c | 19 +++++++++++++------
14
1 file changed, 2 deletions(-)
14
2 files changed, 14 insertions(+), 6 deletions(-)
15
15
16
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/block/block-global-state.h
19
+++ b/include/block/block-global-state.h
20
@@ -XXX,XX +XXX,XX @@ int bdrv_inactivate_all(void);
21
int bdrv_flush_all(void);
22
void bdrv_close_all(void);
23
void bdrv_drain_all_begin(void);
24
+void bdrv_drain_all_begin_nopoll(void);
25
void bdrv_drain_all_end(void);
26
void bdrv_drain_all(void);
27
28
diff --git a/block/io.c b/block/io.c
16
diff --git a/block/io.c b/block/io.c
29
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
30
--- a/block/io.c
18
--- a/block/io.c
31
+++ b/block/io.c
19
+++ b/block/io.c
32
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_all_poll(void)
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
33
* NOTE: no new block jobs or BlockDriverStates can be created between
21
34
* the bdrv_drain_all_begin() and bdrv_drain_all_end() calls.
22
bdrv_parent_drained_end(bs);
35
*/
23
bdrv_drain_invoke(bs, false);
36
-void bdrv_drain_all_begin(void)
24
- bdrv_drain_recurse(bs);
37
+void bdrv_drain_all_begin_nopoll(void)
25
aio_enable_external(bdrv_get_aio_context(bs));
38
{
26
}
39
BlockDriverState *bs = NULL;
27
40
GLOBAL_STATE_CODE();
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
41
29
aio_enable_external(aio_context);
42
- if (qemu_in_coroutine()) {
30
bdrv_parent_drained_end(bs);
43
- bdrv_co_yield_to_drain(NULL, true, NULL, true);
31
bdrv_drain_invoke(bs, false);
44
- return;
32
- bdrv_drain_recurse(bs);
45
- }
46
-
47
/*
48
* bdrv queue is managed by record/replay,
49
* waiting for finishing the I/O requests may
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
51
bdrv_do_drained_begin(bs, NULL, false);
52
aio_context_release(aio_context);
33
aio_context_release(aio_context);
53
}
34
}
54
+}
35
55
+
56
+void bdrv_drain_all_begin(void)
57
+{
58
+ BlockDriverState *bs = NULL;
59
+
60
+ if (qemu_in_coroutine()) {
61
+ bdrv_co_yield_to_drain(NULL, true, NULL, true);
62
+ return;
63
+ }
64
+
65
+ bdrv_drain_all_begin_nopoll();
66
67
/* Now poll the in-flight requests */
68
AIO_WAIT_WHILE(NULL, bdrv_drain_all_poll());
69
--
36
--
70
2.38.1
37
2.13.6
38
39
diff view generated by jsdifflib
1
Polling during bdrv_drained_end() can be problematic (and in the future,
1
Drain requests are propagated to child nodes, parent nodes and directly
2
we may get cases for bdrv_drained_begin() where polling is forbidden,
2
to the AioContext. The order in which this happened was different
3
and we don't care about already in-flight requests, but just want to
3
between all combinations of drain/drain_all and begin/end.
4
prevent new requests from arriving).
5
4
6
The .bdrv_drained_begin/end callbacks running in a coroutine is the only
5
The correct order is to keep children only drained when their parents
7
reason why we have to do this polling, so make them non-coroutine
6
are also drained. This means that at the start of a drained section, the
8
callbacks again. None of the callers actually yield any more.
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.
9
10
10
This means that bdrv_drained_end() effectively doesn't poll any more,
11
This patch changes the three other functions to follow the example of
11
even if AIO_WAIT_WHILE() loops are still there (their condition is false
12
bdrv_drained_begin(), which is the only one that got it right.
12
from the beginning). This is generally not a problem, but in
13
test-bdrv-drain, some additional explicit aio_poll() calls need to be
14
added because the test case wants to verify the final state after BHs
15
have executed.
16
13
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
20
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
21
Message-Id: <20221118174110.55183-4-kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
16
---
24
include/block/block_int-common.h | 10 ++++---
17
block/io.c | 12 ++++++++----
25
block.c | 4 +--
18
1 file changed, 8 insertions(+), 4 deletions(-)
26
block/io.c | 49 +++++---------------------------
27
block/qed.c | 6 ++--
28
block/throttle.c | 8 +++---
29
tests/unit/test-bdrv-drain.c | 18 ++++++------
30
6 files changed, 32 insertions(+), 63 deletions(-)
31
19
32
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
33
index XXXXXXX..XXXXXXX 100644
34
--- a/include/block/block_int-common.h
35
+++ b/include/block/block_int-common.h
36
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
37
void (*bdrv_io_unplug)(BlockDriverState *bs);
38
39
/**
40
- * bdrv_co_drain_begin is called if implemented in the beginning of a
41
+ * bdrv_drain_begin is called if implemented in the beginning of a
42
* drain operation to drain and stop any internal sources of requests in
43
* the driver.
44
- * bdrv_co_drain_end is called if implemented at the end of the drain.
45
+ * bdrv_drain_end is called if implemented at the end of the drain.
46
*
47
* They should be used by the driver to e.g. manage scheduled I/O
48
* requests, or toggle an internal state. After the end of the drain new
49
* requests will continue normally.
50
+ *
51
+ * Implementations of both functions must not call aio_poll().
52
*/
53
- void coroutine_fn (*bdrv_co_drain_begin)(BlockDriverState *bs);
54
- void coroutine_fn (*bdrv_co_drain_end)(BlockDriverState *bs);
55
+ void (*bdrv_drain_begin)(BlockDriverState *bs);
56
+ void (*bdrv_drain_end)(BlockDriverState *bs);
57
58
bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs);
59
bool coroutine_fn (*bdrv_co_can_store_new_dirty_bitmap)(
60
diff --git a/block.c b/block.c
61
index XXXXXXX..XXXXXXX 100644
62
--- a/block.c
63
+++ b/block.c
64
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
65
assert(is_power_of_2(bs->bl.request_alignment));
66
67
for (i = 0; i < bs->quiesce_counter; i++) {
68
- if (drv->bdrv_co_drain_begin) {
69
- drv->bdrv_co_drain_begin(bs);
70
+ if (drv->bdrv_drain_begin) {
71
+ drv->bdrv_drain_begin(bs);
72
}
73
}
74
75
diff --git a/block/io.c b/block/io.c
20
diff --git a/block/io.c b/block/io.c
76
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
77
--- a/block/io.c
22
--- a/block/io.c
78
+++ b/block/io.c
23
+++ b/block/io.c
79
@@ -XXX,XX +XXX,XX @@ typedef struct {
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
80
int *drained_end_counter;
81
} BdrvCoDrainData;
82
83
-static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
84
-{
85
- BdrvCoDrainData *data = opaque;
86
- BlockDriverState *bs = data->bs;
87
-
88
- if (data->begin) {
89
- bs->drv->bdrv_co_drain_begin(bs);
90
- } else {
91
- bs->drv->bdrv_co_drain_end(bs);
92
- }
93
-
94
- /* Set data->done and decrement drained_end_counter before bdrv_wakeup() */
95
- qatomic_mb_set(&data->done, true);
96
- if (!data->begin) {
97
- qatomic_dec(data->drained_end_counter);
98
- }
99
- bdrv_dec_in_flight(bs);
100
-
101
- g_free(data);
102
-}
103
-
104
-/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
105
+/* Recursively call BlockDriver.bdrv_drain_begin/end callbacks */
106
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
107
int *drained_end_counter)
108
{
109
- BdrvCoDrainData *data;
110
-
111
- if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
112
- (!begin && !bs->drv->bdrv_co_drain_end)) {
113
+ if (!bs->drv || (begin && !bs->drv->bdrv_drain_begin) ||
114
+ (!begin && !bs->drv->bdrv_drain_end)) {
115
return;
25
return;
116
}
26
}
117
27
118
- data = g_new(BdrvCoDrainData, 1);
28
+ /* Stop things in parent-to-child order */
119
- *data = (BdrvCoDrainData) {
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
120
- .bs = bs,
30
aio_disable_external(bdrv_get_aio_context(bs));
121
- .done = false,
31
bdrv_parent_drained_begin(bs);
122
- .begin = begin,
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
123
- .drained_end_counter = drained_end_counter,
33
return;
124
- };
125
-
126
- if (!begin) {
127
- qatomic_inc(drained_end_counter);
128
+ if (begin) {
129
+ bs->drv->bdrv_drain_begin(bs);
130
+ } else {
131
+ bs->drv->bdrv_drain_end(bs);
132
}
34
}
133
-
35
134
- /* Make sure the driver callback completes during the polling phase for
36
- bdrv_parent_drained_end(bs);
135
- * drain_begin. */
37
+ /* Re-enable things in child-to-parent order */
136
- bdrv_inc_in_flight(bs);
38
bdrv_drain_invoke(bs, false);
137
- data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data);
39
+ bdrv_parent_drained_end(bs);
138
- aio_co_schedule(bdrv_get_aio_context(bs), data->co);
40
aio_enable_external(bdrv_get_aio_context(bs));
139
}
41
}
140
42
141
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
43
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
142
diff --git a/block/qed.c b/block/qed.c
44
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
143
index XXXXXXX..XXXXXXX 100644
45
AioContext *aio_context = bdrv_get_aio_context(bs);
144
--- a/block/qed.c
46
145
+++ b/block/qed.c
47
+ /* Stop things in parent-to-child order */
146
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn qed_plug_allocating_write_reqs(BDRVQEDState *s)
48
aio_context_acquire(aio_context);
147
assert(!s->allocating_write_reqs_plugged);
49
- bdrv_parent_drained_begin(bs);
148
if (s->allocating_acb != NULL) {
50
aio_disable_external(aio_context);
149
/* Another allocating write came concurrently. This cannot happen
51
+ bdrv_parent_drained_begin(bs);
150
- * from bdrv_qed_co_drain_begin, but it can happen when the timer runs.
52
bdrv_drain_invoke(bs, true);
151
+ * from bdrv_qed_drain_begin, but it can happen when the timer runs.
53
aio_context_release(aio_context);
152
*/
54
153
qemu_co_mutex_unlock(&s->table_lock);
55
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
154
return false;
56
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
155
@@ -XXX,XX +XXX,XX @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs,
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);
156
}
67
}
157
}
68
158
159
-static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs)
160
+static void bdrv_qed_drain_begin(BlockDriverState *bs)
161
{
162
BDRVQEDState *s = bs->opaque;
163
164
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = {
165
.bdrv_co_check = bdrv_qed_co_check,
166
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
167
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
168
- .bdrv_co_drain_begin = bdrv_qed_co_drain_begin,
169
+ .bdrv_drain_begin = bdrv_qed_drain_begin,
170
};
171
172
static void bdrv_qed_init(void)
173
diff --git a/block/throttle.c b/block/throttle.c
174
index XXXXXXX..XXXXXXX 100644
175
--- a/block/throttle.c
176
+++ b/block/throttle.c
177
@@ -XXX,XX +XXX,XX @@ static void throttle_reopen_abort(BDRVReopenState *reopen_state)
178
reopen_state->opaque = NULL;
179
}
180
181
-static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs)
182
+static void throttle_drain_begin(BlockDriverState *bs)
183
{
184
ThrottleGroupMember *tgm = bs->opaque;
185
if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) {
186
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs)
187
}
188
}
189
190
-static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs)
191
+static void throttle_drain_end(BlockDriverState *bs)
192
{
193
ThrottleGroupMember *tgm = bs->opaque;
194
assert(tgm->io_limits_disabled);
195
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_throttle = {
196
.bdrv_reopen_commit = throttle_reopen_commit,
197
.bdrv_reopen_abort = throttle_reopen_abort,
198
199
- .bdrv_co_drain_begin = throttle_co_drain_begin,
200
- .bdrv_co_drain_end = throttle_co_drain_end,
201
+ .bdrv_drain_begin = throttle_drain_begin,
202
+ .bdrv_drain_end = throttle_drain_end,
203
204
.is_filter = true,
205
.strong_runtime_opts = throttle_strong_runtime_opts,
206
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
207
index XXXXXXX..XXXXXXX 100644
208
--- a/tests/unit/test-bdrv-drain.c
209
+++ b/tests/unit/test-bdrv-drain.c
210
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn sleep_in_drain_begin(void *opaque)
211
bdrv_dec_in_flight(bs);
212
}
213
214
-static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
215
+static void bdrv_test_drain_begin(BlockDriverState *bs)
216
{
217
BDRVTestState *s = bs->opaque;
218
s->drain_count++;
219
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
220
}
221
}
222
223
-static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
224
+static void bdrv_test_drain_end(BlockDriverState *bs)
225
{
226
BDRVTestState *s = bs->opaque;
227
s->drain_count--;
228
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
229
.bdrv_close = bdrv_test_close,
230
.bdrv_co_preadv = bdrv_test_co_preadv,
231
232
- .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
233
- .bdrv_co_drain_end = bdrv_test_co_drain_end,
234
+ .bdrv_drain_begin = bdrv_test_drain_begin,
235
+ .bdrv_drain_end = bdrv_test_drain_end,
236
237
.bdrv_child_perm = bdrv_default_perms,
238
239
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_commit_by_drained_end(void)
240
bdrv_drained_begin(bs_child);
241
g_assert(!job_has_completed);
242
bdrv_drained_end(bs_child);
243
+ aio_poll(qemu_get_aio_context(), false);
244
g_assert(job_has_completed);
245
246
bdrv_unref(bs_parents[0]);
247
@@ -XXX,XX +XXX,XX @@ static void test_drop_intermediate_poll(void)
248
249
g_assert(!job_has_completed);
250
ret = bdrv_drop_intermediate(chain[1], chain[0], NULL);
251
+ aio_poll(qemu_get_aio_context(), false);
252
g_assert(ret == 0);
253
g_assert(job_has_completed);
254
255
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_replace_test_drain_co(void *opaque)
256
* .was_drained.
257
* Increment .drain_count.
258
*/
259
-static void coroutine_fn bdrv_replace_test_co_drain_begin(BlockDriverState *bs)
260
+static void bdrv_replace_test_drain_begin(BlockDriverState *bs)
261
{
262
BDRVReplaceTestState *s = bs->opaque;
263
264
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_replace_test_read_entry(void *opaque)
265
* If .drain_count reaches 0 and the node has a backing file, issue a
266
* read request.
267
*/
268
-static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs)
269
+static void bdrv_replace_test_drain_end(BlockDriverState *bs)
270
{
271
BDRVReplaceTestState *s = bs->opaque;
272
273
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_replace_test = {
274
.bdrv_close = bdrv_replace_test_close,
275
.bdrv_co_preadv = bdrv_replace_test_co_preadv,
276
277
- .bdrv_co_drain_begin = bdrv_replace_test_co_drain_begin,
278
- .bdrv_co_drain_end = bdrv_replace_test_co_drain_end,
279
+ .bdrv_drain_begin = bdrv_replace_test_drain_begin,
280
+ .bdrv_drain_end = bdrv_replace_test_drain_end,
281
282
.bdrv_child_perm = bdrv_default_perms,
283
};
284
--
69
--
285
2.38.1
70
2.13.6
71
72
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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.
2
5
3
We know that the string will stay around until the function
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
4
returns, and the parameter of drv->bdrv_co_create_opts is const char*,
5
so it must not be modified either.
6
7
7
Suggested-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
11
Message-Id: <20221128142337.657646-7-eesposit@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
10
---
14
block.c | 7 ++-----
11
hmp.c | 6 ------
15
1 file changed, 2 insertions(+), 5 deletions(-)
12
1 file changed, 6 deletions(-)
16
13
17
diff --git a/block.c b/block.c
14
diff --git a/hmp.c b/hmp.c
18
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
19
--- a/block.c
16
--- a/hmp.c
20
+++ b/block.c
17
+++ b/hmp.c
21
@@ -XXX,XX +XXX,XX @@ int bdrv_create(BlockDriver *drv, const char* filename,
18
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
22
Coroutine *co;
19
{
23
CreateCo cco = {
20
BlockBackend *blk;
24
.drv = drv,
21
BlockBackend *local_blk = NULL;
25
- .filename = g_strdup(filename),
22
- AioContext *aio_context;
26
+ .filename = filename,
23
const char* device = qdict_get_str(qdict, "device");
27
.opts = opts,
24
const char* command = qdict_get_str(qdict, "command");
28
.ret = NOT_DONE,
25
Error *err = NULL;
29
.err = NULL,
26
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
30
@@ -XXX,XX +XXX,XX @@ int bdrv_create(BlockDriver *drv, const char* filename,
31
32
if (!drv->bdrv_co_create_opts) {
33
error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
34
- ret = -ENOTSUP;
35
- goto out;
36
+ return -ENOTSUP;
37
}
38
39
if (qemu_in_coroutine()) {
40
@@ -XXX,XX +XXX,XX @@ int bdrv_create(BlockDriver *drv, const char* filename,
41
}
27
}
42
}
28
}
43
29
44
-out:
30
- aio_context = blk_get_aio_context(blk);
45
- g_free(cco.filename);
31
- aio_context_acquire(aio_context);
46
return ret;
32
-
47
}
33
/*
48
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);
49
--
45
--
50
2.38.1
46
2.13.6
47
48
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
2
2
3
It is always called in coroutine_fn callbacks, therefore
3
Since bdrv_co_preadv does all neccessary checks including
4
it can directly call bdrv_co_create().
4
reading after the end of the backing file, avoid duplication
5
of verification before bdrv_co_preadv call.
5
6
6
Rename it to bdrv_co_create_file too.
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
7
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
11
Message-Id: <20221128142337.657646-9-eesposit@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
11
---
14
include/block/block-global-state.h | 3 ++-
12
block/qcow2.h | 3 ---
15
block.c | 5 +++--
13
block/qcow2.c | 51 ++++++++-------------------------------------------
16
block/crypto.c | 2 +-
14
2 files changed, 8 insertions(+), 46 deletions(-)
17
block/parallels.c | 2 +-
18
block/qcow.c | 2 +-
19
block/qcow2.c | 4 ++--
20
block/qed.c | 2 +-
21
block/raw-format.c | 2 +-
22
block/vdi.c | 2 +-
23
block/vhdx.c | 2 +-
24
block/vmdk.c | 2 +-
25
block/vpc.c | 2 +-
26
12 files changed, 16 insertions(+), 14 deletions(-)
27
15
28
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
16
diff --git a/block/qcow2.h b/block/qcow2.h
29
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
30
--- a/include/block/block-global-state.h
18
--- a/block/qcow2.h
31
+++ b/include/block/block-global-state.h
19
+++ b/block/qcow2.h
32
@@ -XXX,XX +XXX,XX @@ BlockDriver *bdrv_find_protocol(const char *filename,
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
33
BlockDriver *bdrv_find_format(const char *format_name);
34
int bdrv_create(BlockDriver *drv, const char* filename,
35
QemuOpts *opts, Error **errp);
36
-int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp);
37
+int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
38
+ Error **errp);
39
40
BlockDriverState *bdrv_new(void);
41
int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
42
diff --git a/block.c b/block.c
43
index XXXXXXX..XXXXXXX 100644
44
--- a/block.c
45
+++ b/block.c
46
@@ -XXX,XX +XXX,XX @@ out:
47
return ret;
48
}
21
}
49
22
50
-int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
23
/* qcow2.c functions */
51
+int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
52
+ Error **errp)
25
- int64_t sector_num, int nb_sectors);
53
{
26
-
54
QemuOpts *protocol_opts;
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
55
BlockDriver *drv;
28
int refcount_order, bool generous_increase,
56
@@ -XXX,XX +XXX,XX @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
29
uint64_t *refblock_count);
57
goto out;
58
}
59
60
- ret = bdrv_create(drv, filename, protocol_opts, errp);
61
+ ret = bdrv_co_create(drv, filename, protocol_opts, errp);
62
out:
63
qemu_opts_del(protocol_opts);
64
qobject_unref(qdict);
65
diff --git a/block/crypto.c b/block/crypto.c
66
index XXXXXXX..XXXXXXX 100644
67
--- a/block/crypto.c
68
+++ b/block/crypto.c
69
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv,
70
}
71
72
/* Create protocol layer */
73
- ret = bdrv_create_file(filename, opts, errp);
74
+ ret = bdrv_co_create_file(filename, opts, errp);
75
if (ret < 0) {
76
goto fail;
77
}
78
diff --git a/block/parallels.c b/block/parallels.c
79
index XXXXXXX..XXXXXXX 100644
80
--- a/block/parallels.c
81
+++ b/block/parallels.c
82
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(BlockDriver *drv,
83
}
84
85
/* Create and open the file (protocol layer) */
86
- ret = bdrv_create_file(filename, opts, errp);
87
+ ret = bdrv_co_create_file(filename, opts, errp);
88
if (ret < 0) {
89
goto done;
90
}
91
diff --git a/block/qcow.c b/block/qcow.c
92
index XXXXXXX..XXXXXXX 100644
93
--- a/block/qcow.c
94
+++ b/block/qcow.c
95
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(BlockDriver *drv,
96
}
97
98
/* Create and open the file (protocol layer) */
99
- ret = bdrv_create_file(filename, opts, errp);
100
+ ret = bdrv_co_create_file(filename, opts, errp);
101
if (ret < 0) {
102
goto fail;
103
}
104
diff --git a/block/qcow2.c b/block/qcow2.c
30
diff --git a/block/qcow2.c b/block/qcow2.c
105
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
106
--- a/block/qcow2.c
32
--- a/block/qcow2.c
107
+++ b/block/qcow2.c
33
+++ b/block/qcow2.c
108
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv,
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
109
}
35
return status;
110
36
}
111
/* Create and open the file (protocol layer) */
37
112
- ret = bdrv_create_file(filename, opts, errp);
38
-/* handle reading after the end of the backing file */
113
+ ret = bdrv_co_create_file(filename, opts, errp);
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
114
if (ret < 0) {
40
- int64_t offset, int bytes)
115
goto finish;
41
-{
116
}
42
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
117
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv,
43
- int n1;
118
/* Create and open an external data file (protocol layer) */
44
-
119
val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE);
45
- if ((offset + bytes) <= bs_size) {
120
if (val) {
46
- return bytes;
121
- ret = bdrv_create_file(val, opts, errp);
47
- }
122
+ ret = bdrv_co_create_file(val, opts, errp);
48
-
123
if (ret < 0) {
49
- if (offset >= bs_size) {
124
goto finish;
50
- n1 = 0;
125
}
51
- } else {
126
diff --git a/block/qed.c b/block/qed.c
52
- n1 = bs_size - offset;
127
index XXXXXXX..XXXXXXX 100644
53
- }
128
--- a/block/qed.c
54
-
129
+++ b/block/qed.c
55
- qemu_iovec_memset(qiov, n1, 0, bytes - n1);
130
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(BlockDriver *drv,
56
-
131
}
57
- return n1;
132
58
-}
133
/* Create and open the file (protocol layer) */
59
-
134
- ret = bdrv_create_file(filename, opts, errp);
60
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
135
+ ret = bdrv_co_create_file(filename, opts, errp);
61
uint64_t bytes, QEMUIOVector *qiov,
136
if (ret < 0) {
62
int flags)
137
goto fail;
138
}
139
diff --git a/block/raw-format.c b/block/raw-format.c
140
index XXXXXXX..XXXXXXX 100644
141
--- a/block/raw-format.c
142
+++ b/block/raw-format.c
143
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv,
144
QemuOpts *opts,
145
Error **errp)
146
{
63
{
147
- return bdrv_create_file(filename, opts, errp);
64
BDRVQcow2State *s = bs->opaque;
148
+ return bdrv_co_create_file(filename, opts, errp);
65
- int offset_in_cluster, n1;
149
}
66
+ int offset_in_cluster;
150
151
static int raw_open(BlockDriverState *bs, QDict *options, int flags,
152
diff --git a/block/vdi.c b/block/vdi.c
153
index XXXXXXX..XXXXXXX 100644
154
--- a/block/vdi.c
155
+++ b/block/vdi.c
156
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(BlockDriver *drv,
157
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
158
159
/* Create and open the file (protocol layer) */
160
- ret = bdrv_create_file(filename, opts, errp);
161
+ ret = bdrv_co_create_file(filename, opts, errp);
162
if (ret < 0) {
163
goto done;
164
}
165
diff --git a/block/vhdx.c b/block/vhdx.c
166
index XXXXXXX..XXXXXXX 100644
167
--- a/block/vhdx.c
168
+++ b/block/vhdx.c
169
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(BlockDriver *drv,
170
}
171
172
/* Create and open the file (protocol layer) */
173
- ret = bdrv_create_file(filename, opts, errp);
174
+ ret = bdrv_co_create_file(filename, opts, errp);
175
if (ret < 0) {
176
goto fail;
177
}
178
diff --git a/block/vmdk.c b/block/vmdk.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/block/vmdk.c
181
+++ b/block/vmdk.c
182
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vmdk_create_extent(const char *filename,
183
int ret;
67
int ret;
184
BlockBackend *blk = NULL;
68
unsigned int cur_bytes; /* number of bytes in current iteration */
185
69
uint64_t cluster_offset = 0;
186
- ret = bdrv_create_file(filename, opts, errp);
70
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
187
+ ret = bdrv_co_create_file(filename, opts, errp);
71
case QCOW2_CLUSTER_UNALLOCATED:
188
if (ret < 0) {
72
189
goto exit;
73
if (bs->backing) {
190
}
74
- /* read from the base image */
191
diff --git a/block/vpc.c b/block/vpc.c
75
- n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
192
index XXXXXXX..XXXXXXX 100644
76
- offset, cur_bytes);
193
--- a/block/vpc.c
77
- if (n1 > 0) {
194
+++ b/block/vpc.c
78
- QEMUIOVector local_qiov;
195
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(BlockDriver *drv,
79
-
196
}
80
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
197
81
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
198
/* Create and open the file (protocol layer) */
82
-
199
- ret = bdrv_create_file(filename, opts, errp);
83
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
200
+ ret = bdrv_co_create_file(filename, opts, errp);
84
- qemu_co_mutex_unlock(&s->lock);
201
if (ret < 0) {
85
- ret = bdrv_co_preadv(bs->backing, offset, n1,
202
goto fail;
86
- &local_qiov, 0);
203
}
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 */
204
--
104
--
205
2.38.1
105
2.13.6
106
107
diff view generated by jsdifflib
1
The test case assumes that a drain only happens in one specific place
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
where it drains explicitly. This assumption happened to hold true until
2
driver state that cannot be recreated with create options because it
3
now, but block layer functions may drain interally (any graph
3
would require a list with gaps. This causes trouble in at least
4
modifications are going to do that through bdrv_graph_wrlock()), so this
4
.bdrv_refresh_filename().
5
is incorrect. Make sure that the test code in .drained_begin only runs
6
where we actually want it to run.
7
5
8
When scheduling a BH from .drained_begin, we also need to increase the
6
Document this problem so that we won't accidentally mark the command
9
in_flight counter to make sure that the operation is actually completed
7
stable without having addressed it.
10
in time before the node that it works on goes away.
11
8
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20221207131838.239125-10-kwolf@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
14
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
11
---
17
tests/unit/test-bdrv-drain.c | 18 ++++++++++++++++++
12
qapi/block-core.json | 4 ++++
18
1 file changed, 18 insertions(+)
13
1 file changed, 4 insertions(+)
19
14
20
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
21
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
22
--- a/tests/unit/test-bdrv-drain.c
17
--- a/qapi/block-core.json
23
+++ b/tests/unit/test-bdrv-drain.c
18
+++ b/qapi/block-core.json
24
@@ -XXX,XX +XXX,XX @@ struct detach_by_parent_data {
19
@@ -XXX,XX +XXX,XX @@
25
BlockDriverState *c;
20
# does not support all kinds of operations, all kinds of children, nor
26
BdrvChild *child_c;
21
# all block drivers.
27
bool by_parent_cb;
22
#
28
+ bool detach_on_drain;
23
+# FIXME Removing children from a quorum node means introducing gaps in the
29
};
24
+# child indices. This cannot be represented in the 'children' list of
30
static struct detach_by_parent_data detach_by_parent_data;
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
31
26
+#
32
@@ -XXX,XX +XXX,XX @@ static void detach_indirect_bh(void *opaque)
27
# Warning: The data in a new quorum child MUST be consistent with that of
33
{
28
# the rest of the array.
34
struct detach_by_parent_data *data = opaque;
29
#
35
36
+ bdrv_dec_in_flight(data->child_b->bs);
37
bdrv_unref_child(data->parent_b, data->child_b);
38
39
bdrv_ref(data->c);
40
@@ -XXX,XX +XXX,XX @@ static void detach_by_parent_aio_cb(void *opaque, int ret)
41
42
g_assert_cmpint(ret, ==, 0);
43
if (data->by_parent_cb) {
44
+ bdrv_inc_in_flight(data->child_b->bs);
45
detach_indirect_bh(data);
46
}
47
}
48
49
static void detach_by_driver_cb_drained_begin(BdrvChild *child)
50
{
51
+ struct detach_by_parent_data *data = &detach_by_parent_data;
52
+
53
+ if (!data->detach_on_drain) {
54
+ return;
55
+ }
56
+ data->detach_on_drain = false;
57
+
58
+ bdrv_inc_in_flight(data->child_b->bs);
59
aio_bh_schedule_oneshot(qemu_get_current_aio_context(),
60
detach_indirect_bh, &detach_by_parent_data);
61
child_of_bds.drained_begin(child);
62
@@ -XXX,XX +XXX,XX @@ static void test_detach_indirect(bool by_parent_cb)
63
detach_by_driver_cb_class = child_of_bds;
64
detach_by_driver_cb_class.drained_begin =
65
detach_by_driver_cb_drained_begin;
66
+ detach_by_driver_cb_class.drained_end = NULL;
67
+ detach_by_driver_cb_class.drained_poll = NULL;
68
}
69
70
+ detach_by_parent_data = (struct detach_by_parent_data) {
71
+ .detach_on_drain = false,
72
+ };
73
+
74
/* Create all involved nodes */
75
parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR,
76
&error_abort);
77
@@ -XXX,XX +XXX,XX @@ static void test_detach_indirect(bool by_parent_cb)
78
.child_b = child_b,
79
.c = c,
80
.by_parent_cb = by_parent_cb,
81
+ .detach_on_drain = true,
82
};
83
acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, NULL);
84
g_assert(acb != NULL);
85
--
30
--
86
2.38.1
31
2.13.6
32
33
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
From: Doug Gale <doug16k@gmail.com>
2
2
3
Take the rdlock already, before we add the assertions.
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.
4
7
5
All these functions either read the graph recursively, or call
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
6
BlockDriver callbacks that will eventually need to be protected by the
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
7
graph rdlock.
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
9
Do it now to all functions together, because many of these recursively
10
call each other.
11
12
For example, bdrv_co_truncate calls BlockDriver->bdrv_co_truncate, and
13
some driver callbacks implement their own .bdrv_co_truncate by calling
14
bdrv_flush inside. So if bdrv_flush asserts but bdrv_truncate does not
15
take the rdlock yet, the assertion will always fail.
16
17
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Message-Id: <20221207131838.239125-18-kwolf@redhat.com>
20
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
21
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
12
---
24
block/coroutines.h | 2 +-
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
25
include/block/block-io.h | 53 +++++++++++++++++++++++-----------------
14
hw/block/trace-events | 93 ++++++++++++++
26
2 files changed, 32 insertions(+), 23 deletions(-)
15
2 files changed, 390 insertions(+), 52 deletions(-)
27
16
28
diff --git a/block/coroutines.h b/block/coroutines.h
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
29
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
30
--- a/block/coroutines.h
19
--- a/hw/block/nvme.c
31
+++ b/block/coroutines.h
20
+++ b/hw/block/nvme.c
32
@@ -XXX,XX +XXX,XX @@ nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
21
@@ -XXX,XX +XXX,XX @@
33
* the "I/O or GS" API.
22
#include "qapi/visitor.h"
34
*/
23
#include "sysemu/block-backend.h"
35
24
36
-int co_wrapper_mixed
25
+#include "qemu/log.h"
37
+int co_wrapper_mixed_bdrv_rdlock
26
+#include "trace.h"
38
bdrv_common_block_status_above(BlockDriverState *bs,
27
#include "nvme.h"
39
BlockDriverState *base,
28
40
bool include_base,
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
41
diff --git a/include/block/block-io.h b/include/block/block-io.h
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
42
index XXXXXXX..XXXXXXX 100644
681
index XXXXXXX..XXXXXXX 100644
43
--- a/include/block/block-io.h
682
--- a/hw/block/trace-events
44
+++ b/include/block/block-io.h
683
+++ b/hw/block/trace-events
45
@@ -XXX,XX +XXX,XX @@
684
@@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
46
* to catch when they are accidentally called by the wrong API.
685
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
47
*/
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"
48
687
49
-int co_wrapper_mixed bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
688
+# hw/block/nvme.c
50
- int64_t bytes,
689
+# nvme traces for successful events
51
- BdrvRequestFlags flags);
690
+nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
52
+int co_wrapper_mixed_bdrv_rdlock
691
+nvme_irq_pin(void) "pulsing IRQ pin"
53
+bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes,
692
+nvme_irq_masked(void) "IRQ is masked"
54
+ BdrvRequestFlags flags);
693
+nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
55
+
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""
56
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags);
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""
57
-int co_wrapper_mixed bdrv_pread(BdrvChild *child, int64_t offset,
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"
58
- int64_t bytes, void *buf,
697
+nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
59
- BdrvRequestFlags flags);
698
+nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
60
-int co_wrapper_mixed bdrv_pwrite(BdrvChild *child, int64_t offset,
699
+nvme_identify_ctrl(void) "identify controller"
61
- int64_t bytes, const void *buf,
700
+nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
62
- BdrvRequestFlags flags);
701
+nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
63
-int co_wrapper_mixed bdrv_pwrite_sync(BdrvChild *child, int64_t offset,
702
+nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
64
- int64_t bytes, const void *buf,
703
+nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
65
- BdrvRequestFlags flags);
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"
66
+
705
+nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
67
+int co_wrapper_mixed_bdrv_rdlock
706
+nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
68
+bdrv_pread(BdrvChild *child, int64_t offset, int64_t bytes, void *buf,
707
+nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
69
+ BdrvRequestFlags flags);
708
+nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
70
+
709
+nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
71
+int co_wrapper_mixed_bdrv_rdlock
710
+nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
72
+bdrv_pwrite(BdrvChild *child, int64_t offset,int64_t bytes,
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""
73
+ const void *buf, BdrvRequestFlags flags);
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""
74
+
713
+nvme_mmio_start_success(void) "setting controller enable bit succeeded"
75
+int co_wrapper_mixed_bdrv_rdlock
714
+nvme_mmio_stopped(void) "cleared controller enable bit"
76
+bdrv_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes,
715
+nvme_mmio_shutdown_set(void) "shutdown bit set"
77
+ const void *buf, BdrvRequestFlags flags);
716
+nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
78
+
717
+
79
int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset,
718
+# nvme traces for error conditions
80
int64_t bytes, const void *buf,
719
+nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
81
BdrvRequestFlags flags);
720
+nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
82
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset,
721
+nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
83
722
+nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
84
void bdrv_drain(BlockDriverState *bs);
723
+nvme_err_invalid_field(void) "invalid field"
85
724
+nvme_err_invalid_prp(void) "invalid PRP"
86
-int co_wrapper_mixed
725
+nvme_err_invalid_sgl(void) "invalid SGL"
87
+int co_wrapper_mixed_bdrv_rdlock
726
+nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
88
bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
727
+nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
89
PreallocMode prealloc, BdrvRequestFlags flags, Error **errp);
728
+nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
90
729
+nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
91
-int co_wrapper_mixed bdrv_check(BlockDriverState *bs, BdrvCheckResult *res,
730
+nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
92
- BdrvCheckMode fix);
731
+nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
93
+int co_wrapper_mixed_bdrv_rdlock
732
+nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
94
+bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
733
+nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
95
734
+nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
96
/* Invalidate any cached metadata used by image formats */
735
+nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
97
-int co_wrapper_mixed bdrv_invalidate_cache(BlockDriverState *bs,
736
+nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
98
- Error **errp);
737
+nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
99
-int co_wrapper_mixed bdrv_flush(BlockDriverState *bs);
738
+nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
100
-int co_wrapper_mixed bdrv_pdiscard(BdrvChild *child, int64_t offset,
739
+nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
101
- int64_t bytes);
740
+nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
102
-int co_wrapper_mixed
741
+nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
103
+int co_wrapper_mixed_bdrv_rdlock
742
+nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
104
+bdrv_invalidate_cache(BlockDriverState *bs, Error **errp);
743
+nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
105
+
744
+nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
106
+int co_wrapper_mixed_bdrv_rdlock bdrv_flush(BlockDriverState *bs);
745
+nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
107
+
746
+nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
108
+int co_wrapper_mixed_bdrv_rdlock
747
+nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
109
+bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes);
748
+nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
110
+
749
+nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
111
+int co_wrapper_mixed_bdrv_rdlock
750
+nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
112
bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
751
+nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
113
-int co_wrapper_mixed
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"
114
+
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"
115
+int co_wrapper_mixed_bdrv_rdlock
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"
116
bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
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"
117
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"
118
/**
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"
119
--
784
--
120
2.38.1
785
2.13.6
786
787
diff view generated by jsdifflib
1
Instead of using a subtree drain from the top node (which also drains
1
From: Fam Zheng <famz@redhat.com>
2
child nodes of base that we're not even interested in), use a normal
3
drain for base, which automatically drains all of the parents, too.
4
2
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Management tools create overlays of running guests with qemu-img:
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
4
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
5
$ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2
8
Message-Id: <20221118174110.55183-9-kwolf@redhat.com>
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>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
18
---
11
block.c | 4 ++--
19
block.c | 3 ++-
12
1 file changed, 2 insertions(+), 2 deletions(-)
20
1 file changed, 2 insertions(+), 1 deletion(-)
13
21
14
diff --git a/block.c b/block.c
22
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
24
--- a/block.c
17
+++ b/block.c
25
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
19
GLOBAL_STATE_CODE();
27
back_flags = flags;
20
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
21
bdrv_ref(top);
29
22
- bdrv_subtree_drained_begin(top);
30
+ backing_options = qdict_new();
23
+ bdrv_drained_begin(base);
31
if (backing_fmt) {
24
32
- backing_options = qdict_new();
25
if (!top->drv || !base->drv) {
33
qdict_put_str(backing_options, "driver", backing_fmt);
26
goto exit;
34
}
27
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
28
36
29
ret = 0;
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
30
exit:
38
&local_err);
31
- bdrv_subtree_drained_end(top);
32
+ bdrv_drained_end(base);
33
bdrv_unref(top);
34
return ret;
35
}
36
--
39
--
37
2.38.1
40
2.13.6
41
42
diff view generated by jsdifflib
1
bdrv_img_create() polls internally (when calling bdrv_create(), which is
1
From: Thomas Huth <thuth@redhat.com>
2
a co_wrapper), so it can't be called while holding the lock of any
3
AioContext except the current one without causing deadlocks. Drop the
4
lock around the call in external_snapshot_prepare().
5
2
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
7
Message-Id: <20221207131838.239125-11-kwolf@redhat.com>
4
8
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
9
---
11
block.c | 4 ++++
10
blockdev.c | 11 -----------
12
blockdev.c | 4 ++++
11
qemu-doc.texi | 6 ------
13
2 files changed, 8 insertions(+)
12
2 files changed, 17 deletions(-)
14
13
15
diff --git a/block.c b/block.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block.c
18
+++ b/block.c
19
@@ -XXX,XX +XXX,XX @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs)
20
return true;
21
}
22
23
+/*
24
+ * Must not be called while holding the lock of an AioContext other than the
25
+ * current one.
26
+ */
27
void bdrv_img_create(const char *filename, const char *fmt,
28
const char *base_filename, const char *base_fmt,
29
char *options, uint64_t img_size, int flags, bool quiet,
30
diff --git a/blockdev.c b/blockdev.c
14
diff --git a/blockdev.c b/blockdev.c
31
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
32
--- a/blockdev.c
16
--- a/blockdev.c
33
+++ b/blockdev.c
17
+++ b/blockdev.c
34
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
35
goto out;
19
.type = QEMU_OPT_STRING,
36
}
20
.help = "chs translation (auto, lba, none)",
37
bdrv_refresh_filename(state->old_bs);
21
},{
38
+
22
- .name = "boot",
39
+ aio_context_release(aio_context);
23
- .type = QEMU_OPT_BOOL,
40
bdrv_img_create(new_image_file, format,
24
- .help = "(deprecated, ignored)",
41
state->old_bs->filename,
25
- },{
42
state->old_bs->drv->format_name,
26
.name = "addr",
43
NULL, size, flags, false, &local_err);
27
.type = QEMU_OPT_STRING,
44
+ aio_context_acquire(aio_context);
28
.help = "pci address (virtio only)",
45
+
29
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
46
if (local_err) {
30
goto fail;
47
error_propagate(errp, local_err);
31
}
48
goto out;
32
33
- /* Deprecated option boot=[on|off] */
34
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
35
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
36
- "ignored. Future versions will reject this parameter. Please "
37
- "update your scripts.\n");
38
- }
39
-
40
/* Other deprecated options */
41
if (!qtest_enabled()) {
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
49
--
60
--
50
2.38.1
61
2.13.6
62
63
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
Add co_wrapper_bdrv_rdlock and co_wrapper_mixed_bdrv_rdlock option to
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
the block-coroutine-wrapper.py script.
4
complained that we should keep it, so let's remove this legacy option
5
5
now to simplify the code quite a bit.
6
This "_bdrv_rdlock" option takes and releases the graph rdlock when a
6
7
coroutine function is created.
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
This means that when used together with "_mixed", the function marked
9
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
with co_wrapper_mixed_bdrv_rdlock will support both coroutine and
11
non-coroutine case, and in the latter case it will create a coroutine
12
that takes and releases the rdlock. When called from a coroutine, the
13
caller must already hold the graph lock.
14
15
Example:
16
void co_wrapper_mixed_bdrv_rdlock bdrv_f1();
17
18
Becomes
19
20
static void bdrv_co_enter_f1()
21
{
22
bdrv_graph_co_rdlock();
23
bdrv_co_function();
24
bdrv_graph_co_rdunlock();
25
}
26
27
void bdrv_f1()
28
{
29
if (qemu_in_coroutine) {
30
assume_graph_lock();
31
bdrv_co_function();
32
} else {
33
qemu_co_enter(bdrv_co_enter_f1);
34
...
35
}
36
}
37
38
When used alone, the function will not work in coroutine context, and
39
when called in non-coroutine context it will create a new coroutine that
40
takes care of taking and releasing the rdlock automatically.
41
42
Example:
43
void co_wrapper_bdrv_rdlock bdrv_f1();
44
45
Becomes
46
47
static void bdrv_co_enter_f1()
48
{
49
bdrv_graph_co_rdlock();
50
bdrv_co_function();
51
bdrv_graph_co_rdunlock();
52
}
53
54
void bdrv_f1()
55
{
56
assert(!qemu_in_coroutine());
57
qemu_co_enter(bdrv_co_enter_f1);
58
...
59
}
60
61
About their usage:
62
- co_wrapper does not take the rdlock, so it can be used also outside
63
the block layer.
64
- co_wrapper_mixed will be used by many blk_* functions, since the
65
coroutine function needs to call blk_wait_while_drained() and
66
the rdlock *must* be taken afterwards, otherwise it's a deadlock.
67
In the future this annotation will go away, and blk_* will use
68
co_wrapper directly.
69
- co_wrapper_bdrv_rdlock will be used by BlockDriver callbacks, ideally
70
by all of them in the future.
71
- co_wrapper_mixed_bdrv_rdlock will be used by the remaining functions
72
that are still called by coroutine and non-coroutine context. In the
73
future this annotation will go away, as we will split such mixed
74
functions.
75
76
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
77
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
78
Message-Id: <20221207131838.239125-17-kwolf@redhat.com>
79
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
80
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
81
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
82
---
11
---
83
include/block/block-common.h | 9 ++++++++-
12
vl.c | 86 ++-------------------------------------------------------
84
scripts/block-coroutine-wrapper.py | 12 ++++++++++++
13
qemu-doc.texi | 8 ------
85
2 files changed, 20 insertions(+), 1 deletion(-)
14
qemu-options.hx | 19 ++-----------
86
15
3 files changed, 4 insertions(+), 109 deletions(-)
87
diff --git a/include/block/block-common.h b/include/block/block-common.h
16
17
diff --git a/vl.c b/vl.c
88
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
89
--- a/include/block/block-common.h
19
--- a/vl.c
90
+++ b/include/block/block-common.h
20
+++ b/vl.c
91
@@ -XXX,XX +XXX,XX @@
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
92
*
22
const char *boot_order = NULL;
93
* Usage: read docs/devel/block-coroutine-wrapper.rst
23
const char *boot_once = NULL;
94
*
24
DisplayState *ds;
95
- * There are 2 kind of specifiers:
25
- int cyls, heads, secs, translation;
96
+ * There are 4 kind of specifiers:
26
QemuOpts *opts, *machine_opts;
97
* - co_wrapper functions can be called by only non-coroutine context, because
27
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
98
* they always generate a new coroutine.
28
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
99
* - co_wrapper_mixed functions can be called by both coroutine and
29
QemuOptsList *olist;
100
* non-coroutine context.
30
int optind;
101
+ * - co_wrapper_bdrv_rdlock are co_wrapper functions but automatically take and
31
const char *optarg;
102
+ * release the graph rdlock when creating a new coroutine
32
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
103
+ * - co_wrapper_mixed_bdrv_rdlock are co_wrapper_mixed functions but
33
104
+ * automatically take and release the graph rdlock when creating a new
34
cpu_model = NULL;
105
+ * coroutine.
35
snapshot = 0;
106
*/
36
- cyls = heads = secs = 0;
107
#define co_wrapper
37
- translation = BIOS_ATA_TRANSLATION_AUTO;
108
#define co_wrapper_mixed
38
109
+#define co_wrapper_bdrv_rdlock
39
nb_nics = 0;
110
+#define co_wrapper_mixed_bdrv_rdlock
40
111
41
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
112
#include "block/dirty-bitmap.h"
42
if (optind >= argc)
113
#include "block/blockjob.h"
43
break;
114
diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py
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
115
index XXXXXXX..XXXXXXX 100644
144
index XXXXXXX..XXXXXXX 100644
116
--- a/scripts/block-coroutine-wrapper.py
145
--- a/qemu-doc.texi
117
+++ b/scripts/block-coroutine-wrapper.py
146
+++ b/qemu-doc.texi
118
@@ -XXX,XX +XXX,XX @@ def __init__(self, return_type: str, name: str, args: str,
147
@@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the
119
self.struct_name = snake_to_camel(self.name)
148
``-object filter-dump'' argument which works in combination
120
self.args = [ParamDecl(arg.strip()) for arg in args.split(',')]
149
with the modern ``-netdev`` backends instead.
121
self.create_only_co = 'mixed' not in variant
150
122
+ self.graph_rdlock = 'bdrv_rdlock' in variant
151
-@subsection -hdachs (since 2.10.0)
123
152
-
124
subsystem, subname = self.name.split('_', 1)
153
-The ``-hdachs'' argument is now a synonym for setting
125
self.co_name = f'{subsystem}_co_{subname}'
154
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
126
@@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str:
155
-on the ``ide-hd'' device using the ``-device'' argument.
127
"""
156
-The new syntax allows different settings to be provided
128
name = func.co_name
157
-per disk.
129
struct_name = func.struct_name
158
-
130
+ graph_assume_lock = 'assume_graph_lock();' if func.graph_rdlock else ''
159
@subsection -usbdevice (since 2.10.0)
131
+
160
132
return f"""\
161
The ``-usbdevice DEV'' argument is now a synonym for setting
133
{func.return_type} {func.name}({ func.gen_list('{decl}') })
162
diff --git a/qemu-options.hx b/qemu-options.hx
134
{{
163
index XXXXXXX..XXXXXXX 100644
135
if (qemu_in_coroutine()) {{
164
--- a/qemu-options.hx
136
+ {graph_assume_lock}
165
+++ b/qemu-options.hx
137
return {name}({ func.gen_list('{name}') });
166
@@ -XXX,XX +XXX,XX @@ of available connectors of a given interface type.
138
}} else {{
167
@item media=@var{media}
139
{struct_name} s = {{
168
This option defines the type of the media: disk or cdrom.
140
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
169
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
141
name = func.co_name
170
-These options have the same definition as they have in @option{-hdachs}.
142
struct_name = func.struct_name
171
-These parameters are deprecated, use the corresponding parameters
143
172
+Force disk physical geometry and the optional BIOS translation (trans=none or
144
+ graph_lock=''
173
+lba). These parameters are deprecated, use the corresponding parameters
145
+ graph_unlock=''
174
of @code{-device} instead.
146
+ if func.graph_rdlock:
175
@item snapshot=@var{snapshot}
147
+ graph_lock=' bdrv_graph_co_rdlock();'
176
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
148
+ graph_unlock=' bdrv_graph_co_rdunlock();'
177
@@ -XXX,XX +XXX,XX @@ the raw disk image you use is not written back. You can however force
149
+
178
the write back by pressing @key{C-a s} (@pxref{disk_images}).
150
creation_function = create_mixed_wrapper
179
ETEXI
151
if func.create_only_co:
180
152
creation_function = create_co_wrapper
181
-DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
153
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
182
- "-hdachs c,h,s[,t]\n" \
154
{{
183
- " force hard disk 0 physical geometry and the optional BIOS\n" \
155
{struct_name} *s = opaque;
184
- " translation (t=none or lba) (usually QEMU can guess them)\n",
156
185
- QEMU_ARCH_ALL)
157
+{graph_lock}
186
-STEXI
158
s->ret = {name}({ func.gen_list('s->{name}') });
187
-@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
159
+{graph_unlock}
188
-@findex -hdachs
160
s->poll_state.in_progress = false;
189
-Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
161
190
-@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
162
aio_wait_kick();
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"
163
--
199
--
164
2.38.1
200
2.13.6
201
202
diff view generated by jsdifflib
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
Message-Id: <20221207131838.239125-15-kwolf@redhat.com>
2
3
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
10
---
6
include/block/graph-lock.h | 80 +++++++++++++++++++++++++++++++++-----
11
qemu-doc.texi | 15 +++++++++++++++
7
block/graph-lock.c | 3 ++
12
1 file changed, 15 insertions(+)
8
2 files changed, 73 insertions(+), 10 deletions(-)
9
13
10
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
11
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
12
--- a/include/block/graph-lock.h
16
--- a/qemu-doc.texi
13
+++ b/include/block/graph-lock.h
17
+++ b/qemu-doc.texi
14
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
15
#define GRAPH_LOCK_H
19
The ``-drive if=scsi'' argument is replaced by the the
16
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
17
#include "qemu/osdep.h"
21
18
+#include "qemu/clang-tsa.h"
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
19
20
#include "qemu/coroutine.h"
21
22
@@ -XXX,XX +XXX,XX @@
23
*/
24
typedef struct BdrvGraphRWlock BdrvGraphRWlock;
25
26
+/* Dummy lock object to use for Thread Safety Analysis (TSA) */
27
+typedef struct TSA_CAPABILITY("graph-lock") BdrvGraphLock {
28
+} BdrvGraphLock;
29
+
23
+
30
+extern BdrvGraphLock graph_lock;
24
+The drive geometry arguments are replaced by the the geometry arguments
25
+that can be specified with the ``-device'' parameter.
31
+
26
+
32
+/*
27
+@subsection -drive serial=... (since 2.10.0)
33
+ * clang doesn't check consistency in locking annotations between forward
34
+ * declarations and the function definition. Having the annotation on the
35
+ * definition, but not the declaration in a header file, may give the reader
36
+ * a false sense of security because the condition actually remains unchecked
37
+ * for callers in other source files.
38
+ *
39
+ * Therefore, as a convention, for public functions, GRAPH_RDLOCK and
40
+ * GRAPH_WRLOCK annotations should be present only in the header file.
41
+ */
42
+#define GRAPH_WRLOCK TSA_REQUIRES(graph_lock)
43
+#define GRAPH_RDLOCK TSA_REQUIRES_SHARED(graph_lock)
44
+
28
+
45
+/*
29
+The drive serial argument is replaced by the the serial argument
46
+ * TSA annotations are not part of function types, so checks are defeated when
30
+that can be specified with the ``-device'' parameter.
47
+ * using a function pointer. As a workaround, annotate function pointers with
48
+ * this macro that will require that the lock is at least taken while reading
49
+ * the pointer. In most cases this is equivalent to actually protecting the
50
+ * function call.
51
+ */
52
+#define GRAPH_RDLOCK_PTR TSA_GUARDED_BY(graph_lock)
53
+#define GRAPH_WRLOCK_PTR TSA_GUARDED_BY(graph_lock)
54
+
31
+
55
/*
32
+@subsection -drive addr=... (since 2.10.0)
56
* register_aiocontext:
57
* Add AioContext @ctx to the list of AioContext.
58
@@ -XXX,XX +XXX,XX @@ void unregister_aiocontext(AioContext *ctx);
59
* This function polls. Callers must not hold the lock of any AioContext other
60
* than the current one.
61
*/
62
-void bdrv_graph_wrlock(void);
63
+void bdrv_graph_wrlock(void) TSA_ACQUIRE(graph_lock) TSA_NO_TSA;
64
65
/*
66
* bdrv_graph_wrunlock:
67
* Write finished, reset global has_writer to 0 and restart
68
* all readers that are waiting.
69
*/
70
-void bdrv_graph_wrunlock(void);
71
+void bdrv_graph_wrunlock(void) TSA_RELEASE(graph_lock) TSA_NO_TSA;
72
73
/*
74
* bdrv_graph_co_rdlock:
75
@@ -XXX,XX +XXX,XX @@ void bdrv_graph_wrunlock(void);
76
* loop) to take it and wait that the coroutine ends, so that
77
* we always signal that a reader is running.
78
*/
79
-void coroutine_fn bdrv_graph_co_rdlock(void);
80
+void coroutine_fn TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA
81
+bdrv_graph_co_rdlock(void);
82
83
/*
84
* bdrv_graph_rdunlock:
85
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_graph_co_rdlock(void);
86
* If the writer is waiting for reads to finish (has_writer == 1), signal
87
* the writer that we are done via aio_wait_kick() to let it continue.
88
*/
89
-void coroutine_fn bdrv_graph_co_rdunlock(void);
90
+void coroutine_fn TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA
91
+bdrv_graph_co_rdunlock(void);
92
93
/*
94
* bdrv_graph_rd{un}lock_main_loop:
95
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_graph_co_rdunlock(void);
96
* in the main loop. It is just asserting that we are not
97
* in a coroutine and in GLOBAL_STATE_CODE.
98
*/
99
-void bdrv_graph_rdlock_main_loop(void);
100
-void bdrv_graph_rdunlock_main_loop(void);
101
+void TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA
102
+bdrv_graph_rdlock_main_loop(void);
103
+
33
+
104
+void TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA
34
+The drive addr argument is replaced by the the addr argument
105
+bdrv_graph_rdunlock_main_loop(void);
35
+that can be specified with the ``-device'' parameter.
106
107
/*
108
* assert_bdrv_graph_readable:
109
@@ -XXX,XX +XXX,XX @@ void assert_bdrv_graph_readable(void);
110
*/
111
void assert_bdrv_graph_writable(void);
112
113
+/*
114
+ * Calling this function tells TSA that we know that the lock is effectively
115
+ * taken even though we cannot prove it (yet) with GRAPH_RDLOCK. This can be
116
+ * useful in intermediate stages of a conversion to using the GRAPH_RDLOCK
117
+ * macro.
118
+ */
119
+static inline void TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA
120
+assume_graph_lock(void)
121
+{
122
+}
123
+
36
+
124
typedef struct GraphLockable { } GraphLockable;
37
@subsection -net dump (since 2.10.0)
125
38
126
/*
39
The ``--net dump'' argument is now replaced with the
127
@@ -XXX,XX +XXX,XX @@ typedef struct GraphLockable { } GraphLockable;
128
*/
129
#define GML_OBJ_() (&(GraphLockable) { })
130
131
-static inline GraphLockable *graph_lockable_auto_lock(GraphLockable *x)
132
+/*
133
+ * This is not marked as TSA_ACQUIRE() because TSA doesn't understand the
134
+ * cleanup attribute and would therefore complain that the graph is never
135
+ * unlocked. TSA_ASSERT() makes sure that the following calls know that we
136
+ * hold the lock while unlocking is left unchecked.
137
+ */
138
+static inline GraphLockable * TSA_ASSERT(graph_lock) TSA_NO_TSA
139
+graph_lockable_auto_lock(GraphLockable *x)
140
{
141
bdrv_graph_co_rdlock();
142
return x;
143
}
144
145
-static inline void graph_lockable_auto_unlock(GraphLockable *x)
146
+static inline void TSA_NO_TSA
147
+graph_lockable_auto_unlock(GraphLockable *x)
148
{
149
bdrv_graph_co_rdunlock();
150
}
151
@@ -XXX,XX +XXX,XX @@ typedef struct GraphLockableMainloop { } GraphLockableMainloop;
152
*/
153
#define GMLML_OBJ_() (&(GraphLockableMainloop) { })
154
155
-static inline GraphLockableMainloop *
156
+/*
157
+ * This is not marked as TSA_ACQUIRE() because TSA doesn't understand the
158
+ * cleanup attribute and would therefore complain that the graph is never
159
+ * unlocked. TSA_ASSERT() makes sure that the following calls know that we
160
+ * hold the lock while unlocking is left unchecked.
161
+ */
162
+static inline GraphLockableMainloop * TSA_ASSERT(graph_lock) TSA_NO_TSA
163
graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x)
164
{
165
bdrv_graph_rdlock_main_loop();
166
return x;
167
}
168
169
-static inline void
170
+static inline void TSA_NO_TSA
171
graph_lockable_auto_unlock_mainloop(GraphLockableMainloop *x)
172
{
173
bdrv_graph_rdunlock_main_loop();
174
diff --git a/block/graph-lock.c b/block/graph-lock.c
175
index XXXXXXX..XXXXXXX 100644
176
--- a/block/graph-lock.c
177
+++ b/block/graph-lock.c
178
@@ -XXX,XX +XXX,XX @@
179
#include "block/block.h"
180
#include "block/block_int.h"
181
182
+/* Dummy lock object to use for Thread Safety Analysis (TSA) */
183
+BdrvGraphLock graph_lock;
184
+
185
/* Protects the list of aiocontext and orphaned_reader_count */
186
static QemuMutex aio_context_list_lock;
187
188
--
40
--
189
2.38.1
41
2.13.6
42
43
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
Call two different functions depending on whether bdrv_create
3
Signed-off-by: Fam Zheng <famz@redhat.com>
4
is in coroutine or not, following the same pattern as
5
generated_co_wrapper functions.
6
7
This allows to also call the coroutine function directly,
8
without using CreateCo or relying in bdrv_create().
9
10
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
13
Message-Id: <20221128142337.657646-8-eesposit@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
5
---
16
block.c | 69 ++++++++++++++++++++++++++++-----------------------------
6
include/block/block_int.h | 1 -
17
1 file changed, 34 insertions(+), 35 deletions(-)
7
block/io.c | 18 ------------------
8
2 files changed, 19 deletions(-)
18
9
19
diff --git a/block.c b/block.c
10
diff --git a/include/block/block_int.h b/include/block/block_int.h
20
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
21
--- a/block.c
12
--- a/include/block/block_int.h
22
+++ b/block.c
13
+++ b/include/block/block_int.h
23
@@ -XXX,XX +XXX,XX @@ typedef struct CreateCo {
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
24
Error *err;
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
25
} CreateCo;
16
26
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
27
-static void coroutine_fn bdrv_create_co_entry(void *opaque)
18
-bool bdrv_requests_pending(BlockDriverState *bs);
28
+static int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
19
29
+ QemuOpts *opts, Error **errp)
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
30
{
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
31
- Error *local_err = NULL;
22
diff --git a/block/io.c b/block/io.c
32
int ret;
23
index XXXXXXX..XXXXXXX 100644
33
+ GLOBAL_STATE_CODE();
24
--- a/block/io.c
34
+ ERRP_GUARD();
25
+++ b/block/io.c
35
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
36
+ if (!drv->bdrv_co_create_opts) {
27
assert(old >= 1);
37
+ error_setg(errp, "Driver '%s' does not support image creation",
38
+ drv->format_name);
39
+ return -ENOTSUP;
40
+ }
41
+
42
+ ret = drv->bdrv_co_create_opts(drv, filename, opts, errp);
43
+ if (ret < 0 && !*errp) {
44
+ error_setg_errno(errp, -ret, "Could not create image");
45
+ }
46
+
47
+ return ret;
48
+}
49
+
50
+static void coroutine_fn bdrv_create_co_entry(void *opaque)
51
+{
52
CreateCo *cco = opaque;
53
- assert(cco->drv);
54
GLOBAL_STATE_CODE();
55
56
- ret = cco->drv->bdrv_co_create_opts(cco->drv,
57
- cco->filename, cco->opts, &local_err);
58
- error_propagate(&cco->err, local_err);
59
- cco->ret = ret;
60
+ cco->ret = bdrv_co_create(cco->drv, cco->filename, cco->opts, &cco->err);
61
+ aio_wait_kick();
62
}
28
}
63
29
64
int bdrv_create(BlockDriver *drv, const char* filename,
30
-/* Check if any requests are in-flight (including throttled requests) */
65
QemuOpts *opts, Error **errp)
31
-bool bdrv_requests_pending(BlockDriverState *bs)
66
{
32
-{
67
- int ret;
33
- BdrvChild *child;
68
-
34
-
69
GLOBAL_STATE_CODE();
35
- if (atomic_read(&bs->in_flight)) {
70
36
- return true;
71
- Coroutine *co;
72
- CreateCo cco = {
73
- .drv = drv,
74
- .filename = filename,
75
- .opts = opts,
76
- .ret = NOT_DONE,
77
- .err = NULL,
78
- };
79
-
80
- if (!drv->bdrv_co_create_opts) {
81
- error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
82
- return -ENOTSUP;
83
- }
37
- }
84
-
38
-
85
if (qemu_in_coroutine()) {
39
- QLIST_FOREACH(child, &bs->children, next) {
86
/* Fast-path if already in coroutine context */
40
- if (bdrv_requests_pending(child->bs)) {
87
- bdrv_create_co_entry(&cco);
41
- return true;
88
+ return bdrv_co_create(drv, filename, opts, errp);
89
} else {
90
+ Coroutine *co;
91
+ CreateCo cco = {
92
+ .drv = drv,
93
+ .filename = filename,
94
+ .opts = opts,
95
+ .ret = NOT_DONE,
96
+ .err = NULL,
97
+ };
98
+
99
co = qemu_coroutine_create(bdrv_create_co_entry, &cco);
100
qemu_coroutine_enter(co);
101
while (cco.ret == NOT_DONE) {
102
aio_poll(qemu_get_aio_context(), true);
103
}
104
+ error_propagate(errp, cco.err);
105
+ return cco.ret;
106
}
107
-
108
- ret = cco.ret;
109
- if (ret < 0) {
110
- if (cco.err) {
111
- error_propagate(errp, cco.err);
112
- } else {
113
- error_setg_errno(errp, -ret, "Could not create image");
114
- }
42
- }
115
- }
43
- }
116
-
44
-
117
- return ret;
45
- return false;
118
}
46
-}
119
47
-
120
/**
48
typedef struct {
49
Coroutine *co;
50
BlockDriverState *bs;
121
--
51
--
122
2.38.1
52
2.13.6
53
54
diff view generated by jsdifflib
1
In order to make sure that bdrv_replace_child_noperm() doesn't have to
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
poll any more, get rid of the bdrv_parent_drained_begin_single() call.
2
Reviewed-by: Fam Zheng <famz@redhat.com>
3
---
4
block/io.c | 6 ++++++
5
1 file changed, 6 insertions(+)
3
6
4
This is possible now because we can require that the parent is already
5
drained through the child in question when the function is called and we
6
don't call the parent drain callbacks more than once.
7
8
The additional drain calls needed in callers cause the test case to run
9
its code in the drain handler too early (bdrv_attach_child() drains
10
now), so modify it to only enable the code after the test setup has
11
completed.
12
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Message-Id: <20221118174110.55183-15-kwolf@redhat.com>
15
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
16
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
19
include/block/block-io.h | 8 +++
20
block.c | 103 ++++++++++++++++++++++++++++++-----
21
block/io.c | 2 +-
22
tests/unit/test-bdrv-drain.c | 10 ++++
23
4 files changed, 108 insertions(+), 15 deletions(-)
24
25
diff --git a/include/block/block-io.h b/include/block/block-io.h
26
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/block-io.h
28
+++ b/include/block/block-io.h
29
@@ -XXX,XX +XXX,XX @@ bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
30
*/
31
void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
32
33
+/**
34
+ * bdrv_parent_drained_poll_single:
35
+ *
36
+ * Returns true if there is any pending activity to cease before @c can be
37
+ * called quiesced, false otherwise.
38
+ */
39
+bool bdrv_parent_drained_poll_single(BdrvChild *c);
40
+
41
/**
42
* bdrv_parent_drained_end_single:
43
*
44
diff --git a/block.c b/block.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/block.c
47
+++ b/block.c
48
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque)
49
50
GLOBAL_STATE_CODE();
51
/* old_bs reference is transparently moved from @s to @s->child */
52
+ if (!s->child->bs) {
53
+ /*
54
+ * The parents were undrained when removing old_bs from the child. New
55
+ * requests can't have been made, though, because the child was empty.
56
+ *
57
+ * TODO Make bdrv_replace_child_noperm() transactionable to avoid
58
+ * undraining the parent in the first place. Once this is done, having
59
+ * new_bs drained when calling bdrv_replace_child_tran() is not a
60
+ * requirement any more.
61
+ */
62
+ bdrv_parent_drained_begin_single(s->child, false);
63
+ assert(!bdrv_parent_drained_poll_single(s->child));
64
+ }
65
+ assert(s->child->quiesced_parent);
66
bdrv_replace_child_noperm(s->child, s->old_bs);
67
bdrv_unref(new_bs);
68
}
69
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = {
70
*
71
* Note: real unref of old_bs is done only on commit.
72
*
73
+ * Both @child->bs and @new_bs (if non-NULL) must be drained. @new_bs must be
74
+ * kept drained until the transaction is completed.
75
+ *
76
* The function doesn't update permissions, caller is responsible for this.
77
*/
78
static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
79
Transaction *tran)
80
{
81
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
82
+
83
+ assert(child->quiesced_parent);
84
+ assert(!new_bs || new_bs->quiesce_counter);
85
+
86
*s = (BdrvReplaceChildState) {
87
.child = child,
88
.old_bs = child->bs,
89
@@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
90
return permissions[qapi_perm];
91
}
92
93
+/*
94
+ * Replaces the node that a BdrvChild points to without updating permissions.
95
+ *
96
+ * If @new_bs is non-NULL, the parent of @child must already be drained through
97
+ * @child.
98
+ *
99
+ * This function does not poll.
100
+ */
101
static void bdrv_replace_child_noperm(BdrvChild *child,
102
BlockDriverState *new_bs)
103
{
104
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
105
int new_bs_quiesce_counter;
106
107
assert(!child->frozen);
108
+
109
+ /*
110
+ * If we want to change the BdrvChild to point to a drained node as its new
111
+ * child->bs, we need to make sure that its new parent is drained, too. In
112
+ * other words, either child->quiesce_parent must already be true or we must
113
+ * be able to set it and keep the parent's quiesce_counter consistent with
114
+ * that, but without polling or starting new requests (this function
115
+ * guarantees that it doesn't poll, and starting new requests would be
116
+ * against the invariants of drain sections).
117
+ *
118
+ * To keep things simple, we pick the first option (child->quiesce_parent
119
+ * must already be true). We also generalise the rule a bit to make it
120
+ * easier to verify in callers and more likely to be covered in test cases:
121
+ * The parent must be quiesced through this child even if new_bs isn't
122
+ * currently drained.
123
+ *
124
+ * The only exception is for callers that always pass new_bs == NULL. In
125
+ * this case, we obviously never need to consider the case of a drained
126
+ * new_bs, so we can keep the callers simpler by allowing them not to drain
127
+ * the parent.
128
+ */
129
+ assert(!new_bs || child->quiesced_parent);
130
assert(old_bs != new_bs);
131
GLOBAL_STATE_CODE();
132
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
134
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
135
}
136
137
- /*
138
- * If the new child node is drained but the old one was not, flush
139
- * all outstanding requests to the old child node.
140
- */
141
- new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
142
- if (new_bs_quiesce_counter && !child->quiesced_parent) {
143
- bdrv_parent_drained_begin_single(child, true);
144
- }
145
-
146
if (old_bs) {
147
if (child->klass->detach) {
148
child->klass->detach(child);
149
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
150
}
151
152
/*
153
- * If the old child node was drained but the new one is not, allow
154
- * requests to come in only after the new node has been attached.
155
- *
156
- * Update new_bs_quiesce_counter because bdrv_parent_drained_begin_single()
157
- * polls, which could have changed the value.
158
+ * If the parent was drained through this BdrvChild previously, but new_bs
159
+ * is not drained, allow requests to come in only after the new node has
160
+ * been attached.
161
*/
162
new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
163
if (!new_bs_quiesce_counter && child->quiesced_parent) {
164
@@ -XXX,XX +XXX,XX @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs,
165
}
166
167
bdrv_ref(child_bs);
168
+ /*
169
+ * Let every new BdrvChild start with a drained parent. Inserting the child
170
+ * in the graph with bdrv_replace_child_noperm() will undrain it if
171
+ * @child_bs is not drained.
172
+ *
173
+ * The child was only just created and is not yet visible in global state
174
+ * until bdrv_replace_child_noperm() inserts it into the graph, so nobody
175
+ * could have sent requests and polling is not necessary.
176
+ *
177
+ * Note that this means that the parent isn't fully drained yet, we only
178
+ * stop new requests from coming in. This is fine, we don't care about the
179
+ * old requests here, they are not for this child. If another place enters a
180
+ * drain section for the same parent, but wants it to be fully quiesced, it
181
+ * will not run most of the the code in .drained_begin() again (which is not
182
+ * a problem, we already did this), but it will still poll until the parent
183
+ * is fully quiesced, so it will not be negatively affected either.
184
+ */
185
+ bdrv_parent_drained_begin_single(new_child, false);
186
bdrv_replace_child_noperm(new_child, child_bs);
187
188
BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1);
189
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_child(BdrvChild *child, Transaction *tran)
190
}
191
192
if (child->bs) {
193
+ BlockDriverState *bs = child->bs;
194
+ bdrv_drained_begin(bs);
195
bdrv_replace_child_tran(child, NULL, tran);
196
+ bdrv_drained_end(bs);
197
}
198
199
tran_add(tran, &bdrv_remove_child_drv, child);
200
}
201
202
+static void undrain_on_clean_cb(void *opaque)
203
+{
204
+ bdrv_drained_end(opaque);
205
+}
206
+
207
+static TransactionActionDrv undrain_on_clean = {
208
+ .clean = undrain_on_clean_cb,
209
+};
210
+
211
static int bdrv_replace_node_noperm(BlockDriverState *from,
212
BlockDriverState *to,
213
bool auto_skip, Transaction *tran,
214
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
215
216
GLOBAL_STATE_CODE();
217
218
+ bdrv_drained_begin(from);
219
+ bdrv_drained_begin(to);
220
+ tran_add(tran, &undrain_on_clean, from);
221
+ tran_add(tran, &undrain_on_clean, to);
222
+
223
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
224
assert(c->bs == from);
225
if (!should_update_child(c, to)) {
226
diff --git a/block/io.c b/block/io.c
7
diff --git a/block/io.c b/block/io.c
227
index XXXXXXX..XXXXXXX 100644
8
index XXXXXXX..XXXXXXX 100644
228
--- a/block/io.c
9
--- a/block/io.c
229
+++ b/block/io.c
10
+++ b/block/io.c
230
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
231
}
12
BdrvNextIterator it;
232
}
13
GSList *aio_ctxs = NULL, *ctx;
233
14
234
-static bool bdrv_parent_drained_poll_single(BdrvChild *c)
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
235
+bool bdrv_parent_drained_poll_single(BdrvChild *c)
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
236
{
17
+ * nodes in several different AioContexts, so make sure we're in the main
237
if (c->klass->drained_poll) {
18
+ * context. */
238
return c->klass->drained_poll(c);
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
239
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
240
index XXXXXXX..XXXXXXX 100644
241
--- a/tests/unit/test-bdrv-drain.c
242
+++ b/tests/unit/test-bdrv-drain.c
243
@@ -XXX,XX +XXX,XX @@ static void test_drop_intermediate_poll(void)
244
245
246
typedef struct BDRVReplaceTestState {
247
+ bool setup_completed;
248
bool was_drained;
249
bool was_undrained;
250
bool has_read;
251
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_test_drain_begin(BlockDriverState *bs)
252
{
253
BDRVReplaceTestState *s = bs->opaque;
254
255
+ if (!s->setup_completed) {
256
+ return;
257
+ }
258
+
20
+
259
if (!s->drain_count) {
21
block_job_pause_all();
260
s->drain_co = qemu_coroutine_create(bdrv_replace_test_drain_co, bs);
22
261
bdrv_inc_in_flight(bs);
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
262
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_test_drain_end(BlockDriverState *bs)
263
{
264
BDRVReplaceTestState *s = bs->opaque;
265
266
+ if (!s->setup_completed) {
267
+ return;
268
+ }
269
+
270
g_assert(s->drain_count > 0);
271
if (!--s->drain_count) {
272
s->was_undrained = true;
273
@@ -XXX,XX +XXX,XX @@ static void do_test_replace_child_mid_drain(int old_drain_count,
274
bdrv_ref(old_child_bs);
275
bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds,
276
BDRV_CHILD_COW, &error_abort);
277
+ parent_s->setup_completed = true;
278
279
for (i = 0; i < old_drain_count; i++) {
280
bdrv_drained_begin(old_child_bs);
281
--
24
--
282
2.38.1
25
2.13.6
26
27
diff view generated by jsdifflib
1
We only need to call both the BlockDriver's callback and the parent
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
callbacks when going from undrained to drained or vice versa. A second
2
and also doesn't notify other parent nodes of children, which both means
3
drain section doesn't make a difference for the driver or the parent,
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
they weren't supposed to send new requests before and after the second
4
is providing useful functionality only on a single node.
5
drain.
6
5
7
One thing that gets in the way is the 'ignore_bds_parents' parameter in
6
To keep things consistent, we also shouldn't call the block driver
8
bdrv_do_drained_begin_quiesce() and bdrv_do_drained_end(): It means that
7
callbacks recursively.
9
bdrv_drain_all_begin() increases bs->quiesce_counter, but does not
10
quiesce the parent through BdrvChildClass callbacks. If an additional
11
drain section is started now, bs->quiesce_counter will be non-zero, but
12
we would still need to quiesce the parent through BdrvChildClass in
13
order to keep things consistent (and unquiesce it on the matching
14
bdrv_drained_end(), even though the counter would not reach 0 yet as
15
long as the bdrv_drain_all() section is still active).
16
8
17
Instead of keeping track of this, let's just get rid of the parameter.
9
A proper recursive drain version that provides an actually working
18
It was introduced in commit 6cd5c9d7b2d as an optimisation so that
10
drained section for child nodes will be introduced later.
19
during bdrv_drain_all(), we wouldn't recursively drain all parents up to
20
the root for each node, resulting in quadratic complexity. As it happens,
21
calling the callbacks only once solves the same problem, so as of this
22
patch, we'll still have O(n) complexity and ignore_bds_parents is not
23
needed any more.
24
25
This patch only ignores the 'ignore_bds_parents' parameter. It will be
26
removed in a separate patch.
27
11
28
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
29
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
30
Message-Id: <20221118174110.55183-12-kwolf@redhat.com>
31
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
32
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
33
---
14
---
34
include/block/block_int-common.h | 8 ++++----
15
block/io.c | 16 +++++++++-------
35
block.c | 25 +++++++------------------
16
1 file changed, 9 insertions(+), 7 deletions(-)
36
block/io.c | 30 ++++++++++++++++++------------
37
tests/unit/test-bdrv-drain.c | 16 ++++++++++------
38
4 files changed, 39 insertions(+), 40 deletions(-)
39
17
40
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
41
index XXXXXXX..XXXXXXX 100644
42
--- a/include/block/block_int-common.h
43
+++ b/include/block/block_int-common.h
44
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
45
bool frozen;
46
47
/*
48
- * How many times the parent of this child has been drained
49
+ * True if the parent of this child has been drained by this BdrvChild
50
* (through klass->drained_*).
51
- * Usually, this is equal to bs->quiesce_counter (potentially
52
- * reduced by bdrv_drain_all_count). It may differ while the
53
+ *
54
+ * It is generally true if bs->quiesce_counter > 0. It may differ while the
55
* child is entering or leaving a drained section.
56
*/
57
- int parent_quiesce_counter;
58
+ bool quiesced_parent;
59
60
QLIST_ENTRY(BdrvChild) next;
61
QLIST_ENTRY(BdrvChild) next_parent;
62
diff --git a/block.c b/block.c
63
index XXXXXXX..XXXXXXX 100644
64
--- a/block.c
65
+++ b/block.c
66
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
67
{
68
BlockDriverState *old_bs = child->bs;
69
int new_bs_quiesce_counter;
70
- int drain_saldo;
71
72
assert(!child->frozen);
73
assert(old_bs != new_bs);
74
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
75
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
76
}
77
78
- new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
79
- drain_saldo = new_bs_quiesce_counter - child->parent_quiesce_counter;
80
-
81
/*
82
* If the new child node is drained but the old one was not, flush
83
* all outstanding requests to the old child node.
84
*/
85
- while (drain_saldo > 0 && child->klass->drained_begin) {
86
+ new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
87
+ if (new_bs_quiesce_counter && !child->quiesced_parent) {
88
bdrv_parent_drained_begin_single(child, true);
89
- drain_saldo--;
90
}
91
92
if (old_bs) {
93
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
94
if (new_bs) {
95
assert_bdrv_graph_writable(new_bs);
96
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
97
-
98
- /*
99
- * Polling in bdrv_parent_drained_begin_single() may have led to the new
100
- * node's quiesce_counter having been decreased. Not a problem, we just
101
- * need to recognize this here and then invoke drained_end appropriately
102
- * more often.
103
- */
104
- assert(new_bs->quiesce_counter <= new_bs_quiesce_counter);
105
- drain_saldo += new_bs->quiesce_counter - new_bs_quiesce_counter;
106
-
107
if (child->klass->attach) {
108
child->klass->attach(child);
109
}
110
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
111
/*
112
* If the old child node was drained but the new one is not, allow
113
* requests to come in only after the new node has been attached.
114
+ *
115
+ * Update new_bs_quiesce_counter because bdrv_parent_drained_begin_single()
116
+ * polls, which could have changed the value.
117
*/
118
- while (drain_saldo < 0 && child->klass->drained_end) {
119
+ new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
120
+ if (!new_bs_quiesce_counter && child->quiesced_parent) {
121
bdrv_parent_drained_end_single(child);
122
- drain_saldo++;
123
}
124
}
125
126
diff --git a/block/io.c b/block/io.c
18
diff --git a/block/io.c b/block/io.c
127
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
128
--- a/block/io.c
20
--- a/block/io.c
129
+++ b/block/io.c
21
+++ b/block/io.c
130
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *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)
131
{
28
{
132
IO_OR_GS_CODE();
29
BdrvChild *child, *tmp;
133
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
134
- assert(c->parent_quiesce_counter > 0);
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
135
- c->parent_quiesce_counter--;
32
bdrv_coroutine_enter(bs, data.co);
136
+ assert(c->quiesced_parent);
33
BDRV_POLL_WHILE(bs, !data.done);
137
+ c->quiesced_parent = false;
34
138
+
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
139
if (c->klass->drained_end) {
36
- bdrv_drain_invoke(child->bs, begin);
140
c->klass->drained_end(c);
37
+ if (recursive) {
141
}
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
142
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
39
+ bdrv_drain_invoke(child->bs, begin, true);
143
{
144
AioContext *ctx = bdrv_child_get_parent_aio_context(c);
145
IO_OR_GS_CODE();
146
- c->parent_quiesce_counter++;
147
+
148
+ assert(!c->quiesced_parent);
149
+ c->quiesced_parent = true;
150
+
151
if (c->klass->drained_begin) {
152
c->klass->drained_begin(c);
153
}
154
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
155
/* Stop things in parent-to-child order */
156
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
157
aio_disable_external(bdrv_get_aio_context(bs));
158
- }
159
160
- bdrv_parent_drained_begin(bs, parent, ignore_bds_parents);
161
- if (bs->drv && bs->drv->bdrv_drain_begin) {
162
- bs->drv->bdrv_drain_begin(bs);
163
+ /* TODO Remove ignore_bds_parents, we don't consider it any more */
164
+ bdrv_parent_drained_begin(bs, parent, false);
165
+ if (bs->drv && bs->drv->bdrv_drain_begin) {
166
+ bs->drv->bdrv_drain_begin(bs);
167
+ }
40
+ }
168
}
41
}
169
}
42
}
170
43
171
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
172
assert(bs->quiesce_counter > 0);
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
}
173
55
174
/* Re-enable things in child-to-parent order */
56
/* Re-enable things in child-to-parent order */
175
- if (bs->drv && bs->drv->bdrv_drain_end) {
57
- bdrv_drain_invoke(bs, false);
176
- bs->drv->bdrv_drain_end(bs);
58
+ bdrv_drain_invoke(bs, false, false);
177
- }
59
bdrv_parent_drained_end(bs);
178
- bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
60
aio_enable_external(bdrv_get_aio_context(bs));
179
-
180
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
181
if (old_quiesce_counter == 1) {
182
+ if (bs->drv && bs->drv->bdrv_drain_end) {
183
+ bs->drv->bdrv_drain_end(bs);
184
+ }
185
+ /* TODO Remove ignore_bds_parents, we don't consider it any more */
186
+ bdrv_parent_drained_end(bs, parent, false);
187
+
188
aio_enable_external(bdrv_get_aio_context(bs));
189
}
190
}
61
}
191
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
62
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
192
index XXXXXXX..XXXXXXX 100644
63
aio_context_acquire(aio_context);
193
--- a/tests/unit/test-bdrv-drain.c
64
aio_disable_external(aio_context);
194
+++ b/tests/unit/test-bdrv-drain.c
65
bdrv_parent_drained_begin(bs);
195
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_common(enum drain_type drain_type, bool recursive)
66
- bdrv_drain_invoke(bs, true);
196
67
+ bdrv_drain_invoke(bs, true, true);
197
do_drain_begin(drain_type, bs);
68
aio_context_release(aio_context);
198
69
199
- g_assert_cmpint(bs->quiesce_counter, ==, 1);
70
if (!g_slist_find(aio_ctxs, aio_context)) {
200
+ if (drain_type == BDRV_DRAIN_ALL) {
71
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
201
+ g_assert_cmpint(bs->quiesce_counter, ==, 2);
72
202
+ } else {
73
/* Re-enable things in child-to-parent order */
203
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
74
aio_context_acquire(aio_context);
204
+ }
75
- bdrv_drain_invoke(bs, false);
205
g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
76
+ bdrv_drain_invoke(bs, false, true);
206
77
bdrv_parent_drained_end(bs);
207
do_drain_end(drain_type, bs);
78
aio_enable_external(aio_context);
208
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
79
aio_context_release(aio_context);
209
210
for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
211
for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
212
- int backing_quiesce = (outer != BDRV_DRAIN) +
213
- (inner != BDRV_DRAIN);
214
+ int backing_quiesce = (outer == BDRV_DRAIN_ALL) +
215
+ (inner == BDRV_DRAIN_ALL);
216
217
g_assert_cmpint(bs->quiesce_counter, ==, 0);
218
g_assert_cmpint(backing->quiesce_counter, ==, 0);
219
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
220
do_drain_begin(outer, bs);
221
do_drain_begin(inner, bs);
222
223
- g_assert_cmpint(bs->quiesce_counter, ==, 2);
224
+ g_assert_cmpint(bs->quiesce_counter, ==, 2 + !!backing_quiesce);
225
g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
226
- g_assert_cmpint(s->drain_count, ==, 2);
227
- g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce);
228
+ g_assert_cmpint(s->drain_count, ==, 1);
229
+ g_assert_cmpint(backing_s->drain_count, ==, !!backing_quiesce);
230
231
do_drain_end(inner, bs);
232
do_drain_end(outer, bs);
233
--
80
--
234
2.38.1
81
2.13.6
82
83
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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.
2
5
3
Similar to the implementation in lockable.h, implement macros to
6
Also, add a backing file to the test node to test whether the operations
4
automatically take and release the rdlock.
7
work recursively.
5
8
6
Create the empty GraphLockable and GraphLockableMainloop structs only to
7
use it as a type for G_DEFINE_AUTOPTR_CLEANUP_FUNC.
8
9
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20221207131838.239125-4-kwolf@redhat.com>
12
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
10
---
15
include/block/graph-lock.h | 66 ++++++++++++++++++++++++++++++++++++++
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
16
1 file changed, 66 insertions(+)
12
1 file changed, 62 insertions(+), 7 deletions(-)
17
13
18
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
19
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/graph-lock.h
16
--- a/tests/test-bdrv-drain.c
21
+++ b/include/block/graph-lock.h
17
+++ b/tests/test-bdrv-drain.c
22
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_graph_co_rdunlock(void);
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
23
void bdrv_graph_rdlock_main_loop(void);
19
24
void bdrv_graph_rdunlock_main_loop(void);
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
25
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
26
+typedef struct GraphLockable { } GraphLockable;
27
+
22
+
28
+/*
23
+ .bdrv_child_perm = bdrv_format_default_perms,
29
+ * In C, compound literals have the lifetime of an automatic variable.
24
};
30
+ * In C++ it would be different, but then C++ wouldn't need QemuLockable
25
31
+ * either...
26
static void aio_ret_cb(void *opaque, int ret)
32
+ */
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
33
+#define GML_OBJ_() (&(GraphLockable) { })
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
+};
34
+
36
+
35
+static inline GraphLockable *graph_lockable_auto_lock(GraphLockable *x)
37
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
36
+{
38
+{
37
+ bdrv_graph_co_rdlock();
39
+ switch (drain_type) {
38
+ return x;
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
+ }
39
+}
44
+}
40
+
45
+
41
+static inline void graph_lockable_auto_unlock(GraphLockable *x)
46
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
42
+{
47
+{
43
+ bdrv_graph_co_rdunlock();
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
+ }
44
+}
53
+}
45
+
54
+
46
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock)
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);
47
+
72
+
48
+#define WITH_GRAPH_RDLOCK_GUARD_(var) \
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
49
+ for (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \
74
g_assert_cmpint(s->drain_count, ==, 0);
50
+ var; \
75
- bdrv_drain_all_begin();
51
+ graph_lockable_auto_unlock(var), var = NULL)
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
52
+
77
+
53
+#define WITH_GRAPH_RDLOCK_GUARD() \
78
+ do_drain_begin(drain_type, bs);
54
+ WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__))
55
+
79
+
56
+#define GRAPH_RDLOCK_GUARD(x) \
80
g_assert_cmpint(s->drain_count, ==, 1);
57
+ g_autoptr(GraphLockable) \
81
- bdrv_drain_all_end();
58
+ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
59
+ graph_lockable_auto_lock(GML_OBJ_())
60
+
83
+
84
+ do_drain_end(drain_type, bs);
61
+
85
+
62
+typedef struct GraphLockableMainloop { } GraphLockableMainloop;
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);
63
+
97
+
64
+/*
98
+ do_drain_begin(drain_type, bs);
65
+ * In C, compound literals have the lifetime of an automatic variable.
66
+ * In C++ it would be different, but then C++ wouldn't need QemuLockable
67
+ * either...
68
+ */
69
+#define GMLML_OBJ_() (&(GraphLockableMainloop) { })
70
+
99
+
71
+static inline GraphLockableMainloop *
100
g_assert_cmpint(aio_ret, ==, 0);
72
+graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x)
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)
73
+{
116
+{
74
+ bdrv_graph_rdlock_main_loop();
117
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
75
+ return x;
76
+}
118
+}
77
+
119
+
78
+static inline void
120
+static void test_drv_cb_drain(void)
79
+graph_lockable_auto_unlock_mainloop(GraphLockableMainloop *x)
80
+{
121
+{
81
+ bdrv_graph_rdunlock_main_loop();
122
+ test_drv_cb_common(BDRV_DRAIN, false);
82
+}
123
+}
83
+
124
+
84
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockableMainloop,
125
int main(int argc, char **argv)
85
+ graph_lockable_auto_unlock_mainloop)
126
{
86
+
127
bdrv_init();
87
+#define GRAPH_RDLOCK_GUARD_MAINLOOP(x) \
128
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
88
+ g_autoptr(GraphLockableMainloop) \
129
g_test_init(&argc, &argv, NULL);
89
+ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \
130
90
+ graph_lockable_auto_lock_mainloop(GMLML_OBJ_())
131
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
91
+
132
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
92
#endif /* GRAPH_LOCK_H */
133
93
134
return g_test_run();
135
}
94
--
136
--
95
2.38.1
137
2.13.6
138
139
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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.
2
4
3
Remove the old assert_bdrv_graph_writable, and replace it with
4
the new version using graph-lock API.
5
6
See the function documentation for more information.
7
8
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20221207131838.239125-14-kwolf@redhat.com>
11
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
6
---
15
include/block/block_int-global-state.h | 17 -----------------
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
16
include/block/graph-lock.h | 15 +++++++++++++++
8
1 file changed, 45 insertions(+)
17
block.c | 4 ++--
18
block/graph-lock.c | 11 +++++++++++
19
4 files changed, 28 insertions(+), 19 deletions(-)
20
9
21
diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h
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/include/block/block_int-global-state.h
12
--- a/tests/test-bdrv-drain.c
24
+++ b/include/block/block_int-global-state.h
13
+++ b/tests/test-bdrv-drain.c
25
@@ -XXX,XX +XXX,XX @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
26
*/
15
test_drv_cb_common(BDRV_DRAIN, false);
27
void bdrv_drain_all_end_quiesce(BlockDriverState *bs);
16
}
28
17
29
-/**
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
30
- * Make sure that the function is running under both drain and BQL.
19
+{
31
- * The latter protects from concurrent writings
20
+ BlockBackend *blk;
32
- * from the GS API, while the former prevents concurrent reads
21
+ BlockDriverState *bs, *backing;
33
- * from I/O.
34
- */
35
-static inline void assert_bdrv_graph_writable(BlockDriverState *bs)
36
-{
37
- /*
38
- * TODO: this function is incomplete. Because the users of this
39
- * assert lack the necessary drains, check only for BQL.
40
- * Once the necessary drains are added,
41
- * assert also for qatomic_read(&bs->quiesce_counter) > 0
42
- */
43
- assert(qemu_in_main_thread());
44
-}
45
-
46
#endif /* BLOCK_INT_GLOBAL_STATE_H */
47
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
48
index XXXXXXX..XXXXXXX 100644
49
--- a/include/block/graph-lock.h
50
+++ b/include/block/graph-lock.h
51
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_graph_co_rdunlock(void);
52
void bdrv_graph_rdlock_main_loop(void);
53
void bdrv_graph_rdunlock_main_loop(void);
54
55
+/*
56
+ * assert_bdrv_graph_readable:
57
+ * Make sure that the reader is either the main loop,
58
+ * or there is at least a reader helding the rdlock.
59
+ * In this way an incoming writer is aware of the read and waits.
60
+ */
61
+void assert_bdrv_graph_readable(void);
62
+
22
+
63
+/*
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
64
+ * assert_bdrv_graph_writable:
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
65
+ * Make sure that the writer is the main loop and has set @has_writer,
25
+ &error_abort);
66
+ * so that incoming readers will pause.
26
+ blk_insert_bs(blk, bs, &error_abort);
67
+ */
68
+void assert_bdrv_graph_writable(void);
69
+
27
+
70
typedef struct GraphLockable { } GraphLockable;
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
71
29
+ bdrv_set_backing_hd(bs, backing, &error_abort);
72
/*
73
diff --git a/block.c b/block.c
74
index XXXXXXX..XXXXXXX 100644
75
--- a/block.c
76
+++ b/block.c
77
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child)
78
{
79
BlockDriverState *bs = child->opaque;
80
81
- assert_bdrv_graph_writable(bs);
82
+ assert_bdrv_graph_writable();
83
QLIST_INSERT_HEAD(&bs->children, child, next);
84
if (bs->drv->is_filter || (child->role & BDRV_CHILD_FILTERED)) {
85
/*
86
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_detach(BdrvChild *child)
87
bdrv_backing_detach(child);
88
}
89
90
- assert_bdrv_graph_writable(bs);
91
+ assert_bdrv_graph_writable();
92
QLIST_REMOVE(child, next);
93
if (child == bs->backing) {
94
assert(child != bs->file);
95
diff --git a/block/graph-lock.c b/block/graph-lock.c
96
index XXXXXXX..XXXXXXX 100644
97
--- a/block/graph-lock.c
98
+++ b/block/graph-lock.c
99
@@ -XXX,XX +XXX,XX @@ void bdrv_graph_rdunlock_main_loop(void)
100
GLOBAL_STATE_CODE();
101
assert(!qemu_in_coroutine());
102
}
103
+
30
+
104
+void assert_bdrv_graph_readable(void)
31
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
105
+{
32
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
106
+ assert(qemu_in_main_thread() || reader_count());
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);
107
+}
47
+}
108
+
48
+
109
+void assert_bdrv_graph_writable(void)
49
+static void test_quiesce_drain_all(void)
110
+{
50
+{
111
+ assert(qemu_in_main_thread());
51
+ // XXX drain_all doesn't quiesce
112
+ assert(qatomic_read(&has_writer));
52
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
113
+}
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
}
114
--
72
--
115
2.38.1
73
2.13.6
74
75
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
Block jobs already paused themselves when their main BlockBackend
2
entered a drained section. This is not good enough: We also want to
3
pause a block job and may not submit new requests if, for example, the
4
mirror target node should be drained.
2
5
3
These functions end up calling bdrv_*() implemented as generated_co_wrapper
6
This implements .drained_begin/end callbacks in child_job in order to
4
functions.
7
consider all block nodes related to the job, and removes the
5
In addition, they also happen to be always called in coroutine context,
8
BlockBackend callbacks which are unnecessary now because the root of the
6
meaning all callers are coroutine_fn.
9
job main BlockBackend is always referenced with a child_job, too.
7
This means that the g_c_w function will enter the qemu_in_coroutine()
8
case and eventually suspend (or in other words call qemu_coroutine_yield()).
9
Therefore we can mark such functions coroutine_fn too.
10
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
14
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Message-Id: <20221128142337.657646-4-eesposit@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
12
---
18
nbd/server.c | 29 ++++++++++++++++-------------
13
blockjob.c | 22 +++++++++-------------
19
1 file changed, 16 insertions(+), 13 deletions(-)
14
1 file changed, 9 insertions(+), 13 deletions(-)
20
15
21
diff --git a/nbd/server.c b/nbd/server.c
16
diff --git a/blockjob.c b/blockjob.c
22
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
23
--- a/nbd/server.c
18
--- a/blockjob.c
24
+++ b/nbd/server.c
19
+++ b/blockjob.c
25
@@ -XXX,XX +XXX,XX @@ static int nbd_extent_array_add(NBDExtentArray *ea,
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
26
return 0;
21
job->id);
27
}
22
}
28
23
29
-static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
24
-static const BdrvChildRole child_job = {
30
- uint64_t bytes, NBDExtentArray *ea)
25
- .get_parent_desc = child_job_get_parent_desc,
31
+static int coroutine_fn blockstatus_to_extents(BlockDriverState *bs,
26
- .stay_at_node = true,
32
+ uint64_t offset, uint64_t bytes,
27
-};
33
+ NBDExtentArray *ea)
28
-
29
-static void block_job_drained_begin(void *opaque)
30
+static void child_job_drained_begin(BdrvChild *c)
34
{
31
{
35
while (bytes) {
32
- BlockJob *job = opaque;
36
uint32_t flags;
33
+ BlockJob *job = c->opaque;
37
int64_t num;
34
block_job_pause(job);
38
- int ret = bdrv_block_status_above(bs, NULL, offset, bytes, &num,
39
- NULL, NULL);
40
+ int ret = bdrv_co_block_status_above(bs, NULL, offset, bytes, &num,
41
+ NULL, NULL);
42
43
if (ret < 0) {
44
return ret;
45
@@ -XXX,XX +XXX,XX @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
46
return 0;
47
}
35
}
48
36
49
-static int blockalloc_to_extents(BlockDriverState *bs, uint64_t offset,
37
-static void block_job_drained_end(void *opaque)
50
- uint64_t bytes, NBDExtentArray *ea)
38
+static void child_job_drained_end(BdrvChild *c)
51
+static int coroutine_fn blockalloc_to_extents(BlockDriverState *bs,
52
+ uint64_t offset, uint64_t bytes,
53
+ NBDExtentArray *ea)
54
{
39
{
55
while (bytes) {
40
- BlockJob *job = opaque;
56
int64_t num;
41
+ BlockJob *job = c->opaque;
57
- int ret = bdrv_is_allocated_above(bs, NULL, false, offset, bytes,
42
block_job_resume(job);
58
- &num);
59
+ int ret = bdrv_co_is_allocated_above(bs, NULL, false, offset, bytes,
60
+ &num);
61
62
if (ret < 0) {
63
return ret;
64
@@ -XXX,XX +XXX,XX @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
65
}
43
}
66
44
67
/* Get block status from the exported device and send it to the client */
45
-static const BlockDevOps block_job_dev_ops = {
68
-static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
46
- .drained_begin = block_job_drained_begin,
69
- BlockDriverState *bs, uint64_t offset,
47
- .drained_end = block_job_drained_end,
70
- uint32_t length, bool dont_fragment,
48
+static const BdrvChildRole child_job = {
71
- bool last, uint32_t context_id,
49
+ .get_parent_desc = child_job_get_parent_desc,
72
- Error **errp)
50
+ .drained_begin = child_job_drained_begin,
73
+static int
51
+ .drained_end = child_job_drained_end,
74
+coroutine_fn nbd_co_send_block_status(NBDClient *client, uint64_t handle,
52
+ .stay_at_node = true,
75
+ BlockDriverState *bs, uint64_t offset,
53
};
76
+ uint32_t length, bool dont_fragment,
54
77
+ bool last, uint32_t context_id,
55
void block_job_remove_all_bdrv(BlockJob *job)
78
+ Error **errp)
56
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
79
{
57
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
80
int ret;
58
bs->job = job;
81
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
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);
82
--
64
--
83
2.38.1
65
2.13.6
66
67
diff view generated by jsdifflib
1
We want to change .bdrv_co_drained_begin/end() back to be non-coroutine
1
Block jobs must be paused if any of the involved nodes are drained.
2
callbacks, so in preparation, avoid yielding in their implementation.
3
4
This does almost the same as the existing logic in bdrv_drain_invoke(),
5
by creating and entering coroutines internally. However, since the test
6
case is by far the heaviest user of coroutine code in drain callbacks,
7
it is preferable to have the complexity in the test case rather than the
8
drain core, which is already complicated enough without this.
9
10
The behaviour for bdrv_drain_begin() is unchanged because we increase
11
bs->in_flight and this is still polled. However, bdrv_drain_end()
12
doesn't wait for the spawned coroutine to complete any more. This is
13
fine, we don't rely on bdrv_drain_end() restarting all operations
14
immediately before the next aio_poll().
15
2
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
18
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
19
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
20
Message-Id: <20221118174110.55183-3-kwolf@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
4
---
23
tests/unit/test-bdrv-drain.c | 64 ++++++++++++++++++++++++++----------
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
24
1 file changed, 46 insertions(+), 18 deletions(-)
6
1 file changed, 121 insertions(+)
25
7
26
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
27
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
28
--- a/tests/unit/test-bdrv-drain.c
10
--- a/tests/test-bdrv-drain.c
29
+++ b/tests/unit/test-bdrv-drain.c
11
+++ b/tests/test-bdrv-drain.c
30
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVTestState {
12
@@ -XXX,XX +XXX,XX @@
31
bool sleep_in_drain_begin;
13
32
} BDRVTestState;
14
#include "qemu/osdep.h"
33
15
#include "block/block.h"
34
+static void coroutine_fn sleep_in_drain_begin(void *opaque)
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);
22
}
23
24
+
25
+typedef struct TestBlockJob {
26
+ BlockJob common;
27
+ bool should_complete;
28
+} TestBlockJob;
29
+
30
+static void test_job_completed(BlockJob *job, void *opaque)
35
+{
31
+{
36
+ BlockDriverState *bs = opaque;
32
+ block_job_completed(job, 0);
37
+
38
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
39
+ bdrv_dec_in_flight(bs);
40
+}
33
+}
41
+
34
+
42
static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
35
+static void coroutine_fn test_job_start(void *opaque)
43
{
44
BDRVTestState *s = bs->opaque;
45
s->drain_count++;
46
if (s->sleep_in_drain_begin) {
47
- qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
48
+ Coroutine *co = qemu_coroutine_create(sleep_in_drain_begin, bs);
49
+ bdrv_inc_in_flight(bs);
50
+ aio_co_enter(bdrv_get_aio_context(bs), co);
51
}
52
}
53
54
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_replace_test_co_preadv(BlockDriverState *bs,
55
return 0;
56
}
57
58
+static void coroutine_fn bdrv_replace_test_drain_co(void *opaque)
59
+{
36
+{
60
+ BlockDriverState *bs = opaque;
37
+ TestBlockJob *s = opaque;
61
+ BDRVReplaceTestState *s = bs->opaque;
62
+
38
+
63
+ /* Keep waking io_co up until it is done */
39
+ while (!s->should_complete) {
64
+ while (s->io_co) {
40
+ block_job_sleep_ns(&s->common, 100000);
65
+ aio_co_wake(s->io_co);
66
+ s->io_co = NULL;
67
+ qemu_coroutine_yield();
68
+ }
41
+ }
69
+ s->drain_co = NULL;
42
+
70
+ bdrv_dec_in_flight(bs);
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
71
+}
44
+}
72
+
45
+
73
/**
46
+static void test_job_complete(BlockJob *job, Error **errp)
74
* If .drain_count is 0, wake up .io_co if there is one; and set
75
* .was_drained.
76
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_replace_test_co_drain_begin(BlockDriverState *bs)
77
BDRVReplaceTestState *s = bs->opaque;
78
79
if (!s->drain_count) {
80
- /* Keep waking io_co up until it is done */
81
- s->drain_co = qemu_coroutine_self();
82
- while (s->io_co) {
83
- aio_co_wake(s->io_co);
84
- s->io_co = NULL;
85
- qemu_coroutine_yield();
86
- }
87
- s->drain_co = NULL;
88
-
89
+ s->drain_co = qemu_coroutine_create(bdrv_replace_test_drain_co, bs);
90
+ bdrv_inc_in_flight(bs);
91
+ aio_co_enter(bdrv_get_aio_context(bs), s->drain_co);
92
s->was_drained = true;
93
}
94
s->drain_count++;
95
}
96
97
+static void coroutine_fn bdrv_replace_test_read_entry(void *opaque)
98
+{
47
+{
99
+ BlockDriverState *bs = opaque;
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
100
+ char data;
49
+ s->should_complete = true;
101
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1);
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;
102
+ int ret;
63
+ int ret;
103
+
64
+
104
+ /* Queue a read request post-drain */
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
105
+ ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0);
66
+ &error_abort);
106
+ g_assert(ret >= 0);
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
107
+ bdrv_dec_in_flight(bs);
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);
108
+}
129
+}
109
+
130
+
110
/**
131
+static void test_blockjob_drain_all(void)
111
* Reduce .drain_count, set .was_undrained once it reaches 0.
132
+{
112
* If .drain_count reaches 0 and the node has a backing file, issue a
133
+ test_blockjob_common(BDRV_DRAIN_ALL);
113
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs)
134
+}
114
135
+
115
g_assert(s->drain_count > 0);
136
+static void test_blockjob_drain(void)
116
if (!--s->drain_count) {
137
+{
117
- int ret;
138
+ test_blockjob_common(BDRV_DRAIN);
118
-
139
+}
119
s->was_undrained = true;
140
+
120
141
int main(int argc, char **argv)
121
if (bs->backing) {
142
{
122
- char data;
143
bdrv_init();
123
- QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1);
144
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
124
-
145
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
125
- /* Queue a read request post-drain */
146
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
126
- ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0);
147
127
- g_assert(ret >= 0);
148
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
128
+ Coroutine *co = qemu_coroutine_create(bdrv_replace_test_read_entry,
149
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
129
+ bs);
150
+
130
+ bdrv_inc_in_flight(bs);
151
return g_test_run();
131
+ aio_co_enter(bdrv_get_aio_context(bs), co);
132
}
133
}
134
}
152
}
135
--
153
--
136
2.38.1
154
2.13.6
155
156
diff view generated by jsdifflib
1
The next patch adds a parent drain to bdrv_attach_child_common(), which
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
shouldn't be, but is currently called from coroutines in some cases (e.g.
2
so we don't need an additional block_job_pause_all() call.
3
.bdrv_co_create implementations generally open new nodes). Therefore,
4
the assertion that we're not in a coroutine doesn't hold true any more.
5
6
We could just remove the assertion because there is nothing in the
7
function that should be in conflict with running in a coroutine, but
8
just to be on the safe side, we can reverse the caller relationship
9
between bdrv_do_drained_begin() and bdrv_do_drained_begin_quiesce() so
10
that the latter also just drops out of coroutine context and we can
11
still be certain in the future that any drain code doesn't run in
12
coroutines.
13
14
As a nice side effect, the structure of bdrv_do_drained_begin() is now
15
symmetrical with bdrv_do_drained_end().
16
3
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Message-Id: <20221118174110.55183-14-kwolf@redhat.com>
19
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
20
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
5
---
23
block/io.c | 25 ++++++++++++-------------
6
block/io.c | 4 ----
24
1 file changed, 12 insertions(+), 13 deletions(-)
7
tests/test-bdrv-drain.c | 10 ++++------
8
2 files changed, 4 insertions(+), 10 deletions(-)
25
9
26
diff --git a/block/io.c b/block/io.c
10
diff --git a/block/io.c b/block/io.c
27
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
28
--- a/block/io.c
12
--- a/block/io.c
29
+++ b/block/io.c
13
+++ b/block/io.c
30
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
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);
31
}
26
}
27
-
28
- block_job_resume_all();
32
}
29
}
33
30
34
-void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent)
31
void bdrv_drain_all(void)
35
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
36
+ bool poll)
33
index XXXXXXX..XXXXXXX 100644
37
{
34
--- a/tests/test-bdrv-drain.c
38
IO_OR_GS_CODE();
35
+++ b/tests/test-bdrv-drain.c
39
- assert(!qemu_in_coroutine());
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
40
+
37
do_drain_begin(drain_type, src);
41
+ if (qemu_in_coroutine()) {
38
42
+ bdrv_co_yield_to_drain(bs, true, parent, poll);
39
if (drain_type == BDRV_DRAIN_ALL) {
43
+ return;
40
- /* bdrv_drain_all() drains both src and target, and involves an
44
+ }
41
- * additional block_job_pause_all() */
45
42
- g_assert_cmpint(job->pause_count, ==, 3);
46
/* Stop things in parent-to-child order */
43
+ /* bdrv_drain_all() drains both src and target */
47
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
44
+ g_assert_cmpint(job->pause_count, ==, 2);
48
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent)
45
} else {
49
bs->drv->bdrv_drain_begin(bs);
46
g_assert_cmpint(job->pause_count, ==, 1);
50
}
51
}
47
}
52
-}
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
53
-
49
do_drain_begin(drain_type, target);
54
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
50
55
- bool poll)
51
if (drain_type == BDRV_DRAIN_ALL) {
56
-{
52
- /* bdrv_drain_all() drains both src and target, and involves an
57
- if (qemu_in_coroutine()) {
53
- * additional block_job_pause_all() */
58
- bdrv_co_yield_to_drain(bs, true, parent, poll);
54
- g_assert_cmpint(job->pause_count, ==, 3);
59
- return;
55
+ /* bdrv_drain_all() drains both src and target */
60
- }
56
+ g_assert_cmpint(job->pause_count, ==, 2);
61
-
57
} else {
62
- bdrv_do_drained_begin_quiesce(bs, parent);
58
g_assert_cmpint(job->pause_count, ==, 1);
63
64
/*
65
* Wait for drained requests to finish.
66
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
67
}
59
}
68
}
69
70
+void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent)
71
+{
72
+ bdrv_do_drained_begin(bs, parent, false);
73
+}
74
+
75
void bdrv_drained_begin(BlockDriverState *bs)
76
{
77
IO_OR_GS_CODE();
78
--
60
--
79
2.38.1
61
2.13.6
62
63
diff view generated by jsdifflib
1
bdrv_drain_invoke() has now two entirely separate cases that share no
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
code any more and are selected depending on a bool parameter. Each case
2
aio_disable_external() to the outermost drain section, but the block
3
has only one caller. Just inline the function.
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.
4
6
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
10
Message-Id: <20221118174110.55183-6-kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
8
---
13
block/io.c | 23 ++++++-----------------
9
block/io.c | 12 +++++++-----
14
1 file changed, 6 insertions(+), 17 deletions(-)
10
1 file changed, 7 insertions(+), 5 deletions(-)
15
11
16
diff --git a/block/io.c b/block/io.c
12
diff --git a/block/io.c b/block/io.c
17
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
18
--- a/block/io.c
14
--- a/block/io.c
19
+++ b/block/io.c
15
+++ b/block/io.c
20
@@ -XXX,XX +XXX,XX @@ typedef struct {
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
21
bool ignore_bds_parents;
17
22
} BdrvCoDrainData;
18
void bdrv_drained_end(BlockDriverState *bs)
23
19
{
24
-/* Recursively call BlockDriver.bdrv_drain_begin/end callbacks */
20
+ int old_quiesce_counter;
25
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
21
+
26
-{
22
if (qemu_in_coroutine()) {
27
- if (!bs->drv || (begin && !bs->drv->bdrv_drain_begin) ||
23
bdrv_co_yield_to_drain(bs, false);
28
- (!begin && !bs->drv->bdrv_drain_end)) {
24
return;
25
}
26
assert(bs->quiesce_counter > 0);
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
29
- return;
28
- return;
30
- }
29
- }
31
-
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
32
- if (begin) {
31
33
- bs->drv->bdrv_drain_begin(bs);
32
/* Re-enable things in child-to-parent order */
34
- } else {
33
bdrv_drain_invoke(bs, false, false);
35
- bs->drv->bdrv_drain_end(bs);
34
- bdrv_parent_drained_end(bs);
36
- }
35
- aio_enable_external(bdrv_get_aio_context(bs));
37
-}
36
+ if (old_quiesce_counter == 1) {
38
-
37
+ bdrv_parent_drained_end(bs);
39
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
38
+ aio_enable_external(bdrv_get_aio_context(bs));
40
bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
41
BdrvChild *ignore_parent, bool ignore_bds_parents)
42
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
43
}
44
45
bdrv_parent_drained_begin(bs, parent, ignore_bds_parents);
46
- bdrv_drain_invoke(bs, true);
47
+ if (bs->drv && bs->drv->bdrv_drain_begin) {
48
+ bs->drv->bdrv_drain_begin(bs);
49
+ }
39
+ }
50
}
40
}
51
41
52
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
42
/*
53
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
54
assert(bs->quiesce_counter > 0);
55
56
/* Re-enable things in child-to-parent order */
57
- bdrv_drain_invoke(bs, false);
58
+ if (bs->drv && bs->drv->bdrv_drain_end) {
59
+ bs->drv->bdrv_drain_end(bs);
60
+ }
61
bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
62
63
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
64
--
43
--
65
2.38.1
44
2.13.6
45
46
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
3
We are going to increase usage of collecting nodes in a list to then
4
update, and calling bdrv_topological_dfs() each time is not convenient,
5
and not correct as we are going to interleave graph modifying with
6
filling the node list.
7
8
So, let's switch to a function that takes any list of nodes, adds all
9
their subtrees and do topological sort. And finally, refresh
10
permissions.
11
12
While being here, make the function public, as we'll want to use it
13
from blockdev.c in near future.
14
15
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
16
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
17
Message-Id: <20221107163558.618889-5-vsementsov@yandex-team.ru>
18
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
2
---
21
block.c | 51 ++++++++++++++++++++++++++++++++-------------------
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
22
1 file changed, 32 insertions(+), 19 deletions(-)
4
1 file changed, 57 insertions(+)
23
5
24
diff --git a/block.c b/block.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
25
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
26
--- a/block.c
8
--- a/tests/test-bdrv-drain.c
27
+++ b/block.c
9
+++ b/tests/test-bdrv-drain.c
28
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q,
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
29
return 0;
11
enum drain_type {
12
BDRV_DRAIN_ALL,
13
BDRV_DRAIN,
14
+ DRAIN_TYPE_MAX,
15
};
16
17
static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
18
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
19
test_quiesce_common(BDRV_DRAIN, false);
30
}
20
}
31
21
32
-static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
22
+static void test_nested(void)
33
- Transaction *tran, Error **errp)
34
+/*
35
+ * @list is a product of bdrv_topological_dfs() (may be called several times) -
36
+ * a topologically sorted subgraph.
37
+ */
38
+static int bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q,
39
+ Transaction *tran, Error **errp)
40
{
41
int ret;
42
BlockDriverState *bs;
43
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
44
return 0;
45
}
46
47
+/*
48
+ * @list is any list of nodes. List is completed by all subtrees and
49
+ * topologically sorted. It's not a problem if some node occurs in the @list
50
+ * several times.
51
+ */
52
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
53
+ Transaction *tran, Error **errp)
54
+{
23
+{
55
+ g_autoptr(GHashTable) found = g_hash_table_new(NULL, NULL);
24
+ BlockBackend *blk;
56
+ g_autoptr(GSList) refresh_list = NULL;
25
+ BlockDriverState *bs, *backing;
26
+ BDRVTestState *s, *backing_s;
27
+ enum drain_type outer, inner;
57
+
28
+
58
+ for ( ; list; list = list->next) {
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
59
+ refresh_list = bdrv_topological_dfs(refresh_list, found, list->data);
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
+ }
60
+ }
69
+ }
61
+
70
+
62
+ return bdrv_do_refresh_perms(refresh_list, q, tran, errp);
71
+ bdrv_unref(backing);
72
+ bdrv_unref(bs);
73
+ blk_unref(blk);
63
+}
74
+}
64
+
75
+
65
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
76
66
uint64_t *shared_perm)
77
typedef struct TestBlockJob {
67
{
78
BlockJob common;
68
@@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran,
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
69
tran = local_tran = tran_new();
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
70
}
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
71
82
72
- ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
73
+ ret = bdrv_do_refresh_perms(list, NULL, tran, errp);
84
+
74
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
75
if (local_tran) {
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
76
tran_finalize(local_tran, ret);
77
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
78
BlockReopenQueueEntry *bs_entry, *next;
79
AioContext *ctx;
80
Transaction *tran = tran_new();
81
- g_autoptr(GHashTable) found = NULL;
82
g_autoptr(GSList) refresh_list = NULL;
83
84
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
85
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
86
bs_entry->prepared = true;
87
}
88
89
- found = g_hash_table_new(NULL, NULL);
90
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
91
BDRVReopenState *state = &bs_entry->state;
92
93
- refresh_list = bdrv_topological_dfs(refresh_list, found, state->bs);
94
+ refresh_list = g_slist_prepend(refresh_list, state->bs);
95
if (state->old_backing_bs) {
96
- refresh_list = bdrv_topological_dfs(refresh_list, found,
97
- state->old_backing_bs);
98
+ refresh_list = g_slist_prepend(refresh_list, state->old_backing_bs);
99
}
100
if (state->old_file_bs) {
101
- refresh_list = bdrv_topological_dfs(refresh_list, found,
102
- state->old_file_bs);
103
+ refresh_list = g_slist_prepend(refresh_list, state->old_file_bs);
104
}
105
}
106
107
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
108
Error **errp)
109
{
110
Transaction *tran = tran_new();
111
- g_autoptr(GHashTable) found = NULL;
112
g_autoptr(GSList) refresh_list = NULL;
113
BlockDriverState *to_cow_parent = NULL;
114
int ret;
115
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
116
bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran);
117
}
118
119
- found = g_hash_table_new(NULL, NULL);
120
-
121
- refresh_list = bdrv_topological_dfs(refresh_list, found, to);
122
- refresh_list = bdrv_topological_dfs(refresh_list, found, from);
123
+ refresh_list = g_slist_prepend(refresh_list, to);
124
+ refresh_list = g_slist_prepend(refresh_list, from);
125
126
ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
127
if (ret < 0) {
128
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
129
{
130
int ret;
131
Transaction *tran = tran_new();
132
- g_autoptr(GHashTable) found = NULL;
133
g_autoptr(GSList) refresh_list = NULL;
134
BlockDriverState *old_bs = child->bs;
135
136
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
137
138
bdrv_replace_child_tran(child, new_bs, tran);
139
140
- found = g_hash_table_new(NULL, NULL);
141
- refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs);
142
- refresh_list = bdrv_topological_dfs(refresh_list, found, new_bs);
143
+ refresh_list = g_slist_prepend(refresh_list, old_bs);
144
+ refresh_list = g_slist_prepend(refresh_list, new_bs);
145
146
ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
147
87
148
--
88
--
149
2.38.1
89
2.13.6
90
91
diff view generated by jsdifflib
1
ignore_bds_parents is now ignored during drain_begin and drain_end, so
1
This is in preparation for subtree drains, i.e. drained sections that
2
we can just remove it there. It is still a valid optimisation for
2
affect not only a single node, but recursively all child nodes, too.
3
drain_all in bdrv_drained_poll(), so leave it around there.
3
4
4
Calling the parent callbacks for drain is pointless when we just came
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
from that parent node recursively and leads to multiple increases of
6
Message-Id: <20221118174110.55183-13-kwolf@redhat.com>
6
bs->quiesce_counter in a single drain call. Don't do it.
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
7
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
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
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
18
---
11
include/block/block-io.h | 3 +--
19
include/block/block.h | 4 ++--
12
block.c | 2 +-
20
block.c | 13 +++++++++----
13
block/io.c | 58 +++++++++++++++-------------------------
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
14
3 files changed, 24 insertions(+), 39 deletions(-)
22
3 files changed, 45 insertions(+), 19 deletions(-)
15
23
16
diff --git a/include/block/block-io.h b/include/block/block-io.h
24
diff --git a/include/block/block.h b/include/block/block.h
17
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
18
--- a/include/block/block-io.h
26
--- a/include/block/block.h
19
+++ b/include/block/block-io.h
27
+++ b/include/block/block.h
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
21
* Quiesces a BDS like bdrv_drained_begin(), but does not wait for already
29
* Begin a quiesced section of all users of @bs. This is part of
22
* running requests to complete.
30
* bdrv_drained_begin.
23
*/
31
*/
24
-void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
25
- BdrvChild *parent, bool ignore_bds_parents);
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
26
+void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent);
27
34
28
/**
35
/**
29
* bdrv_drained_end:
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:
30
diff --git a/block.c b/block.c
46
diff --git a/block.c b/block.c
31
index XXXXXXX..XXXXXXX 100644
47
index XXXXXXX..XXXXXXX 100644
32
--- a/block.c
48
--- a/block.c
33
+++ b/block.c
49
+++ b/block.c
34
@@ -XXX,XX +XXX,XX @@ static char *bdrv_child_get_parent_desc(BdrvChild *c)
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
35
static void bdrv_child_cb_drained_begin(BdrvChild *child)
51
BlockDriverState *new_bs)
36
{
52
{
37
BlockDriverState *bs = child->opaque;
53
BlockDriverState *old_bs = child->bs;
38
- bdrv_do_drained_begin_quiesce(bs, NULL, false);
54
+ int i;
39
+ bdrv_do_drained_begin_quiesce(bs, NULL);
55
40
}
56
if (old_bs && new_bs) {
41
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
42
static bool bdrv_child_cb_drained_poll(BdrvChild *child)
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
}
43
diff --git a/block/io.c b/block/io.c
97
diff --git a/block/io.c b/block/io.c
44
index XXXXXXX..XXXXXXX 100644
98
index XXXXXXX..XXXXXXX 100644
45
--- a/block/io.c
99
--- a/block/io.c
46
+++ b/block/io.c
100
+++ b/block/io.c
47
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs);
101
@@ -XXX,XX +XXX,XX @@
48
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
102
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
49
int64_t offset, int64_t bytes, BdrvRequestFlags flags);
103
int64_t offset, int bytes, BdrvRequestFlags flags);
50
104
51
-static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
105
-void bdrv_parent_drained_begin(BlockDriverState *bs)
52
- bool ignore_bds_parents)
106
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
53
+static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
54
{
107
{
55
BdrvChild *c, *next;
108
BdrvChild *c, *next;
56
109
57
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
110
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
58
- if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) {
59
+ if (c == ignore) {
111
+ if (c == ignore) {
60
continue;
112
+ continue;
61
}
113
+ }
62
bdrv_parent_drained_begin_single(c, false);
114
if (c->role->drained_begin) {
63
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c)
115
c->role->drained_begin(c);
64
}
116
}
65
}
117
}
66
118
}
67
-static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
119
68
- bool ignore_bds_parents)
120
-void bdrv_parent_drained_end(BlockDriverState *bs)
69
+static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
121
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
70
{
122
{
71
BdrvChild *c;
123
BdrvChild *c, *next;
72
124
73
QLIST_FOREACH(c, &bs->parents, next_parent) {
125
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
74
- if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) {
75
+ if (c == ignore) {
126
+ if (c == ignore) {
76
continue;
127
+ continue;
77
}
128
+ }
78
bdrv_parent_drained_end_single(c);
129
if (c->role->drained_end) {
130
c->role->drained_end(c);
131
}
79
@@ -XXX,XX +XXX,XX @@ typedef struct {
132
@@ -XXX,XX +XXX,XX @@ typedef struct {
133
BlockDriverState *bs;
134
bool done;
80
bool begin;
135
bool begin;
81
bool poll;
136
+ BdrvChild *parent;
82
BdrvChild *parent;
83
- bool ignore_bds_parents;
84
} BdrvCoDrainData;
137
} BdrvCoDrainData;
85
138
86
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
139
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
87
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_poll_top_level(BlockDriverState *bs,
140
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
88
}
141
return waited;
89
142
}
90
static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
143
91
- bool ignore_bds_parents, bool poll);
144
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
92
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
93
- bool ignore_bds_parents);
94
+ bool poll);
95
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
145
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
96
146
+
97
static void bdrv_co_drain_bh_cb(void *opaque)
147
static void bdrv_co_drain_bh_cb(void *opaque)
98
{
148
{
149
BdrvCoDrainData *data = opaque;
99
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
100
aio_context_acquire(ctx);
151
101
bdrv_dec_in_flight(bs);
152
bdrv_dec_in_flight(bs);
102
if (data->begin) {
153
if (data->begin) {
103
- bdrv_do_drained_begin(bs, data->parent, data->ignore_bds_parents,
154
- bdrv_drained_begin(bs);
104
- data->poll);
155
+ bdrv_do_drained_begin(bs, data->parent);
105
+ bdrv_do_drained_begin(bs, data->parent, data->poll);
106
} else {
107
assert(!data->poll);
108
- bdrv_do_drained_end(bs, data->parent, data->ignore_bds_parents);
109
+ bdrv_do_drained_end(bs, data->parent);
110
}
111
aio_context_release(ctx);
112
} else {
156
} else {
157
- bdrv_drained_end(bs);
158
+ bdrv_do_drained_end(bs, data->parent);
159
}
160
161
data->done = true;
113
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
163
}
164
114
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
115
bool begin,
166
- bool begin)
116
BdrvChild *parent,
167
+ bool begin, BdrvChild *parent)
117
- bool ignore_bds_parents,
118
bool poll)
119
{
168
{
120
BdrvCoDrainData data;
169
BdrvCoDrainData data;
170
121
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
172
.bs = bs,
122
.done = false,
173
.done = false,
123
.begin = begin,
174
.begin = begin,
124
.parent = parent,
175
+ .parent = parent,
125
- .ignore_bds_parents = ignore_bds_parents,
126
.poll = poll,
127
};
176
};
128
177
bdrv_inc_in_flight(bs);
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
129
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
130
}
180
assert(data.done);
131
}
181
}
132
182
133
-void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
183
-void bdrv_drained_begin(BlockDriverState *bs)
134
- BdrvChild *parent, bool ignore_bds_parents)
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
135
+void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent)
185
{
136
{
186
if (qemu_in_coroutine()) {
137
IO_OR_GS_CODE();
187
- bdrv_co_yield_to_drain(bs, true);
138
assert(!qemu_in_coroutine());
188
+ bdrv_co_yield_to_drain(bs, true, parent);
139
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
189
return;
190
}
191
140
/* Stop things in parent-to-child order */
192
/* Stop things in parent-to-child order */
141
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
142
aio_disable_external(bdrv_get_aio_context(bs));
194
aio_disable_external(bdrv_get_aio_context(bs));
143
-
195
- bdrv_parent_drained_begin(bs);
144
- /* TODO Remove ignore_bds_parents, we don't consider it any more */
196
}
145
- bdrv_parent_drained_begin(bs, parent, false);
197
146
+ bdrv_parent_drained_begin(bs, parent);
198
+ bdrv_parent_drained_begin(bs, parent);
147
if (bs->drv && bs->drv->bdrv_drain_begin) {
199
bdrv_drain_invoke(bs, true, false);
148
bs->drv->bdrv_drain_begin(bs);
200
bdrv_drain_recurse(bs);
149
}
201
}
150
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
202
151
}
203
-void bdrv_drained_end(BlockDriverState *bs)
152
204
+void bdrv_drained_begin(BlockDriverState *bs)
153
static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
205
+{
154
- bool ignore_bds_parents, bool poll)
206
+ bdrv_do_drained_begin(bs, NULL);
155
+ bool poll)
207
+}
156
{
208
+
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
210
{
211
int old_quiesce_counter;
212
157
if (qemu_in_coroutine()) {
213
if (qemu_in_coroutine()) {
158
- bdrv_co_yield_to_drain(bs, true, parent, ignore_bds_parents, poll);
214
- bdrv_co_yield_to_drain(bs, false);
159
+ bdrv_co_yield_to_drain(bs, true, parent, poll);
215
+ bdrv_co_yield_to_drain(bs, false, parent);
160
return;
216
return;
161
}
217
}
162
163
- bdrv_do_drained_begin_quiesce(bs, parent, ignore_bds_parents);
164
+ bdrv_do_drained_begin_quiesce(bs, parent);
165
166
/*
167
* Wait for drained requests to finish.
168
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
169
* nodes.
170
*/
171
if (poll) {
172
- assert(!ignore_bds_parents);
173
BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent));
174
}
175
}
176
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
177
void bdrv_drained_begin(BlockDriverState *bs)
178
{
179
IO_OR_GS_CODE();
180
- bdrv_do_drained_begin(bs, NULL, false, true);
181
+ bdrv_do_drained_begin(bs, NULL, true);
182
}
183
184
/**
185
* This function does not poll, nor must any of its recursively called
186
* functions.
187
*/
188
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
189
- bool ignore_bds_parents)
190
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
191
{
192
int old_quiesce_counter;
193
194
if (qemu_in_coroutine()) {
195
- bdrv_co_yield_to_drain(bs, false, parent, ignore_bds_parents, false);
196
+ bdrv_co_yield_to_drain(bs, false, parent, false);
197
return;
198
}
199
assert(bs->quiesce_counter > 0);
218
assert(bs->quiesce_counter > 0);
200
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
219
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
201
if (bs->drv && bs->drv->bdrv_drain_end) {
220
202
bs->drv->bdrv_drain_end(bs);
221
/* Re-enable things in child-to-parent order */
203
}
222
bdrv_drain_invoke(bs, false, false);
204
- /* TODO Remove ignore_bds_parents, we don't consider it any more */
223
+ bdrv_parent_drained_end(bs, parent);
205
- bdrv_parent_drained_end(bs, parent, false);
224
if (old_quiesce_counter == 1) {
206
-
225
- bdrv_parent_drained_end(bs);
207
+ bdrv_parent_drained_end(bs, parent);
208
aio_enable_external(bdrv_get_aio_context(bs));
226
aio_enable_external(bdrv_get_aio_context(bs));
209
}
227
}
210
}
228
}
211
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
229
212
void bdrv_drained_end(BlockDriverState *bs)
230
+void bdrv_drained_end(BlockDriverState *bs)
213
{
231
+{
214
IO_OR_GS_CODE();
215
- bdrv_do_drained_end(bs, NULL, false);
216
+ bdrv_do_drained_end(bs, NULL);
232
+ bdrv_do_drained_end(bs, NULL);
217
}
233
+}
218
234
+
219
void bdrv_drain(BlockDriverState *bs)
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.
220
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
221
GLOBAL_STATE_CODE();
239
/* Stop things in parent-to-child order */
222
223
if (qemu_in_coroutine()) {
224
- bdrv_co_yield_to_drain(NULL, true, NULL, true, true);
225
+ bdrv_co_yield_to_drain(NULL, true, NULL, true);
226
return;
227
}
228
229
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
230
AioContext *aio_context = bdrv_get_aio_context(bs);
231
232
aio_context_acquire(aio_context);
240
aio_context_acquire(aio_context);
233
- bdrv_do_drained_begin(bs, NULL, true, false);
241
aio_disable_external(aio_context);
234
+ bdrv_do_drained_begin(bs, NULL, false);
242
- bdrv_parent_drained_begin(bs);
243
+ bdrv_parent_drained_begin(bs, NULL);
244
bdrv_drain_invoke(bs, true, true);
235
aio_context_release(aio_context);
245
aio_context_release(aio_context);
236
}
237
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end_quiesce(BlockDriverState *bs)
239
g_assert(!bs->refcnt);
240
241
while (bs->quiesce_counter) {
242
- bdrv_do_drained_end(bs, NULL, true);
243
+ bdrv_do_drained_end(bs, NULL);
244
}
245
}
246
246
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
248
AioContext *aio_context = bdrv_get_aio_context(bs);
248
/* Re-enable things in child-to-parent order */
249
250
aio_context_acquire(aio_context);
249
aio_context_acquire(aio_context);
251
- bdrv_do_drained_end(bs, NULL, true);
250
bdrv_drain_invoke(bs, false, true);
252
+ bdrv_do_drained_end(bs, NULL);
251
- bdrv_parent_drained_end(bs);
252
+ bdrv_parent_drained_end(bs, NULL);
253
aio_enable_external(aio_context);
253
aio_context_release(aio_context);
254
aio_context_release(aio_context);
254
}
255
}
255
256
--
256
--
257
2.38.1
257
2.13.6
258
259
diff view generated by jsdifflib
1
drained_end_counter is unused now, nobody changes its value any more. It
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
can be removed.
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
until bdrv_drained_end().
3
4
4
In cases where we had two almost identical functions that only differed
5
Add a version that keeps the whole subtree drained. As of this commit,
5
in whether the caller passes drained_end_counter, or whether they would
6
graph changes cannot be allowed during a subtree drained section, but
6
poll for a local drained_end_counter to reach 0, these become a single
7
this will be fixed soon.
7
function.
8
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
11
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Message-Id: <20221118174110.55183-5-kwolf@redhat.com>
13
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
10
---
16
include/block/block-io.h | 24 --------
11
include/block/block.h | 13 +++++++++++++
17
include/block/block_int-common.h | 6 +-
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
18
block.c | 5 +-
13
2 files changed, 56 insertions(+), 11 deletions(-)
19
block/block-backend.c | 4 +-
20
block/io.c | 98 ++++++++------------------------
21
blockjob.c | 2 +-
22
6 files changed, 30 insertions(+), 109 deletions(-)
23
14
24
diff --git a/include/block/block-io.h b/include/block/block-io.h
15
diff --git a/include/block/block.h b/include/block/block.h
25
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/block-io.h
17
--- a/include/block/block.h
27
+++ b/include/block/block-io.h
18
+++ b/include/block/block.h
28
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset,
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
29
int64_t bytes, BdrvRequestFlags read_flags,
20
void bdrv_drained_begin(BlockDriverState *bs);
30
BdrvRequestFlags write_flags);
21
31
22
/**
32
-/**
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
33
- * bdrv_drained_end_no_poll:
24
+ * exclusive access to all child nodes as well.
34
- *
25
+ *
35
- * Same as bdrv_drained_end(), but do not poll for the subgraph to
26
+ * Graph changes are not allowed during a subtree drain section.
36
- * actually become unquiesced. Therefore, no graph changes will occur
27
+ */
37
- * with this function.
28
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
38
- *
29
+
39
- * *drained_end_counter is incremented for every background operation
30
+/**
40
- * that is scheduled, and will be decremented for every operation once
41
- * it settles. The caller must poll until it reaches 0. The counter
42
- * should be accessed using atomic operations only.
43
- */
44
-void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter);
45
-
46
-
47
/*
48
* "I/O or GS" API functions. These functions can run without
49
* the BQL, but only in one specific iothread/main loop.
50
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
51
* bdrv_parent_drained_end_single:
52
*
53
* End a quiesced section for the parent of @c.
54
- *
55
- * This polls @bs's AioContext until all scheduled sub-drained_ends
56
- * have settled, which may result in graph changes.
57
*/
58
void bdrv_parent_drained_end_single(BdrvChild *c);
59
60
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs);
61
* bdrv_drained_end:
31
* bdrv_drained_end:
62
*
32
*
63
* End a quiescent section started by bdrv_drained_begin().
33
* End a quiescent section started by bdrv_drained_begin().
64
- *
65
- * This polls @bs's AioContext until all scheduled sub-drained_ends
66
- * have settled. On one hand, that may result in graph changes. On
67
- * the other, this requires that the caller either runs in the main
68
- * loop; or that all involved nodes (@bs and all of its parents) are
69
- * in the caller's AioContext.
70
*/
34
*/
71
void bdrv_drained_end(BlockDriverState *bs);
35
void bdrv_drained_end(BlockDriverState *bs);
72
36
73
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
37
+/**
74
index XXXXXXX..XXXXXXX 100644
38
+ * End a quiescent section started by bdrv_subtree_drained_begin().
75
--- a/include/block/block_int-common.h
39
+ */
76
+++ b/include/block/block_int-common.h
40
+void bdrv_subtree_drained_end(BlockDriverState *bs);
77
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
41
+
78
* These functions must not change the graph (and therefore also must not
42
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
79
* call aio_poll(), which could change the graph indirectly).
43
Error **errp);
80
*
44
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
81
- * If drained_end() schedules background operations, it must atomically
82
- * increment *drained_end_counter for each such operation and atomically
83
- * decrement it once the operation has settled.
84
- *
85
* Note that this can be nested. If drained_begin() was called twice, new
86
* I/O is allowed only after drained_end() was called twice, too.
87
*/
88
void (*drained_begin)(BdrvChild *child);
89
- void (*drained_end)(BdrvChild *child, int *drained_end_counter);
90
+ void (*drained_end)(BdrvChild *child);
91
92
/*
93
* Returns whether the parent has pending requests for the child. This
94
diff --git a/block.c b/block.c
95
index XXXXXXX..XXXXXXX 100644
96
--- a/block.c
97
+++ b/block.c
98
@@ -XXX,XX +XXX,XX @@ static bool bdrv_child_cb_drained_poll(BdrvChild *child)
99
return bdrv_drain_poll(bs, false, NULL, false);
100
}
101
102
-static void bdrv_child_cb_drained_end(BdrvChild *child,
103
- int *drained_end_counter)
104
+static void bdrv_child_cb_drained_end(BdrvChild *child)
105
{
106
BlockDriverState *bs = child->opaque;
107
- bdrv_drained_end_no_poll(bs, drained_end_counter);
108
+ bdrv_drained_end(bs);
109
}
110
111
static int bdrv_child_cb_inactivate(BdrvChild *child)
112
diff --git a/block/block-backend.c b/block/block-backend.c
113
index XXXXXXX..XXXXXXX 100644
114
--- a/block/block-backend.c
115
+++ b/block/block-backend.c
116
@@ -XXX,XX +XXX,XX @@ static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format,
117
}
118
static void blk_root_drained_begin(BdrvChild *child);
119
static bool blk_root_drained_poll(BdrvChild *child);
120
-static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter);
121
+static void blk_root_drained_end(BdrvChild *child);
122
123
static void blk_root_change_media(BdrvChild *child, bool load);
124
static void blk_root_resize(BdrvChild *child);
125
@@ -XXX,XX +XXX,XX @@ static bool blk_root_drained_poll(BdrvChild *child)
126
return busy || !!blk->in_flight;
127
}
128
129
-static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
130
+static void blk_root_drained_end(BdrvChild *child)
131
{
132
BlockBackend *blk = child->opaque;
133
assert(blk->quiesce_counter);
134
diff --git a/block/io.c b/block/io.c
45
diff --git a/block/io.c b/block/io.c
135
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
136
--- a/block/io.c
47
--- a/block/io.c
137
+++ b/block/io.c
48
+++ b/block/io.c
138
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
49
@@ -XXX,XX +XXX,XX @@ typedef struct {
139
}
50
BlockDriverState *bs;
51
bool done;
52
bool begin;
53
+ bool recursive;
54
BdrvChild *parent;
55
} BdrvCoDrainData;
56
57
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
58
return waited;
140
}
59
}
141
60
142
-static void bdrv_parent_drained_end_single_no_poll(BdrvChild *c,
61
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
143
- int *drained_end_counter)
62
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
144
+void bdrv_parent_drained_end_single(BdrvChild *c)
63
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
145
{
64
+ BdrvChild *parent);
146
+ IO_OR_GS_CODE();
65
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
147
+
66
+ BdrvChild *parent);
148
assert(c->parent_quiesce_counter > 0);
149
c->parent_quiesce_counter--;
150
if (c->klass->drained_end) {
151
- c->klass->drained_end(c, drained_end_counter);
152
+ c->klass->drained_end(c);
153
}
154
}
155
156
-void bdrv_parent_drained_end_single(BdrvChild *c)
157
-{
158
- int drained_end_counter = 0;
159
- AioContext *ctx = bdrv_child_get_parent_aio_context(c);
160
- IO_OR_GS_CODE();
161
- bdrv_parent_drained_end_single_no_poll(c, &drained_end_counter);
162
- AIO_WAIT_WHILE(ctx, qatomic_read(&drained_end_counter) > 0);
163
-}
164
-
165
static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
166
- bool ignore_bds_parents,
167
- int *drained_end_counter)
168
+ bool ignore_bds_parents)
169
{
170
BdrvChild *c;
171
172
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
173
if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) {
174
continue;
175
}
176
- bdrv_parent_drained_end_single_no_poll(c, drained_end_counter);
177
+ bdrv_parent_drained_end_single(c);
178
}
179
}
180
181
@@ -XXX,XX +XXX,XX @@ typedef struct {
182
bool poll;
183
BdrvChild *parent;
184
bool ignore_bds_parents;
185
- int *drained_end_counter;
186
} BdrvCoDrainData;
187
188
/* Recursively call BlockDriver.bdrv_drain_begin/end callbacks */
189
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
190
- int *drained_end_counter)
191
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
192
{
193
if (!bs->drv || (begin && !bs->drv->bdrv_drain_begin) ||
194
(!begin && !bs->drv->bdrv_drain_end)) {
195
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
196
BdrvChild *parent, bool ignore_bds_parents,
197
bool poll);
198
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
199
- BdrvChild *parent, bool ignore_bds_parents,
200
- int *drained_end_counter);
201
+ BdrvChild *parent, bool ignore_bds_parents);
202
67
203
static void bdrv_co_drain_bh_cb(void *opaque)
68
static void bdrv_co_drain_bh_cb(void *opaque)
204
{
69
{
205
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
206
aio_context_acquire(ctx);
71
207
bdrv_dec_in_flight(bs);
72
bdrv_dec_in_flight(bs);
208
if (data->begin) {
73
if (data->begin) {
209
- assert(!data->drained_end_counter);
74
- bdrv_do_drained_begin(bs, data->parent);
210
bdrv_do_drained_begin(bs, data->recursive, data->parent,
75
+ bdrv_do_drained_begin(bs, data->recursive, data->parent);
211
data->ignore_bds_parents, data->poll);
212
} else {
213
assert(!data->poll);
214
bdrv_do_drained_end(bs, data->recursive, data->parent,
215
- data->ignore_bds_parents,
216
- data->drained_end_counter);
217
+ data->ignore_bds_parents);
218
}
219
aio_context_release(ctx);
220
} else {
76
} else {
221
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
77
- bdrv_do_drained_end(bs, data->parent);
222
bool begin, bool recursive,
78
+ bdrv_do_drained_end(bs, data->recursive, data->parent);
223
BdrvChild *parent,
79
}
224
bool ignore_bds_parents,
80
225
- bool poll,
81
data->done = true;
226
- int *drained_end_counter)
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
227
+ bool poll)
83
}
84
85
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
86
- bool begin, BdrvChild *parent)
87
+ bool begin, bool recursive,
88
+ BdrvChild *parent)
228
{
89
{
229
BdrvCoDrainData data;
90
BdrvCoDrainData data;
230
Coroutine *self = qemu_coroutine_self();
91
231
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
92
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
93
.bs = bs,
94
.done = false,
95
.begin = begin,
96
+ .recursive = recursive,
232
.parent = parent,
97
.parent = parent,
233
.ignore_bds_parents = ignore_bds_parents,
234
.poll = poll,
235
- .drained_end_counter = drained_end_counter,
236
};
98
};
237
99
bdrv_inc_in_flight(bs);
238
if (bs) {
100
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
239
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
101
assert(data.done);
240
}
241
242
bdrv_parent_drained_begin(bs, parent, ignore_bds_parents);
243
- bdrv_drain_invoke(bs, true, NULL);
244
+ bdrv_drain_invoke(bs, true);
245
}
102
}
246
103
247
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
104
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
248
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
105
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
249
106
+ BdrvChild *parent)
107
{
108
+ BdrvChild *child, *next;
109
+
250
if (qemu_in_coroutine()) {
110
if (qemu_in_coroutine()) {
251
bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents,
111
- bdrv_co_yield_to_drain(bs, true, parent);
252
- poll, NULL);
112
+ bdrv_co_yield_to_drain(bs, true, recursive, parent);
253
+ poll);
254
return;
113
return;
255
}
114
}
256
115
257
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
116
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
258
117
bdrv_parent_drained_begin(bs, parent);
259
/**
118
bdrv_drain_invoke(bs, true, false);
260
* This function does not poll, nor must any of its recursively called
119
bdrv_drain_recurse(bs);
261
- * functions. The *drained_end_counter pointee will be incremented
120
+
262
- * once for every background operation scheduled, and decremented once
121
+ if (recursive) {
263
- * the operation settles. Therefore, the pointer must remain valid
122
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
264
- * until the pointee reaches 0. That implies that whoever sets up the
123
+ bdrv_do_drained_begin(child->bs, true, child);
265
- * pointee has to poll until it is 0.
124
+ }
266
- *
125
+ }
267
- * We use atomic operations to access *drained_end_counter, because
126
}
268
- * (1) when called from bdrv_set_aio_context_ignore(), the subgraph of
127
269
- * @bs may contain nodes in different AioContexts,
128
void bdrv_drained_begin(BlockDriverState *bs)
270
- * (2) bdrv_drain_all_end() uses the same counter for all nodes,
271
- * regardless of which AioContext they are in.
272
+ * functions.
273
*/
274
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
275
- BdrvChild *parent, bool ignore_bds_parents,
276
- int *drained_end_counter)
277
+ BdrvChild *parent, bool ignore_bds_parents)
278
{
129
{
279
BdrvChild *child;
130
- bdrv_do_drained_begin(bs, NULL);
131
+ bdrv_do_drained_begin(bs, false, NULL);
132
+}
133
+
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
135
+{
136
+ bdrv_do_drained_begin(bs, true, NULL);
137
}
138
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
141
+ BdrvChild *parent)
142
{
143
+ BdrvChild *child, *next;
280
int old_quiesce_counter;
144
int old_quiesce_counter;
281
145
282
- assert(drained_end_counter != NULL);
283
-
284
if (qemu_in_coroutine()) {
146
if (qemu_in_coroutine()) {
285
bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents,
147
- bdrv_co_yield_to_drain(bs, false, parent);
286
- false, drained_end_counter);
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
287
+ false);
288
return;
149
return;
289
}
150
}
290
assert(bs->quiesce_counter > 0);
151
assert(bs->quiesce_counter > 0);
291
152
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
292
/* Re-enable things in child-to-parent order */
293
- bdrv_drain_invoke(bs, false, drained_end_counter);
294
- bdrv_parent_drained_end(bs, parent, ignore_bds_parents,
295
- drained_end_counter);
296
+ bdrv_drain_invoke(bs, false);
297
+ bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
298
299
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
300
if (old_quiesce_counter == 1) {
153
if (old_quiesce_counter == 1) {
301
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
154
aio_enable_external(bdrv_get_aio_context(bs));
302
assert(!ignore_bds_parents);
303
bs->recursive_quiesce_counter--;
304
QLIST_FOREACH(child, &bs->children, next) {
305
- bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents,
306
- drained_end_counter);
307
+ bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents);
308
}
309
}
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
+ }
310
}
162
}
311
163
312
void bdrv_drained_end(BlockDriverState *bs)
164
void bdrv_drained_end(BlockDriverState *bs)
313
{
165
{
314
- int drained_end_counter = 0;
166
- bdrv_do_drained_end(bs, NULL);
315
IO_OR_GS_CODE();
167
+ bdrv_do_drained_end(bs, false, NULL);
316
- bdrv_do_drained_end(bs, false, NULL, false, &drained_end_counter);
168
+}
317
- BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0);
169
+
318
-}
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
319
-
171
+{
320
-void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter)
172
+ bdrv_do_drained_end(bs, true, NULL);
321
-{
322
- IO_CODE();
323
- bdrv_do_drained_end(bs, false, NULL, false, drained_end_counter);
324
+ bdrv_do_drained_end(bs, false, NULL, false);
325
}
173
}
326
174
327
void bdrv_subtree_drained_end(BlockDriverState *bs)
175
/*
328
{
329
- int drained_end_counter = 0;
330
IO_OR_GS_CODE();
331
- bdrv_do_drained_end(bs, true, NULL, false, &drained_end_counter);
332
- BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0);
333
+ bdrv_do_drained_end(bs, true, NULL, false);
334
}
335
336
void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
337
@@ -XXX,XX +XXX,XX @@ void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
338
339
void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
340
{
341
- int drained_end_counter = 0;
342
int i;
343
IO_OR_GS_CODE();
344
345
for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
346
- bdrv_do_drained_end(child->bs, true, child, false,
347
- &drained_end_counter);
348
+ bdrv_do_drained_end(child->bs, true, child, false);
349
}
350
-
351
- BDRV_POLL_WHILE(child->bs, qatomic_read(&drained_end_counter) > 0);
352
}
353
354
void bdrv_drain(BlockDriverState *bs)
355
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
356
GLOBAL_STATE_CODE();
357
358
if (qemu_in_coroutine()) {
359
- bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true, NULL);
360
+ bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true);
361
return;
362
}
363
364
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
365
366
void bdrv_drain_all_end_quiesce(BlockDriverState *bs)
367
{
368
- int drained_end_counter = 0;
369
GLOBAL_STATE_CODE();
370
371
g_assert(bs->quiesce_counter > 0);
372
g_assert(!bs->refcnt);
373
374
while (bs->quiesce_counter) {
375
- bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter);
376
+ bdrv_do_drained_end(bs, false, NULL, true);
377
}
378
- BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0);
379
}
380
381
void bdrv_drain_all_end(void)
382
{
383
BlockDriverState *bs = NULL;
384
- int drained_end_counter = 0;
385
GLOBAL_STATE_CODE();
386
387
/*
388
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
389
AioContext *aio_context = bdrv_get_aio_context(bs);
390
391
aio_context_acquire(aio_context);
392
- bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter);
393
+ bdrv_do_drained_end(bs, false, NULL, true);
394
aio_context_release(aio_context);
395
}
396
397
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
398
- AIO_WAIT_WHILE(NULL, qatomic_read(&drained_end_counter) > 0);
399
-
400
assert(bdrv_drain_all_count > 0);
401
bdrv_drain_all_count--;
402
}
403
diff --git a/blockjob.c b/blockjob.c
404
index XXXXXXX..XXXXXXX 100644
405
--- a/blockjob.c
406
+++ b/blockjob.c
407
@@ -XXX,XX +XXX,XX @@ static bool child_job_drained_poll(BdrvChild *c)
408
}
409
}
410
411
-static void child_job_drained_end(BdrvChild *c, int *drained_end_counter)
412
+static void child_job_drained_end(BdrvChild *c)
413
{
414
BlockJob *job = c->opaque;
415
job_resume(&job->job);
416
--
176
--
417
2.38.1
177
2.13.6
178
179
diff view generated by jsdifflib
1
Subtree drains are not used any more. Remove them.
1
Add a subtree drain version to the existing test cases.
2
3
After this, BdrvChildClass.attach/detach() don't poll any more.
4
2
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
8
Message-Id: <20221118174110.55183-11-kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
4
---
11
include/block/block-io.h | 18 +--
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
12
include/block/block_int-common.h | 1 -
6
1 file changed, 26 insertions(+), 1 deletion(-)
13
include/block/block_int-io.h | 12 --
14
block.c | 20 +--
15
block/io.c | 121 +++-----------
16
tests/unit/test-bdrv-drain.c | 261 ++-----------------------------
17
6 files changed, 44 insertions(+), 389 deletions(-)
18
7
19
diff --git a/include/block/block-io.h b/include/block/block-io.h
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
20
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
21
--- a/include/block/block-io.h
10
--- a/tests/test-bdrv-drain.c
22
+++ b/include/block/block-io.h
11
+++ b/tests/test-bdrv-drain.c
23
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c);
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
24
/**
25
* bdrv_drain_poll:
26
*
27
- * Poll for pending requests in @bs, its parents (except for @ignore_parent),
28
- * and if @recursive is true its children as well (used for subtree drain).
29
+ * Poll for pending requests in @bs and its parents (except for @ignore_parent).
30
*
31
* If @ignore_bds_parents is true, parents that are BlockDriverStates must
32
* ignore the drain request because they will be drained separately (used for
33
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c);
34
*
35
* This is part of bdrv_drained_begin.
36
*/
37
-bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
38
- BdrvChild *ignore_parent, bool ignore_bds_parents);
39
+bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent,
40
+ bool ignore_bds_parents);
41
42
/**
43
* bdrv_drained_begin:
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
45
void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
46
BdrvChild *parent, bool ignore_bds_parents);
47
48
-/**
49
- * Like bdrv_drained_begin, but recursively begins a quiesced section for
50
- * exclusive access to all child nodes as well.
51
- */
52
-void bdrv_subtree_drained_begin(BlockDriverState *bs);
53
-
54
/**
55
* bdrv_drained_end:
56
*
57
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs);
58
*/
59
void bdrv_drained_end(BlockDriverState *bs);
60
61
-/**
62
- * End a quiescent section started by bdrv_subtree_drained_begin().
63
- */
64
-void bdrv_subtree_drained_end(BlockDriverState *bs);
65
-
66
#endif /* BLOCK_IO_H */
67
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
68
index XXXXXXX..XXXXXXX 100644
69
--- a/include/block/block_int-common.h
70
+++ b/include/block/block_int-common.h
71
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
72
73
/* Accessed with atomic ops. */
74
int quiesce_counter;
75
- int recursive_quiesce_counter;
76
77
unsigned int write_gen; /* Current data generation */
78
79
diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h
80
index XXXXXXX..XXXXXXX 100644
81
--- a/include/block/block_int-io.h
82
+++ b/include/block/block_int-io.h
83
@@ -XXX,XX +XXX,XX @@ void bdrv_bsc_invalidate_range(BlockDriverState *bs,
84
*/
85
void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes);
86
87
-
88
-/*
89
- * "I/O or GS" API functions. These functions can run without
90
- * the BQL, but only in one specific iothread/main loop.
91
- *
92
- * See include/block/block-io.h for more information about
93
- * the "I/O or GS" API.
94
- */
95
-
96
-void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
97
-void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
98
-
99
#endif /* BLOCK_INT_IO_H */
100
diff --git a/block.c b/block.c
101
index XXXXXXX..XXXXXXX 100644
102
--- a/block.c
103
+++ b/block.c
104
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_begin(BdrvChild *child)
105
static bool bdrv_child_cb_drained_poll(BdrvChild *child)
106
{
107
BlockDriverState *bs = child->opaque;
108
- return bdrv_drain_poll(bs, false, NULL, false);
109
+ return bdrv_drain_poll(bs, NULL, false);
110
}
111
112
static void bdrv_child_cb_drained_end(BdrvChild *child)
113
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child)
114
assert(!bs->file);
115
bs->file = child;
116
}
117
-
118
- bdrv_apply_subtree_drain(child, bs);
119
}
120
121
static void bdrv_child_cb_detach(BdrvChild *child)
122
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_detach(BdrvChild *child)
123
bdrv_backing_detach(child);
124
}
125
126
- bdrv_unapply_subtree_drain(child, bs);
127
-
128
assert_bdrv_graph_writable(bs);
129
QLIST_REMOVE(child, next);
130
if (child == bs->backing) {
131
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
132
}
133
134
if (old_bs) {
135
- /* Detach first so that the recursive drain sections coming from @child
136
- * are already gone and we only end the drain sections that came from
137
- * elsewhere. */
138
if (child->klass->detach) {
139
child->klass->detach(child);
140
}
141
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
142
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
143
144
/*
145
- * Detaching the old node may have led to the new node's
146
- * quiesce_counter having been decreased. Not a problem, we
147
- * just need to recognize this here and then invoke
148
- * drained_end appropriately more often.
149
+ * Polling in bdrv_parent_drained_begin_single() may have led to the new
150
+ * node's quiesce_counter having been decreased. Not a problem, we just
151
+ * need to recognize this here and then invoke drained_end appropriately
152
+ * more often.
153
*/
154
assert(new_bs->quiesce_counter <= new_bs_quiesce_counter);
155
drain_saldo += new_bs->quiesce_counter - new_bs_quiesce_counter;
156
157
- /* Attach only after starting new drained sections, so that recursive
158
- * drain sections coming from @child don't get an extra .drained_begin
159
- * callback. */
160
if (child->klass->attach) {
161
child->klass->attach(child);
162
}
163
diff --git a/block/io.c b/block/io.c
164
index XXXXXXX..XXXXXXX 100644
165
--- a/block/io.c
166
+++ b/block/io.c
167
@@ -XXX,XX +XXX,XX @@ typedef struct {
168
BlockDriverState *bs;
169
bool done;
170
bool begin;
171
- bool recursive;
172
bool poll;
173
BdrvChild *parent;
174
bool ignore_bds_parents;
175
} BdrvCoDrainData;
176
177
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
178
-bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
179
- BdrvChild *ignore_parent, bool ignore_bds_parents)
180
+bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent,
181
+ bool ignore_bds_parents)
182
{
183
- BdrvChild *child, *next;
184
IO_OR_GS_CODE();
185
186
if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) {
187
@@ -XXX,XX +XXX,XX @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
188
return true;
189
}
190
191
- if (recursive) {
192
- assert(!ignore_bds_parents);
193
- QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
194
- if (bdrv_drain_poll(child->bs, recursive, child, false)) {
195
- return true;
196
- }
197
- }
198
- }
199
-
200
return false;
201
}
202
203
-static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive,
204
+static bool bdrv_drain_poll_top_level(BlockDriverState *bs,
205
BdrvChild *ignore_parent)
206
{
207
- return bdrv_drain_poll(bs, recursive, ignore_parent, false);
208
+ return bdrv_drain_poll(bs, ignore_parent, false);
209
}
210
211
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
212
- BdrvChild *parent, bool ignore_bds_parents,
213
- bool poll);
214
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
215
- BdrvChild *parent, bool ignore_bds_parents);
216
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
217
+ bool ignore_bds_parents, bool poll);
218
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
219
+ bool ignore_bds_parents);
220
221
static void bdrv_co_drain_bh_cb(void *opaque)
222
{
223
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
224
aio_context_acquire(ctx);
225
bdrv_dec_in_flight(bs);
226
if (data->begin) {
227
- bdrv_do_drained_begin(bs, data->recursive, data->parent,
228
- data->ignore_bds_parents, data->poll);
229
+ bdrv_do_drained_begin(bs, data->parent, data->ignore_bds_parents,
230
+ data->poll);
231
} else {
232
assert(!data->poll);
233
- bdrv_do_drained_end(bs, data->recursive, data->parent,
234
- data->ignore_bds_parents);
235
+ bdrv_do_drained_end(bs, data->parent, data->ignore_bds_parents);
236
}
237
aio_context_release(ctx);
238
} else {
239
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
240
}
241
242
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
243
- bool begin, bool recursive,
244
+ bool begin,
245
BdrvChild *parent,
246
bool ignore_bds_parents,
247
bool poll)
248
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
249
.bs = bs,
250
.done = false,
251
.begin = begin,
252
- .recursive = recursive,
253
.parent = parent,
254
.ignore_bds_parents = ignore_bds_parents,
255
.poll = poll,
256
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
257
}
258
}
259
260
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
261
- BdrvChild *parent, bool ignore_bds_parents,
262
- bool poll)
263
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
264
+ bool ignore_bds_parents, bool poll)
265
{
266
- BdrvChild *child, *next;
267
-
268
if (qemu_in_coroutine()) {
269
- bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents,
270
- poll);
271
+ bdrv_co_yield_to_drain(bs, true, parent, ignore_bds_parents, poll);
272
return;
273
}
274
275
bdrv_do_drained_begin_quiesce(bs, parent, ignore_bds_parents);
276
277
- if (recursive) {
278
- assert(!ignore_bds_parents);
279
- bs->recursive_quiesce_counter++;
280
- QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
281
- bdrv_do_drained_begin(child->bs, true, child, ignore_bds_parents,
282
- false);
283
- }
284
- }
285
-
286
/*
287
* Wait for drained requests to finish.
288
*
289
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
290
*/
291
if (poll) {
292
assert(!ignore_bds_parents);
293
- BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, recursive, parent));
294
+ BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent));
295
}
296
}
297
298
void bdrv_drained_begin(BlockDriverState *bs)
299
{
300
IO_OR_GS_CODE();
301
- bdrv_do_drained_begin(bs, false, NULL, false, true);
302
-}
303
-
304
-void bdrv_subtree_drained_begin(BlockDriverState *bs)
305
-{
306
- IO_OR_GS_CODE();
307
- bdrv_do_drained_begin(bs, true, NULL, false, true);
308
+ bdrv_do_drained_begin(bs, NULL, false, true);
309
}
310
311
/**
312
* This function does not poll, nor must any of its recursively called
313
* functions.
314
*/
315
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
316
- BdrvChild *parent, bool ignore_bds_parents)
317
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
318
+ bool ignore_bds_parents)
319
{
320
- BdrvChild *child;
321
int old_quiesce_counter;
322
323
if (qemu_in_coroutine()) {
324
- bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents,
325
- false);
326
+ bdrv_co_yield_to_drain(bs, false, parent, ignore_bds_parents, false);
327
return;
328
}
329
assert(bs->quiesce_counter > 0);
330
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
331
if (old_quiesce_counter == 1) {
332
aio_enable_external(bdrv_get_aio_context(bs));
333
}
334
-
335
- if (recursive) {
336
- assert(!ignore_bds_parents);
337
- bs->recursive_quiesce_counter--;
338
- QLIST_FOREACH(child, &bs->children, next) {
339
- bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents);
340
- }
341
- }
342
}
343
344
void bdrv_drained_end(BlockDriverState *bs)
345
{
346
IO_OR_GS_CODE();
347
- bdrv_do_drained_end(bs, false, NULL, false);
348
-}
349
-
350
-void bdrv_subtree_drained_end(BlockDriverState *bs)
351
-{
352
- IO_OR_GS_CODE();
353
- bdrv_do_drained_end(bs, true, NULL, false);
354
-}
355
-
356
-void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
357
-{
358
- int i;
359
- IO_OR_GS_CODE();
360
-
361
- for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
362
- bdrv_do_drained_begin(child->bs, true, child, false, true);
363
- }
364
-}
365
-
366
-void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
367
-{
368
- int i;
369
- IO_OR_GS_CODE();
370
-
371
- for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
372
- bdrv_do_drained_end(child->bs, true, child, false);
373
- }
374
+ bdrv_do_drained_end(bs, NULL, false);
375
}
376
377
void bdrv_drain(BlockDriverState *bs)
378
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_all_poll(void)
379
while ((bs = bdrv_next_all_states(bs))) {
380
AioContext *aio_context = bdrv_get_aio_context(bs);
381
aio_context_acquire(aio_context);
382
- result |= bdrv_drain_poll(bs, false, NULL, true);
383
+ result |= bdrv_drain_poll(bs, NULL, true);
384
aio_context_release(aio_context);
385
}
386
387
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
388
GLOBAL_STATE_CODE();
389
390
if (qemu_in_coroutine()) {
391
- bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true);
392
+ bdrv_co_yield_to_drain(NULL, true, NULL, true, true);
393
return;
394
}
395
396
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
397
AioContext *aio_context = bdrv_get_aio_context(bs);
398
399
aio_context_acquire(aio_context);
400
- bdrv_do_drained_begin(bs, false, NULL, true, false);
401
+ bdrv_do_drained_begin(bs, NULL, true, false);
402
aio_context_release(aio_context);
403
}
404
405
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end_quiesce(BlockDriverState *bs)
406
g_assert(!bs->refcnt);
407
408
while (bs->quiesce_counter) {
409
- bdrv_do_drained_end(bs, false, NULL, true);
410
+ bdrv_do_drained_end(bs, NULL, true);
411
}
412
}
413
414
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
415
AioContext *aio_context = bdrv_get_aio_context(bs);
416
417
aio_context_acquire(aio_context);
418
- bdrv_do_drained_end(bs, false, NULL, true);
419
+ bdrv_do_drained_end(bs, NULL, true);
420
aio_context_release(aio_context);
421
}
422
423
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
424
index XXXXXXX..XXXXXXX 100644
425
--- a/tests/unit/test-bdrv-drain.c
426
+++ b/tests/unit/test-bdrv-drain.c
427
@@ -XXX,XX +XXX,XX @@ static void call_in_coroutine(void (*entry)(void))
428
enum drain_type {
13
enum drain_type {
429
BDRV_DRAIN_ALL,
14
BDRV_DRAIN_ALL,
430
BDRV_DRAIN,
15
BDRV_DRAIN,
431
- BDRV_SUBTREE_DRAIN,
16
+ BDRV_SUBTREE_DRAIN,
432
DRAIN_TYPE_MAX,
17
DRAIN_TYPE_MAX,
433
};
18
};
434
19
435
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
436
switch (drain_type) {
21
switch (drain_type) {
437
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
438
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
439
- case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
440
default: g_assert_not_reached();
25
default: g_assert_not_reached();
441
}
26
}
442
}
27
}
443
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
28
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
444
switch (drain_type) {
29
switch (drain_type) {
445
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
30
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
446
case BDRV_DRAIN: bdrv_drained_end(bs); break;
31
case BDRV_DRAIN: bdrv_drained_end(bs); break;
447
- case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
32
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
448
default: g_assert_not_reached();
33
default: g_assert_not_reached();
449
}
34
}
450
}
35
}
451
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
36
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
452
test_drv_cb_common(BDRV_DRAIN, false);
37
test_drv_cb_common(BDRV_DRAIN, false);
453
}
38
}
454
39
455
-static void test_drv_cb_drain_subtree(void)
40
+static void test_drv_cb_drain_subtree(void)
456
-{
41
+{
457
- test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
42
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
458
-}
43
+}
459
-
44
+
460
static void test_drv_cb_co_drain_all(void)
461
{
462
call_in_coroutine(test_drv_cb_drain_all);
463
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_co_drain(void)
464
call_in_coroutine(test_drv_cb_drain);
465
}
466
467
-static void test_drv_cb_co_drain_subtree(void)
468
-{
469
- call_in_coroutine(test_drv_cb_drain_subtree);
470
-}
471
-
472
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
473
{
46
{
474
BlockBackend *blk;
47
BlockBackend *blk;
475
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
48
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
476
test_quiesce_common(BDRV_DRAIN, false);
49
test_quiesce_common(BDRV_DRAIN, false);
477
}
50
}
478
51
479
-static void test_quiesce_drain_subtree(void)
52
+static void test_quiesce_drain_subtree(void)
480
-{
53
+{
481
- test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
54
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
482
-}
55
+}
483
-
56
+
484
static void test_quiesce_co_drain_all(void)
485
{
486
call_in_coroutine(test_quiesce_drain_all);
487
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_co_drain(void)
488
call_in_coroutine(test_quiesce_drain);
489
}
490
491
-static void test_quiesce_co_drain_subtree(void)
492
-{
493
- call_in_coroutine(test_quiesce_drain_subtree);
494
-}
495
-
496
static void test_nested(void)
57
static void test_nested(void)
497
{
58
{
498
BlockBackend *blk;
59
BlockBackend *blk;
499
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
60
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
500
blk_unref(blk);
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);
501
}
72
}
502
73
503
-static void test_multiparent(void)
74
+static void test_blockjob_drain_subtree(void)
504
-{
75
+{
505
- BlockBackend *blk_a, *blk_b;
76
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
506
- BlockDriverState *bs_a, *bs_b, *backing;
77
+}
507
- BDRVTestState *a_s, *b_s, *backing_s;
78
+
508
-
79
int main(int argc, char **argv)
509
- blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL);
510
- bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
511
- &error_abort);
512
- a_s = bs_a->opaque;
513
- blk_insert_bs(blk_a, bs_a, &error_abort);
514
-
515
- blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL);
516
- bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
517
- &error_abort);
518
- b_s = bs_b->opaque;
519
- blk_insert_bs(blk_b, bs_b, &error_abort);
520
-
521
- backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
522
- backing_s = backing->opaque;
523
- bdrv_set_backing_hd(bs_a, backing, &error_abort);
524
- bdrv_set_backing_hd(bs_b, backing, &error_abort);
525
-
526
- g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
527
- g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
528
- g_assert_cmpint(backing->quiesce_counter, ==, 0);
529
- g_assert_cmpint(a_s->drain_count, ==, 0);
530
- g_assert_cmpint(b_s->drain_count, ==, 0);
531
- g_assert_cmpint(backing_s->drain_count, ==, 0);
532
-
533
- do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
534
-
535
- g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
536
- g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
537
- g_assert_cmpint(backing->quiesce_counter, ==, 1);
538
- g_assert_cmpint(a_s->drain_count, ==, 1);
539
- g_assert_cmpint(b_s->drain_count, ==, 1);
540
- g_assert_cmpint(backing_s->drain_count, ==, 1);
541
-
542
- do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
543
-
544
- g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
545
- g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
546
- g_assert_cmpint(backing->quiesce_counter, ==, 2);
547
- g_assert_cmpint(a_s->drain_count, ==, 2);
548
- g_assert_cmpint(b_s->drain_count, ==, 2);
549
- g_assert_cmpint(backing_s->drain_count, ==, 2);
550
-
551
- do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
552
-
553
- g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
554
- g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
555
- g_assert_cmpint(backing->quiesce_counter, ==, 1);
556
- g_assert_cmpint(a_s->drain_count, ==, 1);
557
- g_assert_cmpint(b_s->drain_count, ==, 1);
558
- g_assert_cmpint(backing_s->drain_count, ==, 1);
559
-
560
- do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
561
-
562
- g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
563
- g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
564
- g_assert_cmpint(backing->quiesce_counter, ==, 0);
565
- g_assert_cmpint(a_s->drain_count, ==, 0);
566
- g_assert_cmpint(b_s->drain_count, ==, 0);
567
- g_assert_cmpint(backing_s->drain_count, ==, 0);
568
-
569
- bdrv_unref(backing);
570
- bdrv_unref(bs_a);
571
- bdrv_unref(bs_b);
572
- blk_unref(blk_a);
573
- blk_unref(blk_b);
574
-}
575
-
576
-static void test_graph_change_drain_subtree(void)
577
-{
578
- BlockBackend *blk_a, *blk_b;
579
- BlockDriverState *bs_a, *bs_b, *backing;
580
- BDRVTestState *a_s, *b_s, *backing_s;
581
-
582
- blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL);
583
- bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
584
- &error_abort);
585
- a_s = bs_a->opaque;
586
- blk_insert_bs(blk_a, bs_a, &error_abort);
587
-
588
- blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL);
589
- bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
590
- &error_abort);
591
- b_s = bs_b->opaque;
592
- blk_insert_bs(blk_b, bs_b, &error_abort);
593
-
594
- backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
595
- backing_s = backing->opaque;
596
- bdrv_set_backing_hd(bs_a, backing, &error_abort);
597
-
598
- g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
599
- g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
600
- g_assert_cmpint(backing->quiesce_counter, ==, 0);
601
- g_assert_cmpint(a_s->drain_count, ==, 0);
602
- g_assert_cmpint(b_s->drain_count, ==, 0);
603
- g_assert_cmpint(backing_s->drain_count, ==, 0);
604
-
605
- do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
606
- do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
607
- do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
608
- do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
609
- do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
610
-
611
- bdrv_set_backing_hd(bs_b, backing, &error_abort);
612
- g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
613
- g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
614
- g_assert_cmpint(backing->quiesce_counter, ==, 5);
615
- g_assert_cmpint(a_s->drain_count, ==, 5);
616
- g_assert_cmpint(b_s->drain_count, ==, 5);
617
- g_assert_cmpint(backing_s->drain_count, ==, 5);
618
-
619
- bdrv_set_backing_hd(bs_b, NULL, &error_abort);
620
- g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
621
- g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
622
- g_assert_cmpint(backing->quiesce_counter, ==, 3);
623
- g_assert_cmpint(a_s->drain_count, ==, 3);
624
- g_assert_cmpint(b_s->drain_count, ==, 2);
625
- g_assert_cmpint(backing_s->drain_count, ==, 3);
626
-
627
- bdrv_set_backing_hd(bs_b, backing, &error_abort);
628
- g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
629
- g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
630
- g_assert_cmpint(backing->quiesce_counter, ==, 5);
631
- g_assert_cmpint(a_s->drain_count, ==, 5);
632
- g_assert_cmpint(b_s->drain_count, ==, 5);
633
- g_assert_cmpint(backing_s->drain_count, ==, 5);
634
-
635
- do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
636
- do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
637
- do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
638
- do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
639
- do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
640
-
641
- g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
642
- g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
643
- g_assert_cmpint(backing->quiesce_counter, ==, 0);
644
- g_assert_cmpint(a_s->drain_count, ==, 0);
645
- g_assert_cmpint(b_s->drain_count, ==, 0);
646
- g_assert_cmpint(backing_s->drain_count, ==, 0);
647
-
648
- bdrv_unref(backing);
649
- bdrv_unref(bs_a);
650
- bdrv_unref(bs_b);
651
- blk_unref(blk_a);
652
- blk_unref(blk_b);
653
-}
654
-
655
static void test_graph_change_drain_all(void)
656
{
80
{
657
BlockBackend *blk_a, *blk_b;
81
bdrv_init();
658
@@ -XXX,XX +XXX,XX @@ static void test_iothread_drain(void)
659
test_iothread_common(BDRV_DRAIN, 1);
660
}
661
662
-static void test_iothread_drain_subtree(void)
663
-{
664
- test_iothread_common(BDRV_SUBTREE_DRAIN, 0);
665
- test_iothread_common(BDRV_SUBTREE_DRAIN, 1);
666
-}
667
-
668
669
typedef struct TestBlockJob {
670
BlockJob common;
671
@@ -XXX,XX +XXX,XX @@ enum test_job_result {
672
enum test_job_drain_node {
673
TEST_JOB_DRAIN_SRC,
674
TEST_JOB_DRAIN_SRC_CHILD,
675
- TEST_JOB_DRAIN_SRC_PARENT,
676
};
677
678
static void test_blockjob_common_drain_node(enum drain_type drain_type,
679
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type,
680
case TEST_JOB_DRAIN_SRC_CHILD:
681
drain_bs = src_backing;
682
break;
683
- case TEST_JOB_DRAIN_SRC_PARENT:
684
- drain_bs = src_overlay;
685
- break;
686
default:
687
g_assert_not_reached();
688
}
689
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type, bool use_iothread,
690
TEST_JOB_DRAIN_SRC);
691
test_blockjob_common_drain_node(drain_type, use_iothread, result,
692
TEST_JOB_DRAIN_SRC_CHILD);
693
- if (drain_type == BDRV_SUBTREE_DRAIN) {
694
- test_blockjob_common_drain_node(drain_type, use_iothread, result,
695
- TEST_JOB_DRAIN_SRC_PARENT);
696
- }
697
}
698
699
static void test_blockjob_drain_all(void)
700
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_drain(void)
701
test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_SUCCESS);
702
}
703
704
-static void test_blockjob_drain_subtree(void)
705
-{
706
- test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_SUCCESS);
707
-}
708
-
709
static void test_blockjob_error_drain_all(void)
710
{
711
test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_RUN);
712
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_error_drain(void)
713
test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_PREPARE);
714
}
715
716
-static void test_blockjob_error_drain_subtree(void)
717
-{
718
- test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_RUN);
719
- test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_PREPARE);
720
-}
721
-
722
static void test_blockjob_iothread_drain_all(void)
723
{
724
test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_SUCCESS);
725
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_iothread_drain(void)
726
test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_SUCCESS);
727
}
728
729
-static void test_blockjob_iothread_drain_subtree(void)
730
-{
731
- test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_SUCCESS);
732
-}
733
-
734
static void test_blockjob_iothread_error_drain_all(void)
735
{
736
test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_RUN);
737
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_iothread_error_drain(void)
738
test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_PREPARE);
739
}
740
741
-static void test_blockjob_iothread_error_drain_subtree(void)
742
-{
743
- test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_RUN);
744
- test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_PREPARE);
745
-}
746
-
747
748
typedef struct BDRVTestTopState {
749
BdrvChild *wait_child;
750
@@ -XXX,XX +XXX,XX @@ static void do_test_delete_by_drain(bool detach_instead_of_delete,
751
bdrv_drain(child_bs);
752
bdrv_unref(child_bs);
753
break;
754
- case BDRV_SUBTREE_DRAIN:
755
- /* Would have to ref/unref bs here for !detach_instead_of_delete, but
756
- * then the whole test becomes pointless because the graph changes
757
- * don't occur during the drain any more. */
758
- assert(detach_instead_of_delete);
759
- bdrv_subtree_drained_begin(bs);
760
- bdrv_subtree_drained_end(bs);
761
- break;
762
case BDRV_DRAIN_ALL:
763
bdrv_drain_all_begin();
764
bdrv_drain_all_end();
765
@@ -XXX,XX +XXX,XX @@ static void test_detach_by_drain(void)
766
do_test_delete_by_drain(true, BDRV_DRAIN);
767
}
768
769
-static void test_detach_by_drain_subtree(void)
770
-{
771
- do_test_delete_by_drain(true, BDRV_SUBTREE_DRAIN);
772
-}
773
-
774
775
struct detach_by_parent_data {
776
BlockDriverState *parent_b;
777
@@ -XXX,XX +XXX,XX @@ static void test_detach_indirect(bool by_parent_cb)
778
g_assert(acb != NULL);
779
780
/* Drain and check the expected result */
781
- bdrv_subtree_drained_begin(parent_b);
782
+ bdrv_drained_begin(parent_b);
783
+ bdrv_drained_begin(a);
784
+ bdrv_drained_begin(b);
785
+ bdrv_drained_begin(c);
786
787
g_assert(detach_by_parent_data.child_c != NULL);
788
789
@@ -XXX,XX +XXX,XX @@ static void test_detach_indirect(bool by_parent_cb)
790
g_assert(QLIST_NEXT(child_a, next) == NULL);
791
792
g_assert_cmpint(parent_a->quiesce_counter, ==, 1);
793
- g_assert_cmpint(parent_b->quiesce_counter, ==, 1);
794
+ g_assert_cmpint(parent_b->quiesce_counter, ==, 3);
795
g_assert_cmpint(a->quiesce_counter, ==, 1);
796
- g_assert_cmpint(b->quiesce_counter, ==, 0);
797
+ g_assert_cmpint(b->quiesce_counter, ==, 1);
798
g_assert_cmpint(c->quiesce_counter, ==, 1);
799
800
- bdrv_subtree_drained_end(parent_b);
801
+ bdrv_drained_end(parent_b);
802
+ bdrv_drained_end(a);
803
+ bdrv_drained_end(b);
804
+ bdrv_drained_end(c);
805
806
bdrv_unref(parent_b);
807
blk_unref(blk);
808
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
82
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
809
83
810
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
84
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
811
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
85
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
812
- g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
86
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
813
- test_drv_cb_drain_subtree);
87
+ test_drv_cb_drain_subtree);
814
815
g_test_add_func("/bdrv-drain/driver-cb/co/drain_all",
816
test_drv_cb_co_drain_all);
817
g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
818
- g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
819
- test_drv_cb_co_drain_subtree);
820
-
821
88
822
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
89
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
823
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
90
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
824
- g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
91
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
825
- test_quiesce_drain_subtree);
92
+ test_quiesce_drain_subtree);
826
827
g_test_add_func("/bdrv-drain/quiesce/co/drain_all",
828
test_quiesce_co_drain_all);
829
g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
830
- g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
831
- test_quiesce_co_drain_subtree);
832
93
833
g_test_add_func("/bdrv-drain/nested", test_nested);
94
g_test_add_func("/bdrv-drain/nested", test_nested);
834
- g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
835
836
- g_test_add_func("/bdrv-drain/graph-change/drain_subtree",
837
- test_graph_change_drain_subtree);
838
g_test_add_func("/bdrv-drain/graph-change/drain_all",
839
test_graph_change_drain_all);
840
841
g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all);
842
g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain);
843
- g_test_add_func("/bdrv-drain/iothread/drain_subtree",
844
- test_iothread_drain_subtree);
845
95
846
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
96
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
847
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
97
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
848
- g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
98
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
849
- test_blockjob_drain_subtree);
99
+ test_blockjob_drain_subtree);
850
100
851
g_test_add_func("/bdrv-drain/blockjob/error/drain_all",
101
return g_test_run();
852
test_blockjob_error_drain_all);
102
}
853
g_test_add_func("/bdrv-drain/blockjob/error/drain",
854
test_blockjob_error_drain);
855
- g_test_add_func("/bdrv-drain/blockjob/error/drain_subtree",
856
- test_blockjob_error_drain_subtree);
857
858
g_test_add_func("/bdrv-drain/blockjob/iothread/drain_all",
859
test_blockjob_iothread_drain_all);
860
g_test_add_func("/bdrv-drain/blockjob/iothread/drain",
861
test_blockjob_iothread_drain);
862
- g_test_add_func("/bdrv-drain/blockjob/iothread/drain_subtree",
863
- test_blockjob_iothread_drain_subtree);
864
865
g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_all",
866
test_blockjob_iothread_error_drain_all);
867
g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain",
868
test_blockjob_iothread_error_drain);
869
- g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_subtree",
870
- test_blockjob_iothread_error_drain_subtree);
871
872
g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain);
873
g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all);
874
g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain);
875
- g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree);
876
g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb);
877
g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb);
878
879
--
103
--
880
2.38.1
104
2.13.6
105
106
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
If bdrv_do_drained_begin/end() are called in coroutine context, they
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.
2
4
3
Avoid mixing bdrv_* functions with blk_*, so create blk_* counterparts
4
for bdrv_block_status_above and bdrv_is_allocated_above.
5
6
Note that since blk_co_block_status_above only calls the g_c_w function
7
bdrv_common_block_status_above and is marked as coroutine_fn, call
8
directly bdrv_co_common_block_status_above() to avoid using a g_c_w.
9
Same applies to blk_co_is_allocated_above.
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
13
Message-Id: <20221128142337.657646-5-eesposit@redhat.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
6
---
17
include/sysemu/block-backend-io.h | 9 +++++++++
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
18
block/block-backend.c | 21 ++++++++++++++++++++
8
1 file changed, 59 insertions(+)
19
block/commit.c | 4 ++--
20
nbd/server.c | 32 +++++++++++++++----------------
21
4 files changed, 48 insertions(+), 18 deletions(-)
22
9
23
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
24
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
25
--- a/include/sysemu/block-backend-io.h
12
--- a/tests/test-bdrv-drain.c
26
+++ b/include/sysemu/block-backend-io.h
13
+++ b/tests/test-bdrv-drain.c
27
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
28
int64_t bytes, BdrvRequestFlags read_flags,
15
*aio_ret = ret;
29
BdrvRequestFlags write_flags);
30
31
+int coroutine_fn blk_co_block_status_above(BlockBackend *blk,
32
+ BlockDriverState *base,
33
+ int64_t offset, int64_t bytes,
34
+ int64_t *pnum, int64_t *map,
35
+ BlockDriverState **file);
36
+int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk,
37
+ BlockDriverState *base,
38
+ bool include_base, int64_t offset,
39
+ int64_t bytes, int64_t *pnum);
40
41
/*
42
* "I/O or GS" API functions. These functions can run without
43
diff --git a/block/block-backend.c b/block/block-backend.c
44
index XXXXXXX..XXXXXXX 100644
45
--- a/block/block-backend.c
46
+++ b/block/block-backend.c
47
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
48
return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags);
49
}
16
}
50
17
51
+int coroutine_fn blk_co_block_status_above(BlockBackend *blk,
18
+typedef struct CallInCoroutineData {
52
+ BlockDriverState *base,
19
+ void (*entry)(void);
53
+ int64_t offset, int64_t bytes,
20
+ bool done;
54
+ int64_t *pnum, int64_t *map,
21
+} CallInCoroutineData;
55
+ BlockDriverState **file)
22
+
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
56
+{
24
+{
57
+ IO_CODE();
25
+ CallInCoroutineData *data = opaque;
58
+ return bdrv_co_block_status_above(blk_bs(blk), base, offset, bytes, pnum,
26
+
59
+ map, file);
27
+ data->entry();
28
+ data->done = true;
60
+}
29
+}
61
+
30
+
62
+int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk,
31
+static void call_in_coroutine(void (*entry)(void))
63
+ BlockDriverState *base,
64
+ bool include_base, int64_t offset,
65
+ int64_t bytes, int64_t *pnum)
66
+{
32
+{
67
+ IO_CODE();
33
+ Coroutine *co;
68
+ return bdrv_co_is_allocated_above(blk_bs(blk), base, include_base, offset,
34
+ CallInCoroutineData data = {
69
+ bytes, pnum);
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);
43
+ }
70
+}
44
+}
71
+
45
+
72
typedef struct BlkRwCo {
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
{
73
BlockBackend *blk;
65
BlockBackend *blk;
74
int64_t offset;
66
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void)
75
diff --git a/block/commit.c b/block/commit.c
67
test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
76
index XXXXXXX..XXXXXXX 100644
77
--- a/block/commit.c
78
+++ b/block/commit.c
79
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
80
break;
81
}
82
/* Copy if allocated above the base */
83
- ret = bdrv_is_allocated_above(blk_bs(s->top), s->base_overlay, true,
84
- offset, COMMIT_BUFFER_SIZE, &n);
85
+ ret = blk_co_is_allocated_above(s->top, s->base_overlay, true,
86
+ offset, COMMIT_BUFFER_SIZE, &n);
87
copy = (ret > 0);
88
trace_commit_one_iteration(s, offset, n, ret);
89
if (copy) {
90
diff --git a/nbd/server.c b/nbd/server.c
91
index XXXXXXX..XXXXXXX 100644
92
--- a/nbd/server.c
93
+++ b/nbd/server.c
94
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_co_send_structured_error(NBDClient *client,
95
}
68
}
96
69
97
/* Do a sparse read and send the structured reply to the client.
70
+static void test_quiesce_co_drain(void)
98
- * Returns -errno if sending fails. bdrv_block_status_above() failure is
71
+{
99
+ * Returns -errno if sending fails. blk_co_block_status_above() failure is
72
+ call_in_coroutine(test_quiesce_drain);
100
* reported to the client, at which point this function succeeds.
73
+}
101
*/
74
+
102
static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
75
+static void test_quiesce_co_drain_subtree(void)
103
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
76
+{
104
77
+ call_in_coroutine(test_quiesce_drain_subtree);
105
while (progress < size) {
78
+}
106
int64_t pnum;
79
+
107
- int status = bdrv_block_status_above(blk_bs(exp->common.blk), NULL,
80
static void test_nested(void)
108
- offset + progress,
109
- size - progress, &pnum, NULL,
110
- NULL);
111
+ int status = blk_co_block_status_above(exp->common.blk, NULL,
112
+ offset + progress,
113
+ size - progress, &pnum, NULL,
114
+ NULL);
115
bool final;
116
117
if (status < 0) {
118
@@ -XXX,XX +XXX,XX @@ static int nbd_extent_array_add(NBDExtentArray *ea,
119
return 0;
120
}
121
122
-static int coroutine_fn blockstatus_to_extents(BlockDriverState *bs,
123
+static int coroutine_fn blockstatus_to_extents(BlockBackend *blk,
124
uint64_t offset, uint64_t bytes,
125
NBDExtentArray *ea)
126
{
81
{
127
while (bytes) {
82
BlockBackend *blk;
128
uint32_t flags;
83
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
129
int64_t num;
84
g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
130
- int ret = bdrv_co_block_status_above(bs, NULL, offset, bytes, &num,
85
test_drv_cb_drain_subtree);
131
- NULL, NULL);
86
132
+ int ret = blk_co_block_status_above(blk, NULL, offset, bytes, &num,
87
+ // XXX bdrv_drain_all() doesn't work in coroutine context
133
+ NULL, NULL);
88
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
134
89
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
135
if (ret < 0) {
90
+ test_drv_cb_co_drain_subtree);
136
return ret;
91
+
137
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blockstatus_to_extents(BlockDriverState *bs,
92
+
138
return 0;
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
139
}
94
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
140
95
g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
141
-static int coroutine_fn blockalloc_to_extents(BlockDriverState *bs,
96
test_quiesce_drain_subtree);
142
+static int coroutine_fn blockalloc_to_extents(BlockBackend *blk,
97
143
uint64_t offset, uint64_t bytes,
98
+ // XXX bdrv_drain_all() doesn't work in coroutine context
144
NBDExtentArray *ea)
99
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
145
{
100
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
146
while (bytes) {
101
+ test_quiesce_co_drain_subtree);
147
int64_t num;
102
+
148
- int ret = bdrv_co_is_allocated_above(bs, NULL, false, offset, bytes,
103
g_test_add_func("/bdrv-drain/nested", test_nested);
149
- &num);
104
150
+ int ret = blk_co_is_allocated_above(blk, NULL, false, offset, bytes,
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
151
+ &num);
152
153
if (ret < 0) {
154
return ret;
155
@@ -XXX,XX +XXX,XX @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
156
/* Get block status from the exported device and send it to the client */
157
static int
158
coroutine_fn nbd_co_send_block_status(NBDClient *client, uint64_t handle,
159
- BlockDriverState *bs, uint64_t offset,
160
+ BlockBackend *blk, uint64_t offset,
161
uint32_t length, bool dont_fragment,
162
bool last, uint32_t context_id,
163
Error **errp)
164
@@ -XXX,XX +XXX,XX @@ coroutine_fn nbd_co_send_block_status(NBDClient *client, uint64_t handle,
165
g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
166
167
if (context_id == NBD_META_ID_BASE_ALLOCATION) {
168
- ret = blockstatus_to_extents(bs, offset, length, ea);
169
+ ret = blockstatus_to_extents(blk, offset, length, ea);
170
} else {
171
- ret = blockalloc_to_extents(bs, offset, length, ea);
172
+ ret = blockalloc_to_extents(blk, offset, length, ea);
173
}
174
if (ret < 0) {
175
return nbd_co_send_structured_error(
176
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
177
178
if (client->export_meta.base_allocation) {
179
ret = nbd_co_send_block_status(client, request->handle,
180
- blk_bs(exp->common.blk),
181
+ exp->common.blk,
182
request->from,
183
request->len, dont_fragment,
184
!--contexts_remaining,
185
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
186
187
if (client->export_meta.allocation_depth) {
188
ret = nbd_co_send_block_status(client, request->handle,
189
- blk_bs(exp->common.blk),
190
+ exp->common.blk,
191
request->from, request->len,
192
dont_fragment,
193
!--contexts_remaining,
194
--
106
--
195
2.38.1
107
2.13.6
108
109
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
Test that drain sections are correctly propagated through the graph.
2
2
3
Add/remove the AioContext in aio_context_list in graph-lock.c when it is
4
created/destroyed. This allows using the graph locking operations from
5
this AioContext.
6
7
In order to allow linking util/async.c with binaries that don't include
8
the block layer, introduce stubs for (un)register_aiocontext().
9
10
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20221207131838.239125-5-kwolf@redhat.com>
13
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
4
---
17
stubs/graph-lock.c | 10 ++++++++++
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
18
util/async.c | 4 ++++
6
1 file changed, 74 insertions(+)
19
stubs/meson.build | 1 +
20
3 files changed, 15 insertions(+)
21
create mode 100644 stubs/graph-lock.c
22
7
23
diff --git a/stubs/graph-lock.c b/stubs/graph-lock.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
24
new file mode 100644
9
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX
10
--- a/tests/test-bdrv-drain.c
26
--- /dev/null
11
+++ b/tests/test-bdrv-drain.c
27
+++ b/stubs/graph-lock.c
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
28
@@ -XXX,XX +XXX,XX @@
13
blk_unref(blk);
29
+#include "qemu/osdep.h"
14
}
30
+#include "block/graph-lock.h"
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;
31
+
21
+
32
+void register_aiocontext(AioContext *ctx)
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
33
+{
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);
34
+}
87
+}
35
+
88
+
36
+void unregister_aiocontext(AioContext *ctx)
89
37
+{
90
typedef struct TestBlockJob {
38
+}
91
BlockJob common;
39
diff --git a/util/async.c b/util/async.c
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
40
index XXXXXXX..XXXXXXX 100644
93
test_quiesce_co_drain_subtree);
41
--- a/util/async.c
94
42
+++ b/util/async.c
95
g_test_add_func("/bdrv-drain/nested", test_nested);
43
@@ -XXX,XX +XXX,XX @@
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
44
#include "qapi/error.h"
97
45
#include "block/aio.h"
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
46
#include "block/thread-pool.h"
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
47
+#include "block/graph-lock.h"
48
#include "qemu/main-loop.h"
49
#include "qemu/atomic.h"
50
#include "qemu/rcu_queue.h"
51
@@ -XXX,XX +XXX,XX @@ aio_ctx_finalize(GSource *source)
52
qemu_rec_mutex_destroy(&ctx->lock);
53
qemu_lockcnt_destroy(&ctx->list_lock);
54
timerlistgroup_deinit(&ctx->tlg);
55
+ unregister_aiocontext(ctx);
56
aio_context_destroy(ctx);
57
}
58
59
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
60
ctx->thread_pool_min = 0;
61
ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
62
63
+ register_aiocontext(ctx);
64
+
65
return ctx;
66
fail:
67
g_source_destroy(&ctx->source);
68
diff --git a/stubs/meson.build b/stubs/meson.build
69
index XXXXXXX..XXXXXXX 100644
70
--- a/stubs/meson.build
71
+++ b/stubs/meson.build
72
@@ -XXX,XX +XXX,XX @@ stub_ss.add(files('error-printf.c'))
73
stub_ss.add(files('fdset.c'))
74
stub_ss.add(files('gdbstub.c'))
75
stub_ss.add(files('get-vm-name.c'))
76
+stub_ss.add(files('graph-lock.c'))
77
if linux_io_uring.found()
78
stub_ss.add(files('io_uring.c'))
79
endif
80
--
100
--
81
2.38.1
101
2.13.6
102
103
diff view generated by jsdifflib
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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
Protect the main function where graph is modified.
3
can be correctly applied when children are added or removed during the
4
4
drained section.
5
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
5
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
With this change, it is safe to modify the graph even inside a
7
Message-Id: <20221207131838.239125-12-kwolf@redhat.com>
7
bdrv_subtree_drained_begin/end() section.
8
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
8
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
10
---
12
block.c | 7 +++----
11
include/block/block.h | 2 --
13
1 file changed, 3 insertions(+), 4 deletions(-)
12
include/block/block_int.h | 5 +++++
14
13
block.c | 32 +++++++++++++++++++++++++++++---
14
block/io.c | 28 ++++++++++++++++++++++++----
15
4 files changed, 58 insertions(+), 9 deletions(-)
16
17
diff --git a/include/block/block.h b/include/block/block.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/block.h
20
+++ b/include/block/block.h
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
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
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);
15
diff --git a/block.c b/block.c
53
diff --git a/block.c b/block.c
16
index XXXXXXX..XXXXXXX 100644
54
index XXXXXXX..XXXXXXX 100644
17
--- a/block.c
55
--- a/block.c
18
+++ b/block.c
56
+++ b/block.c
19
@@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
20
*
58
bdrv_drained_end(bs);
21
* If @new_bs is non-NULL, the parent of @child must already be drained through
59
}
22
* @child.
60
23
- *
61
+static void bdrv_child_cb_attach(BdrvChild *child)
24
- * This function does not poll.
62
+{
25
*/
63
+ BlockDriverState *bs = child->opaque;
26
static void bdrv_replace_child_noperm(BdrvChild *child,
64
+ bdrv_apply_subtree_drain(child, bs);
27
BlockDriverState *new_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,
83
};
84
85
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = {
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,
92
};
93
94
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_attach(BdrvChild *c)
95
parent->backing_blocker);
96
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
97
parent->backing_blocker);
98
+
99
+ bdrv_child_cb_attach(c);
100
}
101
102
static void bdrv_backing_detach(BdrvChild *c)
103
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_detach(BdrvChild *c)
104
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
105
error_free(parent->backing_blocker);
106
parent->backing_blocker = NULL;
107
+
108
+ bdrv_child_cb_detach(c);
109
}
110
111
/*
28
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
29
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
30
}
114
}
31
32
+ /* TODO Pull this up into the callers to avoid polling here */
33
+ bdrv_graph_wrlock();
34
if (old_bs) {
115
if (old_bs) {
35
if (child->klass->detach) {
116
+ /* Detach first so that the recursive drain sections coming from @child
36
child->klass->detach(child);
117
+ * are already gone and we only end the drain sections that came from
37
}
118
+ * elsewhere. */
38
- assert_bdrv_graph_writable(old_bs);
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
- }
39
QLIST_REMOVE(child, next_parent);
130
QLIST_REMOVE(child, next_parent);
40
}
131
}
41
132
42
child->bs = new_bs;
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
43
134
}
44
if (new_bs) {
135
}
45
- assert_bdrv_graph_writable(new_bs);
136
46
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
137
+ /* Attach only after starting new drained sections, so that recursive
47
if (child->klass->attach) {
138
+ * drain sections coming from @child don't get an extra .drained_begin
48
child->klass->attach(child);
139
+ * callback. */
49
}
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)
155
{
156
BdrvChild *child, *next;
157
158
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
159
bdrv_drain_recurse(bs);
160
161
if (recursive) {
162
+ bs->recursive_quiesce_counter++;
163
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
164
bdrv_do_drained_begin(child->bs, true, child);
165
}
166
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
167
bdrv_do_drained_begin(bs, true, NULL);
168
}
169
170
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
171
- BdrvChild *parent)
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,
50
}
178
}
51
+ bdrv_graph_wrunlock();
179
52
180
if (recursive) {
53
/*
181
+ bs->recursive_quiesce_counter--;
54
* If the parent was drained through this BdrvChild previously, but new_bs
182
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
183
bdrv_do_drained_end(child->bs, true, child);
184
}
185
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs)
186
bdrv_do_drained_end(bs, true, NULL);
187
}
188
189
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
190
+{
191
+ int i;
192
+
193
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
194
+ bdrv_do_drained_begin(child->bs, true, child);
195
+ }
196
+}
197
+
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
199
+{
200
+ int i;
201
+
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
203
+ bdrv_do_drained_end(child->bs, true, child);
204
+ }
205
+}
206
+
207
/*
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
209
* and suspend block driver's internal I/O until next request arrives.
55
--
210
--
56
2.38.1
211
2.13.6
212
213
diff view generated by jsdifflib
1
We want to change .bdrv_co_drained_begin() back to be a non-coroutine
2
callback, so in preparation, avoid yielding in its implementation.
3
4
Because we increase bs->in_flight and bdrv_drained_begin() polls, the
5
behaviour is unchanged.
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
9
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
11
Message-Id: <20221118174110.55183-2-kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
2
---
14
block/qed.c | 20 +++++++++++++++++---
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
15
1 file changed, 17 insertions(+), 3 deletions(-)
4
1 file changed, 80 insertions(+)
16
5
17
diff --git a/block/qed.c b/block/qed.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
18
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
19
--- a/block/qed.c
8
--- a/tests/test-bdrv-drain.c
20
+++ b/block/qed.c
9
+++ b/tests/test-bdrv-drain.c
21
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qed_unplug_allocating_write_reqs(BDRVQEDState *s)
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
22
qemu_co_mutex_unlock(&s->table_lock);
11
blk_unref(blk_b);
23
}
12
}
24
13
25
-static void coroutine_fn qed_need_check_timer_entry(void *opaque)
14
+static void test_graph_change(void)
26
+static void coroutine_fn qed_need_check_timer(BDRVQEDState *s)
27
{
28
- BDRVQEDState *s = opaque;
29
int ret;
30
31
trace_qed_need_check_timer_cb(s);
32
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qed_need_check_timer_entry(void *opaque)
33
(void) ret;
34
}
35
36
+static void coroutine_fn qed_need_check_timer_entry(void *opaque)
37
+{
15
+{
38
+ BDRVQEDState *s = opaque;
16
+ BlockBackend *blk_a, *blk_b;
17
+ BlockDriverState *bs_a, *bs_b, *backing;
18
+ BDRVTestState *a_s, *b_s, *backing_s;
39
+
19
+
40
+ qed_need_check_timer(opaque);
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
41
+ bdrv_dec_in_flight(s->bs);
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
22
+ &error_abort);
23
+ a_s = bs_a->opaque;
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
25
+
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
28
+ &error_abort);
29
+ b_s = bs_b->opaque;
30
+ blk_insert_bs(blk_b, bs_b, &error_abort);
31
+
32
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
33
+ backing_s = backing->opaque;
34
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
35
+
36
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
37
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
38
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
39
+ g_assert_cmpint(a_s->drain_count, ==, 0);
40
+ g_assert_cmpint(b_s->drain_count, ==, 0);
41
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
42
+
43
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
44
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
45
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
47
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
48
+
49
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
50
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
51
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
52
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
53
+ g_assert_cmpint(a_s->drain_count, ==, 5);
54
+ g_assert_cmpint(b_s->drain_count, ==, 5);
55
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
56
+
57
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
58
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
59
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
61
+ g_assert_cmpint(a_s->drain_count, ==, 3);
62
+ g_assert_cmpint(b_s->drain_count, ==, 2);
63
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
64
+
65
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
69
+ g_assert_cmpint(a_s->drain_count, ==, 5);
70
+ g_assert_cmpint(b_s->drain_count, ==, 5);
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);
42
+}
91
+}
43
+
92
+
44
static void qed_need_check_timer_cb(void *opaque)
93
45
{
94
typedef struct TestBlockJob {
46
+ BDRVQEDState *s = opaque;
95
BlockJob common;
47
Coroutine *co = qemu_coroutine_create(qed_need_check_timer_entry, opaque);
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
48
+
97
49
+ bdrv_inc_in_flight(s->bs);
98
g_test_add_func("/bdrv-drain/nested", test_nested);
50
qemu_coroutine_enter(co);
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
51
}
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
52
101
53
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs)
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
54
* header is flushed.
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
55
*/
56
if (s->need_check_timer && timer_pending(s->need_check_timer)) {
57
+ Coroutine *co;
58
+
59
qed_cancel_need_check_timer(s);
60
- qed_need_check_timer_entry(s);
61
+ co = qemu_coroutine_create(qed_need_check_timer_entry, s);
62
+ bdrv_inc_in_flight(bs);
63
+ aio_co_enter(bdrv_get_aio_context(bs), co);
64
}
65
}
66
67
--
104
--
68
2.38.1
105
2.13.6
106
107
diff view generated by jsdifflib
Deleted patch
1
Callers don't agree whether bdrv_reopen_queue_child() should be called
2
with the AioContext lock held or not. Standardise on holding the lock
3
(as done by QMP blockdev-reopen and the replication block driver) and
4
fix bdrv_reopen() to do the same.
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20221118174110.55183-7-kwolf@redhat.com>
8
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block.c | 7 +++++--
13
1 file changed, 5 insertions(+), 2 deletions(-)
14
15
diff --git a/block.c b/block.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block.c
18
+++ b/block.c
19
@@ -XXX,XX +XXX,XX @@ static bool bdrv_recurse_has_child(BlockDriverState *bs,
20
* bs_queue, or the existing bs_queue being used.
21
*
22
* bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
23
+ *
24
+ * To be called with bs->aio_context locked.
25
*/
26
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
27
BlockDriverState *bs,
28
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
29
return bs_queue;
30
}
31
32
+/* To be called with bs->aio_context locked */
33
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
34
BlockDriverState *bs,
35
QDict *options, bool keep_old_opts)
36
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
37
GLOBAL_STATE_CODE();
38
39
bdrv_subtree_drained_begin(bs);
40
+ queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts);
41
+
42
if (ctx != qemu_get_aio_context()) {
43
aio_context_release(ctx);
44
}
45
-
46
- queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts);
47
ret = bdrv_reopen_multiple(queue, errp);
48
49
if (ctx != qemu_get_aio_context()) {
50
--
51
2.38.1
diff view generated by jsdifflib
1
The subtree drain was introduced in commit b1e1af394d9 as a way to avoid
1
Since commit bde70715, base is the only node that is reopened in
2
graph changes between finding the base node and changing the block graph
2
commit_start(). This means that the code, which still involves an
3
as necessary on completion of the image streaming job.
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
4
5
The block graph could change between these two points because
6
bdrv_set_backing_hd() first drains the parent node, which involved
7
polling and can do anything.
8
9
Subtree draining was an imperfect way to make this less likely (because
10
with it, fewer callbacks are called during this window). Everyone agreed
11
that it's not really the right solution, and it was only committed as a
12
stopgap solution.
13
14
This replaces the subtree drain with a solution that simply drains the
15
parent node before we try to find the base node, and then call a version
16
of bdrv_set_backing_hd() that doesn't drain, but just asserts that the
17
parent node is already drained.
18
19
This way, any graph changes caused by draining happen before we start
20
looking at the graph and things stay consistent between finding the base
21
node and changing the graph.
22
4
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
24
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
25
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
26
Message-Id: <20221118174110.55183-10-kwolf@redhat.com>
27
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
28
---
7
---
29
include/block/block-global-state.h | 3 +++
8
block/commit.c | 8 +-------
30
block.c | 17 ++++++++++++++---
9
1 file changed, 1 insertion(+), 7 deletions(-)
31
block/stream.c | 26 ++++++++++++++++----------
32
3 files changed, 33 insertions(+), 13 deletions(-)
33
10
34
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
11
diff --git a/block/commit.c b/block/commit.c
35
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
36
--- a/include/block/block-global-state.h
13
--- a/block/commit.c
37
+++ b/include/block/block-global-state.h
14
+++ b/block/commit.c
38
@@ -XXX,XX +XXX,XX @@ int bdrv_open_file_child(const char *filename,
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
39
BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp);
16
const char *filter_node_name, Error **errp)
40
int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
41
Error **errp);
42
+int bdrv_set_backing_hd_drained(BlockDriverState *bs,
43
+ BlockDriverState *backing_hd,
44
+ Error **errp);
45
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
46
const char *bdref_key, Error **errp);
47
BlockDriverState *bdrv_open(const char *filename, const char *reference,
48
diff --git a/block.c b/block.c
49
index XXXXXXX..XXXXXXX 100644
50
--- a/block.c
51
+++ b/block.c
52
@@ -XXX,XX +XXX,XX @@ static int bdrv_set_backing_noperm(BlockDriverState *bs,
53
return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp);
54
}
55
56
-int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
57
- Error **errp)
58
+int bdrv_set_backing_hd_drained(BlockDriverState *bs,
59
+ BlockDriverState *backing_hd,
60
+ Error **errp)
61
{
17
{
62
int ret;
18
CommitBlockJob *s;
63
Transaction *tran = tran_new();
19
- BlockReopenQueue *reopen_queue = NULL;
64
20
int orig_base_flags;
65
GLOBAL_STATE_CODE();
21
BlockDriverState *iter;
66
- bdrv_drained_begin(bs);
22
BlockDriverState *commit_top_bs = NULL;
67
+ assert(bs->quiesce_counter > 0);
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
68
24
/* convert base to r/w, if necessary */
69
ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp);
25
orig_base_flags = bdrv_get_flags(base);
70
if (ret < 0) {
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
71
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
72
ret = bdrv_refresh_perms(bs, tran, errp);
28
- orig_base_flags | BDRV_O_RDWR);
73
out:
74
tran_finalize(tran, ret);
75
+ return ret;
76
+}
77
78
+int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
79
+ Error **errp)
80
+{
81
+ int ret;
82
+ GLOBAL_STATE_CODE();
83
+
84
+ bdrv_drained_begin(bs);
85
+ ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp);
86
bdrv_drained_end(bs);
87
88
return ret;
89
diff --git a/block/stream.c b/block/stream.c
90
index XXXXXXX..XXXXXXX 100644
91
--- a/block/stream.c
92
+++ b/block/stream.c
93
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
94
bdrv_cor_filter_drop(s->cor_filter_bs);
95
s->cor_filter_bs = NULL;
96
97
- bdrv_subtree_drained_begin(s->above_base);
98
+ /*
99
+ * bdrv_set_backing_hd() requires that unfiltered_bs is drained. Drain
100
+ * already here and use bdrv_set_backing_hd_drained() instead because
101
+ * the polling during drained_begin() might change the graph, and if we do
102
+ * this only later, we may end up working with the wrong base node (or it
103
+ * might even have gone away by the time we want to use it).
104
+ */
105
+ bdrv_drained_begin(unfiltered_bs);
106
107
base = bdrv_filter_or_cow_bs(s->above_base);
108
- if (base) {
109
- bdrv_ref(base);
110
- }
29
- }
111
-
30
-
112
unfiltered_base = bdrv_skip_filters(base);
31
- if (reopen_queue) {
113
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
114
if (bdrv_cow_child(unfiltered_bs)) {
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
115
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
34
if (local_err != NULL) {
116
}
35
error_propagate(errp, local_err);
117
}
36
goto fail;
118
119
- bdrv_set_backing_hd(unfiltered_bs, base, &local_err);
120
+ bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err);
121
+
122
+ /*
123
+ * This call will do I/O, so the graph can change again from here on.
124
+ * We have already completed the graph change, so we are not in danger
125
+ * of operating on the wrong node any more if this happens.
126
+ */
127
ret = bdrv_change_backing_file(unfiltered_bs, base_id, base_fmt, false);
128
if (local_err) {
129
error_report_err(local_err);
130
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
131
}
132
133
out:
134
- if (base) {
135
- bdrv_unref(base);
136
- }
137
- bdrv_subtree_drained_end(s->above_base);
138
+ bdrv_drained_end(unfiltered_bs);
139
return ret;
140
}
141
142
--
37
--
143
2.38.1
38
2.13.6
39
40
diff view generated by jsdifflib
1
bdrv_reopen() and friends use subtree drains as a lazy way of covering
1
The bdrv_reopen*() implementation doesn't like it if the graph is
2
all the nodes they touch. Turns out that this lazy way is a lot more
2
changed between queuing nodes for reopen and actually reopening them
3
complicated than just draining the nodes individually, even not
3
(one of the reasons is that queuing can be recursive).
4
accounting for the additional complexity in the drain mechanism itself.
5
4
6
Simplify the code by switching to draining the individual nodes that are
5
So instead of draining the device only in bdrv_reopen_multiple(),
7
already managed in the BlockReopenQueue anyway.
6
require that callers already drained all affected nodes, and assert this
7
in bdrv_reopen_queue().
8
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20221118174110.55183-8-kwolf@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
11
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
11
---
15
block.c | 16 +++++++++-------
12
block.c | 23 ++++++++++++++++-------
16
block/replication.c | 6 ------
13
block/replication.c | 6 ++++++
17
blockdev.c | 13 -------------
14
qemu-io-cmds.c | 3 +++
18
3 files changed, 9 insertions(+), 26 deletions(-)
15
3 files changed, 25 insertions(+), 7 deletions(-)
19
16
20
diff --git a/block.c b/block.c
17
diff --git a/block.c b/block.c
21
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
22
--- a/block.c
19
--- a/block.c
23
+++ b/block.c
20
+++ b/block.c
24
@@ -XXX,XX +XXX,XX @@ static bool bdrv_recurse_has_child(BlockDriverState *bs,
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
25
* returns a pointer to bs_queue, which is either the newly allocated
22
* returns a pointer to bs_queue, which is either the newly allocated
26
* bs_queue, or the existing bs_queue being used.
23
* bs_queue, or the existing bs_queue being used.
27
*
24
*
28
- * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
25
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
29
+ * bs is drained here and undrained by bdrv_reopen_queue_free().
30
*
31
* To be called with bs->aio_context locked.
32
*/
26
*/
27
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
28
BlockDriverState *bs,
33
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
29
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
34
int flags;
30
BdrvChild *child;
35
QemuOpts *opts;
31
QDict *old_options, *explicit_options;
36
32
37
- /* Make sure that the caller remembered to use a drained section. This is
33
+ /* Make sure that the caller remembered to use a drained section. This is
38
- * important to avoid graph changes between the recursive queuing here and
34
+ * important to avoid graph changes between the recursive queuing here and
39
- * bdrv_reopen_multiple(). */
35
+ * bdrv_reopen_multiple(). */
40
- assert(bs->quiesce_counter > 0);
36
+ assert(bs->quiesce_counter > 0);
41
GLOBAL_STATE_CODE();
42
43
+ bdrv_drained_begin(bs);
44
+
37
+
45
if (bs_queue == NULL) {
38
if (bs_queue == NULL) {
46
bs_queue = g_new0(BlockReopenQueue, 1);
39
bs_queue = g_new0(BlockReopenQueue, 1);
47
QTAILQ_INIT(bs_queue);
40
QSIMPLEQ_INIT(bs_queue);
48
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue)
41
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
49
if (bs_queue) {
42
* If all devices prepare successfully, then the changes are committed
50
BlockReopenQueueEntry *bs_entry, *next;
43
* to all devices.
51
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
44
*
52
+ AioContext *ctx = bdrv_get_aio_context(bs_entry->state.bs);
45
+ * All affected nodes must be drained between bdrv_reopen_queue() and
53
+
46
+ * bdrv_reopen_multiple().
54
+ aio_context_acquire(ctx);
47
*/
55
+ bdrv_drained_end(bs_entry->state.bs);
48
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
56
+ aio_context_release(ctx);
49
{
57
+
50
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
58
qobject_unref(bs_entry->state.explicit_options);
51
59
qobject_unref(bs_entry->state.options);
52
assert(bs_queue != NULL);
60
g_free(bs_entry);
53
61
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
54
- aio_context_release(ctx);
62
55
- bdrv_drain_all_begin();
63
GLOBAL_STATE_CODE();
56
- aio_context_acquire(ctx);
64
57
-
65
- bdrv_subtree_drained_begin(bs);
58
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
66
queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts);
59
+ assert(bs_entry->state.bs->quiesce_counter > 0);
67
60
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
68
if (ctx != qemu_get_aio_context()) {
61
error_propagate(errp, local_err);
69
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
62
goto cleanup;
70
if (ctx != qemu_get_aio_context()) {
63
@@ -XXX,XX +XXX,XX @@ cleanup:
71
aio_context_acquire(ctx);
72
}
64
}
73
- bdrv_subtree_drained_end(bs);
65
g_free(bs_queue);
74
66
67
- bdrv_drain_all_end();
68
-
75
return ret;
69
return ret;
76
}
70
}
71
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
73
{
74
int ret = -1;
75
Error *local_err = NULL;
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
77
+ BlockReopenQueue *queue;
78
79
+ bdrv_subtree_drained_begin(bs);
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);
85
}
86
+
87
+ bdrv_subtree_drained_end(bs);
88
+
89
return ret;
90
}
91
77
diff --git a/block/replication.c b/block/replication.c
92
diff --git a/block/replication.c b/block/replication.c
78
index XXXXXXX..XXXXXXX 100644
93
index XXXXXXX..XXXXXXX 100644
79
--- a/block/replication.c
94
--- a/block/replication.c
80
+++ b/block/replication.c
95
+++ b/block/replication.c
81
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
96
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
82
s->orig_secondary_read_only = bdrv_is_read_only(secondary_disk->bs);
97
new_secondary_flags = s->orig_secondary_flags;
83
}
98
}
84
99
85
- bdrv_subtree_drained_begin(hidden_disk->bs);
100
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
86
- bdrv_subtree_drained_begin(secondary_disk->bs);
101
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
87
-
102
+
88
if (s->orig_hidden_read_only) {
103
if (orig_hidden_flags != new_hidden_flags) {
89
QDict *opts = qdict_new();
104
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
90
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
105
new_hidden_flags);
91
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
106
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
92
aio_context_acquire(ctx);
107
reopen_queue, &local_err);
93
}
108
error_propagate(errp, local_err);
94
}
109
}
95
-
110
+
96
- bdrv_subtree_drained_end(hidden_disk->bs);
111
+ bdrv_subtree_drained_end(s->hidden_disk->bs);
97
- bdrv_subtree_drained_end(secondary_disk->bs);
112
+ bdrv_subtree_drained_end(s->secondary_disk->bs);
98
}
113
}
99
114
100
static void backup_job_cleanup(BlockDriverState *bs)
115
static void backup_job_cleanup(BlockDriverState *bs)
101
diff --git a/blockdev.c b/blockdev.c
116
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
102
index XXXXXXX..XXXXXXX 100644
117
index XXXXXXX..XXXXXXX 100644
103
--- a/blockdev.c
118
--- a/qemu-io-cmds.c
104
+++ b/blockdev.c
119
+++ b/qemu-io-cmds.c
105
@@ -XXX,XX +XXX,XX @@ fail:
120
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
106
void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
121
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
107
{
122
qemu_opts_reset(&reopen_opts);
108
BlockReopenQueue *queue = NULL;
123
109
- GSList *drained = NULL;
124
+ bdrv_subtree_drained_begin(bs);
110
- GSList *p;
125
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
111
126
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
112
/* Add each one of the BDS that we want to reopen to the queue */
127
+ bdrv_subtree_drained_end(bs);
113
for (; reopen_list != NULL; reopen_list = reopen_list->next) {
128
+
114
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
129
if (local_err) {
115
ctx = bdrv_get_aio_context(bs);
130
error_report_err(local_err);
116
aio_context_acquire(ctx);
131
} else {
117
118
- bdrv_subtree_drained_begin(bs);
119
queue = bdrv_reopen_queue(queue, bs, qdict, false);
120
- drained = g_slist_prepend(drained, bs);
121
122
aio_context_release(ctx);
123
}
124
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
125
126
fail:
127
bdrv_reopen_queue_free(queue);
128
- for (p = drained; p; p = p->next) {
129
- BlockDriverState *bs = p->data;
130
- AioContext *ctx = bdrv_get_aio_context(bs);
131
-
132
- aio_context_acquire(ctx);
133
- bdrv_subtree_drained_end(bs);
134
- aio_context_release(ctx);
135
- }
136
- g_slist_free(drained);
137
}
138
139
void qmp_blockdev_del(const char *node_name, Error **errp)
140
--
132
--
141
2.38.1
133
2.13.6
134
135
diff view generated by jsdifflib
Deleted patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
1
3
These functions end up calling bdrv_common_block_status_above(), a
4
generated_co_wrapper function.
5
In addition, they also happen to be always called in coroutine context,
6
meaning all callers are coroutine_fn.
7
This means that the g_c_w function will enter the qemu_in_coroutine()
8
case and eventually suspend (or in other words call qemu_coroutine_yield()).
9
Therefore we can mark such functions coroutine_fn too.
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Message-Id: <20221128142337.657646-3-eesposit@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
include/block/block-copy.h | 5 +++--
19
block/block-copy.c | 21 ++++++++++++---------
20
2 files changed, 15 insertions(+), 11 deletions(-)
21
22
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/block-copy.h
25
+++ b/include/block/block-copy.h
26
@@ -XXX,XX +XXX,XX @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm);
27
void block_copy_state_free(BlockCopyState *s);
28
29
void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes);
30
-int64_t block_copy_reset_unallocated(BlockCopyState *s,
31
- int64_t offset, int64_t *count);
32
+int64_t coroutine_fn block_copy_reset_unallocated(BlockCopyState *s,
33
+ int64_t offset,
34
+ int64_t *count);
35
36
int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
37
bool ignore_ratelimit, uint64_t timeout_ns,
38
diff --git a/block/block-copy.c b/block/block-copy.c
39
index XXXXXXX..XXXXXXX 100644
40
--- a/block/block-copy.c
41
+++ b/block/block-copy.c
42
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
43
return ret;
44
}
45
46
-static int block_copy_block_status(BlockCopyState *s, int64_t offset,
47
- int64_t bytes, int64_t *pnum)
48
+static coroutine_fn int block_copy_block_status(BlockCopyState *s,
49
+ int64_t offset,
50
+ int64_t bytes, int64_t *pnum)
51
{
52
int64_t num;
53
BlockDriverState *base;
54
@@ -XXX,XX +XXX,XX @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset,
55
base = NULL;
56
}
57
58
- ret = bdrv_block_status_above(s->source->bs, base, offset, bytes, &num,
59
- NULL, NULL);
60
+ ret = bdrv_co_block_status_above(s->source->bs, base, offset, bytes, &num,
61
+ NULL, NULL);
62
if (ret < 0 || num < s->cluster_size) {
63
/*
64
* On error or if failed to obtain large enough chunk just fallback to
65
@@ -XXX,XX +XXX,XX @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset,
66
* Check if the cluster starting at offset is allocated or not.
67
* return via pnum the number of contiguous clusters sharing this allocation.
68
*/
69
-static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
70
- int64_t *pnum)
71
+static int coroutine_fn block_copy_is_cluster_allocated(BlockCopyState *s,
72
+ int64_t offset,
73
+ int64_t *pnum)
74
{
75
BlockDriverState *bs = s->source->bs;
76
int64_t count, total_count = 0;
77
@@ -XXX,XX +XXX,XX @@ static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
78
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
79
80
while (true) {
81
- ret = bdrv_is_allocated(bs, offset, bytes, &count);
82
+ ret = bdrv_co_is_allocated(bs, offset, bytes, &count);
83
if (ret < 0) {
84
return ret;
85
}
86
@@ -XXX,XX +XXX,XX @@ void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes)
87
* @return 0 when the cluster at @offset was unallocated,
88
* 1 otherwise, and -ret on error.
89
*/
90
-int64_t block_copy_reset_unallocated(BlockCopyState *s,
91
- int64_t offset, int64_t *count)
92
+int64_t coroutine_fn block_copy_reset_unallocated(BlockCopyState *s,
93
+ int64_t offset,
94
+ int64_t *count)
95
{
96
int ret;
97
int64_t clusters, bytes;
98
--
99
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
1
3
These functions end up calling bdrv_create() implemented as generated_co_wrapper
4
functions.
5
In addition, they also happen to be always called in coroutine context,
6
meaning all callers are coroutine_fn.
7
This means that the g_c_w function will enter the qemu_in_coroutine()
8
case and eventually suspend (or in other words call qemu_coroutine_yield()).
9
Therefore we can mark such functions coroutine_fn too.
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Message-Id: <20221128142337.657646-6-eesposit@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
block/vmdk.c | 36 +++++++++++++++++++-----------------
19
1 file changed, 19 insertions(+), 17 deletions(-)
20
21
diff --git a/block/vmdk.c b/block/vmdk.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/block/vmdk.c
24
+++ b/block/vmdk.c
25
@@ -XXX,XX +XXX,XX @@ exit:
26
return ret;
27
}
28
29
-static int vmdk_create_extent(const char *filename, int64_t filesize,
30
- bool flat, bool compress, bool zeroed_grain,
31
- BlockBackend **pbb,
32
- QemuOpts *opts, Error **errp)
33
+static int coroutine_fn vmdk_create_extent(const char *filename,
34
+ int64_t filesize, bool flat,
35
+ bool compress, bool zeroed_grain,
36
+ BlockBackend **pbb,
37
+ QemuOpts *opts, Error **errp)
38
{
39
int ret;
40
BlockBackend *blk = NULL;
41
@@ -XXX,XX +XXX,XX @@ static int filename_decompose(const char *filename, char *path, char *prefix,
42
* non-split format.
43
* idx >= 1: get the n-th extent if in a split subformat
44
*/
45
-typedef BlockBackend *(*vmdk_create_extent_fn)(int64_t size,
46
- int idx,
47
- bool flat,
48
- bool split,
49
- bool compress,
50
- bool zeroed_grain,
51
- void *opaque,
52
- Error **errp);
53
+typedef BlockBackend * coroutine_fn (*vmdk_create_extent_fn)(int64_t size,
54
+ int idx,
55
+ bool flat,
56
+ bool split,
57
+ bool compress,
58
+ bool zeroed_grain,
59
+ void *opaque,
60
+ Error **errp);
61
62
static void vmdk_desc_add_extent(GString *desc,
63
const char *extent_line_fmt,
64
@@ -XXX,XX +XXX,XX @@ typedef struct {
65
QemuOpts *opts;
66
} VMDKCreateOptsData;
67
68
-static BlockBackend *vmdk_co_create_opts_cb(int64_t size, int idx,
69
+static BlockBackend * coroutine_fn vmdk_co_create_opts_cb(int64_t size, int idx,
70
bool flat, bool split, bool compress,
71
bool zeroed_grain, void *opaque,
72
Error **errp)
73
@@ -XXX,XX +XXX,XX @@ exit:
74
return ret;
75
}
76
77
-static BlockBackend *vmdk_co_create_cb(int64_t size, int idx,
78
- bool flat, bool split, bool compress,
79
- bool zeroed_grain, void *opaque,
80
- Error **errp)
81
+static BlockBackend * coroutine_fn vmdk_co_create_cb(int64_t size, int idx,
82
+ bool flat, bool split,
83
+ bool compress,
84
+ bool zeroed_grain,
85
+ void *opaque, Error **errp)
86
{
87
int ret;
88
BlockDriverState *bs;
89
--
90
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
1
3
In preparation to the incoming new function specifiers,
4
rename g_c_w with a more meaningful name and document it.
5
6
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
8
Message-Id: <20221128142337.657646-10-eesposit@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
docs/devel/block-coroutine-wrapper.rst | 6 +--
13
block/coroutines.h | 4 +-
14
include/block/block-common.h | 11 +++--
15
include/block/block-io.h | 44 ++++++++---------
16
include/sysemu/block-backend-io.h | 68 +++++++++++++-------------
17
scripts/block-coroutine-wrapper.py | 6 +--
18
6 files changed, 71 insertions(+), 68 deletions(-)
19
20
diff --git a/docs/devel/block-coroutine-wrapper.rst b/docs/devel/block-coroutine-wrapper.rst
21
index XXXXXXX..XXXXXXX 100644
22
--- a/docs/devel/block-coroutine-wrapper.rst
23
+++ b/docs/devel/block-coroutine-wrapper.rst
24
@@ -XXX,XX +XXX,XX @@ called ``bdrv_foo(<same args>)``. In this case the script can help. To
25
trigger the generation:
26
27
1. You need ``bdrv_foo`` declaration somewhere (for example, in
28
- ``block/coroutines.h``) with the ``generated_co_wrapper`` mark,
29
+ ``block/coroutines.h``) with the ``co_wrapper_mixed`` mark,
30
like this:
31
32
.. code-block:: c
33
34
- int generated_co_wrapper bdrv_foo(<some args>);
35
+ int co_wrapper_mixed bdrv_foo(<some args>);
36
37
2. You need to feed this declaration to block-coroutine-wrapper script.
38
For this, add the .h (or .c) file with the declaration to the
39
@@ -XXX,XX +XXX,XX @@ Links
40
41
1. The script location is ``scripts/block-coroutine-wrapper.py``.
42
43
-2. Generic place for private ``generated_co_wrapper`` declarations is
44
+2. Generic place for private ``co_wrapper_mixed`` declarations is
45
``block/coroutines.h``, for public declarations:
46
``include/block/block.h``
47
48
diff --git a/block/coroutines.h b/block/coroutines.h
49
index XXXXXXX..XXXXXXX 100644
50
--- a/block/coroutines.h
51
+++ b/block/coroutines.h
52
@@ -XXX,XX +XXX,XX @@ nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
53
* the "I/O or GS" API.
54
*/
55
56
-int generated_co_wrapper
57
+int co_wrapper_mixed
58
bdrv_common_block_status_above(BlockDriverState *bs,
59
BlockDriverState *base,
60
bool include_base,
61
@@ -XXX,XX +XXX,XX @@ bdrv_common_block_status_above(BlockDriverState *bs,
62
int64_t *map,
63
BlockDriverState **file,
64
int *depth);
65
-int generated_co_wrapper
66
+int co_wrapper_mixed
67
nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp);
68
69
#endif /* BLOCK_COROUTINES_H */
70
diff --git a/include/block/block-common.h b/include/block/block-common.h
71
index XXXXXXX..XXXXXXX 100644
72
--- a/include/block/block-common.h
73
+++ b/include/block/block-common.h
74
@@ -XXX,XX +XXX,XX @@
75
#include "qemu/transactions.h"
76
77
/*
78
- * generated_co_wrapper
79
+ * co_wrapper{*}: Function specifiers used by block-coroutine-wrapper.py
80
*
81
- * Function specifier, which does nothing but mark functions to be
82
+ * Function specifiers, which do nothing but mark functions to be
83
* generated by scripts/block-coroutine-wrapper.py
84
*
85
- * Read more in docs/devel/block-coroutine-wrapper.rst
86
+ * Usage: read docs/devel/block-coroutine-wrapper.rst
87
+ *
88
+ * co_wrapper_mixed functions can be called by both coroutine and
89
+ * non-coroutine context.
90
*/
91
-#define generated_co_wrapper
92
+#define co_wrapper_mixed
93
94
/* block.c */
95
typedef struct BlockDriver BlockDriver;
96
diff --git a/include/block/block-io.h b/include/block/block-io.h
97
index XXXXXXX..XXXXXXX 100644
98
--- a/include/block/block-io.h
99
+++ b/include/block/block-io.h
100
@@ -XXX,XX +XXX,XX @@
101
* to catch when they are accidentally called by the wrong API.
102
*/
103
104
-int generated_co_wrapper bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
105
- int64_t bytes,
106
- BdrvRequestFlags flags);
107
+int co_wrapper_mixed bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
108
+ int64_t bytes,
109
+ BdrvRequestFlags flags);
110
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags);
111
-int generated_co_wrapper bdrv_pread(BdrvChild *child, int64_t offset,
112
- int64_t bytes, void *buf,
113
- BdrvRequestFlags flags);
114
-int generated_co_wrapper bdrv_pwrite(BdrvChild *child, int64_t offset,
115
- int64_t bytes, const void *buf,
116
- BdrvRequestFlags flags);
117
-int generated_co_wrapper bdrv_pwrite_sync(BdrvChild *child, int64_t offset,
118
- int64_t bytes, const void *buf,
119
- BdrvRequestFlags flags);
120
+int co_wrapper_mixed bdrv_pread(BdrvChild *child, int64_t offset,
121
+ int64_t bytes, void *buf,
122
+ BdrvRequestFlags flags);
123
+int co_wrapper_mixed bdrv_pwrite(BdrvChild *child, int64_t offset,
124
+ int64_t bytes, const void *buf,
125
+ BdrvRequestFlags flags);
126
+int co_wrapper_mixed bdrv_pwrite_sync(BdrvChild *child, int64_t offset,
127
+ int64_t bytes, const void *buf,
128
+ BdrvRequestFlags flags);
129
int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset,
130
int64_t bytes, const void *buf,
131
BdrvRequestFlags flags);
132
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset,
133
134
void bdrv_drain(BlockDriverState *bs);
135
136
-int generated_co_wrapper
137
+int co_wrapper_mixed
138
bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
139
PreallocMode prealloc, BdrvRequestFlags flags, Error **errp);
140
141
-int generated_co_wrapper bdrv_check(BlockDriverState *bs, BdrvCheckResult *res,
142
- BdrvCheckMode fix);
143
+int co_wrapper_mixed bdrv_check(BlockDriverState *bs, BdrvCheckResult *res,
144
+ BdrvCheckMode fix);
145
146
/* Invalidate any cached metadata used by image formats */
147
-int generated_co_wrapper bdrv_invalidate_cache(BlockDriverState *bs,
148
- Error **errp);
149
-int generated_co_wrapper bdrv_flush(BlockDriverState *bs);
150
-int generated_co_wrapper bdrv_pdiscard(BdrvChild *child, int64_t offset,
151
- int64_t bytes);
152
-int generated_co_wrapper
153
+int co_wrapper_mixed bdrv_invalidate_cache(BlockDriverState *bs,
154
+ Error **errp);
155
+int co_wrapper_mixed bdrv_flush(BlockDriverState *bs);
156
+int co_wrapper_mixed bdrv_pdiscard(BdrvChild *child, int64_t offset,
157
+ int64_t bytes);
158
+int co_wrapper_mixed
159
bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
160
-int generated_co_wrapper
161
+int co_wrapper_mixed
162
bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
163
164
/**
165
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
166
index XXXXXXX..XXXXXXX 100644
167
--- a/include/sysemu/block-backend-io.h
168
+++ b/include/sysemu/block-backend-io.h
169
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk,
170
* the "I/O or GS" API.
171
*/
172
173
-int generated_co_wrapper blk_pread(BlockBackend *blk, int64_t offset,
174
- int64_t bytes, void *buf,
175
- BdrvRequestFlags flags);
176
+int co_wrapper_mixed blk_pread(BlockBackend *blk, int64_t offset,
177
+ int64_t bytes, void *buf,
178
+ BdrvRequestFlags flags);
179
int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes,
180
void *buf, BdrvRequestFlags flags);
181
182
-int generated_co_wrapper blk_preadv(BlockBackend *blk, int64_t offset,
183
- int64_t bytes, QEMUIOVector *qiov,
184
- BdrvRequestFlags flags);
185
+int co_wrapper_mixed blk_preadv(BlockBackend *blk, int64_t offset,
186
+ int64_t bytes, QEMUIOVector *qiov,
187
+ BdrvRequestFlags flags);
188
int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
189
int64_t bytes, QEMUIOVector *qiov,
190
BdrvRequestFlags flags);
191
192
-int generated_co_wrapper blk_preadv_part(BlockBackend *blk, int64_t offset,
193
- int64_t bytes, QEMUIOVector *qiov,
194
- size_t qiov_offset,
195
- BdrvRequestFlags flags);
196
+int co_wrapper_mixed blk_preadv_part(BlockBackend *blk, int64_t offset,
197
+ int64_t bytes, QEMUIOVector *qiov,
198
+ size_t qiov_offset,
199
+ BdrvRequestFlags flags);
200
int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset,
201
int64_t bytes, QEMUIOVector *qiov,
202
size_t qiov_offset, BdrvRequestFlags flags);
203
204
-int generated_co_wrapper blk_pwrite(BlockBackend *blk, int64_t offset,
205
- int64_t bytes, const void *buf,
206
- BdrvRequestFlags flags);
207
+int co_wrapper_mixed blk_pwrite(BlockBackend *blk, int64_t offset,
208
+ int64_t bytes, const void *buf,
209
+ BdrvRequestFlags flags);
210
int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes,
211
const void *buf, BdrvRequestFlags flags);
212
213
-int generated_co_wrapper blk_pwritev(BlockBackend *blk, int64_t offset,
214
- int64_t bytes, QEMUIOVector *qiov,
215
- BdrvRequestFlags flags);
216
+int co_wrapper_mixed blk_pwritev(BlockBackend *blk, int64_t offset,
217
+ int64_t bytes, QEMUIOVector *qiov,
218
+ BdrvRequestFlags flags);
219
int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
220
int64_t bytes, QEMUIOVector *qiov,
221
BdrvRequestFlags flags);
222
223
-int generated_co_wrapper blk_pwritev_part(BlockBackend *blk, int64_t offset,
224
- int64_t bytes, QEMUIOVector *qiov,
225
- size_t qiov_offset,
226
- BdrvRequestFlags flags);
227
+int co_wrapper_mixed blk_pwritev_part(BlockBackend *blk, int64_t offset,
228
+ int64_t bytes, QEMUIOVector *qiov,
229
+ size_t qiov_offset,
230
+ BdrvRequestFlags flags);
231
int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset,
232
int64_t bytes,
233
QEMUIOVector *qiov, size_t qiov_offset,
234
BdrvRequestFlags flags);
235
236
-int generated_co_wrapper blk_pwrite_compressed(BlockBackend *blk,
237
- int64_t offset, int64_t bytes,
238
- const void *buf);
239
+int co_wrapper_mixed blk_pwrite_compressed(BlockBackend *blk,
240
+ int64_t offset, int64_t bytes,
241
+ const void *buf);
242
int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset,
243
int64_t bytes, const void *buf);
244
245
-int generated_co_wrapper blk_pwrite_zeroes(BlockBackend *blk, int64_t offset,
246
- int64_t bytes,
247
- BdrvRequestFlags flags);
248
+int co_wrapper_mixed blk_pwrite_zeroes(BlockBackend *blk, int64_t offset,
249
+ int64_t bytes,
250
+ BdrvRequestFlags flags);
251
int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
252
int64_t bytes, BdrvRequestFlags flags);
253
254
-int generated_co_wrapper blk_pdiscard(BlockBackend *blk, int64_t offset,
255
- int64_t bytes);
256
+int co_wrapper_mixed blk_pdiscard(BlockBackend *blk, int64_t offset,
257
+ int64_t bytes);
258
int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset,
259
int64_t bytes);
260
261
-int generated_co_wrapper blk_flush(BlockBackend *blk);
262
+int co_wrapper_mixed blk_flush(BlockBackend *blk);
263
int coroutine_fn blk_co_flush(BlockBackend *blk);
264
265
-int generated_co_wrapper blk_ioctl(BlockBackend *blk, unsigned long int req,
266
- void *buf);
267
+int co_wrapper_mixed blk_ioctl(BlockBackend *blk, unsigned long int req,
268
+ void *buf);
269
int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req,
270
void *buf);
271
272
-int generated_co_wrapper blk_truncate(BlockBackend *blk, int64_t offset,
273
- bool exact, PreallocMode prealloc,
274
- BdrvRequestFlags flags, Error **errp);
275
+int co_wrapper_mixed blk_truncate(BlockBackend *blk, int64_t offset,
276
+ bool exact, PreallocMode prealloc,
277
+ BdrvRequestFlags flags, Error **errp);
278
int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact,
279
PreallocMode prealloc, BdrvRequestFlags flags,
280
Error **errp);
281
diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py
282
index XXXXXXX..XXXXXXX 100644
283
--- a/scripts/block-coroutine-wrapper.py
284
+++ b/scripts/block-coroutine-wrapper.py
285
@@ -XXX,XX +XXX,XX @@
286
"""Generate coroutine wrappers for block subsystem.
287
288
The program parses one or several concatenated c files from stdin,
289
-searches for functions with the 'generated_co_wrapper' specifier
290
+searches for functions with the 'co_wrapper_mixed' specifier
291
and generates corresponding wrappers on stdout.
292
293
Usage: block-coroutine-wrapper.py generated-file.c FILE.[ch]...
294
@@ -XXX,XX +XXX,XX @@ def gen_block(self, format: str) -> str:
295
return '\n'.join(format.format_map(arg.__dict__) for arg in self.args)
296
297
298
-# Match wrappers declared with a generated_co_wrapper mark
299
-func_decl_re = re.compile(r'^int\s*generated_co_wrapper\s*'
300
+# Match wrappers declared with a co_wrapper_mixed mark
301
+func_decl_re = re.compile(r'^int\s*co_wrapper_mixed\s*'
302
r'(?P<wrapper_name>[a-z][a-z0-9_]*)'
303
r'\((?P<args>[^)]*)\);$', re.MULTILINE)
304
305
--
306
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
1
3
This new annotation starts just a function wrapper that creates
4
a new coroutine. It assumes the caller is not a coroutine.
5
It will be the default annotation to be used in the future.
6
7
This is much better as c_w_mixed, because it is clear if the caller
8
is a coroutine or not, and provides the advantage of automating
9
the code creation. In the future all c_w_mixed functions will be
10
substituted by co_wrapper.
11
12
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
14
Message-Id: <20221128142337.657646-11-eesposit@redhat.com>
15
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
docs/devel/block-coroutine-wrapper.rst | 6 +-
19
include/block/block-common.h | 8 +-
20
scripts/block-coroutine-wrapper.py | 110 +++++++++++++++++--------
21
3 files changed, 86 insertions(+), 38 deletions(-)
22
23
diff --git a/docs/devel/block-coroutine-wrapper.rst b/docs/devel/block-coroutine-wrapper.rst
24
index XXXXXXX..XXXXXXX 100644
25
--- a/docs/devel/block-coroutine-wrapper.rst
26
+++ b/docs/devel/block-coroutine-wrapper.rst
27
@@ -XXX,XX +XXX,XX @@ called ``bdrv_foo(<same args>)``. In this case the script can help. To
28
trigger the generation:
29
30
1. You need ``bdrv_foo`` declaration somewhere (for example, in
31
- ``block/coroutines.h``) with the ``co_wrapper_mixed`` mark,
32
+ ``block/coroutines.h``) with the ``co_wrapper`` mark,
33
like this:
34
35
.. code-block:: c
36
37
- int co_wrapper_mixed bdrv_foo(<some args>);
38
+ int co_wrapper bdrv_foo(<some args>);
39
40
2. You need to feed this declaration to block-coroutine-wrapper script.
41
For this, add the .h (or .c) file with the declaration to the
42
@@ -XXX,XX +XXX,XX @@ Links
43
44
1. The script location is ``scripts/block-coroutine-wrapper.py``.
45
46
-2. Generic place for private ``co_wrapper_mixed`` declarations is
47
+2. Generic place for private ``co_wrapper`` declarations is
48
``block/coroutines.h``, for public declarations:
49
``include/block/block.h``
50
51
diff --git a/include/block/block-common.h b/include/block/block-common.h
52
index XXXXXXX..XXXXXXX 100644
53
--- a/include/block/block-common.h
54
+++ b/include/block/block-common.h
55
@@ -XXX,XX +XXX,XX @@
56
*
57
* Usage: read docs/devel/block-coroutine-wrapper.rst
58
*
59
- * co_wrapper_mixed functions can be called by both coroutine and
60
- * non-coroutine context.
61
+ * There are 2 kind of specifiers:
62
+ * - co_wrapper functions can be called by only non-coroutine context, because
63
+ * they always generate a new coroutine.
64
+ * - co_wrapper_mixed functions can be called by both coroutine and
65
+ * non-coroutine context.
66
*/
67
+#define co_wrapper
68
#define co_wrapper_mixed
69
70
/* block.c */
71
diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py
72
index XXXXXXX..XXXXXXX 100644
73
--- a/scripts/block-coroutine-wrapper.py
74
+++ b/scripts/block-coroutine-wrapper.py
75
@@ -XXX,XX +XXX,XX @@
76
"""Generate coroutine wrappers for block subsystem.
77
78
The program parses one or several concatenated c files from stdin,
79
-searches for functions with the 'co_wrapper_mixed' specifier
80
+searches for functions with the 'co_wrapper' specifier
81
and generates corresponding wrappers on stdout.
82
83
Usage: block-coroutine-wrapper.py generated-file.c FILE.[ch]...
84
@@ -XXX,XX +XXX,XX @@ def __init__(self, param_decl: str) -> None:
85
86
87
class FuncDecl:
88
- def __init__(self, return_type: str, name: str, args: str) -> None:
89
+ def __init__(self, return_type: str, name: str, args: str,
90
+ variant: str) -> None:
91
self.return_type = return_type.strip()
92
self.name = name.strip()
93
+ self.struct_name = snake_to_camel(self.name)
94
self.args = [ParamDecl(arg.strip()) for arg in args.split(',')]
95
+ self.create_only_co = 'mixed' not in variant
96
+
97
+ subsystem, subname = self.name.split('_', 1)
98
+ self.co_name = f'{subsystem}_co_{subname}'
99
+
100
+ t = self.args[0].type
101
+ if t == 'BlockDriverState *':
102
+ bs = 'bs'
103
+ elif t == 'BdrvChild *':
104
+ bs = 'child->bs'
105
+ else:
106
+ bs = 'blk_bs(blk)'
107
+ self.bs = bs
108
109
def gen_list(self, format: str) -> str:
110
return ', '.join(format.format_map(arg.__dict__) for arg in self.args)
111
@@ -XXX,XX +XXX,XX @@ def gen_block(self, format: str) -> str:
112
return '\n'.join(format.format_map(arg.__dict__) for arg in self.args)
113
114
115
-# Match wrappers declared with a co_wrapper_mixed mark
116
-func_decl_re = re.compile(r'^int\s*co_wrapper_mixed\s*'
117
+# Match wrappers declared with a co_wrapper mark
118
+func_decl_re = re.compile(r'^int\s*co_wrapper'
119
+ r'(?P<variant>(_[a-z][a-z0-9_]*)?)\s*'
120
r'(?P<wrapper_name>[a-z][a-z0-9_]*)'
121
r'\((?P<args>[^)]*)\);$', re.MULTILINE)
122
123
@@ -XXX,XX +XXX,XX @@ def func_decl_iter(text: str) -> Iterator:
124
for m in func_decl_re.finditer(text):
125
yield FuncDecl(return_type='int',
126
name=m.group('wrapper_name'),
127
- args=m.group('args'))
128
+ args=m.group('args'),
129
+ variant=m.group('variant'))
130
131
132
def snake_to_camel(func_name: str) -> str:
133
@@ -XXX,XX +XXX,XX @@ def snake_to_camel(func_name: str) -> str:
134
return ''.join(words)
135
136
137
+def create_mixed_wrapper(func: FuncDecl) -> str:
138
+ """
139
+ Checks if we are already in coroutine
140
+ """
141
+ name = func.co_name
142
+ struct_name = func.struct_name
143
+ return f"""\
144
+int {func.name}({ func.gen_list('{decl}') })
145
+{{
146
+ if (qemu_in_coroutine()) {{
147
+ return {name}({ func.gen_list('{name}') });
148
+ }} else {{
149
+ {struct_name} s = {{
150
+ .poll_state.bs = {func.bs},
151
+ .poll_state.in_progress = true,
152
+
153
+{ func.gen_block(' .{name} = {name},') }
154
+ }};
155
+
156
+ s.poll_state.co = qemu_coroutine_create({name}_entry, &s);
157
+
158
+ return bdrv_poll_co(&s.poll_state);
159
+ }}
160
+}}"""
161
+
162
+
163
+def create_co_wrapper(func: FuncDecl) -> str:
164
+ """
165
+ Assumes we are not in coroutine, and creates one
166
+ """
167
+ name = func.co_name
168
+ struct_name = func.struct_name
169
+ return f"""\
170
+int {func.name}({ func.gen_list('{decl}') })
171
+{{
172
+ {struct_name} s = {{
173
+ .poll_state.bs = {func.bs},
174
+ .poll_state.in_progress = true,
175
+
176
+{ func.gen_block(' .{name} = {name},') }
177
+ }};
178
+ assert(!qemu_in_coroutine());
179
+
180
+ s.poll_state.co = qemu_coroutine_create({name}_entry, &s);
181
+
182
+ return bdrv_poll_co(&s.poll_state);
183
+}}"""
184
+
185
+
186
def gen_wrapper(func: FuncDecl) -> str:
187
assert not '_co_' in func.name
188
assert func.return_type == 'int'
189
assert func.args[0].type in ['BlockDriverState *', 'BdrvChild *',
190
'BlockBackend *']
191
192
- subsystem, subname = func.name.split('_', 1)
193
-
194
- name = f'{subsystem}_co_{subname}'
195
+ name = func.co_name
196
+ struct_name = func.struct_name
197
198
- t = func.args[0].type
199
- if t == 'BlockDriverState *':
200
- bs = 'bs'
201
- elif t == 'BdrvChild *':
202
- bs = 'child->bs'
203
- else:
204
- bs = 'blk_bs(blk)'
205
- struct_name = snake_to_camel(name)
206
+ creation_function = create_mixed_wrapper
207
+ if func.create_only_co:
208
+ creation_function = create_co_wrapper
209
210
return f"""\
211
/*
212
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
213
aio_wait_kick();
214
}}
215
216
-int {func.name}({ func.gen_list('{decl}') })
217
-{{
218
- if (qemu_in_coroutine()) {{
219
- return {name}({ func.gen_list('{name}') });
220
- }} else {{
221
- {struct_name} s = {{
222
- .poll_state.bs = {bs},
223
- .poll_state.in_progress = true,
224
-
225
-{ func.gen_block(' .{name} = {name},') }
226
- }};
227
-
228
- s.poll_state.co = qemu_coroutine_create({name}_entry, &s);
229
-
230
- return bdrv_poll_co(&s.poll_state);
231
- }}
232
-}}"""
233
+{creation_function(func)}"""
234
235
236
def gen_wrappers(input_code: str) -> str:
237
--
238
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
1
3
Right now, we take the first parameter of the function to get the
4
BlockDriverState to pass to bdrv_poll_co(), that internally calls
5
functions that figure in which aiocontext the coroutine should run.
6
7
However, it is useless to pass a bs just to get its own AioContext,
8
so instead pass it directly, and default to the main loop if no
9
BlockDriverState is passed as parameter.
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
13
Message-Id: <20221128142337.657646-12-eesposit@redhat.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
17
block/block-gen.h | 6 +++---
18
scripts/block-coroutine-wrapper.py | 16 ++++++++--------
19
2 files changed, 11 insertions(+), 11 deletions(-)
20
21
diff --git a/block/block-gen.h b/block/block-gen.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/block/block-gen.h
24
+++ b/block/block-gen.h
25
@@ -XXX,XX +XXX,XX @@
26
27
/* Base structure for argument packing structures */
28
typedef struct BdrvPollCo {
29
- BlockDriverState *bs;
30
+ AioContext *ctx;
31
bool in_progress;
32
int ret;
33
Coroutine *co; /* Keep pointer here for debugging */
34
@@ -XXX,XX +XXX,XX @@ static inline int bdrv_poll_co(BdrvPollCo *s)
35
{
36
assert(!qemu_in_coroutine());
37
38
- bdrv_coroutine_enter(s->bs, s->co);
39
- BDRV_POLL_WHILE(s->bs, s->in_progress);
40
+ aio_co_enter(s->ctx, s->co);
41
+ AIO_WAIT_WHILE(s->ctx, s->in_progress);
42
43
return s->ret;
44
}
45
diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py
46
index XXXXXXX..XXXXXXX 100644
47
--- a/scripts/block-coroutine-wrapper.py
48
+++ b/scripts/block-coroutine-wrapper.py
49
@@ -XXX,XX +XXX,XX @@ def __init__(self, return_type: str, name: str, args: str,
50
51
t = self.args[0].type
52
if t == 'BlockDriverState *':
53
- bs = 'bs'
54
+ ctx = 'bdrv_get_aio_context(bs)'
55
elif t == 'BdrvChild *':
56
- bs = 'child->bs'
57
+ ctx = 'bdrv_get_aio_context(child->bs)'
58
+ elif t == 'BlockBackend *':
59
+ ctx = 'blk_get_aio_context(blk)'
60
else:
61
- bs = 'blk_bs(blk)'
62
- self.bs = bs
63
+ ctx = 'qemu_get_aio_context()'
64
+ self.ctx = ctx
65
66
def gen_list(self, format: str) -> str:
67
return ', '.join(format.format_map(arg.__dict__) for arg in self.args)
68
@@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str:
69
return {name}({ func.gen_list('{name}') });
70
}} else {{
71
{struct_name} s = {{
72
- .poll_state.bs = {func.bs},
73
+ .poll_state.ctx = {func.ctx},
74
.poll_state.in_progress = true,
75
76
{ func.gen_block(' .{name} = {name},') }
77
@@ -XXX,XX +XXX,XX @@ def create_co_wrapper(func: FuncDecl) -> str:
78
int {func.name}({ func.gen_list('{decl}') })
79
{{
80
{struct_name} s = {{
81
- .poll_state.bs = {func.bs},
82
+ .poll_state.ctx = {func.ctx},
83
.poll_state.in_progress = true,
84
85
{ func.gen_block(' .{name} = {name},') }
86
@@ -XXX,XX +XXX,XX @@ def create_co_wrapper(func: FuncDecl) -> str:
87
def gen_wrapper(func: FuncDecl) -> str:
88
assert not '_co_' in func.name
89
assert func.return_type == 'int'
90
- assert func.args[0].type in ['BlockDriverState *', 'BdrvChild *',
91
- 'BlockBackend *']
92
93
name = func.co_name
94
struct_name = func.struct_name
95
--
96
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
1
3
Extend the regex to cover also return type, pointers included.
4
This implies that the value returned by the function cannot be
5
a simple "int" anymore, but the custom return type.
6
Therefore remove poll_state->ret and instead use a per-function
7
custom "ret" field.
8
9
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
12
Message-Id: <20221128142337.657646-13-eesposit@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
block/block-gen.h | 5 +----
16
scripts/block-coroutine-wrapper.py | 19 +++++++++++--------
17
2 files changed, 12 insertions(+), 12 deletions(-)
18
19
diff --git a/block/block-gen.h b/block/block-gen.h
20
index XXXXXXX..XXXXXXX 100644
21
--- a/block/block-gen.h
22
+++ b/block/block-gen.h
23
@@ -XXX,XX +XXX,XX @@
24
typedef struct BdrvPollCo {
25
AioContext *ctx;
26
bool in_progress;
27
- int ret;
28
Coroutine *co; /* Keep pointer here for debugging */
29
} BdrvPollCo;
30
31
-static inline int bdrv_poll_co(BdrvPollCo *s)
32
+static inline void bdrv_poll_co(BdrvPollCo *s)
33
{
34
assert(!qemu_in_coroutine());
35
36
aio_co_enter(s->ctx, s->co);
37
AIO_WAIT_WHILE(s->ctx, s->in_progress);
38
-
39
- return s->ret;
40
}
41
42
#endif /* BLOCK_BLOCK_GEN_H */
43
diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py
44
index XXXXXXX..XXXXXXX 100644
45
--- a/scripts/block-coroutine-wrapper.py
46
+++ b/scripts/block-coroutine-wrapper.py
47
@@ -XXX,XX +XXX,XX @@ def gen_block(self, format: str) -> str:
48
49
50
# Match wrappers declared with a co_wrapper mark
51
-func_decl_re = re.compile(r'^int\s*co_wrapper'
52
+func_decl_re = re.compile(r'^(?P<return_type>[a-zA-Z][a-zA-Z0-9_]* [\*]?)'
53
+ r'\s*co_wrapper'
54
r'(?P<variant>(_[a-z][a-z0-9_]*)?)\s*'
55
r'(?P<wrapper_name>[a-z][a-z0-9_]*)'
56
r'\((?P<args>[^)]*)\);$', re.MULTILINE)
57
@@ -XXX,XX +XXX,XX @@ def gen_block(self, format: str) -> str:
58
59
def func_decl_iter(text: str) -> Iterator:
60
for m in func_decl_re.finditer(text):
61
- yield FuncDecl(return_type='int',
62
+ yield FuncDecl(return_type=m.group('return_type'),
63
name=m.group('wrapper_name'),
64
args=m.group('args'),
65
variant=m.group('variant'))
66
@@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str:
67
name = func.co_name
68
struct_name = func.struct_name
69
return f"""\
70
-int {func.name}({ func.gen_list('{decl}') })
71
+{func.return_type} {func.name}({ func.gen_list('{decl}') })
72
{{
73
if (qemu_in_coroutine()) {{
74
return {name}({ func.gen_list('{name}') });
75
@@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str:
76
77
s.poll_state.co = qemu_coroutine_create({name}_entry, &s);
78
79
- return bdrv_poll_co(&s.poll_state);
80
+ bdrv_poll_co(&s.poll_state);
81
+ return s.ret;
82
}}
83
}}"""
84
85
@@ -XXX,XX +XXX,XX @@ def create_co_wrapper(func: FuncDecl) -> str:
86
name = func.co_name
87
struct_name = func.struct_name
88
return f"""\
89
-int {func.name}({ func.gen_list('{decl}') })
90
+{func.return_type} {func.name}({ func.gen_list('{decl}') })
91
{{
92
{struct_name} s = {{
93
.poll_state.ctx = {func.ctx},
94
@@ -XXX,XX +XXX,XX @@ def create_co_wrapper(func: FuncDecl) -> str:
95
96
s.poll_state.co = qemu_coroutine_create({name}_entry, &s);
97
98
- return bdrv_poll_co(&s.poll_state);
99
+ bdrv_poll_co(&s.poll_state);
100
+ return s.ret;
101
}}"""
102
103
104
def gen_wrapper(func: FuncDecl) -> str:
105
assert not '_co_' in func.name
106
- assert func.return_type == 'int'
107
108
name = func.co_name
109
struct_name = func.struct_name
110
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
111
112
typedef struct {struct_name} {{
113
BdrvPollCo poll_state;
114
+ {func.return_type} ret;
115
{ func.gen_block(' {decl};') }
116
}} {struct_name};
117
118
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
119
{{
120
{struct_name} *s = opaque;
121
122
- s->poll_state.ret = {name}({ func.gen_list('s->{name}') });
123
+ s->ret = {name}({ func.gen_list('s->{name}') });
124
s->poll_state.in_progress = false;
125
126
aio_wait_kick();
127
--
128
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
1
3
This function is never called in coroutine context, therefore
4
instead of manually creating a new coroutine, delegate it to the
5
block-coroutine-wrapper script, defining it as co_wrapper.
6
7
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
10
Message-Id: <20221128142337.657646-14-eesposit@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
include/block/block-global-state.h | 8 ++++--
14
block.c | 41 ++----------------------------
15
2 files changed, 8 insertions(+), 41 deletions(-)
16
17
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/block-global-state.h
20
+++ b/include/block/block-global-state.h
21
@@ -XXX,XX +XXX,XX @@ BlockDriver *bdrv_find_protocol(const char *filename,
22
bool allow_protocol_prefix,
23
Error **errp);
24
BlockDriver *bdrv_find_format(const char *format_name);
25
-int bdrv_create(BlockDriver *drv, const char* filename,
26
- QemuOpts *opts, Error **errp);
27
+
28
+int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
29
+ QemuOpts *opts, Error **errp);
30
+int co_wrapper bdrv_create(BlockDriver *drv, const char *filename,
31
+ QemuOpts *opts, Error **errp);
32
+
33
int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
34
Error **errp);
35
36
diff --git a/block.c b/block.c
37
index XXXXXXX..XXXXXXX 100644
38
--- a/block.c
39
+++ b/block.c
40
@@ -XXX,XX +XXX,XX @@ typedef struct CreateCo {
41
Error *err;
42
} CreateCo;
43
44
-static int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
45
- QemuOpts *opts, Error **errp)
46
+int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
47
+ QemuOpts *opts, Error **errp)
48
{
49
int ret;
50
GLOBAL_STATE_CODE();
51
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
52
return ret;
53
}
54
55
-static void coroutine_fn bdrv_create_co_entry(void *opaque)
56
-{
57
- CreateCo *cco = opaque;
58
- GLOBAL_STATE_CODE();
59
-
60
- cco->ret = bdrv_co_create(cco->drv, cco->filename, cco->opts, &cco->err);
61
- aio_wait_kick();
62
-}
63
-
64
-int bdrv_create(BlockDriver *drv, const char* filename,
65
- QemuOpts *opts, Error **errp)
66
-{
67
- GLOBAL_STATE_CODE();
68
-
69
- if (qemu_in_coroutine()) {
70
- /* Fast-path if already in coroutine context */
71
- return bdrv_co_create(drv, filename, opts, errp);
72
- } else {
73
- Coroutine *co;
74
- CreateCo cco = {
75
- .drv = drv,
76
- .filename = filename,
77
- .opts = opts,
78
- .ret = NOT_DONE,
79
- .err = NULL,
80
- };
81
-
82
- co = qemu_coroutine_create(bdrv_create_co_entry, &cco);
83
- qemu_coroutine_enter(co);
84
- while (cco.ret == NOT_DONE) {
85
- aio_poll(qemu_get_aio_context(), true);
86
- }
87
- error_propagate(errp, cco.err);
88
- return cco.ret;
89
- }
90
-}
91
-
92
/**
93
* Helper function for bdrv_create_file_fallback(): Resize @blk to at
94
* least the given @minimum_size.
95
--
96
2.38.1
diff view generated by jsdifflib
Deleted patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
1
3
bdrv_can_store_new_dirty_bitmap and bdrv_remove_persistent_dirty_bitmap
4
check if they are running in a coroutine, directly calling the
5
coroutine callback if it's the case.
6
Except that no coroutine calls such functions, therefore that check
7
can be removed, and function creation can be offloaded to
8
c_w.
9
10
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
13
Message-Id: <20221128142337.657646-15-eesposit@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
16
include/block/block-common.h | 5 +-
17
include/block/block-io.h | 10 +++-
18
include/block/dirty-bitmap.h | 10 +++-
19
block/dirty-bitmap.c | 88 +-----------------------------------
20
block/meson.build | 1 +
21
5 files changed, 22 insertions(+), 92 deletions(-)
22
23
diff --git a/include/block/block-common.h b/include/block/block-common.h
24
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/block-common.h
26
+++ b/include/block/block-common.h
27
@@ -XXX,XX +XXX,XX @@
28
#include "qemu/iov.h"
29
#include "qemu/coroutine.h"
30
#include "block/accounting.h"
31
-#include "block/dirty-bitmap.h"
32
-#include "block/blockjob.h"
33
#include "qemu/hbitmap.h"
34
#include "qemu/transactions.h"
35
36
@@ -XXX,XX +XXX,XX @@
37
#define co_wrapper
38
#define co_wrapper_mixed
39
40
+#include "block/dirty-bitmap.h"
41
+#include "block/blockjob.h"
42
+
43
/* block.c */
44
typedef struct BlockDriver BlockDriver;
45
typedef struct BdrvChild BdrvChild;
46
diff --git a/include/block/block-io.h b/include/block/block-io.h
47
index XXXXXXX..XXXXXXX 100644
48
--- a/include/block/block-io.h
49
+++ b/include/block/block-io.h
50
@@ -XXX,XX +XXX,XX @@ AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c);
51
void bdrv_io_plug(BlockDriverState *bs);
52
void bdrv_io_unplug(BlockDriverState *bs);
53
54
-bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
55
- uint32_t granularity, Error **errp);
56
+bool coroutine_fn bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
57
+ const char *name,
58
+ uint32_t granularity,
59
+ Error **errp);
60
+bool co_wrapper bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs,
61
+ const char *name,
62
+ uint32_t granularity,
63
+ Error **errp);
64
65
/**
66
*
67
diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
68
index XXXXXXX..XXXXXXX 100644
69
--- a/include/block/dirty-bitmap.h
70
+++ b/include/block/dirty-bitmap.h
71
@@ -XXX,XX +XXX,XX @@ int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
72
Error **errp);
73
void bdrv_release_dirty_bitmap(BdrvDirtyBitmap *bitmap);
74
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs);
75
-int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
76
- Error **errp);
77
+
78
+int coroutine_fn bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs,
79
+ const char *name,
80
+ Error **errp);
81
+int co_wrapper bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
82
+ const char *name,
83
+ Error **errp);
84
+
85
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
86
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
87
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap);
88
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
89
index XXXXXXX..XXXXXXX 100644
90
--- a/block/dirty-bitmap.c
91
+++ b/block/dirty-bitmap.c
92
@@ -XXX,XX +XXX,XX @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
93
* not fail.
94
* This function doesn't release corresponding BdrvDirtyBitmap.
95
*/
96
-static int coroutine_fn
97
+int coroutine_fn
98
bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
99
Error **errp)
100
{
101
@@ -XXX,XX +XXX,XX @@ bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
102
return 0;
103
}
104
105
-typedef struct BdrvRemovePersistentDirtyBitmapCo {
106
- BlockDriverState *bs;
107
- const char *name;
108
- Error **errp;
109
- int ret;
110
-} BdrvRemovePersistentDirtyBitmapCo;
111
-
112
-static void coroutine_fn
113
-bdrv_co_remove_persistent_dirty_bitmap_entry(void *opaque)
114
-{
115
- BdrvRemovePersistentDirtyBitmapCo *s = opaque;
116
-
117
- s->ret = bdrv_co_remove_persistent_dirty_bitmap(s->bs, s->name, s->errp);
118
- aio_wait_kick();
119
-}
120
-
121
-int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
122
- Error **errp)
123
-{
124
- if (qemu_in_coroutine()) {
125
- return bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp);
126
- } else {
127
- Coroutine *co;
128
- BdrvRemovePersistentDirtyBitmapCo s = {
129
- .bs = bs,
130
- .name = name,
131
- .errp = errp,
132
- .ret = -EINPROGRESS,
133
- };
134
-
135
- co = qemu_coroutine_create(bdrv_co_remove_persistent_dirty_bitmap_entry,
136
- &s);
137
- bdrv_coroutine_enter(bs, co);
138
- BDRV_POLL_WHILE(bs, s.ret == -EINPROGRESS);
139
-
140
- return s.ret;
141
- }
142
-}
143
-
144
bool
145
bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs)
146
{
147
@@ -XXX,XX +XXX,XX @@ bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs)
148
return false;
149
}
150
151
-static bool coroutine_fn
152
+bool coroutine_fn
153
bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
154
uint32_t granularity, Error **errp)
155
{
156
@@ -XXX,XX +XXX,XX @@ bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
157
return drv->bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp);
158
}
159
160
-typedef struct BdrvCanStoreNewDirtyBitmapCo {
161
- BlockDriverState *bs;
162
- const char *name;
163
- uint32_t granularity;
164
- Error **errp;
165
- bool ret;
166
-
167
- bool in_progress;
168
-} BdrvCanStoreNewDirtyBitmapCo;
169
-
170
-static void coroutine_fn bdrv_co_can_store_new_dirty_bitmap_entry(void *opaque)
171
-{
172
- BdrvCanStoreNewDirtyBitmapCo *s = opaque;
173
-
174
- s->ret = bdrv_co_can_store_new_dirty_bitmap(s->bs, s->name, s->granularity,
175
- s->errp);
176
- s->in_progress = false;
177
- aio_wait_kick();
178
-}
179
-
180
-bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
181
- uint32_t granularity, Error **errp)
182
-{
183
- IO_CODE();
184
- if (qemu_in_coroutine()) {
185
- return bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp);
186
- } else {
187
- Coroutine *co;
188
- BdrvCanStoreNewDirtyBitmapCo s = {
189
- .bs = bs,
190
- .name = name,
191
- .granularity = granularity,
192
- .errp = errp,
193
- .in_progress = true,
194
- };
195
-
196
- co = qemu_coroutine_create(bdrv_co_can_store_new_dirty_bitmap_entry,
197
- &s);
198
- bdrv_coroutine_enter(bs, co);
199
- BDRV_POLL_WHILE(bs, s.in_progress);
200
-
201
- return s.ret;
202
- }
203
-}
204
-
205
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
206
{
207
bdrv_dirty_bitmaps_lock(bitmap->bs);
208
diff --git a/block/meson.build b/block/meson.build
209
index XXXXXXX..XXXXXXX 100644
210
--- a/block/meson.build
211
+++ b/block/meson.build
212
@@ -XXX,XX +XXX,XX @@ block_gen_c = custom_target('block-gen.c',
213
output: 'block-gen.c',
214
input: files(
215
'../include/block/block-io.h',
216
+ '../include/block/dirty-bitmap.h',
217
'../include/block/block-global-state.h',
218
'../include/sysemu/block-backend-io.h',
219
'coroutines.h'
220
--
221
2.38.1
diff view generated by jsdifflib
Deleted patch
1
This defines macros that allow clang to perform Thread Safety Analysis
2
based on function and variable annotations that specify the locking
3
rules. On non-clang compilers, the annotations are ignored.
4
1
5
Imported tsa.h from the original repository with the pthread_mutex_t
6
wrapper removed:
7
8
https://github.com/jhi/clang-thread-safety-analysis-for-c.git
9
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20221207131838.239125-6-kwolf@redhat.com>
12
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
include/qemu/clang-tsa.h | 101 +++++++++++++++++++++++++++++++++++++++
16
1 file changed, 101 insertions(+)
17
create mode 100644 include/qemu/clang-tsa.h
18
19
diff --git a/include/qemu/clang-tsa.h b/include/qemu/clang-tsa.h
20
new file mode 100644
21
index XXXXXXX..XXXXXXX
22
--- /dev/null
23
+++ b/include/qemu/clang-tsa.h
24
@@ -XXX,XX +XXX,XX @@
25
+#ifndef CLANG_TSA_H
26
+#define CLANG_TSA_H
27
+
28
+/*
29
+ * Copyright 2018 Jarkko Hietaniemi <jhi@iki.fi>
30
+ *
31
+ * Permission is hereby granted, free of charge, to any person obtaining
32
+ * a copy of this software and associated documentation files (the "Software"),
33
+ * to deal in the Software without restriction, including without
34
+ * limitation the rights to use, copy, modify, merge, publish,
35
+ * distribute, sublicense, and/or sell copies of the Software, and to
36
+ * permit persons to whom the Software is furnished to do so, subject to
37
+ * the following conditions:
38
+ *
39
+ * The above copyright notice and this permission notice shall be
40
+ * included in all copies or substantial portions of the Software.
41
+ *
42
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49
+ */
50
+
51
+/* http://clang.llvm.org/docs/ThreadSafetyAnalysis.html
52
+ *
53
+ * TSA is available since clang 3.6-ish.
54
+ */
55
+#ifdef __clang__
56
+# define TSA(x) __attribute__((x))
57
+#else
58
+# define TSA(x) /* No TSA, make TSA attributes no-ops. */
59
+#endif
60
+
61
+/* TSA_CAPABILITY() is used to annotate typedefs:
62
+ *
63
+ * typedef pthread_mutex_t TSA_CAPABILITY("mutex") tsa_mutex;
64
+ */
65
+#define TSA_CAPABILITY(x) TSA(capability(x))
66
+
67
+/* TSA_GUARDED_BY() is used to annotate global variables,
68
+ * the data is guarded:
69
+ *
70
+ * Foo foo TSA_GUARDED_BY(mutex);
71
+ */
72
+#define TSA_GUARDED_BY(x) TSA(guarded_by(x))
73
+
74
+/* TSA_PT_GUARDED_BY() is used to annotate global pointers, the data
75
+ * behind the pointer is guarded.
76
+ *
77
+ * Foo* ptr TSA_PT_GUARDED_BY(mutex);
78
+ */
79
+#define TSA_PT_GUARDED_BY(x) TSA(pt_guarded_by(x))
80
+
81
+/* The TSA_REQUIRES() is used to annotate functions: the caller of the
82
+ * function MUST hold the resource, the function will NOT release it.
83
+ *
84
+ * More than one mutex may be specified, comma-separated.
85
+ *
86
+ * void Foo(void) TSA_REQUIRES(mutex);
87
+ */
88
+#define TSA_REQUIRES(...) TSA(requires_capability(__VA_ARGS__))
89
+
90
+/* TSA_EXCLUDES() is used to annotate functions: the caller of the
91
+ * function MUST NOT hold resource, the function first acquires the
92
+ * resource, and then releases it.
93
+ *
94
+ * More than one mutex may be specified, comma-separated.
95
+ *
96
+ * void Foo(void) TSA_EXCLUDES(mutex);
97
+ */
98
+#define TSA_EXCLUDES(...) TSA(locks_excluded(__VA_ARGS__))
99
+
100
+/* TSA_ACQUIRE() is used to annotate functions: the caller of the
101
+ * function MUST NOT hold the resource, the function will acquire the
102
+ * resource, but NOT release it.
103
+ *
104
+ * More than one mutex may be specified, comma-separated.
105
+ *
106
+ * void Foo(void) TSA_ACQUIRE(mutex);
107
+ */
108
+#define TSA_ACQUIRE(...) TSA(acquire_capability(__VA_ARGS__))
109
+
110
+/* TSA_RELEASE() is used to annotate functions: the caller of the
111
+ * function MUST hold the resource, but the function will then release it.
112
+ *
113
+ * More than one mutex may be specified, comma-separated.
114
+ *
115
+ * void Foo(void) TSA_RELEASE(mutex);
116
+ */
117
+#define TSA_RELEASE(...) TSA(release_capability(__VA_ARGS__))
118
+
119
+/* TSA_NO_TSA is used to annotate functions. Use only when you need to.
120
+ *
121
+ * void Foo(void) TSA_NO_TSA;
122
+ */
123
+#define TSA_NO_TSA TSA(no_thread_safety_analysis)
124
+
125
+#endif /* #ifndef CLANG_TSA_H */
126
--
127
2.38.1
diff view generated by jsdifflib
Deleted patch
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Message-Id: <20221207131838.239125-7-kwolf@redhat.com>
3
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
6
include/qemu/clang-tsa.h | 9 +++++++++
7
1 file changed, 9 insertions(+)
8
1
9
diff --git a/include/qemu/clang-tsa.h b/include/qemu/clang-tsa.h
10
index XXXXXXX..XXXXXXX 100644
11
--- a/include/qemu/clang-tsa.h
12
+++ b/include/qemu/clang-tsa.h
13
@@ -XXX,XX +XXX,XX @@
14
*/
15
#define TSA_NO_TSA TSA(no_thread_safety_analysis)
16
17
+/*
18
+ * TSA_ASSERT() is used to annotate functions: This function will assert that
19
+ * the lock is held. When it returns, the caller of the function is assumed to
20
+ * already hold the resource.
21
+ *
22
+ * More than one mutex may be specified, comma-separated.
23
+ */
24
+#define TSA_ASSERT(...) TSA(assert_capability(__VA_ARGS__))
25
+
26
#endif /* #ifndef CLANG_TSA_H */
27
--
28
2.38.1
diff view generated by jsdifflib
Deleted patch
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Message-Id: <20221207131838.239125-8-kwolf@redhat.com>
3
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
6
include/qemu/clang-tsa.h | 4 ++++
7
1 file changed, 4 insertions(+)
8
1
9
diff --git a/include/qemu/clang-tsa.h b/include/qemu/clang-tsa.h
10
index XXXXXXX..XXXXXXX 100644
11
--- a/include/qemu/clang-tsa.h
12
+++ b/include/qemu/clang-tsa.h
13
@@ -XXX,XX +XXX,XX @@
14
* void Foo(void) TSA_REQUIRES(mutex);
15
*/
16
#define TSA_REQUIRES(...) TSA(requires_capability(__VA_ARGS__))
17
+#define TSA_REQUIRES_SHARED(...) TSA(requires_shared_capability(__VA_ARGS__))
18
19
/* TSA_EXCLUDES() is used to annotate functions: the caller of the
20
* function MUST NOT hold resource, the function first acquires the
21
@@ -XXX,XX +XXX,XX @@
22
* void Foo(void) TSA_ACQUIRE(mutex);
23
*/
24
#define TSA_ACQUIRE(...) TSA(acquire_capability(__VA_ARGS__))
25
+#define TSA_ACQUIRE_SHARED(...) TSA(acquire_shared_capability(__VA_ARGS__))
26
27
/* TSA_RELEASE() is used to annotate functions: the caller of the
28
* function MUST hold the resource, but the function will then release it.
29
@@ -XXX,XX +XXX,XX @@
30
* void Foo(void) TSA_RELEASE(mutex);
31
*/
32
#define TSA_RELEASE(...) TSA(release_capability(__VA_ARGS__))
33
+#define TSA_RELEASE_SHARED(...) TSA(release_shared_capability(__VA_ARGS__))
34
35
/* TSA_NO_TSA is used to annotate functions. Use only when you need to.
36
*
37
@@ -XXX,XX +XXX,XX @@
38
* More than one mutex may be specified, comma-separated.
39
*/
40
#define TSA_ASSERT(...) TSA(assert_capability(__VA_ARGS__))
41
+#define TSA_ASSERT_SHARED(...) TSA(assert_shared_capability(__VA_ARGS__))
42
43
#endif /* #ifndef CLANG_TSA_H */
44
--
45
2.38.1
diff view generated by jsdifflib
Deleted patch
1
This enables clang's thread safety analysis (TSA), which we'll use to
2
statically check the block graph locking.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Message-Id: <20221207131838.239125-9-kwolf@redhat.com>
6
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
configure | 1 +
10
1 file changed, 1 insertion(+)
11
12
diff --git a/configure b/configure
13
index XXXXXXX..XXXXXXX 100755
14
--- a/configure
15
+++ b/configure
16
@@ -XXX,XX +XXX,XX @@ add_to warn_flags -Wnested-externs
17
add_to warn_flags -Wendif-labels
18
add_to warn_flags -Wexpansion-to-defined
19
add_to warn_flags -Wimplicit-fallthrough=2
20
+add_to warn_flags -Wthread-safety
21
22
nowarn_flags=
23
add_to nowarn_flags -Wno-initializer-overrides
24
--
25
2.38.1
diff view generated by jsdifflib