1
The following changes since commit 5704c36d25ee84e7129722cb0db53df9faefe943:
1
The following changes since commit 5204b499a6cae4dfd9fe762d5e6e82224892383b:
2
2
3
Merge remote-tracking branch 'remotes/kraxel/tags/fixes-31-20181112-pull-request' into staging (2018-11-12 15:55:40 +0000)
3
mailmap: Fix Stefan Weil author email (2022-12-13 15:56:57 -0500)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
https://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to 1a42e5d8298d1b0f90d2254e7d559391dd3a45ca:
9
for you to fetch changes up to 2ad19e5dc950d4b340894846b9e71c0b20f9a1cc:
10
10
11
Merge remote-tracking branch 'mreitz/tags/pull-block-2018-11-12' into queue-block (2018-11-12 17:57:32 +0100)
11
block: GRAPH_RDLOCK for functions only called by co_wrappers (2022-12-14 13:13:07 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
15
16
- file-posix: Don't waste a file descriptor for locking, don't lock the
16
- Code cleanups around block graph modification
17
same bit multiple times
17
- Simplify drain
18
- nvme: Fix double free and memory leak
18
- coroutine_fn correctness fixes, including splitting generated
19
- Misc error handling fixes
19
coroutine wrappers into co_wrapper (to be called only from
20
- Added NULL checks found by static analysis
20
non-coroutine context) and co_wrapper_mixed (both coroutine and
21
- Allow more block drivers to not be included in the qemu build
21
non-coroutine context)
22
- Introduce a block graph rwlock
22
23
23
----------------------------------------------------------------
24
----------------------------------------------------------------
24
Fam Zheng (4):
25
Emanuele Giuseppe Esposito (21):
25
file-posix: Use error API properly
26
block-io: introduce coroutine_fn duplicates for bdrv_common_block_status_above callers
26
file-posix: Skip effectiveless OFD lock operations
27
block-copy: add coroutine_fn annotations
27
file-posix: Drop s->lock_fd
28
nbd/server.c: add coroutine_fn annotations
28
tests: Add unit tests for image locking
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
29
47
30
Jeff Cody (1):
48
Kevin Wolf (25):
31
block: Make more block drivers compile-time configurable
49
qed: Don't yield in bdrv_qed_co_drain_begin()
50
test-bdrv-drain: Don't yield in .bdrv_co_drained_begin/end()
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
32
74
33
Kevin Wolf (1):
75
Paolo Bonzini (1):
34
Merge remote-tracking branch 'mreitz/tags/pull-block-2018-11-12' into queue-block
76
graph-lock: Introduce a lock to protect block graph operations
35
77
36
Li Qiang (2):
78
Vladimir Sementsov-Ogievskiy (4):
37
nvme: don't unref ctrl_mem when device unrealized
79
block: Inline bdrv_detach_child()
38
nvme: free cmbuf in nvme_exit
80
block: drop bdrv_remove_filter_or_cow_child
81
block: bdrv_refresh_perms(): allow external tran
82
block: refactor bdrv_list_refresh_perms to allow any list of nodes
39
83
40
Liam Merwick (5):
84
docs/devel/block-coroutine-wrapper.rst | 6 +-
41
job: Fix off-by-one assert checks for JobSTT and JobVerbTable
85
configure | 1 +
42
block: Null pointer dereference in blk_root_get_parent_desc()
86
block/block-gen.h | 11 +-
43
qemu-img: assert block_job_get() does not return NULL in img_commit()
87
block/coroutines.h | 21 +-
44
block: Fix potential Null pointer dereferences in vvfat.c
88
include/block/aio.h | 9 +
45
qcow2: Read outside array bounds in qcow2_pre_write_overlap_check()
89
include/block/block-common.h | 27 ++-
46
90
include/block/block-copy.h | 5 +-
47
Peter Maydell (1):
91
include/block/block-global-state.h | 15 +-
48
blockdev: Consistently use snapshot_node_name in external_snapshot_prepare()
92
include/block/block-io.h | 136 +++++------
49
93
include/block/block_int-common.h | 49 ++--
50
zhenwei pi (1):
94
include/block/block_int-global-state.h | 17 --
51
blockdev: handle error on block latency histogram set error
95
include/block/block_int-io.h | 12 -
52
96
include/block/block_int.h | 1 +
53
configure | 91 ++++++++++++++++++++++++++
97
include/block/dirty-bitmap.h | 10 +-
54
block/block-backend.c | 3 +-
98
include/block/graph-lock.h | 280 +++++++++++++++++++++++
55
block/file-posix.c | 122 ++++++++++++++++++++---------------
99
include/qemu/clang-tsa.h | 114 ++++++++++
56
block/qcow2-refcount.c | 18 +++---
100
include/sysemu/block-backend-io.h | 77 ++++---
57
block/vvfat.c | 46 ++++++++-----
101
block.c | 404 ++++++++++++++++++---------------
58
blockdev.c | 21 ++++--
102
block/block-backend.c | 25 +-
59
hw/block/nvme.c | 6 +-
103
block/block-copy.c | 21 +-
60
job.c | 4 +-
104
block/commit.c | 4 +-
61
qemu-img.c | 1 +
105
block/crypto.c | 2 +-
62
tests/test-image-locking.c | 157 +++++++++++++++++++++++++++++++++++++++++++++
106
block/dirty-bitmap.c | 88 +------
63
block/Makefile.objs | 22 +++++--
107
block/graph-lock.c | 275 ++++++++++++++++++++++
64
tests/Makefile.include | 2 +
108
block/io.c | 367 ++++++++++--------------------
65
12 files changed, 400 insertions(+), 93 deletions(-)
109
block/parallels.c | 2 +-
66
create mode 100644 tests/test-image-locking.c
110
block/qcow.c | 2 +-
67
111
block/qcow2.c | 4 +-
112
block/qed.c | 28 ++-
113
block/raw-format.c | 2 +-
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
1
2
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
New patch
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
1
2
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
1
From: Fam Zheng <famz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
2
3
If we know we've already locked the bytes, don't do it again; similarly
3
Allow passing external Transaction pointer, stop creating extra
4
don't unlock a byte if we haven't locked it. This doesn't change the
4
Transaction objects.
5
behavior, but fixes a corner case explained below.
6
5
7
Libvirt had an error handling bug that an image can get its (ownership,
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
8
file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
9
QEMU. Specifically, an image in use by Libvirt VM has:
8
Message-Id: <20221107163558.618889-4-vsementsov@yandex-team.ru>
10
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
$ ls -lhZ b.img
12
-rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img
13
14
Trying to attach it a second time won't work because of image locking.
15
And after the error, it becomes:
16
17
$ ls -lhZ b.img
18
-rw-r--r--. root root system_u:object_r:virt_image_t:s0 b.img
19
20
Then, we won't be able to do OFD lock operations with the existing fd.
21
In other words, the code such as in blk_detach_dev:
22
23
blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);
24
25
can abort() QEMU, out of environmental changes.
26
27
This patch is an easy fix to this and the change is regardlessly
28
reasonable, so do it.
29
30
Signed-off-by: Fam Zheng <famz@redhat.com>
31
Reviewed-by: Max Reitz <mreitz@redhat.com>
32
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
33
---
11
---
34
block/file-posix.c | 54 +++++++++++++++++++++++++++++++++++++---------
12
block.c | 31 ++++++++++++++++++++-----------
35
1 file changed, 44 insertions(+), 10 deletions(-)
13
1 file changed, 20 insertions(+), 11 deletions(-)
36
14
37
diff --git a/block/file-posix.c b/block/file-posix.c
15
diff --git a/block.c b/block.c
38
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
39
--- a/block/file-posix.c
17
--- a/block.c
40
+++ b/block/file-posix.c
18
+++ b/block.c
41
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
19
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
42
uint64_t perm;
20
}
43
uint64_t shared_perm;
21
44
22
45
+ /* The perms bits whose corresponding bytes are already locked in
23
-static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
46
+ * s->lock_fd. */
24
+/* @tran is allowed to be NULL. In this case no rollback is possible */
47
+ uint64_t locked_perm;
25
+static int bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran,
48
+ uint64_t locked_shared_perm;
26
+ Error **errp)
49
+
50
#ifdef CONFIG_XFS
51
bool is_xfs:1;
52
#endif
53
@@ -XXX,XX +XXX,XX @@ typedef enum {
54
* file; if @unlock == true, also unlock the unneeded bytes.
55
* @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
56
*/
57
-static int raw_apply_lock_bytes(int fd,
58
+static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
59
uint64_t perm_lock_bits,
60
uint64_t shared_perm_lock_bits,
61
bool unlock, Error **errp)
62
{
27
{
63
int ret;
28
int ret;
64
int i;
29
- Transaction *tran = tran_new();
65
+ uint64_t locked_perm, locked_shared_perm;
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
+ }
66
+
37
+
67
+ if (s) {
38
ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
68
+ locked_perm = s->locked_perm;
39
- tran_finalize(tran, ret);
69
+ locked_shared_perm = s->locked_shared_perm;
40
+
70
+ } else {
41
+ if (local_tran) {
71
+ /*
42
+ tran_finalize(local_tran, ret);
72
+ * We don't have the previous bits, just lock/unlock for each of the
73
+ * requested bits.
74
+ */
75
+ if (unlock) {
76
+ locked_perm = BLK_PERM_ALL;
77
+ locked_shared_perm = BLK_PERM_ALL;
78
+ } else {
79
+ locked_perm = 0;
80
+ locked_shared_perm = 0;
81
+ }
82
+ }
43
+ }
83
44
84
PERM_FOREACH(i) {
45
return ret;
85
int off = RAW_LOCK_PERM_BASE + i;
46
}
86
- if (perm_lock_bits & (1ULL << i)) {
47
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
87
+ uint64_t bit = (1ULL << i);
48
88
+ if ((perm_lock_bits & bit) && !(locked_perm & bit)) {
49
bdrv_child_set_perm(c, perm, shared, tran);
89
ret = qemu_lock_fd(fd, off, 1, false);
50
90
if (ret) {
51
- ret = bdrv_refresh_perms(c->bs, &local_err);
91
error_setg(errp, "Failed to lock byte %d", off);
52
+ ret = bdrv_refresh_perms(c->bs, tran, &local_err);
92
return ret;
53
93
+ } else if (s) {
54
tran_finalize(tran, ret);
94
+ s->locked_perm |= bit;
55
95
}
56
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
96
- } else if (unlock) {
57
goto out;
97
+ } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) {
98
ret = qemu_unlock_fd(fd, off, 1);
99
if (ret) {
100
error_setg(errp, "Failed to unlock byte %d", off);
101
return ret;
102
+ } else if (s) {
103
+ s->locked_perm &= ~bit;
104
}
105
}
106
}
58
}
107
PERM_FOREACH(i) {
59
108
int off = RAW_LOCK_SHARED_BASE + i;
60
- ret = bdrv_refresh_perms(child_bs, errp);
109
- if (shared_perm_lock_bits & (1ULL << i)) {
61
+ ret = bdrv_refresh_perms(child_bs, tran, errp);
110
+ uint64_t bit = (1ULL << i);
62
111
+ if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) {
63
out:
112
ret = qemu_lock_fd(fd, off, 1, false);
64
tran_finalize(tran, ret);
113
if (ret) {
65
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
114
error_setg(errp, "Failed to lock byte %d", off);
66
goto out;
115
return ret;
116
+ } else if (s) {
117
+ s->locked_shared_perm |= bit;
118
}
119
- } else if (unlock) {
120
+ } else if (unlock && (locked_shared_perm & bit) &&
121
+ !(shared_perm_lock_bits & bit)) {
122
ret = qemu_unlock_fd(fd, off, 1);
123
if (ret) {
124
error_setg(errp, "Failed to unlock byte %d", off);
125
return ret;
126
+ } else if (s) {
127
+ s->locked_shared_perm &= ~bit;
128
}
129
}
130
}
67
}
131
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
68
132
69
- ret = bdrv_refresh_perms(parent_bs, errp);
133
switch (op) {
70
+ ret = bdrv_refresh_perms(parent_bs, tran, errp);
134
case RAW_PL_PREPARE:
71
if (ret < 0) {
135
- ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm,
72
goto out;
136
+ ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm,
137
~s->shared_perm | ~new_shared,
138
false, errp);
139
if (!ret) {
140
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
141
op = RAW_PL_ABORT;
142
/* fall through to unlock bytes. */
143
case RAW_PL_ABORT:
144
- raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm,
145
+ raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm,
146
true, &local_err);
147
if (local_err) {
148
/* Theoretically the above call only unlocks bytes and it cannot
149
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
150
}
151
break;
152
case RAW_PL_COMMIT:
153
- raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared,
154
+ raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared,
155
true, &local_err);
156
if (local_err) {
157
/* Theoretically the above call only unlocks bytes and it cannot
158
@@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
159
shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
160
161
/* Step one: Take locks */
162
- result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp);
163
+ result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp);
164
if (result < 0) {
165
goto out_close;
166
}
73
}
167
@@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
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;
168
}
85
}
169
86
170
out_unlock:
87
- ret = bdrv_refresh_perms(bs, errp);
171
- raw_apply_lock_bytes(fd, 0, 0, true, &local_err);
88
+ ret = bdrv_refresh_perms(bs, tran, errp);
172
+ raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err);
89
out:
173
if (local_err) {
90
tran_finalize(tran, ret);
174
/* The above call should not fail, and if it does, that does
91
175
* not mean the whole creation operation has failed. So
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) {
176
--
119
--
177
2.19.1
120
2.38.1
178
179
diff view generated by jsdifflib
1
From: Fam Zheng <famz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
2
3
Use error_report for situations that affect user operation (i.e. we're
3
We are going to increase usage of collecting nodes in a list to then
4
actually returning error), and warn_report/warn_report_err when some
4
update, and calling bdrv_topological_dfs() each time is not convenient,
5
less critical error happened but the user operation can still carry on.
5
and not correct as we are going to interleave graph modifying with
6
filling the node list.
6
7
7
For raw_normalize_devicepath, add Error parameter to propagate to
8
So, let's switch to a function that takes any list of nodes, adds all
8
its callers.
9
their subtrees and do topological sort. And finally, refresh
10
permissions.
9
11
10
Suggested-by: Markus Armbruster <armbru@redhat.com>
12
While being here, make the function public, as we'll want to use it
11
Signed-off-by: Fam Zheng <famz@redhat.com>
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>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
20
---
14
block/file-posix.c | 39 ++++++++++++++++-----------------------
21
block.c | 51 ++++++++++++++++++++++++++++++++-------------------
15
1 file changed, 16 insertions(+), 23 deletions(-)
22
1 file changed, 32 insertions(+), 19 deletions(-)
16
23
17
diff --git a/block/file-posix.c b/block/file-posix.c
24
diff --git a/block.c b/block.c
18
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
19
--- a/block/file-posix.c
26
--- a/block.c
20
+++ b/block/file-posix.c
27
+++ b/block.c
21
@@ -XXX,XX +XXX,XX @@ static int cdrom_reopen(BlockDriverState *bs);
28
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q,
22
#endif
23
24
#if defined(__NetBSD__)
25
-static int raw_normalize_devicepath(const char **filename)
26
+static int raw_normalize_devicepath(const char **filename, Error **errp)
27
{
28
static char namebuf[PATH_MAX];
29
const char *dp, *fname;
30
@@ -XXX,XX +XXX,XX @@ static int raw_normalize_devicepath(const char **filename)
31
fname = *filename;
32
dp = strrchr(fname, '/');
33
if (lstat(fname, &sb) < 0) {
34
- fprintf(stderr, "%s: stat failed: %s\n",
35
- fname, strerror(errno));
36
+ error_setg_errno(errp, errno, "%s: stat failed", fname);
37
return -errno;
38
}
39
40
@@ -XXX,XX +XXX,XX @@ static int raw_normalize_devicepath(const char **filename)
41
snprintf(namebuf, PATH_MAX, "%.*s/r%s",
42
(int)(dp - fname), fname, dp + 1);
43
}
44
- fprintf(stderr, "%s is a block device", fname);
45
*filename = namebuf;
46
- fprintf(stderr, ", using %s\n", *filename);
47
+ warn_report("%s is a block device, using %s", fname, *filename);
48
49
return 0;
29
return 0;
50
}
30
}
51
#else
31
52
-static int raw_normalize_devicepath(const char **filename)
32
-static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
53
+static int raw_normalize_devicepath(const char **filename, Error **errp)
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)
54
{
40
{
41
int ret;
42
BlockDriverState *bs;
43
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
55
return 0;
44
return 0;
56
}
45
}
57
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
46
58
47
+/*
59
filename = qemu_opt_get(opts, "filename");
48
+ * @list is any list of nodes. List is completed by all subtrees and
60
49
+ * topologically sorted. It's not a problem if some node occurs in the @list
61
- ret = raw_normalize_devicepath(&filename);
50
+ * several times.
62
+ ret = raw_normalize_devicepath(&filename, errp);
51
+ */
63
if (ret != 0) {
52
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
64
- error_setg_errno(errp, -ret, "Could not normalize device path");
53
+ Transaction *tran, Error **errp)
65
goto fail;
54
+{
55
+ g_autoptr(GHashTable) found = g_hash_table_new(NULL, NULL);
56
+ g_autoptr(GSList) refresh_list = NULL;
57
+
58
+ for ( ; list; list = list->next) {
59
+ refresh_list = bdrv_topological_dfs(refresh_list, found, list->data);
60
+ }
61
+
62
+ return bdrv_do_refresh_perms(refresh_list, q, tran, errp);
63
+}
64
+
65
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
66
uint64_t *shared_perm)
67
{
68
@@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran,
69
tran = local_tran = tran_new();
66
}
70
}
67
71
68
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
72
- ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
69
case ON_OFF_AUTO_ON:
73
+ ret = bdrv_do_refresh_perms(list, NULL, tran, errp);
70
s->use_lock = true;
74
71
if (!qemu_has_ofd_lock()) {
75
if (local_tran) {
72
- fprintf(stderr,
76
tran_finalize(local_tran, ret);
73
- "File lock requested but OFD locking syscall is "
77
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
74
- "unavailable, falling back to POSIX file locks.\n"
78
BlockReopenQueueEntry *bs_entry, *next;
75
- "Due to the implementation, locks can be lost "
79
AioContext *ctx;
76
- "unexpectedly.\n");
80
Transaction *tran = tran_new();
77
+ warn_report("File lock requested but OFD locking syscall is "
81
- g_autoptr(GHashTable) found = NULL;
78
+ "unavailable, falling back to POSIX file locks");
82
g_autoptr(GSList) refresh_list = NULL;
79
+ error_printf("Due to the implementation, locks can be lost "
83
80
+ "unexpectedly.\n");
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);
81
}
99
}
82
break;
100
if (state->old_file_bs) {
83
case ON_OFF_AUTO_OFF:
101
- refresh_list = bdrv_topological_dfs(refresh_list, found,
84
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
102
- state->old_file_bs);
85
/* Theoretically the above call only unlocks bytes and it cannot
103
+ refresh_list = g_slist_prepend(refresh_list, state->old_file_bs);
86
* fail. Something weird happened, report it.
87
*/
88
- error_report_err(local_err);
89
+ warn_report_err(local_err);
90
}
104
}
91
break;
92
case RAW_PL_COMMIT:
93
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
94
/* Theoretically the above call only unlocks bytes and it cannot
95
* fail. Something weird happened, report it.
96
*/
97
- error_report_err(local_err);
98
+ warn_report_err(local_err);
99
}
100
break;
101
}
105
}
102
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
106
103
/* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
107
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
104
if (rs->fd == -1) {
108
Error **errp)
105
const char *normalized_filename = state->bs->filename;
109
{
106
- ret = raw_normalize_devicepath(&normalized_filename);
110
Transaction *tran = tran_new();
107
- if (ret < 0) {
111
- g_autoptr(GHashTable) found = NULL;
108
- error_setg_errno(errp, -ret, "Could not normalize device path");
112
g_autoptr(GSList) refresh_list = NULL;
109
- } else {
113
BlockDriverState *to_cow_parent = NULL;
110
+ ret = raw_normalize_devicepath(&normalized_filename, errp);
114
int ret;
111
+ if (ret >= 0) {
115
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
112
assert(!(rs->open_flags & O_CREAT));
116
bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran);
113
rs->fd = qemu_open(normalized_filename, rs->open_flags);
114
if (rs->fd == -1) {
115
@@ -XXX,XX +XXX,XX @@ static int aio_worker(void *arg)
116
ret = handle_aiocb_truncate(aiocb);
117
break;
118
default:
119
- fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
120
+ error_report("invalid aio request (0x%x)", aiocb->aio_type);
121
ret = -EINVAL;
122
break;
123
}
117
}
124
@@ -XXX,XX +XXX,XX @@ out_unlock:
118
125
* not mean the whole creation operation has failed. So
119
- found = g_hash_table_new(NULL, NULL);
126
* report it the user for their convenience, but do not report
120
-
127
* it to the caller. */
121
- refresh_list = bdrv_topological_dfs(refresh_list, found, to);
128
- error_report_err(local_err);
122
- refresh_list = bdrv_topological_dfs(refresh_list, found, from);
129
+ warn_report_err(local_err);
123
+ refresh_list = g_slist_prepend(refresh_list, to);
130
}
124
+ refresh_list = g_slist_prepend(refresh_list, from);
131
125
132
out_close:
126
ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
133
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts
134
135
(void)has_prefix;
136
137
- ret = raw_normalize_devicepath(&filename);
138
+ ret = raw_normalize_devicepath(&filename, errp);
139
if (ret < 0) {
127
if (ret < 0) {
140
- error_setg_errno(errp, -ret, "Could not normalize device path");
128
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
141
return ret;
129
{
142
}
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);
143
147
144
--
148
--
145
2.19.1
149
2.38.1
146
147
diff view generated by jsdifflib
New patch
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.
1
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>
13
---
14
block/qed.c | 20 +++++++++++++++++---
15
1 file changed, 17 insertions(+), 3 deletions(-)
16
17
diff --git a/block/qed.c b/block/qed.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block/qed.c
20
+++ b/block/qed.c
21
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qed_unplug_allocating_write_reqs(BDRVQEDState *s)
22
qemu_co_mutex_unlock(&s->table_lock);
23
}
24
25
-static void coroutine_fn qed_need_check_timer_entry(void *opaque)
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
+{
38
+ BDRVQEDState *s = opaque;
39
+
40
+ qed_need_check_timer(opaque);
41
+ bdrv_dec_in_flight(s->bs);
42
+}
43
+
44
static void qed_need_check_timer_cb(void *opaque)
45
{
46
+ BDRVQEDState *s = opaque;
47
Coroutine *co = qemu_coroutine_create(qed_need_check_timer_entry, opaque);
48
+
49
+ bdrv_inc_in_flight(s->bs);
50
qemu_coroutine_enter(co);
51
}
52
53
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs)
54
* header is flushed.
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
--
68
2.38.1
diff view generated by jsdifflib
New patch
1
We want to change .bdrv_co_drained_begin/end() back to be non-coroutine
2
callbacks, so in preparation, avoid yielding in their implementation.
1
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
16
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
---
23
tests/unit/test-bdrv-drain.c | 64 ++++++++++++++++++++++++++----------
24
1 file changed, 46 insertions(+), 18 deletions(-)
25
26
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/tests/unit/test-bdrv-drain.c
29
+++ b/tests/unit/test-bdrv-drain.c
30
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVTestState {
31
bool sleep_in_drain_begin;
32
} BDRVTestState;
33
34
+static void coroutine_fn sleep_in_drain_begin(void *opaque)
35
+{
36
+ BlockDriverState *bs = opaque;
37
+
38
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
39
+ bdrv_dec_in_flight(bs);
40
+}
41
+
42
static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
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
+{
60
+ BlockDriverState *bs = opaque;
61
+ BDRVReplaceTestState *s = bs->opaque;
62
+
63
+ /* Keep waking io_co up until it is done */
64
+ while (s->io_co) {
65
+ aio_co_wake(s->io_co);
66
+ s->io_co = NULL;
67
+ qemu_coroutine_yield();
68
+ }
69
+ s->drain_co = NULL;
70
+ bdrv_dec_in_flight(bs);
71
+}
72
+
73
/**
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
+{
99
+ BlockDriverState *bs = opaque;
100
+ char data;
101
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1);
102
+ int ret;
103
+
104
+ /* Queue a read request post-drain */
105
+ ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0);
106
+ g_assert(ret >= 0);
107
+ bdrv_dec_in_flight(bs);
108
+}
109
+
110
/**
111
* Reduce .drain_count, set .was_undrained once it reaches 0.
112
* If .drain_count reaches 0 and the node has a backing file, issue a
113
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs)
114
115
g_assert(s->drain_count > 0);
116
if (!--s->drain_count) {
117
- int ret;
118
-
119
s->was_undrained = true;
120
121
if (bs->backing) {
122
- char data;
123
- QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1);
124
-
125
- /* Queue a read request post-drain */
126
- ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0);
127
- g_assert(ret >= 0);
128
+ Coroutine *co = qemu_coroutine_create(bdrv_replace_test_read_entry,
129
+ bs);
130
+ bdrv_inc_in_flight(bs);
131
+ aio_co_enter(bdrv_get_aio_context(bs), co);
132
}
133
}
134
}
135
--
136
2.38.1
diff view generated by jsdifflib
New patch
1
1
Polling during bdrv_drained_end() can be problematic (and in the future,
2
we may get cases for bdrv_drained_begin() where polling is forbidden,
3
and we don't care about already in-flight requests, but just want to
4
prevent new requests from arriving).
5
6
The .bdrv_drained_begin/end callbacks running in a coroutine is the only
7
reason why we have to do this polling, so make them non-coroutine
8
callbacks again. None of the callers actually yield any more.
9
10
This means that bdrv_drained_end() effectively doesn't poll any more,
11
even if AIO_WAIT_WHILE() loops are still there (their condition is false
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
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
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
---
24
include/block/block_int-common.h | 10 ++++---
25
block.c | 4 +--
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
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
76
index XXXXXXX..XXXXXXX 100644
77
--- a/block/io.c
78
+++ b/block/io.c
79
@@ -XXX,XX +XXX,XX @@ typedef struct {
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;
116
}
117
118
- data = g_new(BdrvCoDrainData, 1);
119
- *data = (BdrvCoDrainData) {
120
- .bs = bs,
121
- .done = false,
122
- .begin = begin,
123
- .drained_end_counter = drained_end_counter,
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
}
133
-
134
- /* Make sure the driver callback completes during the polling phase for
135
- * drain_begin. */
136
- bdrv_inc_in_flight(bs);
137
- data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data);
138
- aio_co_schedule(bdrv_get_aio_context(bs), data->co);
139
}
140
141
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
142
diff --git a/block/qed.c b/block/qed.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/block/qed.c
145
+++ b/block/qed.c
146
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn qed_plug_allocating_write_reqs(BDRVQEDState *s)
147
assert(!s->allocating_write_reqs_plugged);
148
if (s->allocating_acb != NULL) {
149
/* Another allocating write came concurrently. This cannot happen
150
- * from bdrv_qed_co_drain_begin, but it can happen when the timer runs.
151
+ * from bdrv_qed_drain_begin, but it can happen when the timer runs.
152
*/
153
qemu_co_mutex_unlock(&s->table_lock);
154
return false;
155
@@ -XXX,XX +XXX,XX @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs,
156
}
157
}
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
--
285
2.38.1
diff view generated by jsdifflib
New patch
1
drained_end_counter is unused now, nobody changes its value any more. It
2
can be removed.
1
3
4
In cases where we had two almost identical functions that only differed
5
in whether the caller passes drained_end_counter, or whether they would
6
poll for a local drained_end_counter to reach 0, these become a single
7
function.
8
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
---
16
include/block/block-io.h | 24 --------
17
include/block/block_int-common.h | 6 +-
18
block.c | 5 +-
19
block/block-backend.c | 4 +-
20
block/io.c | 98 ++++++++------------------------
21
blockjob.c | 2 +-
22
6 files changed, 30 insertions(+), 109 deletions(-)
23
24
diff --git a/include/block/block-io.h b/include/block/block-io.h
25
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/block-io.h
27
+++ b/include/block/block-io.h
28
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset,
29
int64_t bytes, BdrvRequestFlags read_flags,
30
BdrvRequestFlags write_flags);
31
32
-/**
33
- * bdrv_drained_end_no_poll:
34
- *
35
- * Same as bdrv_drained_end(), but do not poll for the subgraph to
36
- * actually become unquiesced. Therefore, no graph changes will occur
37
- * with this function.
38
- *
39
- * *drained_end_counter is incremented for every background operation
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:
62
*
63
* 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
*/
71
void bdrv_drained_end(BlockDriverState *bs);
72
73
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
74
index XXXXXXX..XXXXXXX 100644
75
--- a/include/block/block_int-common.h
76
+++ b/include/block/block_int-common.h
77
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
78
* These functions must not change the graph (and therefore also must not
79
* call aio_poll(), which could change the graph indirectly).
80
*
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
135
index XXXXXXX..XXXXXXX 100644
136
--- a/block/io.c
137
+++ b/block/io.c
138
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
139
}
140
}
141
142
-static void bdrv_parent_drained_end_single_no_poll(BdrvChild *c,
143
- int *drained_end_counter)
144
+void bdrv_parent_drained_end_single(BdrvChild *c)
145
{
146
+ IO_OR_GS_CODE();
147
+
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
203
static void bdrv_co_drain_bh_cb(void *opaque)
204
{
205
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
206
aio_context_acquire(ctx);
207
bdrv_dec_in_flight(bs);
208
if (data->begin) {
209
- assert(!data->drained_end_counter);
210
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 {
221
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
222
bool begin, bool recursive,
223
BdrvChild *parent,
224
bool ignore_bds_parents,
225
- bool poll,
226
- int *drained_end_counter)
227
+ bool poll)
228
{
229
BdrvCoDrainData data;
230
Coroutine *self = qemu_coroutine_self();
231
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
232
.parent = parent,
233
.ignore_bds_parents = ignore_bds_parents,
234
.poll = poll,
235
- .drained_end_counter = drained_end_counter,
236
};
237
238
if (bs) {
239
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
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
}
246
247
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
248
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
249
250
if (qemu_in_coroutine()) {
251
bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents,
252
- poll, NULL);
253
+ poll);
254
return;
255
}
256
257
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
258
259
/**
260
* This function does not poll, nor must any of its recursively called
261
- * functions. The *drained_end_counter pointee will be incremented
262
- * once for every background operation scheduled, and decremented once
263
- * the operation settles. Therefore, the pointer must remain valid
264
- * until the pointee reaches 0. That implies that whoever sets up the
265
- * pointee has to poll until it is 0.
266
- *
267
- * We use atomic operations to access *drained_end_counter, because
268
- * (1) when called from bdrv_set_aio_context_ignore(), the subgraph of
269
- * @bs may contain nodes in different AioContexts,
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
{
279
BdrvChild *child;
280
int old_quiesce_counter;
281
282
- assert(drained_end_counter != NULL);
283
-
284
if (qemu_in_coroutine()) {
285
bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents,
286
- false, drained_end_counter);
287
+ false);
288
return;
289
}
290
assert(bs->quiesce_counter > 0);
291
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) {
301
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
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
}
310
}
311
312
void bdrv_drained_end(BlockDriverState *bs)
313
{
314
- int drained_end_counter = 0;
315
IO_OR_GS_CODE();
316
- bdrv_do_drained_end(bs, false, NULL, false, &drained_end_counter);
317
- BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0);
318
-}
319
-
320
-void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter)
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
}
326
327
void bdrv_subtree_drained_end(BlockDriverState *bs)
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
--
417
2.38.1
diff view generated by jsdifflib
New patch
1
bdrv_drain_invoke() has now two entirely separate cases that share no
2
code any more and are selected depending on a bool parameter. Each case
3
has only one caller. Just inline the function.
1
4
5
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
---
13
block/io.c | 23 ++++++-----------------
14
1 file changed, 6 insertions(+), 17 deletions(-)
15
16
diff --git a/block/io.c b/block/io.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/io.c
19
+++ b/block/io.c
20
@@ -XXX,XX +XXX,XX @@ typedef struct {
21
bool ignore_bds_parents;
22
} BdrvCoDrainData;
23
24
-/* Recursively call BlockDriver.bdrv_drain_begin/end callbacks */
25
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
26
-{
27
- if (!bs->drv || (begin && !bs->drv->bdrv_drain_begin) ||
28
- (!begin && !bs->drv->bdrv_drain_end)) {
29
- return;
30
- }
31
-
32
- if (begin) {
33
- bs->drv->bdrv_drain_begin(bs);
34
- } else {
35
- bs->drv->bdrv_drain_end(bs);
36
- }
37
-}
38
-
39
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
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
+ }
50
}
51
52
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
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
--
65
2.38.1
diff view generated by jsdifflib
New 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.
1
5
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
From: zhenwei pi <pizhenwei@bytedance.com>
1
bdrv_reopen() and friends use subtree drains as a lazy way of covering
2
all the nodes they touch. Turns out that this lazy way is a lot more
3
complicated than just draining the nodes individually, even not
4
accounting for the additional complexity in the drain mechanism itself.
2
5
3
Function block_latency_histogram_set may return error, but qapi ignore this.
6
Simplify the code by switching to draining the individual nodes that are
4
This can be reproduced easily by qmp command:
7
already managed in the BlockReopenQueue anyway.
5
virsh qemu-monitor-command INSTANCE '{"execute":"x-block-latency-histogram-set",
6
"arguments":{"device":"drive-virtio-disk1","boundaries":[10,200,40]}}'
7
In fact this command does not work, but we still get success result.
8
8
9
qmp_x_block_latency_histogram_set is a batch setting API, report error ASAP.
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
10
Message-Id: <20221118174110.55183-8-kwolf@redhat.com>
11
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
11
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
---
14
blockdev.c | 19 ++++++++++++++++---
15
block.c | 16 +++++++++-------
15
1 file changed, 16 insertions(+), 3 deletions(-)
16
block/replication.c | 6 ------
17
blockdev.c | 13 -------------
18
3 files changed, 9 insertions(+), 26 deletions(-)
16
19
20
diff --git a/block.c b/block.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/block.c
23
+++ b/block.c
24
@@ -XXX,XX +XXX,XX @@ static bool bdrv_recurse_has_child(BlockDriverState *bs,
25
* returns a pointer to bs_queue, which is either the newly allocated
26
* bs_queue, or the existing bs_queue being used.
27
*
28
- * 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
*/
33
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
34
int flags;
35
QemuOpts *opts;
36
37
- /* 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
39
- * bdrv_reopen_multiple(). */
40
- assert(bs->quiesce_counter > 0);
41
GLOBAL_STATE_CODE();
42
43
+ bdrv_drained_begin(bs);
44
+
45
if (bs_queue == NULL) {
46
bs_queue = g_new0(BlockReopenQueue, 1);
47
QTAILQ_INIT(bs_queue);
48
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue)
49
if (bs_queue) {
50
BlockReopenQueueEntry *bs_entry, *next;
51
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
52
+ AioContext *ctx = bdrv_get_aio_context(bs_entry->state.bs);
53
+
54
+ aio_context_acquire(ctx);
55
+ bdrv_drained_end(bs_entry->state.bs);
56
+ aio_context_release(ctx);
57
+
58
qobject_unref(bs_entry->state.explicit_options);
59
qobject_unref(bs_entry->state.options);
60
g_free(bs_entry);
61
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
62
63
GLOBAL_STATE_CODE();
64
65
- bdrv_subtree_drained_begin(bs);
66
queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts);
67
68
if (ctx != qemu_get_aio_context()) {
69
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
70
if (ctx != qemu_get_aio_context()) {
71
aio_context_acquire(ctx);
72
}
73
- bdrv_subtree_drained_end(bs);
74
75
return ret;
76
}
77
diff --git a/block/replication.c b/block/replication.c
78
index XXXXXXX..XXXXXXX 100644
79
--- a/block/replication.c
80
+++ b/block/replication.c
81
@@ -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);
83
}
84
85
- bdrv_subtree_drained_begin(hidden_disk->bs);
86
- bdrv_subtree_drained_begin(secondary_disk->bs);
87
-
88
if (s->orig_hidden_read_only) {
89
QDict *opts = qdict_new();
90
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
91
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
92
aio_context_acquire(ctx);
93
}
94
}
95
-
96
- bdrv_subtree_drained_end(hidden_disk->bs);
97
- bdrv_subtree_drained_end(secondary_disk->bs);
98
}
99
100
static void backup_job_cleanup(BlockDriverState *bs)
17
diff --git a/blockdev.c b/blockdev.c
101
diff --git a/blockdev.c b/blockdev.c
18
index XXXXXXX..XXXXXXX 100644
102
index XXXXXXX..XXXXXXX 100644
19
--- a/blockdev.c
103
--- a/blockdev.c
20
+++ b/blockdev.c
104
+++ b/blockdev.c
21
@@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set(
105
@@ -XXX,XX +XXX,XX @@ fail:
106
void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
22
{
107
{
23
BlockBackend *blk = blk_by_name(device);
108
BlockReopenQueue *queue = NULL;
24
BlockAcctStats *stats;
109
- GSList *drained = NULL;
25
+ int ret;
110
- GSList *p;
26
111
27
if (!blk) {
112
/* Add each one of the BDS that we want to reopen to the queue */
28
error_setg(errp, "Device '%s' not found", device);
113
for (; reopen_list != NULL; reopen_list = reopen_list->next) {
29
@@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set(
114
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
115
ctx = bdrv_get_aio_context(bs);
116
aio_context_acquire(ctx);
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);
30
}
123
}
31
124
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
32
if (has_boundaries || has_boundaries_read) {
125
33
- block_latency_histogram_set(
126
fail:
34
+ ret = block_latency_histogram_set(
127
bdrv_reopen_queue_free(queue);
35
stats, BLOCK_ACCT_READ,
128
- for (p = drained; p; p = p->next) {
36
has_boundaries_read ? boundaries_read : boundaries);
129
- BlockDriverState *bs = p->data;
37
+ if (ret) {
130
- AioContext *ctx = bdrv_get_aio_context(bs);
38
+ error_setg(errp, "Device '%s' set read boundaries fail", device);
131
-
39
+ return;
132
- aio_context_acquire(ctx);
40
+ }
133
- bdrv_subtree_drained_end(bs);
41
}
134
- aio_context_release(ctx);
42
135
- }
43
if (has_boundaries || has_boundaries_write) {
136
- g_slist_free(drained);
44
- block_latency_histogram_set(
45
+ ret = block_latency_histogram_set(
46
stats, BLOCK_ACCT_WRITE,
47
has_boundaries_write ? boundaries_write : boundaries);
48
+ if (ret) {
49
+ error_setg(errp, "Device '%s' set write boundaries fail", device);
50
+ return;
51
+ }
52
}
53
54
if (has_boundaries || has_boundaries_flush) {
55
- block_latency_histogram_set(
56
+ ret = block_latency_histogram_set(
57
stats, BLOCK_ACCT_FLUSH,
58
has_boundaries_flush ? boundaries_flush : boundaries);
59
+ if (ret) {
60
+ error_setg(errp, "Device '%s' set flush boundaries fail", device);
61
+ return;
62
+ }
63
}
64
}
137
}
65
138
139
void qmp_blockdev_del(const char *node_name, Error **errp)
66
--
140
--
67
2.19.1
141
2.38.1
68
69
diff view generated by jsdifflib
1
From: Liam Merwick <Liam.Merwick@oracle.com>
1
Instead of using a subtree drain from the top node (which also drains
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.
2
4
3
In the assert checking the array dereference of JobVerbTable[verb]
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
in job_apply_verb() the check of the index, verb, allows an overrun
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
5
because an index equal to the array size is permitted.
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
6
8
Message-Id: <20221118174110.55183-9-kwolf@redhat.com>
7
Similarly, in the assert check of JobSTT[s0][s1] with index s1
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
in job_state_transition(), an off-by-one overrun is not flagged
9
either.
10
11
This is not a run-time issue as there are no callers actually
12
passing in the max value.
13
14
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
15
Reviewed-by: Darren Kenny <Darren.Kenny@oracle.com>
16
Reviewed-by: Mark Kanda <Mark.Kanda@oracle.com>
17
Reviewed-by: Eric Blake <eblake@redhat.com>
18
Reviewed-by: John Snow <jsnow@redhat.com>
19
Message-id: 1541453919-25973-2-git-send-email-Liam.Merwick@oracle.com
20
Signed-off-by: Max Reitz <mreitz@redhat.com>
21
---
10
---
22
job.c | 4 ++--
11
block.c | 4 ++--
23
1 file changed, 2 insertions(+), 2 deletions(-)
12
1 file changed, 2 insertions(+), 2 deletions(-)
24
13
25
diff --git a/job.c b/job.c
14
diff --git a/block.c b/block.c
26
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
27
--- a/job.c
16
--- a/block.c
28
+++ b/job.c
17
+++ b/block.c
29
@@ -XXX,XX +XXX,XX @@ bool job_is_internal(Job *job)
18
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
30
static void job_state_transition(Job *job, JobStatus s1)
19
GLOBAL_STATE_CODE();
31
{
20
32
JobStatus s0 = job->status;
21
bdrv_ref(top);
33
- assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
22
- bdrv_subtree_drained_begin(top);
34
+ assert(s1 >= 0 && s1 < JOB_STATUS__MAX);
23
+ bdrv_drained_begin(base);
35
trace_job_state_transition(job, job->ret,
24
36
JobSTT[s0][s1] ? "allowed" : "disallowed",
25
if (!top->drv || !base->drv) {
37
JobStatus_str(s0), JobStatus_str(s1));
26
goto exit;
38
@@ -XXX,XX +XXX,XX @@ static void job_state_transition(Job *job, JobStatus s1)
27
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
39
int job_apply_verb(Job *job, JobVerb verb, Error **errp)
28
40
{
29
ret = 0;
41
JobStatus s0 = job->status;
30
exit:
42
- assert(verb >= 0 && verb <= JOB_VERB__MAX);
31
- bdrv_subtree_drained_end(top);
43
+ assert(verb >= 0 && verb < JOB_VERB__MAX);
32
+ bdrv_drained_end(base);
44
trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb),
33
bdrv_unref(top);
45
JobVerbTable[verb][s0] ? "allowed" : "prohibited");
34
return ret;
46
if (JobVerbTable[verb][s0]) {
35
}
47
--
36
--
48
2.19.1
37
2.38.1
49
50
diff view generated by jsdifflib
New patch
1
The subtree drain was introduced in commit b1e1af394d9 as a way to avoid
2
graph changes between finding the base node and changing the block graph
3
as necessary on completion of the image streaming job.
1
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
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
24
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
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
---
29
include/block/block-global-state.h | 3 +++
30
block.c | 17 ++++++++++++++---
31
block/stream.c | 26 ++++++++++++++++----------
32
3 files changed, 33 insertions(+), 13 deletions(-)
33
34
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
35
index XXXXXXX..XXXXXXX 100644
36
--- a/include/block/block-global-state.h
37
+++ b/include/block/block-global-state.h
38
@@ -XXX,XX +XXX,XX @@ int bdrv_open_file_child(const char *filename,
39
BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, 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
{
62
int ret;
63
Transaction *tran = tran_new();
64
65
GLOBAL_STATE_CODE();
66
- bdrv_drained_begin(bs);
67
+ assert(bs->quiesce_counter > 0);
68
69
ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp);
70
if (ret < 0) {
71
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
72
ret = bdrv_refresh_perms(bs, tran, errp);
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
- }
111
-
112
unfiltered_base = bdrv_skip_filters(base);
113
114
if (bdrv_cow_child(unfiltered_bs)) {
115
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
116
}
117
}
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
--
143
2.38.1
diff view generated by jsdifflib
New patch
1
Subtree drains are not used any more. Remove them.
1
2
3
After this, BdrvChildClass.attach/detach() don't poll any more.
4
5
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
---
11
include/block/block-io.h | 18 +--
12
include/block/block_int-common.h | 1 -
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
19
diff --git a/include/block/block-io.h b/include/block/block-io.h
20
index XXXXXXX..XXXXXXX 100644
21
--- a/include/block/block-io.h
22
+++ b/include/block/block-io.h
23
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c);
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 {
429
BDRV_DRAIN_ALL,
430
BDRV_DRAIN,
431
- BDRV_SUBTREE_DRAIN,
432
DRAIN_TYPE_MAX,
433
};
434
435
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
436
switch (drain_type) {
437
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
438
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
439
- case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
440
default: g_assert_not_reached();
441
}
442
}
443
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
444
switch (drain_type) {
445
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
446
case BDRV_DRAIN: bdrv_drained_end(bs); break;
447
- case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
448
default: g_assert_not_reached();
449
}
450
}
451
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
452
test_drv_cb_common(BDRV_DRAIN, false);
453
}
454
455
-static void test_drv_cb_drain_subtree(void)
456
-{
457
- test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
458
-}
459
-
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)
473
{
474
BlockBackend *blk;
475
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
476
test_quiesce_common(BDRV_DRAIN, false);
477
}
478
479
-static void test_quiesce_drain_subtree(void)
480
-{
481
- test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
482
-}
483
-
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)
497
{
498
BlockBackend *blk;
499
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
500
blk_unref(blk);
501
}
502
503
-static void test_multiparent(void)
504
-{
505
- BlockBackend *blk_a, *blk_b;
506
- BlockDriverState *bs_a, *bs_b, *backing;
507
- BDRVTestState *a_s, *b_s, *backing_s;
508
-
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
{
657
BlockBackend *blk_a, *blk_b;
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)
809
810
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);
812
- g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
813
- 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
822
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);
824
- g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
825
- 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
833
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
846
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);
848
- g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
849
- test_blockjob_drain_subtree);
850
851
g_test_add_func("/bdrv-drain/blockjob/error/drain_all",
852
test_blockjob_error_drain_all);
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
--
880
2.38.1
diff view generated by jsdifflib
New patch
1
1
We only need to call both the BlockDriver's callback and the parent
2
callbacks when going from undrained to drained or vice versa. A second
3
drain section doesn't make a difference for the driver or the parent,
4
they weren't supposed to send new requests before and after the second
5
drain.
6
7
One thing that gets in the way is the 'ignore_bds_parents' parameter in
8
bdrv_do_drained_begin_quiesce() and bdrv_do_drained_end(): It means that
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
17
Instead of keeping track of this, let's just get rid of the parameter.
18
It was introduced in commit 6cd5c9d7b2d as an optimisation so that
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
28
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
29
Reviewed-by: Hanna Reitz <hreitz@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
---
34
include/block/block_int-common.h | 8 ++++----
35
block.c | 25 +++++++------------------
36
block/io.c | 30 ++++++++++++++++++------------
37
tests/unit/test-bdrv-drain.c | 16 ++++++++++------
38
4 files changed, 39 insertions(+), 40 deletions(-)
39
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
127
index XXXXXXX..XXXXXXX 100644
128
--- a/block/io.c
129
+++ b/block/io.c
130
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c)
131
{
132
IO_OR_GS_CODE();
133
134
- assert(c->parent_quiesce_counter > 0);
135
- c->parent_quiesce_counter--;
136
+ assert(c->quiesced_parent);
137
+ c->quiesced_parent = false;
138
+
139
if (c->klass->drained_end) {
140
c->klass->drained_end(c);
141
}
142
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
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
+ }
168
}
169
}
170
171
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
172
assert(bs->quiesce_counter > 0);
173
174
/* Re-enable things in child-to-parent order */
175
- if (bs->drv && bs->drv->bdrv_drain_end) {
176
- bs->drv->bdrv_drain_end(bs);
177
- }
178
- bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
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
}
191
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
192
index XXXXXXX..XXXXXXX 100644
193
--- a/tests/unit/test-bdrv-drain.c
194
+++ b/tests/unit/test-bdrv-drain.c
195
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_common(enum drain_type drain_type, bool recursive)
196
197
do_drain_begin(drain_type, bs);
198
199
- g_assert_cmpint(bs->quiesce_counter, ==, 1);
200
+ if (drain_type == BDRV_DRAIN_ALL) {
201
+ g_assert_cmpint(bs->quiesce_counter, ==, 2);
202
+ } else {
203
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
204
+ }
205
g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
206
207
do_drain_end(drain_type, bs);
208
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
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
--
234
2.38.1
diff view generated by jsdifflib
New patch
1
ignore_bds_parents is now ignored during drain_begin and drain_end, so
2
we can just remove it there. It is still a valid optimisation for
3
drain_all in bdrv_drained_poll(), so leave it around there.
1
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20221118174110.55183-13-kwolf@redhat.com>
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
include/block/block-io.h | 3 +--
12
block.c | 2 +-
13
block/io.c | 58 +++++++++++++++-------------------------
14
3 files changed, 24 insertions(+), 39 deletions(-)
15
16
diff --git a/include/block/block-io.h b/include/block/block-io.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/block/block-io.h
19
+++ b/include/block/block-io.h
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
21
* Quiesces a BDS like bdrv_drained_begin(), but does not wait for already
22
* running requests to complete.
23
*/
24
-void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
25
- BdrvChild *parent, bool ignore_bds_parents);
26
+void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent);
27
28
/**
29
* bdrv_drained_end:
30
diff --git a/block.c b/block.c
31
index XXXXXXX..XXXXXXX 100644
32
--- a/block.c
33
+++ b/block.c
34
@@ -XXX,XX +XXX,XX @@ static char *bdrv_child_get_parent_desc(BdrvChild *c)
35
static void bdrv_child_cb_drained_begin(BdrvChild *child)
36
{
37
BlockDriverState *bs = child->opaque;
38
- bdrv_do_drained_begin_quiesce(bs, NULL, false);
39
+ bdrv_do_drained_begin_quiesce(bs, NULL);
40
}
41
42
static bool bdrv_child_cb_drained_poll(BdrvChild *child)
43
diff --git a/block/io.c b/block/io.c
44
index XXXXXXX..XXXXXXX 100644
45
--- a/block/io.c
46
+++ b/block/io.c
47
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs);
48
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
49
int64_t offset, int64_t bytes, BdrvRequestFlags flags);
50
51
-static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
52
- bool ignore_bds_parents)
53
+static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
54
{
55
BdrvChild *c, *next;
56
57
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) {
60
continue;
61
}
62
bdrv_parent_drained_begin_single(c, false);
63
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c)
64
}
65
}
66
67
-static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
68
- bool ignore_bds_parents)
69
+static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
70
{
71
BdrvChild *c;
72
73
QLIST_FOREACH(c, &bs->parents, next_parent) {
74
- if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) {
75
+ if (c == ignore) {
76
continue;
77
}
78
bdrv_parent_drained_end_single(c);
79
@@ -XXX,XX +XXX,XX @@ typedef struct {
80
bool begin;
81
bool poll;
82
BdrvChild *parent;
83
- bool ignore_bds_parents;
84
} BdrvCoDrainData;
85
86
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
87
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_poll_top_level(BlockDriverState *bs,
88
}
89
90
static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
91
- bool ignore_bds_parents, bool poll);
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);
96
97
static void bdrv_co_drain_bh_cb(void *opaque)
98
{
99
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
100
aio_context_acquire(ctx);
101
bdrv_dec_in_flight(bs);
102
if (data->begin) {
103
- bdrv_do_drained_begin(bs, data->parent, data->ignore_bds_parents,
104
- data->poll);
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 {
113
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
114
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
115
bool begin,
116
BdrvChild *parent,
117
- bool ignore_bds_parents,
118
bool poll)
119
{
120
BdrvCoDrainData data;
121
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
122
.done = false,
123
.begin = begin,
124
.parent = parent,
125
- .ignore_bds_parents = ignore_bds_parents,
126
.poll = poll,
127
};
128
129
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
130
}
131
}
132
133
-void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
134
- BdrvChild *parent, bool ignore_bds_parents)
135
+void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent)
136
{
137
IO_OR_GS_CODE();
138
assert(!qemu_in_coroutine());
139
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
140
/* Stop things in parent-to-child order */
141
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
142
aio_disable_external(bdrv_get_aio_context(bs));
143
-
144
- /* TODO Remove ignore_bds_parents, we don't consider it any more */
145
- bdrv_parent_drained_begin(bs, parent, false);
146
+ bdrv_parent_drained_begin(bs, parent);
147
if (bs->drv && bs->drv->bdrv_drain_begin) {
148
bs->drv->bdrv_drain_begin(bs);
149
}
150
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
151
}
152
153
static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
154
- bool ignore_bds_parents, bool poll)
155
+ bool poll)
156
{
157
if (qemu_in_coroutine()) {
158
- bdrv_co_yield_to_drain(bs, true, parent, ignore_bds_parents, poll);
159
+ bdrv_co_yield_to_drain(bs, true, parent, poll);
160
return;
161
}
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);
200
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
201
if (bs->drv && bs->drv->bdrv_drain_end) {
202
bs->drv->bdrv_drain_end(bs);
203
}
204
- /* TODO Remove ignore_bds_parents, we don't consider it any more */
205
- bdrv_parent_drained_end(bs, parent, false);
206
-
207
+ bdrv_parent_drained_end(bs, parent);
208
aio_enable_external(bdrv_get_aio_context(bs));
209
}
210
}
211
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
212
void bdrv_drained_end(BlockDriverState *bs)
213
{
214
IO_OR_GS_CODE();
215
- bdrv_do_drained_end(bs, NULL, false);
216
+ bdrv_do_drained_end(bs, NULL);
217
}
218
219
void bdrv_drain(BlockDriverState *bs)
220
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
221
GLOBAL_STATE_CODE();
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);
233
- bdrv_do_drained_begin(bs, NULL, true, false);
234
+ bdrv_do_drained_begin(bs, NULL, false);
235
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
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
248
AioContext *aio_context = bdrv_get_aio_context(bs);
249
250
aio_context_acquire(aio_context);
251
- bdrv_do_drained_end(bs, NULL, true);
252
+ bdrv_do_drained_end(bs, NULL);
253
aio_context_release(aio_context);
254
}
255
256
--
257
2.38.1
diff view generated by jsdifflib
New patch
1
The next patch adds a parent drain to bdrv_attach_child_common(), which
2
shouldn't be, but is currently called from coroutines in some cases (e.g.
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.
1
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
17
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
---
23
block/io.c | 25 ++++++++++++-------------
24
1 file changed, 12 insertions(+), 13 deletions(-)
25
26
diff --git a/block/io.c b/block/io.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/block/io.c
29
+++ b/block/io.c
30
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
31
}
32
}
33
34
-void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent)
35
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
36
+ bool poll)
37
{
38
IO_OR_GS_CODE();
39
- assert(!qemu_in_coroutine());
40
+
41
+ if (qemu_in_coroutine()) {
42
+ bdrv_co_yield_to_drain(bs, true, parent, poll);
43
+ return;
44
+ }
45
46
/* Stop things in parent-to-child order */
47
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
48
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent)
49
bs->drv->bdrv_drain_begin(bs);
50
}
51
}
52
-}
53
-
54
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
55
- bool poll)
56
-{
57
- if (qemu_in_coroutine()) {
58
- bdrv_co_yield_to_drain(bs, true, parent, poll);
59
- return;
60
- }
61
-
62
- bdrv_do_drained_begin_quiesce(bs, parent);
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
}
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
--
79
2.38.1
diff view generated by jsdifflib
New patch
1
1
In order to make sure that bdrv_replace_child_noperm() doesn't have to
2
poll any more, get rid of the bdrv_parent_drained_begin_single() call.
3
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
227
index XXXXXXX..XXXXXXX 100644
228
--- a/block/io.c
229
+++ b/block/io.c
230
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
231
}
232
}
233
234
-static bool bdrv_parent_drained_poll_single(BdrvChild *c)
235
+bool bdrv_parent_drained_poll_single(BdrvChild *c)
236
{
237
if (c->klass->drained_poll) {
238
return c->klass->drained_poll(c);
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
+
259
if (!s->drain_count) {
260
s->drain_co = qemu_coroutine_create(bdrv_replace_test_drain_co, bs);
261
bdrv_inc_in_flight(bs);
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
--
282
2.38.1
diff view generated by jsdifflib
New patch
1
All callers of bdrv_parent_drained_begin_single() pass poll=false now,
2
so we don't need the parameter any more.
1
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Message-Id: <20221118174110.55183-16-kwolf@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
---
10
include/block/block-io.h | 5 ++---
11
block.c | 4 ++--
12
block/io.c | 8 ++------
13
3 files changed, 6 insertions(+), 11 deletions(-)
14
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
55
index XXXXXXX..XXXXXXX 100644
56
--- a/block/io.c
57
+++ b/block/io.c
58
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
59
if (c == ignore) {
60
continue;
61
}
62
- bdrv_parent_drained_begin_single(c, false);
63
+ bdrv_parent_drained_begin_single(c);
64
}
65
}
66
67
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore,
68
return busy;
69
}
70
71
-void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
72
+void bdrv_parent_drained_begin_single(BdrvChild *c)
73
{
74
- AioContext *ctx = bdrv_child_get_parent_aio_context(c);
75
IO_OR_GS_CODE();
76
77
assert(!c->quiesced_parent);
78
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
79
if (c->klass->drained_begin) {
80
c->klass->drained_begin(c);
81
}
82
- if (poll) {
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
--
89
2.38.1
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
3
bdrv_common_block_status_above() is a g_c_w, and it is being called by
4
many "wrapper" functions like bdrv_is_allocated(),
5
bdrv_is_allocated_above() and bdrv_block_status_above().
6
7
Because we want to eventually split the coroutine from non-coroutine
8
case in g_c_w, create duplicate wrappers that take care of directly
9
calling the same coroutine functions called in the g_c_w.
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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>
16
---
17
include/block/block-io.h | 15 +++++++++++
18
block/io.c | 58 +++++++++++++++++++++++++++++++++++++---
19
2 files changed, 70 insertions(+), 3 deletions(-)
20
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
56
index XXXXXXX..XXXXXXX 100644
57
--- a/block/io.c
58
+++ b/block/io.c
59
@@ -XXX,XX +XXX,XX @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
60
return ret;
61
}
62
63
+int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs,
64
+ BlockDriverState *base,
65
+ int64_t offset, int64_t bytes,
66
+ int64_t *pnum, int64_t *map,
67
+ BlockDriverState **file)
68
+{
69
+ IO_CODE();
70
+ return bdrv_co_common_block_status_above(bs, base, false, true, offset,
71
+ bytes, pnum, map, file, NULL);
72
+}
73
+
74
int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base,
75
int64_t offset, int64_t bytes, int64_t *pnum,
76
int64_t *map, BlockDriverState **file)
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
}
80
81
+int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset,
82
+ int64_t bytes, int64_t *pnum)
83
+{
84
+ int ret;
85
+ int64_t dummy;
86
+ IO_CODE();
87
+
88
+ ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset,
89
+ bytes, pnum ? pnum : &dummy, NULL,
90
+ NULL, NULL);
91
+ if (ret < 0) {
92
+ return ret;
93
+ }
94
+ return !!(ret & BDRV_BLOCK_ALLOCATED);
95
+}
96
+
97
int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
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
}
103
104
+/* See bdrv_is_allocated_above for documentation */
105
+int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
106
+ BlockDriverState *base,
107
+ bool include_base, int64_t offset,
108
+ int64_t bytes, int64_t *pnum)
109
+{
110
+ int depth;
111
+ int ret;
112
+ IO_CODE();
113
+
114
+ ret = bdrv_co_common_block_status_above(top, base, include_base, false,
115
+ offset, bytes, pnum, NULL, NULL,
116
+ &depth);
117
+ if (ret < 0) {
118
+ return ret;
119
+ }
120
+
121
+ if (ret & BDRV_BLOCK_ALLOCATED) {
122
+ return depth;
123
+ }
124
+ return 0;
125
+}
126
+
127
/*
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
}
146
--
147
2.38.1
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
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
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
3
These functions end up calling bdrv_*() 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: 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>
17
---
18
nbd/server.c | 29 ++++++++++++++++-------------
19
1 file changed, 16 insertions(+), 13 deletions(-)
20
21
diff --git a/nbd/server.c b/nbd/server.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/nbd/server.c
24
+++ b/nbd/server.c
25
@@ -XXX,XX +XXX,XX @@ static int nbd_extent_array_add(NBDExtentArray *ea,
26
return 0;
27
}
28
29
-static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
30
- uint64_t bytes, NBDExtentArray *ea)
31
+static int coroutine_fn blockstatus_to_extents(BlockDriverState *bs,
32
+ uint64_t offset, uint64_t bytes,
33
+ NBDExtentArray *ea)
34
{
35
while (bytes) {
36
uint32_t flags;
37
int64_t num;
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
}
48
49
-static int blockalloc_to_extents(BlockDriverState *bs, uint64_t offset,
50
- uint64_t bytes, NBDExtentArray *ea)
51
+static int coroutine_fn blockalloc_to_extents(BlockDriverState *bs,
52
+ uint64_t offset, uint64_t bytes,
53
+ NBDExtentArray *ea)
54
{
55
while (bytes) {
56
int64_t num;
57
- int ret = bdrv_is_allocated_above(bs, NULL, false, offset, bytes,
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
}
66
67
/* Get block status from the exported device and send it to the client */
68
-static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
69
- BlockDriverState *bs, uint64_t offset,
70
- uint32_t length, bool dont_fragment,
71
- bool last, uint32_t context_id,
72
- Error **errp)
73
+static int
74
+coroutine_fn nbd_co_send_block_status(NBDClient *client, uint64_t handle,
75
+ BlockDriverState *bs, uint64_t offset,
76
+ uint32_t length, bool dont_fragment,
77
+ bool last, uint32_t context_id,
78
+ Error **errp)
79
{
80
int ret;
81
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
82
--
83
2.38.1
diff view generated by jsdifflib
1
From: Liam Merwick <Liam.Merwick@oracle.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
The dev_id returned by the call to blk_get_attached_dev_id() in
3
Avoid mixing bdrv_* functions with blk_*, so create blk_* counterparts
4
blk_root_get_parent_desc() can be NULL (an internal call to
4
for bdrv_block_status_above and bdrv_is_allocated_above.
5
object_get_canonical_path may have returned NULL).
6
5
7
Instead of just checking this case before before dereferencing,
6
Note that since blk_co_block_status_above only calls the g_c_w function
8
adjust blk_get_attached_dev_id() to return the empty string if no
7
bdrv_common_block_status_above and is marked as coroutine_fn, call
9
object path can be found (similar to the case when blk->dev is NULL
8
directly bdrv_co_common_block_status_above() to avoid using a g_c_w.
10
and an empty string is returned).
9
Same applies to blk_co_is_allocated_above.
11
10
12
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
13
Message-id: 1541453919-25973-3-git-send-email-Liam.Merwick@oracle.com
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20221128142337.657646-5-eesposit@redhat.com>
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
16
---
17
block/block-backend.c | 3 ++-
17
include/sysemu/block-backend-io.h | 9 +++++++++
18
1 file changed, 2 insertions(+), 1 deletion(-)
18
block/block-backend.c | 21 ++++++++++++++++++++
19
block/commit.c | 4 ++--
20
nbd/server.c | 32 +++++++++++++++----------------
21
4 files changed, 48 insertions(+), 18 deletions(-)
19
22
23
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
24
index XXXXXXX..XXXXXXX 100644
25
--- a/include/sysemu/block-backend-io.h
26
+++ b/include/sysemu/block-backend-io.h
27
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
28
int64_t bytes, BdrvRequestFlags read_flags,
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
20
diff --git a/block/block-backend.c b/block/block-backend.c
43
diff --git a/block/block-backend.c b/block/block-backend.c
21
index XXXXXXX..XXXXXXX 100644
44
index XXXXXXX..XXXXXXX 100644
22
--- a/block/block-backend.c
45
--- a/block/block-backend.c
23
+++ b/block/block-backend.c
46
+++ b/block/block-backend.c
24
@@ -XXX,XX +XXX,XX @@ char *blk_get_attached_dev_id(BlockBackend *blk)
47
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
25
} else if (dev->id) {
48
return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags);
26
return g_strdup(dev->id);
49
}
50
51
+int coroutine_fn blk_co_block_status_above(BlockBackend *blk,
52
+ BlockDriverState *base,
53
+ int64_t offset, int64_t bytes,
54
+ int64_t *pnum, int64_t *map,
55
+ BlockDriverState **file)
56
+{
57
+ IO_CODE();
58
+ return bdrv_co_block_status_above(blk_bs(blk), base, offset, bytes, pnum,
59
+ map, file);
60
+}
61
+
62
+int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk,
63
+ BlockDriverState *base,
64
+ bool include_base, int64_t offset,
65
+ int64_t bytes, int64_t *pnum)
66
+{
67
+ IO_CODE();
68
+ return bdrv_co_is_allocated_above(blk_bs(blk), base, include_base, offset,
69
+ bytes, pnum);
70
+}
71
+
72
typedef struct BlkRwCo {
73
BlockBackend *blk;
74
int64_t offset;
75
diff --git a/block/commit.c b/block/commit.c
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
}
96
97
/* Do a sparse read and send the structured reply to the client.
98
- * Returns -errno if sending fails. bdrv_block_status_above() failure is
99
+ * Returns -errno if sending fails. blk_co_block_status_above() failure is
100
* reported to the client, at which point this function succeeds.
101
*/
102
static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
103
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
104
105
while (progress < size) {
106
int64_t pnum;
107
- int status = bdrv_block_status_above(blk_bs(exp->common.blk), NULL,
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
{
127
while (bytes) {
128
uint32_t flags;
129
int64_t num;
130
- int ret = bdrv_co_block_status_above(bs, NULL, offset, bytes, &num,
131
- NULL, NULL);
132
+ int ret = blk_co_block_status_above(blk, NULL, offset, bytes, &num,
133
+ NULL, NULL);
134
135
if (ret < 0) {
136
return ret;
137
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blockstatus_to_extents(BlockDriverState *bs,
138
return 0;
139
}
140
141
-static int coroutine_fn blockalloc_to_extents(BlockDriverState *bs,
142
+static int coroutine_fn blockalloc_to_extents(BlockBackend *blk,
143
uint64_t offset, uint64_t bytes,
144
NBDExtentArray *ea)
145
{
146
while (bytes) {
147
int64_t num;
148
- int ret = bdrv_co_is_allocated_above(bs, NULL, false, offset, bytes,
149
- &num);
150
+ int ret = blk_co_is_allocated_above(blk, NULL, false, offset, bytes,
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);
27
}
173
}
28
- return object_get_canonical_path(OBJECT(dev));
174
if (ret < 0) {
29
+
175
return nbd_co_send_structured_error(
30
+ return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
176
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
31
}
177
32
178
if (client->export_meta.base_allocation) {
33
/*
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,
34
--
194
--
35
2.19.1
195
2.38.1
36
37
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
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
1
From: Li Qiang <liq3ea@gmail.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
This avoid a memory leak in unhotplug nvme device.
3
We know that the string will stay around until the function
4
returns, and the parameter of drv->bdrv_co_create_opts is const char*,
5
so it must not be modified either.
4
6
5
Signed-off-by: Li Qiang <liq3ea@gmail.com>
7
Suggested-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Philippe Mathieu-Daudé <philmd@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>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
13
---
9
hw/block/nvme.c | 3 +++
14
block.c | 7 ++-----
10
1 file changed, 3 insertions(+)
15
1 file changed, 2 insertions(+), 5 deletions(-)
11
16
12
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
17
diff --git a/block.c b/block.c
13
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
14
--- a/hw/block/nvme.c
19
--- a/block.c
15
+++ b/hw/block/nvme.c
20
+++ b/block.c
16
@@ -XXX,XX +XXX,XX @@ static void nvme_exit(PCIDevice *pci_dev)
21
@@ -XXX,XX +XXX,XX @@ int bdrv_create(BlockDriver *drv, const char* filename,
17
g_free(n->cq);
22
Coroutine *co;
18
g_free(n->sq);
23
CreateCo cco = {
19
24
.drv = drv,
20
+ if (n->cmb_size_mb) {
25
- .filename = g_strdup(filename),
21
+ g_free(n->cmbuf);
26
+ .filename = filename,
22
+ }
27
.opts = opts,
23
msix_uninit_exclusive_bar(pci_dev);
28
.ret = NOT_DONE,
29
.err = NULL,
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
}
42
}
43
44
-out:
45
- g_free(cco.filename);
46
return ret;
24
}
47
}
25
48
26
--
49
--
27
2.19.1
50
2.38.1
28
29
diff view generated by jsdifflib
1
From: Fam Zheng <famz@redhat.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
The lock_fd field is not strictly necessary because transferring locked
3
Call two different functions depending on whether bdrv_create
4
bytes from old fd to the new one shouldn't fail anyway. This spares the
4
is in coroutine or not, following the same pattern as
5
user one fd per image.
5
generated_co_wrapper functions.
6
6
7
Signed-off-by: Fam Zheng <famz@redhat.com>
7
This allows to also call the coroutine function directly,
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
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>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
15
---
11
block/file-posix.c | 37 +++++++++++++------------------------
16
block.c | 69 ++++++++++++++++++++++++++++-----------------------------
12
1 file changed, 13 insertions(+), 24 deletions(-)
17
1 file changed, 34 insertions(+), 35 deletions(-)
13
18
14
diff --git a/block/file-posix.c b/block/file-posix.c
19
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
16
--- a/block/file-posix.c
21
--- a/block.c
17
+++ b/block/file-posix.c
22
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ do { \
23
@@ -XXX,XX +XXX,XX @@ typedef struct CreateCo {
19
24
Error *err;
20
typedef struct BDRVRawState {
25
} CreateCo;
21
int fd;
26
22
- int lock_fd;
27
-static void coroutine_fn bdrv_create_co_entry(void *opaque)
23
bool use_lock;
28
+static int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
24
int type;
29
+ QemuOpts *opts, Error **errp)
25
int open_flags;
30
{
26
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
31
- Error *local_err = NULL;
27
uint64_t shared_perm;
32
int ret;
28
33
+ GLOBAL_STATE_CODE();
29
/* The perms bits whose corresponding bytes are already locked in
34
+ ERRP_GUARD();
30
- * s->lock_fd. */
35
31
+ * s->fd. */
36
+ if (!drv->bdrv_co_create_opts) {
32
uint64_t locked_perm;
37
+ error_setg(errp, "Driver '%s' does not support image creation",
33
uint64_t locked_shared_perm;
38
+ drv->format_name);
34
39
+ return -ENOTSUP;
35
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
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
}
63
64
int bdrv_create(BlockDriver *drv, const char* filename,
65
QemuOpts *opts, Error **errp)
66
{
67
- int ret;
68
-
69
GLOBAL_STATE_CODE();
70
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
- }
84
-
85
if (qemu_in_coroutine()) {
86
/* Fast-path if already in coroutine context */
87
- bdrv_create_co_entry(&cco);
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;
36
}
106
}
37
s->fd = fd;
107
-
38
108
- ret = cco.ret;
39
- s->lock_fd = -1;
109
- if (ret < 0) {
40
- if (s->use_lock) {
110
- if (cco.err) {
41
- fd = qemu_open(filename, s->open_flags);
111
- error_propagate(errp, cco.err);
42
- if (fd < 0) {
112
- } else {
43
- ret = -errno;
113
- error_setg_errno(errp, -ret, "Could not create image");
44
- error_setg_errno(errp, errno, "Could not open '%s' for locking",
45
- filename);
46
- qemu_close(s->fd);
47
- goto fail;
48
- }
114
- }
49
- s->lock_fd = fd;
50
- }
115
- }
51
s->perm = 0;
52
s->shared_perm = BLK_PERM_ALL;
53
54
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
55
return 0;
56
}
57
58
- assert(s->lock_fd > 0);
59
-
116
-
60
switch (op) {
117
- return ret;
61
case RAW_PL_PREPARE:
62
- ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm,
63
+ ret = raw_apply_lock_bytes(s, s->fd, s->perm | new_perm,
64
~s->shared_perm | ~new_shared,
65
false, errp);
66
if (!ret) {
67
- ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp);
68
+ ret = raw_check_lock_bytes(s->fd, new_perm, new_shared, errp);
69
if (!ret) {
70
return 0;
71
}
72
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
73
op = RAW_PL_ABORT;
74
/* fall through to unlock bytes. */
75
case RAW_PL_ABORT:
76
- raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm,
77
+ raw_apply_lock_bytes(s, s->fd, s->perm, ~s->shared_perm,
78
true, &local_err);
79
if (local_err) {
80
/* Theoretically the above call only unlocks bytes and it cannot
81
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
82
}
83
break;
84
case RAW_PL_COMMIT:
85
- raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared,
86
+ raw_apply_lock_bytes(s, s->fd, new_perm, ~new_shared,
87
true, &local_err);
88
if (local_err) {
89
/* Theoretically the above call only unlocks bytes and it cannot
90
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
91
{
92
BDRVRawReopenState *rs = state->opaque;
93
BDRVRawState *s = state->bs->opaque;
94
+ Error *local_err = NULL;
95
96
s->check_cache_dropped = rs->check_cache_dropped;
97
s->open_flags = rs->open_flags;
98
99
+ /* Copy locks to the new fd before closing the old one. */
100
+ raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
101
+ ~s->locked_shared_perm, false, &local_err);
102
+ if (local_err) {
103
+ /* shouldn't fail in a sane host, but report it just in case. */
104
+ error_report_err(local_err);
105
+ }
106
qemu_close(s->fd);
107
s->fd = rs->fd;
108
109
@@ -XXX,XX +XXX,XX @@ static void raw_close(BlockDriverState *bs)
110
qemu_close(s->fd);
111
s->fd = -1;
112
}
113
- if (s->lock_fd >= 0) {
114
- qemu_close(s->lock_fd);
115
- s->lock_fd = -1;
116
- }
117
}
118
}
118
119
119
/**
120
/**
120
--
121
--
121
2.19.1
122
2.38.1
122
123
diff view generated by jsdifflib
New patch
1
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
3
It is always called in coroutine_fn callbacks, therefore
4
it can directly call bdrv_co_create().
5
6
Rename it to bdrv_co_create_file too.
7
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-9-eesposit@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
include/block/block-global-state.h | 3 ++-
15
block.c | 5 +++--
16
block/crypto.c | 2 +-
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
28
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
29
index XXXXXXX..XXXXXXX 100644
30
--- a/include/block/block-global-state.h
31
+++ b/include/block/block-global-state.h
32
@@ -XXX,XX +XXX,XX @@ BlockDriver *bdrv_find_protocol(const char *filename,
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
}
49
50
-int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
51
+int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
52
+ Error **errp)
53
{
54
QemuOpts *protocol_opts;
55
BlockDriver *drv;
56
@@ -XXX,XX +XXX,XX @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
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
105
index XXXXXXX..XXXXXXX 100644
106
--- a/block/qcow2.c
107
+++ b/block/qcow2.c
108
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv,
109
}
110
111
/* Create and open the file (protocol layer) */
112
- ret = bdrv_create_file(filename, opts, errp);
113
+ ret = bdrv_co_create_file(filename, opts, errp);
114
if (ret < 0) {
115
goto finish;
116
}
117
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv,
118
/* Create and open an external data file (protocol layer) */
119
val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE);
120
if (val) {
121
- ret = bdrv_create_file(val, opts, errp);
122
+ ret = bdrv_co_create_file(val, opts, errp);
123
if (ret < 0) {
124
goto finish;
125
}
126
diff --git a/block/qed.c b/block/qed.c
127
index XXXXXXX..XXXXXXX 100644
128
--- a/block/qed.c
129
+++ b/block/qed.c
130
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(BlockDriver *drv,
131
}
132
133
/* Create and open the file (protocol layer) */
134
- ret = bdrv_create_file(filename, opts, errp);
135
+ ret = bdrv_co_create_file(filename, opts, errp);
136
if (ret < 0) {
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
{
147
- return bdrv_create_file(filename, opts, errp);
148
+ return bdrv_co_create_file(filename, opts, errp);
149
}
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;
184
BlockBackend *blk = NULL;
185
186
- ret = bdrv_create_file(filename, opts, errp);
187
+ ret = bdrv_co_create_file(filename, opts, errp);
188
if (ret < 0) {
189
goto exit;
190
}
191
diff --git a/block/vpc.c b/block/vpc.c
192
index XXXXXXX..XXXXXXX 100644
193
--- a/block/vpc.c
194
+++ b/block/vpc.c
195
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(BlockDriver *drv,
196
}
197
198
/* Create and open the file (protocol layer) */
199
- ret = bdrv_create_file(filename, opts, errp);
200
+ ret = bdrv_co_create_file(filename, opts, errp);
201
if (ret < 0) {
202
goto fail;
203
}
204
--
205
2.38.1
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
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
New patch
1
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
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
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
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
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
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
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
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
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
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
New patch
1
Provide a separate function that just quiesces the users of a node to
2
prevent new requests from coming in, but without waiting for the already
3
in-flight I/O to complete.
1
4
5
This function can be used in contexts where polling is not allowed.
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20221207131838.239125-2-kwolf@redhat.com>
9
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
include/block/block-global-state.h | 1 +
13
block/io.c | 19 +++++++++++++------
14
2 files changed, 14 insertions(+), 6 deletions(-)
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
29
index XXXXXXX..XXXXXXX 100644
30
--- a/block/io.c
31
+++ b/block/io.c
32
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_all_poll(void)
33
* NOTE: no new block jobs or BlockDriverStates can be created between
34
* the bdrv_drain_all_begin() and bdrv_drain_all_end() calls.
35
*/
36
-void bdrv_drain_all_begin(void)
37
+void bdrv_drain_all_begin_nopoll(void)
38
{
39
BlockDriverState *bs = NULL;
40
GLOBAL_STATE_CODE();
41
42
- if (qemu_in_coroutine()) {
43
- bdrv_co_yield_to_drain(NULL, true, NULL, true);
44
- return;
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);
53
}
54
+}
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
--
70
2.38.1
diff view generated by jsdifflib
1
From: Fam Zheng <famz@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Signed-off-by: Fam Zheng <famz@redhat.com>
3
Block layer graph operations are always run under BQL in the main loop.
4
This is proved by the assertion qemu_in_main_thread() and its wrapper
5
macro GLOBAL_STATE_CODE.
6
7
However, there are also concurrent coroutines running in other iothreads
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>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
41
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
42
---
6
tests/test-image-locking.c | 157 +++++++++++++++++++++++++++++++++++++
43
include/block/aio.h | 9 ++
7
tests/Makefile.include | 2 +
44
include/block/block_int.h | 1 +
8
2 files changed, 159 insertions(+)
45
include/block/graph-lock.h | 139 ++++++++++++++++++++
9
create mode 100644 tests/test-image-locking.c
46
block/graph-lock.c | 261 +++++++++++++++++++++++++++++++++++++
10
47
block/meson.build | 1 +
11
diff --git a/tests/test-image-locking.c b/tests/test-image-locking.c
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
12
new file mode 100644
92
new file mode 100644
13
index XXXXXXX..XXXXXXX
93
index XXXXXXX..XXXXXXX
14
--- /dev/null
94
--- /dev/null
15
+++ b/tests/test-image-locking.c
95
+++ b/include/block/graph-lock.h
16
@@ -XXX,XX +XXX,XX @@
96
@@ -XXX,XX +XXX,XX @@
17
+/*
97
+/*
18
+ * Image locking tests
98
+ * Graph lock: rwlock to protect block layer graph manipulations (add/remove
19
+ *
99
+ * edges and nodes)
20
+ * Copyright (c) 2018 Red Hat Inc.
100
+ *
21
+ *
101
+ * Copyright (c) 2022 Red Hat
22
+ * Author: Fam Zheng <famz@redhat.com>
102
+ *
23
+ *
103
+ * This library is free software; you can redistribute it and/or
24
+ * 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
25
+ * of this software and associated documentation files (the "Software"), to deal
105
+ * License as published by the Free Software Foundation; either
26
+ * in the Software without restriction, including without limitation the rights
106
+ * version 2.1 of the License, or (at your option) any later version.
27
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
107
+ *
28
+ * copies of the Software, and to permit persons to whom the Software is
108
+ * This library is distributed in the hope that it will be useful,
29
+ * furnished to do so, subject to the following conditions:
109
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
30
+ *
110
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31
+ * The above copyright notice and this permission notice shall be included in
111
+ * Lesser General Public License for more details.
32
+ * all copies or substantial portions of the Software.
112
+ *
33
+ *
113
+ * You should have received a copy of the GNU Lesser General Public
34
+ * 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/>.
35
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
115
+ */
36
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
116
+#ifndef GRAPH_LOCK_H
37
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
117
+#define GRAPH_LOCK_H
38
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40
+ * THE SOFTWARE.
41
+ */
42
+
118
+
43
+#include "qemu/osdep.h"
119
+#include "qemu/osdep.h"
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
+ */
261
+
262
+#include "qemu/osdep.h"
263
+#include "qemu/main-loop.h"
264
+#include "block/graph-lock.h"
44
+#include "block/block.h"
265
+#include "block/block.h"
45
+#include "sysemu/block-backend.h"
266
+#include "block/block_int.h"
46
+#include "qapi/error.h"
267
+
47
+#include "qapi/qmp/qdict.h"
268
+/* Protects the list of aiocontext and orphaned_reader_count */
48
+
269
+static QemuMutex aio_context_list_lock;
49
+static BlockBackend *open_image(const char *path,
270
+
50
+ uint64_t perm, uint64_t shared_perm,
271
+/* Written and read with atomic operations. */
51
+ Error **errp)
272
+static int has_writer;
52
+{
273
+
53
+ Error *local_err = NULL;
274
+/*
54
+ BlockBackend *blk;
275
+ * A reader coroutine could move from an AioContext to another.
55
+ QDict *options = qdict_new();
276
+ * If this happens, there is no problem from the point of view of
56
+
277
+ * counters. The problem is that the total count becomes
57
+ qdict_put_str(options, "driver", "raw");
278
+ * unbalanced if one of the two AioContexts gets deleted.
58
+ blk = blk_new_open(path, NULL, options, BDRV_O_RDWR, &local_err);
279
+ * The count of readers must remain correct, so the AioContext's
59
+ if (blk) {
280
+ * balance is transferred to this glboal variable.
60
+ g_assert_null(local_err);
281
+ * Protected by aio_context_list_lock.
61
+ if (blk_set_perm(blk, perm, shared_perm, errp)) {
282
+ */
62
+ blk_unref(blk);
283
+static uint32_t orphaned_reader_count;
63
+ blk = NULL;
284
+
64
+ }
285
+/* Queue of readers waiting for the writer to finish */
65
+ } else {
286
+static CoQueue reader_queue;
66
+ error_propagate(errp, local_err);
287
+
288
+struct BdrvGraphRWlock {
289
+ /* How many readers are currently reading the graph. */
290
+ uint32_t reader_count;
291
+
292
+ /*
293
+ * List of BdrvGraphRWlock kept in graph-lock.c
294
+ * Protected by aio_context_list_lock
295
+ */
296
+ QTAILQ_ENTRY(BdrvGraphRWlock) next_aio;
297
+};
298
+
299
+/*
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
+{
309
+ qemu_mutex_init(&aio_context_list_lock);
310
+ qemu_co_queue_init(&reader_queue);
311
+}
312
+
313
+void register_aiocontext(AioContext *ctx)
314
+{
315
+ ctx->bdrv_graph = g_new0(BdrvGraphRWlock, 1);
316
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
317
+ assert(ctx->bdrv_graph->reader_count == 0);
318
+ QTAILQ_INSERT_TAIL(&aio_context_list, ctx->bdrv_graph, next_aio);
319
+}
320
+
321
+void unregister_aiocontext(AioContext *ctx)
322
+{
323
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
324
+ orphaned_reader_count += ctx->bdrv_graph->reader_count;
325
+ QTAILQ_REMOVE(&aio_context_list, ctx->bdrv_graph, next_aio);
326
+ g_free(ctx->bdrv_graph);
327
+}
328
+
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);
67
+ }
340
+ }
68
+ return blk;
341
+
69
+}
342
+ /* shouldn't overflow unless there are 2^31 readers */
70
+
343
+ assert((int32_t)rd >= 0);
71
+static void check_locked_bytes(int fd, uint64_t perm_locks,
344
+ return rd;
72
+ uint64_t shared_perm_locks)
345
+}
73
+{
346
+
74
+ int i;
347
+void bdrv_graph_wrlock(void)
75
+
348
+{
76
+ if (!perm_locks && !shared_perm_locks) {
349
+ GLOBAL_STATE_CODE();
77
+ g_assert(!qemu_lock_fd_test(fd, 0, 0, true));
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()) {
78
+ return;
405
+ return;
79
+ }
406
+ }
80
+ for (i = 0; (1ULL << i) <= BLK_PERM_ALL; i++) {
407
+
81
+ uint64_t bit = (1ULL << i);
408
+ for (;;) {
82
+ bool perm_expected = !!(bit & perm_locks);
409
+ qatomic_set(&bdrv_graph->reader_count,
83
+ bool shared_perm_expected = !!(bit & shared_perm_locks);
410
+ bdrv_graph->reader_count + 1);
84
+ g_assert_cmpint(perm_expected, ==,
411
+ /* make sure writer sees reader_count before we check has_writer */
85
+ !!qemu_lock_fd_test(fd, 100 + i, 1, true));
412
+ smp_mb();
86
+ g_assert_cmpint(shared_perm_expected, ==,
413
+
87
+ !!qemu_lock_fd_test(fd, 200 + i, 1, true));
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
+ }
88
+ }
463
+ }
89
+}
464
+}
90
+
465
+
91
+static void test_image_locking_basic(void)
466
+void coroutine_fn bdrv_graph_co_rdunlock(void)
92
+{
467
+{
93
+ BlockBackend *blk1, *blk2, *blk3;
468
+ BdrvGraphRWlock *bdrv_graph;
94
+ char img_path[] = "/tmp/qtest.XXXXXX";
469
+ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
95
+ uint64_t perm, shared_perm;
470
+
96
+
471
+ /* Do not lock if in main thread */
97
+ int fd = mkstemp(img_path);
472
+ if (qemu_in_main_thread()) {
98
+ assert(fd >= 0);
473
+ return;
99
+
100
+ perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ;
101
+ shared_perm = BLK_PERM_ALL;
102
+ blk1 = open_image(img_path, perm, shared_perm, &error_abort);
103
+ g_assert(blk1);
104
+
105
+ check_locked_bytes(fd, perm, ~shared_perm);
106
+
107
+ /* compatible perm between blk1 and blk2 */
108
+ blk2 = open_image(img_path, perm | BLK_PERM_RESIZE, shared_perm, NULL);
109
+ g_assert(blk2);
110
+ check_locked_bytes(fd, perm | BLK_PERM_RESIZE, ~shared_perm);
111
+
112
+ /* incompatible perm with already open blk1 and blk2 */
113
+ blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, NULL);
114
+ g_assert_null(blk3);
115
+
116
+ blk_unref(blk2);
117
+
118
+ /* Check that extra bytes in blk2 are correctly unlocked */
119
+ check_locked_bytes(fd, perm, ~shared_perm);
120
+
121
+ blk_unref(blk1);
122
+
123
+ /* Image is unused, no lock there */
124
+ check_locked_bytes(fd, 0, 0);
125
+ blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, &error_abort);
126
+ g_assert(blk3);
127
+ blk_unref(blk3);
128
+ close(fd);
129
+ unlink(img_path);
130
+}
131
+
132
+static void test_set_perm_abort(void)
133
+{
134
+ BlockBackend *blk1, *blk2;
135
+ char img_path[] = "/tmp/qtest.XXXXXX";
136
+ uint64_t perm, shared_perm;
137
+ int r;
138
+ int fd = mkstemp(img_path);
139
+ assert(fd >= 0);
140
+
141
+ perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ;
142
+ shared_perm = BLK_PERM_ALL;
143
+ blk1 = open_image(img_path, perm, shared_perm, &error_abort);
144
+ g_assert(blk1);
145
+
146
+ blk2 = open_image(img_path, perm, shared_perm, &error_abort);
147
+ g_assert(blk2);
148
+
149
+ check_locked_bytes(fd, perm, ~shared_perm);
150
+
151
+ /* A failed blk_set_perm mustn't change perm status (locked bytes) */
152
+ r = blk_set_perm(blk2, perm | BLK_PERM_RESIZE, BLK_PERM_WRITE_UNCHANGED,
153
+ NULL);
154
+ g_assert_cmpint(r, !=, 0);
155
+ check_locked_bytes(fd, perm, ~shared_perm);
156
+ blk_unref(blk1);
157
+ blk_unref(blk2);
158
+}
159
+
160
+int main(int argc, char **argv)
161
+{
162
+ bdrv_init();
163
+ qemu_init_main_loop(&error_abort);
164
+
165
+ g_test_init(&argc, &argv, NULL);
166
+
167
+ if (qemu_has_ofd_lock()) {
168
+ g_test_add_func("/image-locking/basic", test_image_locking_basic);
169
+ g_test_add_func("/image-locking/set-perm-abort", test_set_perm_abort);
170
+ }
474
+ }
171
+
475
+
172
+ return g_test_run();
476
+ qatomic_store_release(&bdrv_graph->reader_count,
173
+}
477
+ bdrv_graph->reader_count - 1);
174
diff --git a/tests/Makefile.include b/tests/Makefile.include
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
175
index XXXXXXX..XXXXXXX 100644
504
index XXXXXXX..XXXXXXX 100644
176
--- a/tests/Makefile.include
505
--- a/block/meson.build
177
+++ b/tests/Makefile.include
506
+++ b/block/meson.build
178
@@ -XXX,XX +XXX,XX @@ check-unit-y += tests/test-bdrv-drain$(EXESUF)
507
@@ -XXX,XX +XXX,XX @@ block_ss.add(files(
179
check-unit-y += tests/test-blockjob$(EXESUF)
508
'blkverify.c',
180
check-unit-y += tests/test-blockjob-txn$(EXESUF)
509
'block-backend.c',
181
check-unit-y += tests/test-block-backend$(EXESUF)
510
'block-copy.c',
182
+check-unit-y += tests/test-image-locking$(EXESUF)
511
+ 'graph-lock.c',
183
check-unit-y += tests/test-x86-cpuid$(EXESUF)
512
'commit.c',
184
# all code tested by test-x86-cpuid is inside topology.h
513
'copy-on-read.c',
185
ifeq ($(CONFIG_SOFTMMU),y)
514
'preallocate.c',
186
@@ -XXX,XX +XXX,XX @@ tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(te
187
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
188
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
189
tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y)
190
+tests/test-image-locking$(EXESUF): tests/test-image-locking.o $(test-block-obj-y) $(test-util-obj-y)
191
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
192
tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)
193
tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) $(test-crypto-obj-y)
194
--
515
--
195
2.19.1
516
2.38.1
196
197
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
3
Similar to the implementation in lockable.h, implement macros to
4
automatically take and release the rdlock.
5
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>
14
---
15
include/block/graph-lock.h | 66 ++++++++++++++++++++++++++++++++++++++
16
1 file changed, 66 insertions(+)
17
18
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
19
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/graph-lock.h
21
+++ b/include/block/graph-lock.h
22
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_graph_co_rdunlock(void);
23
void bdrv_graph_rdlock_main_loop(void);
24
void bdrv_graph_rdunlock_main_loop(void);
25
26
+typedef struct GraphLockable { } GraphLockable;
27
+
28
+/*
29
+ * In C, compound literals have the lifetime of an automatic variable.
30
+ * In C++ it would be different, but then C++ wouldn't need QemuLockable
31
+ * either...
32
+ */
33
+#define GML_OBJ_() (&(GraphLockable) { })
34
+
35
+static inline GraphLockable *graph_lockable_auto_lock(GraphLockable *x)
36
+{
37
+ bdrv_graph_co_rdlock();
38
+ return x;
39
+}
40
+
41
+static inline void graph_lockable_auto_unlock(GraphLockable *x)
42
+{
43
+ bdrv_graph_co_rdunlock();
44
+}
45
+
46
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock)
47
+
48
+#define WITH_GRAPH_RDLOCK_GUARD_(var) \
49
+ for (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \
50
+ var; \
51
+ graph_lockable_auto_unlock(var), var = NULL)
52
+
53
+#define WITH_GRAPH_RDLOCK_GUARD() \
54
+ WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__))
55
+
56
+#define GRAPH_RDLOCK_GUARD(x) \
57
+ g_autoptr(GraphLockable) \
58
+ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \
59
+ graph_lockable_auto_lock(GML_OBJ_())
60
+
61
+
62
+typedef struct GraphLockableMainloop { } GraphLockableMainloop;
63
+
64
+/*
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
+
71
+static inline GraphLockableMainloop *
72
+graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x)
73
+{
74
+ bdrv_graph_rdlock_main_loop();
75
+ return x;
76
+}
77
+
78
+static inline void
79
+graph_lockable_auto_unlock_mainloop(GraphLockableMainloop *x)
80
+{
81
+ bdrv_graph_rdunlock_main_loop();
82
+}
83
+
84
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockableMainloop,
85
+ graph_lockable_auto_unlock_mainloop)
86
+
87
+#define GRAPH_RDLOCK_GUARD_MAINLOOP(x) \
88
+ g_autoptr(GraphLockableMainloop) \
89
+ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \
90
+ graph_lockable_auto_lock_mainloop(GMLML_OBJ_())
91
+
92
#endif /* GRAPH_LOCK_H */
93
94
--
95
2.38.1
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
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>
16
---
17
stubs/graph-lock.c | 10 ++++++++++
18
util/async.c | 4 ++++
19
stubs/meson.build | 1 +
20
3 files changed, 15 insertions(+)
21
create mode 100644 stubs/graph-lock.c
22
23
diff --git a/stubs/graph-lock.c b/stubs/graph-lock.c
24
new file mode 100644
25
index XXXXXXX..XXXXXXX
26
--- /dev/null
27
+++ b/stubs/graph-lock.c
28
@@ -XXX,XX +XXX,XX @@
29
+#include "qemu/osdep.h"
30
+#include "block/graph-lock.h"
31
+
32
+void register_aiocontext(AioContext *ctx)
33
+{
34
+}
35
+
36
+void unregister_aiocontext(AioContext *ctx)
37
+{
38
+}
39
diff --git a/util/async.c b/util/async.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/util/async.c
42
+++ b/util/async.c
43
@@ -XXX,XX +XXX,XX @@
44
#include "qapi/error.h"
45
#include "block/aio.h"
46
#include "block/thread-pool.h"
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
--
81
2.38.1
diff view generated by jsdifflib
New 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.
1
4
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
New 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(+)
1
8
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
New 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(+)
1
8
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
1
From: Jeff Cody <jcody@redhat.com>
1
This enables clang's thread safety analysis (TSA), which we'll use to
2
statically check the block graph locking.
2
3
3
This adds configure options to control the following block drivers:
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
5
Message-Id: <20221207131838.239125-9-kwolf@redhat.com>
5
* Bochs
6
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
* Cloop
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
* Dmg
8
* Qcow (V1)
9
* Vdi
10
* Vvfat
11
* qed
12
* parallels
13
* sheepdog
14
15
Each of these defaults to being enabled.
16
17
Signed-off-by: Jeff Cody <jcody@redhat.com>
18
Signed-off-by: Markus Armbruster <armbru@redhat.com>
19
Message-id: 20181107063644.2254-1-armbru@redhat.com
20
Signed-off-by: Max Reitz <mreitz@redhat.com>
21
---
8
---
22
configure | 91 +++++++++++++++++++++++++++++++++++++++++++++
9
configure | 1 +
23
block/Makefile.objs | 22 ++++++++---
10
1 file changed, 1 insertion(+)
24
2 files changed, 107 insertions(+), 6 deletions(-)
25
11
26
diff --git a/configure b/configure
12
diff --git a/configure b/configure
27
index XXXXXXX..XXXXXXX 100755
13
index XXXXXXX..XXXXXXX 100755
28
--- a/configure
14
--- a/configure
29
+++ b/configure
15
+++ b/configure
30
@@ -XXX,XX +XXX,XX @@ tcmalloc="no"
16
@@ -XXX,XX +XXX,XX @@ add_to warn_flags -Wnested-externs
31
jemalloc="no"
17
add_to warn_flags -Wendif-labels
32
replication="yes"
18
add_to warn_flags -Wexpansion-to-defined
33
vxhs=""
19
add_to warn_flags -Wimplicit-fallthrough=2
34
+bochs="yes"
20
+add_to warn_flags -Wthread-safety
35
+cloop="yes"
21
36
+dmg="yes"
22
nowarn_flags=
37
+qcow1="yes"
23
add_to nowarn_flags -Wno-initializer-overrides
38
+vdi="yes"
39
+vvfat="yes"
40
+qed="yes"
41
+parallels="yes"
42
+sheepdog="yes"
43
libxml2=""
44
docker="no"
45
debug_mutex="no"
46
@@ -XXX,XX +XXX,XX @@ for opt do
47
;;
48
--enable-vxhs) vxhs="yes"
49
;;
50
+ --disable-bochs) bochs="no"
51
+ ;;
52
+ --enable-bochs) bochs="yes"
53
+ ;;
54
+ --disable-cloop) cloop="no"
55
+ ;;
56
+ --enable-cloop) cloop="yes"
57
+ ;;
58
+ --disable-dmg) dmg="no"
59
+ ;;
60
+ --enable-dmg) dmg="yes"
61
+ ;;
62
+ --disable-qcow1) qcow1="no"
63
+ ;;
64
+ --enable-qcow1) qcow1="yes"
65
+ ;;
66
+ --disable-vdi) vdi="no"
67
+ ;;
68
+ --enable-vdi) vdi="yes"
69
+ ;;
70
+ --disable-vvfat) vvfat="no"
71
+ ;;
72
+ --enable-vvfat) vvfat="yes"
73
+ ;;
74
+ --disable-qed) qed="no"
75
+ ;;
76
+ --enable-qed) qed="yes"
77
+ ;;
78
+ --disable-parallels) parallels="no"
79
+ ;;
80
+ --enable-parallels) parallels="yes"
81
+ ;;
82
+ --disable-sheepdog) sheepdog="no"
83
+ ;;
84
+ --enable-sheepdog) sheepdog="yes"
85
+ ;;
86
--disable-vhost-user) vhost_user="no"
87
;;
88
--enable-vhost-user)
89
@@ -XXX,XX +XXX,XX @@ disabled with --disable-FEATURE, default is enabled if available:
90
qom-cast-debug cast debugging support
91
tools build qemu-io, qemu-nbd and qemu-image tools
92
vxhs Veritas HyperScale vDisk backend support
93
+ bochs bochs image format support
94
+ cloop cloop image format support
95
+ dmg dmg image format support
96
+ qcow1 qcow v1 image format support
97
+ vdi vdi image format support
98
+ vvfat vvfat image format support
99
+ qed qed image format support
100
+ parallels parallels image format support
101
+ sheepdog sheepdog block driver support
102
crypto-afalg Linux AF_ALG crypto backend driver
103
vhost-user vhost-user support
104
capstone capstone disassembler support
105
@@ -XXX,XX +XXX,XX @@ echo "jemalloc support $jemalloc"
106
echo "avx2 optimization $avx2_opt"
107
echo "replication support $replication"
108
echo "VxHS block device $vxhs"
109
+echo "bochs support $bochs"
110
+echo "cloop support $cloop"
111
+echo "dmg support $dmg"
112
+echo "qcow v1 support $qcow1"
113
+echo "vdi support $vdi"
114
+echo "vvfat support $vvfat"
115
+echo "qed support $qed"
116
+echo "parallels support $parallels"
117
+echo "sheepdog support $sheepdog"
118
echo "capstone $capstone"
119
echo "docker $docker"
120
echo "libpmem support $libpmem"
121
@@ -XXX,XX +XXX,XX @@ if test "$libpmem" = "yes" ; then
122
echo "CONFIG_LIBPMEM=y" >> $config_host_mak
123
fi
124
125
+if test "$bochs" = "yes" ; then
126
+ echo "CONFIG_BOCHS=y" >> $config_host_mak
127
+fi
128
+if test "$cloop" = "yes" ; then
129
+ echo "CONFIG_CLOOP=y" >> $config_host_mak
130
+fi
131
+if test "$dmg" = "yes" ; then
132
+ echo "CONFIG_DMG=y" >> $config_host_mak
133
+fi
134
+if test "$qcow1" = "yes" ; then
135
+ echo "CONFIG_QCOW1=y" >> $config_host_mak
136
+fi
137
+if test "$vdi" = "yes" ; then
138
+ echo "CONFIG_VDI=y" >> $config_host_mak
139
+fi
140
+if test "$vvfat" = "yes" ; then
141
+ echo "CONFIG_VVFAT=y" >> $config_host_mak
142
+fi
143
+if test "$qed" = "yes" ; then
144
+ echo "CONFIG_QED=y" >> $config_host_mak
145
+fi
146
+if test "$parallels" = "yes" ; then
147
+ echo "CONFIG_PARALLELS=y" >> $config_host_mak
148
+fi
149
+if test "$sheepdog" = "yes" ; then
150
+ echo "CONFIG_SHEEPDOG=y" >> $config_host_mak
151
+fi
152
+
153
if test "$tcg_interpreter" = "yes"; then
154
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
155
elif test "$ARCH" = "sparc64" ; then
156
diff --git a/block/Makefile.objs b/block/Makefile.objs
157
index XXXXXXX..XXXXXXX 100644
158
--- a/block/Makefile.objs
159
+++ b/block/Makefile.objs
160
@@ -XXX,XX +XXX,XX @@
161
-block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
162
+block-obj-y += raw-format.o vmdk.o vpc.o
163
+block-obj-$(CONFIG_QCOW1) += qcow.o
164
+block-obj-$(CONFIG_VDI) += vdi.o
165
+block-obj-$(CONFIG_CLOOP) += cloop.o
166
+block-obj-$(CONFIG_BOCHS) += bochs.o
167
+block-obj-$(CONFIG_VVFAT) += vvfat.o
168
+block-obj-$(CONFIG_DMG) += dmg.o
169
+
170
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
171
-block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
172
-block-obj-y += qed-check.o
173
+block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
174
+block-obj-$(CONFIG_QED) += qed-check.o
175
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
176
block-obj-y += quorum.o
177
-block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
178
+block-obj-y += blkdebug.o blkverify.o blkreplay.o
179
+block-obj-$(CONFIG_PARALLELS) += parallels.o
180
block-obj-y += blklogwrites.o
181
block-obj-y += block-backend.o snapshot.o qapi.o
182
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
183
@@ -XXX,XX +XXX,XX @@ block-obj-y += null.o mirror.o commit.o io.o create.o
184
block-obj-y += throttle-groups.o
185
block-obj-$(CONFIG_LINUX) += nvme.o
186
187
-block-obj-y += nbd.o nbd-client.o sheepdog.o
188
+block-obj-y += nbd.o nbd-client.o
189
+block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
190
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
191
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
192
block-obj-$(CONFIG_LIBNFS) += nfs.o
193
@@ -XXX,XX +XXX,XX @@ gluster.o-libs := $(GLUSTERFS_LIBS)
194
vxhs.o-libs := $(VXHS_LIBS)
195
ssh.o-cflags := $(LIBSSH2_CFLAGS)
196
ssh.o-libs := $(LIBSSH2_LIBS)
197
-block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
198
+block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
199
+block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
200
dmg-bz2.o-libs := $(BZIP2_LIBS)
201
qcow.o-libs := -lz
202
linux-aio.o-libs := -laio
203
--
24
--
204
2.19.1
25
2.38.1
205
206
diff view generated by jsdifflib
New patch
1
The test case assumes that a drain only happens in one specific place
2
where it drains explicitly. This assumption happened to hold true until
3
now, but block layer functions may drain interally (any graph
4
modifications are going to do that through bdrv_graph_wrlock()), so this
5
is incorrect. Make sure that the test code in .drained_begin only runs
6
where we actually want it to run.
1
7
8
When scheduling a BH from .drained_begin, we also need to increase the
9
in_flight counter to make sure that the operation is actually completed
10
in time before the node that it works on goes away.
11
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20221207131838.239125-10-kwolf@redhat.com>
14
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
17
tests/unit/test-bdrv-drain.c | 18 ++++++++++++++++++
18
1 file changed, 18 insertions(+)
19
20
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/tests/unit/test-bdrv-drain.c
23
+++ b/tests/unit/test-bdrv-drain.c
24
@@ -XXX,XX +XXX,XX @@ struct detach_by_parent_data {
25
BlockDriverState *c;
26
BdrvChild *child_c;
27
bool by_parent_cb;
28
+ bool detach_on_drain;
29
};
30
static struct detach_by_parent_data detach_by_parent_data;
31
32
@@ -XXX,XX +XXX,XX @@ static void detach_indirect_bh(void *opaque)
33
{
34
struct detach_by_parent_data *data = opaque;
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
--
86
2.38.1
diff view generated by jsdifflib
1
From: Peter Maydell <peter.maydell@linaro.org>
1
bdrv_img_create() polls internally (when calling bdrv_create(), which is
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().
2
5
3
In the function external_snapshot_prepare() we have a
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
BlockdevSnapshotSync struct, which has the usual combination
7
Message-Id: <20221207131838.239125-11-kwolf@redhat.com>
5
of has_snapshot_node_name and snapshot_node_name fields for an
8
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
optional field. We set up a local variable
7
const char *snapshot_node_name =
8
s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
9
10
and then mostly use "if (!snapshot_node_name)" for checking
11
whether we have a snapshot node name. The exception is that in
12
one place we check s->has_snapshot_node_name instead. This
13
confuses Coverity (CID 1396473), which thinks it might be
14
possible to get here with s->has_snapshot_node_name true but
15
snapshot_node_name NULL, and warns that the call to
16
qdict_put_str() will segfault in that case.
17
18
Make the code consistent and unconfuse Coverity by using
19
the same check for this conditional that we do in the rest
20
of the surrounding code.
21
22
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
23
Reviewed-by: Alberto Garcia <berto@igalia.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
10
---
26
blockdev.c | 2 +-
11
block.c | 4 ++++
27
1 file changed, 1 insertion(+), 1 deletion(-)
12
blockdev.c | 4 ++++
13
2 files changed, 8 insertions(+)
28
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 @@ 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,
29
diff --git a/blockdev.c b/blockdev.c
30
diff --git a/blockdev.c b/blockdev.c
30
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
31
--- a/blockdev.c
32
--- a/blockdev.c
32
+++ b/blockdev.c
33
+++ b/blockdev.c
33
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
34
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
34
}
35
goto out;
35
36
}
36
options = qdict_new();
37
bdrv_refresh_filename(state->old_bs);
37
- if (s->has_snapshot_node_name) {
38
+
38
+ if (snapshot_node_name) {
39
+ aio_context_release(aio_context);
39
qdict_put_str(options, "node-name", snapshot_node_name);
40
bdrv_img_create(new_image_file, format,
40
}
41
state->old_bs->filename,
41
qdict_put_str(options, "driver", format);
42
state->old_bs->drv->format_name,
43
NULL, size, flags, false, &local_err);
44
+ aio_context_acquire(aio_context);
45
+
46
if (local_err) {
47
error_propagate(errp, local_err);
48
goto out;
42
--
49
--
43
2.19.1
50
2.38.1
44
45
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
3
Protect the main function where graph is modified.
4
5
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20221207131838.239125-12-kwolf@redhat.com>
8
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block.c | 7 +++----
13
1 file changed, 3 insertions(+), 4 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 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
20
*
21
* If @new_bs is non-NULL, the parent of @child must already be drained through
22
* @child.
23
- *
24
- * This function does not poll.
25
*/
26
static void bdrv_replace_child_noperm(BdrvChild *child,
27
BlockDriverState *new_bs)
28
@@ -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));
30
}
31
32
+ /* TODO Pull this up into the callers to avoid polling here */
33
+ bdrv_graph_wrlock();
34
if (old_bs) {
35
if (child->klass->detach) {
36
child->klass->detach(child);
37
}
38
- assert_bdrv_graph_writable(old_bs);
39
QLIST_REMOVE(child, next_parent);
40
}
41
42
child->bs = new_bs;
43
44
if (new_bs) {
45
- assert_bdrv_graph_writable(new_bs);
46
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
47
if (child->klass->attach) {
48
child->klass->attach(child);
49
}
50
}
51
+ bdrv_graph_wrunlock();
52
53
/*
54
* If the parent was drained through this BdrvChild previously, but new_bs
55
--
56
2.38.1
diff view generated by jsdifflib
1
From: Li Qiang <liq3ea@gmail.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Currently, when hotplug/unhotplug nvme device, it will cause an
3
We don't protect bdrv->aio_context with the graph rwlock,
4
assert in object.c. Following is the backtrack:
4
so these assertions are not needed
5
5
6
ERROR:qom/object.c:981:object_unref: assertion failed: (obj->ref > 0)
6
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Thread 2 "qemu-system-x86" received signal SIGABRT, Aborted.
8
Message-Id: <20221207131838.239125-13-kwolf@redhat.com>
9
[Switching to Thread 0x7fffcbd32700 (LWP 18844)]
9
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
0x00007fffdb9e4fff in raise () from /lib/x86_64-linux-gnu/libc.so.6
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
(gdb) bt
12
/lib/x86_64-linux-gnu/libglib-2.0.so.0
13
/lib/x86_64-linux-gnu/libglib-2.0.so.0
14
qom/object.c:981
15
/home/liqiang02/qemu-upstream/qemu/memory.c:1732
16
/home/liqiang02/qemu-upstream/qemu/memory.c:285
17
util/qemu-thread-posix.c:504
18
/lib/x86_64-linux-gnu/libpthread.so.0
19
20
This is caused by memory_region_unref in nvme_exit.
21
22
Remove it to make the PCIdevice refcount correct.
23
24
Signed-off-by: Li Qiang <liq3ea@gmail.com>
25
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
27
---
12
---
28
hw/block/nvme.c | 3 ---
13
block.c | 3 ---
29
1 file changed, 3 deletions(-)
14
1 file changed, 3 deletions(-)
30
15
31
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
16
diff --git a/block.c b/block.c
32
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
33
--- a/hw/block/nvme.c
18
--- a/block.c
34
+++ b/hw/block/nvme.c
19
+++ b/block.c
35
@@ -XXX,XX +XXX,XX @@ static void nvme_exit(PCIDevice *pci_dev)
20
@@ -XXX,XX +XXX,XX @@ static void bdrv_detach_aio_context(BlockDriverState *bs)
36
g_free(n->namespaces);
21
if (bs->quiesce_counter) {
37
g_free(n->cq);
22
aio_enable_external(bs->aio_context);
38
g_free(n->sq);
23
}
39
- if (n->cmbsz) {
24
- assert_bdrv_graph_writable(bs);
40
- memory_region_unref(&n->ctrl_mem);
25
bs->aio_context = NULL;
41
- }
42
43
msix_uninit_exclusive_bar(pci_dev);
44
}
26
}
27
28
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_aio_context(BlockDriverState *bs,
29
aio_disable_external(new_context);
30
}
31
32
- assert_bdrv_graph_writable(bs);
33
bs->aio_context = new_context;
34
35
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
36
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_aio_context_commit(void *opaque)
37
BlockDriverState *bs = (BlockDriverState *) state->bs;
38
AioContext *new_context = state->new_ctx;
39
AioContext *old_context = bdrv_get_aio_context(bs);
40
- assert_bdrv_graph_writable(bs);
41
42
/*
43
* Take the old AioContex when detaching it from bs.
45
--
44
--
46
2.19.1
45
2.38.1
47
48
diff view generated by jsdifflib
1
From: Liam Merwick <Liam.Merwick@oracle.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Although the function block_job_get() can return NULL, it would be a
3
Remove the old assert_bdrv_graph_writable, and replace it with
4
serious bug if it did so (because the job yields before executing anything
4
the new version using graph-lock API.
5
(if it started successfully); but otherwise, commit_active_start() would
6
have returned an error). However, as a precaution, before dereferencing
7
the 'job' pointer in img_commit() assert it is not NULL.
8
5
9
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
6
See the function documentation for more information.
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
11
Message-id: 1541453919-25973-4-git-send-email-Liam.Merwick@oracle.com
8
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Signed-off-by: Max Reitz <mreitz@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>
13
---
14
---
14
qemu-img.c | 1 +
15
include/block/block_int-global-state.h | 17 -----------------
15
1 file changed, 1 insertion(+)
16
include/block/graph-lock.h | 15 +++++++++++++++
17
block.c | 4 ++--
18
block/graph-lock.c | 11 +++++++++++
19
4 files changed, 28 insertions(+), 19 deletions(-)
16
20
17
diff --git a/qemu-img.c b/qemu-img.c
21
diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h
18
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
19
--- a/qemu-img.c
23
--- a/include/block/block_int-global-state.h
20
+++ b/qemu-img.c
24
+++ b/include/block/block_int-global-state.h
21
@@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv)
25
@@ -XXX,XX +XXX,XX @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
26
*/
27
void bdrv_drain_all_end_quiesce(BlockDriverState *bs);
28
29
-/**
30
- * Make sure that the function is running under both drain and BQL.
31
- * The latter protects from concurrent writings
32
- * from the GS API, while the former prevents concurrent reads
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
+
63
+/*
64
+ * assert_bdrv_graph_writable:
65
+ * Make sure that the writer is the main loop and has set @has_writer,
66
+ * so that incoming readers will pause.
67
+ */
68
+void assert_bdrv_graph_writable(void);
69
+
70
typedef struct GraphLockable { } GraphLockable;
71
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);
22
}
88
}
23
89
24
job = block_job_get("commit");
90
- assert_bdrv_graph_writable(bs);
25
+ assert(job);
91
+ assert_bdrv_graph_writable();
26
run_block_job(job, &local_err);
92
QLIST_REMOVE(child, next);
27
if (local_err) {
93
if (child == bs->backing) {
28
goto unref_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
+
104
+void assert_bdrv_graph_readable(void)
105
+{
106
+ assert(qemu_in_main_thread() || reader_count());
107
+}
108
+
109
+void assert_bdrv_graph_writable(void)
110
+{
111
+ assert(qemu_in_main_thread());
112
+ assert(qatomic_read(&has_writer));
113
+}
29
--
114
--
30
2.19.1
115
2.38.1
31
32
diff view generated by jsdifflib
1
From: Liam Merwick <Liam.Merwick@oracle.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Message-Id: <20221207131838.239125-15-kwolf@redhat.com>
3
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
6
include/block/graph-lock.h | 80 +++++++++++++++++++++++++++++++++-----
7
block/graph-lock.c | 3 ++
8
2 files changed, 73 insertions(+), 10 deletions(-)
2
9
3
The commit for 0e4e4318eaa5 increments QCOW2_OL_MAX_BITNR but does not
10
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
4
add an array entry for QCOW2_OL_BITMAP_DIRECTORY_BITNR to metadata_ol_names[].
5
As a result, an array dereference of metadata_ol_names[8] in
6
qcow2_pre_write_overlap_check() could result in a read outside of the array bounds.
7
8
Fixes: 0e4e4318eaa5 ('qcow2: add overlap check for bitmap directory')
9
10
Cc: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Message-id: 1541453919-25973-6-git-send-email-Liam.Merwick@oracle.com
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
---
17
block/qcow2-refcount.c | 18 ++++++++++--------
18
1 file changed, 10 insertions(+), 8 deletions(-)
19
20
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
21
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
22
--- a/block/qcow2-refcount.c
12
--- a/include/block/graph-lock.h
23
+++ b/block/qcow2-refcount.c
13
+++ b/include/block/graph-lock.h
24
@@ -XXX,XX +XXX,XX @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
14
@@ -XXX,XX +XXX,XX @@
15
#define GRAPH_LOCK_H
16
17
#include "qemu/osdep.h"
18
+#include "qemu/clang-tsa.h"
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
+
30
+extern BdrvGraphLock graph_lock;
31
+
32
+/*
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
+
45
+/*
46
+ * TSA annotations are not part of function types, so checks are defeated when
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
+
55
/*
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
+
104
+void TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA
105
+bdrv_graph_rdunlock_main_loop(void);
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
+
124
typedef struct GraphLockable { } GraphLockable;
125
126
/*
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;
25
}
143
}
26
144
27
static const char *metadata_ol_names[] = {
145
-static inline void graph_lockable_auto_unlock(GraphLockable *x)
28
- [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header",
146
+static inline void TSA_NO_TSA
29
- [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table",
147
+graph_lockable_auto_unlock(GraphLockable *x)
30
- [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table",
148
{
31
- [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table",
149
bdrv_graph_co_rdunlock();
32
- [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block",
150
}
33
- [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
151
@@ -XXX,XX +XXX,XX @@ typedef struct GraphLockableMainloop { } GraphLockableMainloop;
34
- [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
152
*/
35
- [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
153
#define GMLML_OBJ_() (&(GraphLockableMainloop) { })
36
+ [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header",
154
37
+ [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table",
155
-static inline GraphLockableMainloop *
38
+ [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table",
156
+/*
39
+ [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table",
157
+ * This is not marked as TSA_ACQUIRE() because TSA doesn't understand the
40
+ [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block",
158
+ * cleanup attribute and would therefore complain that the graph is never
41
+ [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
159
+ * unlocked. TSA_ASSERT() makes sure that the following calls know that we
42
+ [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
160
+ * hold the lock while unlocking is left unchecked.
43
+ [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
161
+ */
44
+ [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory",
162
+static inline GraphLockableMainloop * TSA_ASSERT(graph_lock) TSA_NO_TSA
45
};
163
graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x)
46
+QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names));
164
{
47
165
bdrv_graph_rdlock_main_loop();
48
/*
166
return x;
49
* First performs a check for metadata overlaps (through
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
50
--
188
--
51
2.19.1
189
2.38.1
52
53
diff view generated by jsdifflib
1
From: Liam Merwick <Liam.Merwick@oracle.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Message-Id: <20221207131838.239125-16-kwolf@redhat.com>
3
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
6
include/block/block_int-common.h | 4 ++--
7
include/block/graph-lock.h | 4 ++--
8
block.c | 4 ++--
9
3 files changed, 6 insertions(+), 6 deletions(-)
2
10
3
The calls to find_mapping_for_cluster() may return NULL but it
11
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
4
isn't always checked for before dereferencing the value returned.
5
Additionally, add some asserts to cover cases where NULL can't
6
be returned but which might not be obvious at first glance.
7
8
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
9
Message-id: 1541453919-25973-5-git-send-email-Liam.Merwick@oracle.com
10
[mreitz: Dropped superfluous check of "mapping" following an assertion
11
that it is not NULL, and fixed some indentation]
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
14
block/vvfat.c | 46 ++++++++++++++++++++++++++++++----------------
15
1 file changed, 30 insertions(+), 16 deletions(-)
16
17
diff --git a/block/vvfat.c b/block/vvfat.c
18
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
19
--- a/block/vvfat.c
13
--- a/include/block/block_int-common.h
20
+++ b/block/vvfat.c
14
+++ b/include/block/block_int-common.h
21
@@ -XXX,XX +XXX,XX @@ static inline void array_free(array_t* array)
15
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
22
/* does not automatically grow */
16
void (*activate)(BdrvChild *child, Error **errp);
23
static inline void* array_get(array_t* array,unsigned int index) {
17
int (*inactivate)(BdrvChild *child);
24
assert(index < array->next);
18
25
+ assert(array->pointer);
19
- void (*attach)(BdrvChild *child);
26
return array->pointer + index * array->item_size;
20
- void (*detach)(BdrvChild *child);
21
+ void GRAPH_WRLOCK_PTR (*attach)(BdrvChild *child);
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;
27
}
53
}
28
54
29
-static inline int array_ensure_allocated(array_t* array, int index)
55
-static void bdrv_child_cb_attach(BdrvChild *child)
30
+static inline void array_ensure_allocated(array_t *array, int index)
56
+static void GRAPH_WRLOCK bdrv_child_cb_attach(BdrvChild *child)
31
{
57
{
32
if((index + 1) * array->item_size > array->size) {
58
BlockDriverState *bs = child->opaque;
33
int new_size = (index + 32) * array->item_size;
59
34
array->pointer = g_realloc(array->pointer, new_size);
60
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child)
35
- if (!array->pointer)
36
- return -1;
37
+ assert(array->pointer);
38
memset(array->pointer + array->size, 0, new_size - array->size);
39
array->size = new_size;
40
array->next = index + 1;
41
}
61
}
42
-
43
- return 0;
44
}
62
}
45
63
46
static inline void* array_get_next(array_t* array) {
64
-static void bdrv_child_cb_detach(BdrvChild *child)
47
unsigned int next = array->next;
65
+static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child)
48
66
{
49
- if (array_ensure_allocated(array, next) < 0)
67
BlockDriverState *bs = child->opaque;
50
- return NULL;
51
-
52
+ array_ensure_allocated(array, next);
53
array->next = next + 1;
54
return array_get(array, next);
55
}
56
@@ -XXX,XX +XXX,XX @@ static int commit_direntries(BDRVVVFATState* s,
57
direntry_t* direntry = array_get(&(s->directory), dir_index);
58
uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
59
mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
60
-
61
int factor = 0x10 * s->sectors_per_cluster;
62
int old_cluster_count, new_cluster_count;
63
- int current_dir_index = mapping->info.dir.first_dir_index;
64
- int first_dir_index = current_dir_index;
65
+ int current_dir_index;
66
+ int first_dir_index;
67
int ret, i;
68
uint32_t c;
69
70
-DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
71
-
72
assert(direntry);
73
assert(mapping);
74
assert(mapping->begin == first_cluster);
75
@@ -XXX,XX +XXX,XX @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp
76
assert(mapping->mode & MODE_DIRECTORY);
77
assert(dir_index == 0 || is_directory(direntry));
78
79
+ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n",
80
+ mapping->path, parent_mapping_index));
81
+
82
+ current_dir_index = mapping->info.dir.first_dir_index;
83
+ first_dir_index = current_dir_index;
84
mapping->info.dir.parent_mapping_index = parent_mapping_index;
85
86
if (first_cluster == 0) {
87
@@ -XXX,XX +XXX,XX @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp
88
direntry = array_get(&(s->directory), first_dir_index + i);
89
if (is_directory(direntry) && !is_dot(direntry)) {
90
mapping = find_mapping_for_cluster(s, first_cluster);
91
+ if (mapping == NULL) {
92
+ return -1;
93
+ }
94
assert(mapping->mode & MODE_DIRECTORY);
95
ret = commit_direntries(s, first_dir_index + i,
96
array_index(&(s->mapping), mapping));
97
@@ -XXX,XX +XXX,XX @@ static int commit_one_file(BDRVVVFATState* s,
98
assert(offset < size);
99
assert((offset % s->cluster_size) == 0);
100
101
+ if (mapping == NULL) {
102
+ return -1;
103
+ }
104
+
105
for (i = s->cluster_size; i < offset; i += s->cluster_size)
106
c = modified_fat_get(s, c);
107
108
@@ -XXX,XX +XXX,XX @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
109
if (commit->action == ACTION_RENAME) {
110
mapping_t* mapping = find_mapping_for_cluster(s,
111
commit->param.rename.cluster);
112
- char* old_path = mapping->path;
113
+ char *old_path;
114
115
+ if (mapping == NULL) {
116
+ return -1;
117
+ }
118
+ old_path = mapping->path;
119
assert(commit->path);
120
mapping->path = commit->path;
121
if (rename(old_path, mapping->path))
122
@@ -XXX,XX +XXX,XX @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
123
direntry_t* d = direntry + i;
124
125
if (is_file(d) || (is_directory(d) && !is_dot(d))) {
126
+ int l;
127
+ char *new_path;
128
mapping_t* m = find_mapping_for_cluster(s,
129
begin_of_direntry(d));
130
- int l = strlen(m->path);
131
- char* new_path = g_malloc(l + diff + 1);
132
+ if (m == NULL) {
133
+ return -1;
134
+ }
135
+ l = strlen(m->path);
136
+ new_path = g_malloc(l + diff + 1);
137
138
assert(!strncmp(m->path, mapping->path, l2));
139
68
140
--
69
--
141
2.19.1
70
2.38.1
142
143
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
3
Add co_wrapper_bdrv_rdlock and co_wrapper_mixed_bdrv_rdlock option to
4
the block-coroutine-wrapper.py script.
5
6
This "_bdrv_rdlock" option takes and releases the graph rdlock when a
7
coroutine function is created.
8
9
This means that when used together with "_mixed", the function marked
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>
82
---
83
include/block/block-common.h | 9 ++++++++-
84
scripts/block-coroutine-wrapper.py | 12 ++++++++++++
85
2 files changed, 20 insertions(+), 1 deletion(-)
86
87
diff --git a/include/block/block-common.h b/include/block/block-common.h
88
index XXXXXXX..XXXXXXX 100644
89
--- a/include/block/block-common.h
90
+++ b/include/block/block-common.h
91
@@ -XXX,XX +XXX,XX @@
92
*
93
* Usage: read docs/devel/block-coroutine-wrapper.rst
94
*
95
- * There are 2 kind of specifiers:
96
+ * There are 4 kind of specifiers:
97
* - co_wrapper functions can be called by only non-coroutine context, because
98
* they always generate a new coroutine.
99
* - co_wrapper_mixed functions can be called by both coroutine and
100
* non-coroutine context.
101
+ * - co_wrapper_bdrv_rdlock are co_wrapper functions but automatically take and
102
+ * release the graph rdlock when creating a new coroutine
103
+ * - co_wrapper_mixed_bdrv_rdlock are co_wrapper_mixed functions but
104
+ * automatically take and release the graph rdlock when creating a new
105
+ * coroutine.
106
*/
107
#define co_wrapper
108
#define co_wrapper_mixed
109
+#define co_wrapper_bdrv_rdlock
110
+#define co_wrapper_mixed_bdrv_rdlock
111
112
#include "block/dirty-bitmap.h"
113
#include "block/blockjob.h"
114
diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py
115
index XXXXXXX..XXXXXXX 100644
116
--- a/scripts/block-coroutine-wrapper.py
117
+++ b/scripts/block-coroutine-wrapper.py
118
@@ -XXX,XX +XXX,XX @@ def __init__(self, return_type: str, name: str, args: str,
119
self.struct_name = snake_to_camel(self.name)
120
self.args = [ParamDecl(arg.strip()) for arg in args.split(',')]
121
self.create_only_co = 'mixed' not in variant
122
+ self.graph_rdlock = 'bdrv_rdlock' in variant
123
124
subsystem, subname = self.name.split('_', 1)
125
self.co_name = f'{subsystem}_co_{subname}'
126
@@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str:
127
"""
128
name = func.co_name
129
struct_name = func.struct_name
130
+ graph_assume_lock = 'assume_graph_lock();' if func.graph_rdlock else ''
131
+
132
return f"""\
133
{func.return_type} {func.name}({ func.gen_list('{decl}') })
134
{{
135
if (qemu_in_coroutine()) {{
136
+ {graph_assume_lock}
137
return {name}({ func.gen_list('{name}') });
138
}} else {{
139
{struct_name} s = {{
140
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
141
name = func.co_name
142
struct_name = func.struct_name
143
144
+ graph_lock=''
145
+ graph_unlock=''
146
+ if func.graph_rdlock:
147
+ graph_lock=' bdrv_graph_co_rdlock();'
148
+ graph_unlock=' bdrv_graph_co_rdunlock();'
149
+
150
creation_function = create_mixed_wrapper
151
if func.create_only_co:
152
creation_function = create_co_wrapper
153
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
154
{{
155
{struct_name} *s = opaque;
156
157
+{graph_lock}
158
s->ret = {name}({ func.gen_list('s->{name}') });
159
+{graph_unlock}
160
s->poll_state.in_progress = false;
161
162
aio_wait_kick();
163
--
164
2.38.1
diff view generated by jsdifflib
New patch
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
1
2
3
Take the rdlock already, before we add the assertions.
4
5
All these functions either read the graph recursively, or call
6
BlockDriver callbacks that will eventually need to be protected by the
7
graph rdlock.
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>
23
---
24
block/coroutines.h | 2 +-
25
include/block/block-io.h | 53 +++++++++++++++++++++++-----------------
26
2 files changed, 32 insertions(+), 23 deletions(-)
27
28
diff --git a/block/coroutines.h b/block/coroutines.h
29
index XXXXXXX..XXXXXXX 100644
30
--- a/block/coroutines.h
31
+++ b/block/coroutines.h
32
@@ -XXX,XX +XXX,XX @@ nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
33
* the "I/O or GS" API.
34
*/
35
36
-int co_wrapper_mixed
37
+int co_wrapper_mixed_bdrv_rdlock
38
bdrv_common_block_status_above(BlockDriverState *bs,
39
BlockDriverState *base,
40
bool include_base,
41
diff --git a/include/block/block-io.h b/include/block/block-io.h
42
index XXXXXXX..XXXXXXX 100644
43
--- a/include/block/block-io.h
44
+++ b/include/block/block-io.h
45
@@ -XXX,XX +XXX,XX @@
46
* to catch when they are accidentally called by the wrong API.
47
*/
48
49
-int co_wrapper_mixed bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
50
- int64_t bytes,
51
- BdrvRequestFlags flags);
52
+int co_wrapper_mixed_bdrv_rdlock
53
+bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes,
54
+ BdrvRequestFlags flags);
55
+
56
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags);
57
-int co_wrapper_mixed bdrv_pread(BdrvChild *child, int64_t offset,
58
- int64_t bytes, void *buf,
59
- BdrvRequestFlags flags);
60
-int co_wrapper_mixed bdrv_pwrite(BdrvChild *child, int64_t offset,
61
- int64_t bytes, const void *buf,
62
- BdrvRequestFlags flags);
63
-int co_wrapper_mixed bdrv_pwrite_sync(BdrvChild *child, int64_t offset,
64
- int64_t bytes, const void *buf,
65
- BdrvRequestFlags flags);
66
+
67
+int co_wrapper_mixed_bdrv_rdlock
68
+bdrv_pread(BdrvChild *child, int64_t offset, int64_t bytes, void *buf,
69
+ BdrvRequestFlags flags);
70
+
71
+int co_wrapper_mixed_bdrv_rdlock
72
+bdrv_pwrite(BdrvChild *child, int64_t offset,int64_t bytes,
73
+ const void *buf, BdrvRequestFlags flags);
74
+
75
+int co_wrapper_mixed_bdrv_rdlock
76
+bdrv_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes,
77
+ const void *buf, BdrvRequestFlags flags);
78
+
79
int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset,
80
int64_t bytes, const void *buf,
81
BdrvRequestFlags flags);
82
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset,
83
84
void bdrv_drain(BlockDriverState *bs);
85
86
-int co_wrapper_mixed
87
+int co_wrapper_mixed_bdrv_rdlock
88
bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
89
PreallocMode prealloc, BdrvRequestFlags flags, Error **errp);
90
91
-int co_wrapper_mixed bdrv_check(BlockDriverState *bs, BdrvCheckResult *res,
92
- BdrvCheckMode fix);
93
+int co_wrapper_mixed_bdrv_rdlock
94
+bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
95
96
/* Invalidate any cached metadata used by image formats */
97
-int co_wrapper_mixed bdrv_invalidate_cache(BlockDriverState *bs,
98
- Error **errp);
99
-int co_wrapper_mixed bdrv_flush(BlockDriverState *bs);
100
-int co_wrapper_mixed bdrv_pdiscard(BdrvChild *child, int64_t offset,
101
- int64_t bytes);
102
-int co_wrapper_mixed
103
+int co_wrapper_mixed_bdrv_rdlock
104
+bdrv_invalidate_cache(BlockDriverState *bs, Error **errp);
105
+
106
+int co_wrapper_mixed_bdrv_rdlock bdrv_flush(BlockDriverState *bs);
107
+
108
+int co_wrapper_mixed_bdrv_rdlock
109
+bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes);
110
+
111
+int co_wrapper_mixed_bdrv_rdlock
112
bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
113
-int co_wrapper_mixed
114
+
115
+int co_wrapper_mixed_bdrv_rdlock
116
bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
117
118
/**
119
--
120
2.38.1
diff view generated by jsdifflib
New patch
1
The generated coroutine wrappers already take care to take the lock in
2
the non-coroutine path, and assume that the lock is already taken in the
3
coroutine path.
1
4
5
The only thing we need to do for the wrapped function is adding the
6
GRAPH_RDLOCK annotation. Doing so also allows us to mark the
7
corresponding callbacks in BlockDriver as GRAPH_RDLOCK_PTR.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20221207131838.239125-19-kwolf@redhat.com>
11
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
block/coroutines.h | 17 ++++++++++-------
15
include/block/block_int-common.h | 20 +++++++++-----------
16
block.c | 2 ++
17
block/io.c | 2 ++
18
4 files changed, 23 insertions(+), 18 deletions(-)
19
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
121
index XXXXXXX..XXXXXXX 100644
122
--- a/block/io.c
123
+++ b/block/io.c
124
@@ -XXX,XX +XXX,XX @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
125
BlockDriverState *child_bs = bdrv_primary_bs(bs);
126
int ret;
127
IO_CODE();
128
+ assert_bdrv_graph_readable();
129
130
ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL);
131
if (ret < 0) {
132
@@ -XXX,XX +XXX,XX @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
133
BlockDriverState *child_bs = bdrv_primary_bs(bs);
134
int ret;
135
IO_CODE();
136
+ assert_bdrv_graph_readable();
137
138
ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL);
139
if (ret < 0) {
140
--
141
2.38.1
diff view generated by jsdifflib