1
The following changes since commit ccdf06c1db192152ac70a1dd974c624f566cb7d4:
1
The following changes since commit 5204b499a6cae4dfd9fe762d5e6e82224892383b:
2
2
3
Open 6.1 development tree (2021-04-30 11:15:40 +0100)
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 68bf7336533faa6aa90fdd4558edddbf5d8ef814:
9
for you to fetch changes up to 2ad19e5dc950d4b340894846b9e71c0b20f9a1cc:
10
10
11
vhost-user-blk: Fail gracefully on too large queue size (2021-04-30 12:27:48 +0200)
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
- Fix permission update order problems with block graph changes
16
- Code cleanups around block graph modification
17
- qemu-img convert: Unshare write permission for source
17
- Simplify drain
18
- vhost-user-blk: Fail gracefully on too large queue size
18
- coroutine_fn correctness fixes, including splitting generated
19
coroutine wrappers into co_wrapper (to be called only from
20
non-coroutine context) and co_wrapper_mixed (both coroutine and
21
non-coroutine context)
22
- Introduce a block graph rwlock
19
23
20
----------------------------------------------------------------
24
----------------------------------------------------------------
21
Kevin Wolf (3):
25
Emanuele Giuseppe Esposito (21):
22
block: Add BDRV_O_NO_SHARE for blk_new_open()
26
block-io: introduce coroutine_fn duplicates for bdrv_common_block_status_above callers
23
qemu-img convert: Unshare write permission for source
27
block-copy: add coroutine_fn annotations
24
vhost-user-blk: Fail gracefully on too large queue size
28
nbd/server.c: add coroutine_fn annotations
29
block-backend: replace bdrv_*_above with blk_*_above
30
block/vmdk: add coroutine_fn annotations
31
block: avoid duplicating filename string in bdrv_create
32
block: distinguish between bdrv_create running in coroutine and not
33
block: bdrv_create_file is a coroutine_fn
34
block: rename generated_co_wrapper in co_wrapper_mixed
35
block-coroutine-wrapper.py: introduce co_wrapper
36
block-coroutine-wrapper.py: support functions without bs arg
37
block-coroutine-wrapper.py: support also basic return types
38
block: convert bdrv_create to co_wrapper
39
block/dirty-bitmap: convert coroutine-only functions to co_wrapper
40
graph-lock: Implement guard macros
41
async: Register/unregister aiocontext in graph lock list
42
block: wrlock in bdrv_replace_child_noperm
43
block: remove unnecessary assert_bdrv_graph_writable()
44
block: assert that graph read and writes are performed correctly
45
block-coroutine-wrapper.py: introduce annotations that take the graph rdlock
46
block: use co_wrapper_mixed_bdrv_rdlock in functions taking the rdlock
25
47
26
Vladimir Sementsov-Ogievskiy (36):
48
Kevin Wolf (25):
27
tests/test-bdrv-graph-mod: add test_parallel_exclusive_write
49
qed: Don't yield in bdrv_qed_co_drain_begin()
28
tests/test-bdrv-graph-mod: add test_parallel_perm_update
50
test-bdrv-drain: Don't yield in .bdrv_co_drained_begin/end()
29
tests/test-bdrv-graph-mod: add test_append_greedy_filter
51
block: Revert .bdrv_drained_begin/end to non-coroutine_fn
30
block: bdrv_append(): don't consume reference
52
block: Remove drained_end_counter
31
block: BdrvChildClass: add .get_parent_aio_context handler
53
block: Inline bdrv_drain_invoke()
32
block: drop ctx argument from bdrv_root_attach_child
54
block: Fix locking for bdrv_reopen_queue_child()
33
block: make bdrv_reopen_{prepare,commit,abort} private
55
block: Drain individual nodes during reopen
34
util: add transactions.c
56
block: Don't use subtree drains in bdrv_drop_intermediate()
35
block: bdrv_refresh_perms: check for parents permissions conflict
57
stream: Replace subtree drain with a single node drain
36
block: refactor bdrv_child* permission functions
58
block: Remove subtree drains
37
block: rewrite bdrv_child_try_set_perm() using bdrv_refresh_perms()
59
block: Call drain callbacks only once
38
block: inline bdrv_child_*() permission functions calls
60
block: Remove ignore_bds_parents parameter from drain_begin/end.
39
block: use topological sort for permission update
61
block: Drop out of coroutine in bdrv_do_drained_begin_quiesce()
40
block: add bdrv_drv_set_perm transaction action
62
block: Don't poll in bdrv_replace_child_noperm()
41
block: add bdrv_list_* permission update functions
63
block: Remove poll parameter from bdrv_parent_drained_begin_single()
42
block: add bdrv_replace_child_safe() transaction action
64
block: Factor out bdrv_drain_all_begin_nopoll()
43
block: fix bdrv_replace_node_common
65
Import clang-tsa.h
44
block: add bdrv_attach_child_common() transaction action
66
clang-tsa: Add TSA_ASSERT() macro
45
block: add bdrv_attach_child_noperm() transaction action
67
clang-tsa: Add macros for shared locks
46
block: split out bdrv_replace_node_noperm()
68
configure: Enable -Wthread-safety if present
47
block: adapt bdrv_append() for inserting filters
69
test-bdrv-drain: Fix incorrrect drain assumptions
48
block: add bdrv_remove_filter_or_cow transaction action
70
block: Fix locking in external_snapshot_prepare()
49
block: introduce bdrv_drop_filter()
71
graph-lock: TSA annotations for lock/unlock functions
50
block/backup-top: drop .active
72
Mark assert_bdrv_graph_readable/writable() GRAPH_RD/WRLOCK
51
block: drop ignore_children for permission update functions
73
block: GRAPH_RDLOCK for functions only called by co_wrappers
52
block: make bdrv_unset_inherits_from to be a transaction action
53
block: make bdrv_refresh_limits() to be a transaction action
54
block: add bdrv_set_backing_noperm() transaction action
55
block: bdrv_reopen_multiple(): move bdrv_flush to separate pre-prepare
56
block: bdrv_reopen_multiple: refresh permissions on updated graph
57
block: drop unused permission update functions
58
block: inline bdrv_check_perm_common()
59
block: inline bdrv_replace_child()
60
block: refactor bdrv_child_set_perm_safe() transaction action
61
block: rename bdrv_replace_child_safe() to bdrv_replace_child()
62
block: refactor bdrv_node_check_perm()
63
74
64
include/block/block.h | 14 +-
75
Paolo Bonzini (1):
65
include/block/block_int.h | 8 +-
76
graph-lock: Introduce a lock to protect block graph operations
66
include/qemu/transactions.h | 63 ++
67
block.c | 1329 ++++++++++++++++++++-------------
68
block/backup-top.c | 48 +-
69
block/block-backend.c | 30 +-
70
block/commit.c | 1 +
71
block/file-posix.c | 91 +--
72
block/io.c | 31 +-
73
block/mirror.c | 3 -
74
blockdev.c | 4 -
75
blockjob.c | 11 +-
76
hw/block/vhost-user-blk.c | 5 +
77
qemu-img.c | 2 +-
78
tests/unit/test-bdrv-drain.c | 2 +-
79
tests/unit/test-bdrv-graph-mod.c | 209 +++++-
80
util/transactions.c | 96 +++
81
MAINTAINERS | 6 +
82
tests/qemu-iotests/245 | 2 +-
83
tests/qemu-iotests/283.out | 2 +-
84
tests/qemu-iotests/tests/qsd-jobs.out | 2 +-
85
util/meson.build | 1 +
86
22 files changed, 1280 insertions(+), 680 deletions(-)
87
create mode 100644 include/qemu/transactions.h
88
create mode 100644 util/transactions.c
89
77
78
Vladimir Sementsov-Ogievskiy (4):
79
block: Inline bdrv_detach_child()
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
90
83
84
docs/devel/block-coroutine-wrapper.rst | 6 +-
85
configure | 1 +
86
block/block-gen.h | 11 +-
87
block/coroutines.h | 21 +-
88
include/block/aio.h | 9 +
89
include/block/block-common.h | 27 ++-
90
include/block/block-copy.h | 5 +-
91
include/block/block-global-state.h | 15 +-
92
include/block/block-io.h | 136 +++++------
93
include/block/block_int-common.h | 49 ++--
94
include/block/block_int-global-state.h | 17 --
95
include/block/block_int-io.h | 12 -
96
include/block/block_int.h | 1 +
97
include/block/dirty-bitmap.h | 10 +-
98
include/block/graph-lock.h | 280 +++++++++++++++++++++++
99
include/qemu/clang-tsa.h | 114 ++++++++++
100
include/sysemu/block-backend-io.h | 77 ++++---
101
block.c | 404 ++++++++++++++++++---------------
102
block/block-backend.c | 25 +-
103
block/block-copy.c | 21 +-
104
block/commit.c | 4 +-
105
block/crypto.c | 2 +-
106
block/dirty-bitmap.c | 88 +------
107
block/graph-lock.c | 275 ++++++++++++++++++++++
108
block/io.c | 367 ++++++++++--------------------
109
block/parallels.c | 2 +-
110
block/qcow.c | 2 +-
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
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
2
3
bdrv_replace_child() has only one caller, the second argument is
3
The only caller is bdrv_root_unref_child(), let's just do the logic
4
unused. Inline it now. This triggers deletion of some more unused
4
directly in it. It simplifies further conversion of
5
interfaces.
5
bdrv_root_unref_child() to transaction actions.
6
6
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20210428151804.439460-34-vsementsov@virtuozzo.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
---
12
block.c | 101 ++++++++++----------------------------------------------
13
block.c | 46 +++++++++++++++++++---------------------------
13
1 file changed, 18 insertions(+), 83 deletions(-)
14
1 file changed, 19 insertions(+), 27 deletions(-)
14
15
15
diff --git a/block.c b/block.c
16
diff --git a/block.c b/block.c
16
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
17
--- a/block.c
18
--- a/block.c
18
+++ b/block.c
19
+++ b/block.c
19
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
20
@@ -XXX,XX +XXX,XX @@ static BdrvChild *bdrv_attach_child_noperm(BlockDriverState *parent_bs,
20
return 0;
21
tran, errp);
21
}
22
}
22
23
23
-static void bdrv_node_set_perm(BlockDriverState *bs)
24
-static void bdrv_detach_child(BdrvChild *child)
24
-{
25
- BlockDriver *drv = bs->drv;
26
- BdrvChild *c;
27
-
28
- if (!drv) {
29
- return;
30
- }
31
-
32
- bdrv_drv_set_perm_commit(bs);
33
-
34
- /* Drivers that never have children can omit .bdrv_child_perm() */
35
- if (!drv->bdrv_child_perm) {
36
- assert(QLIST_EMPTY(&bs->children));
37
- return;
38
- }
39
-
40
- /* Update all children */
41
- QLIST_FOREACH(c, &bs->children, next) {
42
- bdrv_child_set_perm_commit(c);
43
- }
44
-}
45
-
46
-static void bdrv_list_set_perm(GSList *list)
47
-{
48
- for ( ; list; list = list->next) {
49
- bdrv_node_set_perm((BlockDriverState *)list->data);
50
- }
51
-}
52
-
53
-static void bdrv_set_perm(BlockDriverState *bs)
54
-{
55
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
56
- return bdrv_list_set_perm(list);
57
-}
58
-
59
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
60
uint64_t *shared_perm)
61
{
62
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
63
}
64
}
65
66
-/*
67
- * Updates @child to change its reference to point to @new_bs, including
68
- * checking and applying the necessary permission updates both to the old node
69
- * and to @new_bs.
70
- *
71
- * NULL is passed as @new_bs for removing the reference before freeing @child.
72
- *
73
- * If @new_bs is not NULL, bdrv_check_perm() must be called beforehand, as this
74
- * function uses bdrv_set_perm() to update the permissions according to the new
75
- * reference that @new_bs gets.
76
- *
77
- * Callers must ensure that child->frozen is false.
78
- */
79
-static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
80
-{
25
-{
81
- BlockDriverState *old_bs = child->bs;
26
- BlockDriverState *old_bs = child->bs;
82
-
27
-
83
- /* Asserts that child->frozen == false */
28
- GLOBAL_STATE_CODE();
84
- bdrv_replace_child_noperm(child, new_bs);
29
- bdrv_replace_child_noperm(child, NULL);
85
-
30
- bdrv_child_free(child);
86
- /*
87
- * Start with the new node's permissions. If @new_bs is a (direct
88
- * or indirect) child of @old_bs, we must complete the permission
89
- * update on @new_bs before we loosen the restrictions on @old_bs.
90
- * Otherwise, bdrv_check_perm() on @old_bs would re-initiate
91
- * updating the permissions of @new_bs, and thus not purely loosen
92
- * restrictions.
93
- */
94
- if (new_bs) {
95
- bdrv_set_perm(new_bs);
96
- }
97
-
31
-
98
- if (old_bs) {
32
- if (old_bs) {
99
- /*
33
- /*
100
- * Update permissions for old node. We're just taking a parent away, so
34
- * Update permissions for old node. We're just taking a parent away, so
101
- * we're loosening restrictions. Errors of permission update are not
35
- * we're loosening restrictions. Errors of permission update are not
102
- * fatal in this case, ignore them.
36
- * fatal in this case, ignore them.
103
- */
37
- */
104
- bdrv_refresh_perms(old_bs, NULL);
38
- bdrv_refresh_perms(old_bs, NULL);
105
-
39
-
106
- /* When the parent requiring a non-default AioContext is removed, the
40
- /*
107
- * node moves back to the main AioContext */
41
- * When the parent requiring a non-default AioContext is removed, the
108
- bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
42
- * node moves back to the main AioContext
43
- */
44
- bdrv_try_change_aio_context(old_bs, qemu_get_aio_context(), NULL, NULL);
109
- }
45
- }
110
-}
46
-}
111
-
47
-
112
static void bdrv_child_free(void *opaque)
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)
113
{
54
{
114
BdrvChild *c = opaque;
55
- BlockDriverState *child_bs;
115
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
56
+ BlockDriverState *child_bs = child->bs;
116
57
117
static void bdrv_detach_child(BdrvChild *child)
58
GLOBAL_STATE_CODE();
118
{
59
+ bdrv_replace_child_noperm(child, NULL);
119
- bdrv_replace_child(child, NULL);
60
+ bdrv_child_free(child);
120
+ BlockDriverState *old_bs = child->bs;
121
+
61
+
122
+ bdrv_replace_child_noperm(child, NULL);
62
+ if (child_bs) {
123
bdrv_remove_empty_child(child);
124
+
125
+ if (old_bs) {
126
+ /*
63
+ /*
127
+ * Update permissions for old node. We're just taking a parent away, so
64
+ * Update permissions for old node. We're just taking a parent away, so
128
+ * we're loosening restrictions. Errors of permission update are not
65
+ * we're loosening restrictions. Errors of permission update are not
129
+ * fatal in this case, ignore them.
66
+ * fatal in this case, ignore them.
130
+ */
67
+ */
131
+ bdrv_refresh_perms(old_bs, NULL);
68
+ bdrv_refresh_perms(child_bs, NULL);
132
+
69
+
133
+ /*
70
+ /*
134
+ * When the parent requiring a non-default AioContext is removed, the
71
+ * When the parent requiring a non-default AioContext is removed, the
135
+ * node moves back to the main AioContext
72
+ * node moves back to the main AioContext
136
+ */
73
+ */
137
+ bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
74
+ bdrv_try_change_aio_context(child_bs, qemu_get_aio_context(), NULL,
75
+ NULL);
138
+ }
76
+ }
77
78
- child_bs = child->bs;
79
- bdrv_detach_child(child);
80
bdrv_unref(child_bs);
139
}
81
}
140
82
141
/*
142
--
83
--
143
2.30.2
84
2.38.1
144
145
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
2
3
bdrv_check_perm_common() has only one caller, so no more sense in
3
Drop this simple wrapper used only in one place. We have too many graph
4
"common".
4
modifying functions even without it.
5
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-33-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
---
11
block.c | 32 +++-----------------------------
12
block.c | 15 +--------------
12
1 file changed, 3 insertions(+), 29 deletions(-)
13
1 file changed, 1 insertion(+), 14 deletions(-)
13
14
14
diff --git a/block.c b/block.c
15
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
17
--- a/block.c
17
+++ b/block.c
18
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
19
@@ -XXX,XX +XXX,XX @@ static bool bdrv_recurse_has_child(BlockDriverState *bs,
19
return 0;
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);
20
}
30
}
21
31
22
-/*
32
-/*
23
- * If use_cumulative_perms is true, use cumulative_perms and
33
- * A function to remove backing-chain child of @bs if exists: cow child for
24
- * cumulative_shared_perms for first element of the list. Otherwise just refresh
34
- * format nodes (always .backing) and filter child for filters (may be .file or
25
- * all permissions.
35
- * .backing)
26
- */
36
- */
27
-static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
37
-static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
28
- bool use_cumulative_perms,
38
- Transaction *tran)
29
- uint64_t cumulative_perms,
30
- uint64_t cumulative_shared_perms,
31
- Transaction *tran, Error **errp)
32
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
33
+ Transaction *tran, Error **errp)
34
{
35
int ret;
36
+ uint64_t cumulative_perms, cumulative_shared_perms;
37
BlockDriverState *bs;
38
39
- if (use_cumulative_perms) {
40
- bs = list->data;
41
-
42
- ret = bdrv_node_check_perm(bs, q, cumulative_perms,
43
- cumulative_shared_perms,
44
- tran, errp);
45
- if (ret < 0) {
46
- return ret;
47
- }
48
-
49
- list = list->next;
50
- }
51
-
52
for ( ; list; list = list->next) {
53
bs = list->data;
54
55
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
56
return 0;
57
}
58
59
-static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
60
- Transaction *tran, Error **errp)
61
-{
39
-{
62
- return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
40
- bdrv_remove_child(bdrv_filter_or_cow_child(bs), tran);
63
-}
41
-}
64
-
42
-
65
static void bdrv_node_set_perm(BlockDriverState *bs)
43
static int bdrv_replace_node_noperm(BlockDriverState *from,
66
{
44
BlockDriverState *to,
67
BlockDriver *drv = bs->drv;
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);
68
--
55
--
69
2.30.2
56
2.38.1
70
71
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
2
3
Refactor calling driver callbacks to a separate transaction action to
3
Allow passing external Transaction pointer, stop creating extra
4
be used later.
4
Transaction objects.
5
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
8
Message-Id: <20221107163558.618889-4-vsementsov@yandex-team.ru>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-15-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
---
11
block.c | 70 ++++++++++++++++++++++++++++++++++++++++++++-------------
12
block.c | 31 ++++++++++++++++++++-----------
12
1 file changed, 54 insertions(+), 16 deletions(-)
13
1 file changed, 20 insertions(+), 11 deletions(-)
13
14
14
diff --git a/block.c b/block.c
15
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
17
--- a/block.c
17
+++ b/block.c
18
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
19
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
19
}
20
}
20
}
21
21
22
+static void bdrv_drv_set_perm_commit(void *opaque)
22
23
+{
23
-static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
24
+ BlockDriverState *bs = opaque;
24
+/* @tran is allowed to be NULL. In this case no rollback is possible */
25
+ uint64_t cumulative_perms, cumulative_shared_perms;
25
+static int bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran,
26
+
26
+ Error **errp)
27
+ if (bs->drv->bdrv_set_perm) {
27
{
28
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
28
int ret;
29
+ &cumulative_shared_perms);
29
- Transaction *tran = tran_new();
30
+ bs->drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
30
+ Transaction *local_tran = NULL;
31
+ }
31
g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
32
+}
32
GLOBAL_STATE_CODE();
33
+
33
34
+static void bdrv_drv_set_perm_abort(void *opaque)
34
+ if (!tran) {
35
+{
35
+ tran = local_tran = tran_new();
36
+ BlockDriverState *bs = opaque;
37
+
38
+ if (bs->drv->bdrv_abort_perm_update) {
39
+ bs->drv->bdrv_abort_perm_update(bs);
40
+ }
41
+}
42
+
43
+TransactionActionDrv bdrv_drv_set_perm_drv = {
44
+ .abort = bdrv_drv_set_perm_abort,
45
+ .commit = bdrv_drv_set_perm_commit,
46
+};
47
+
48
+static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
49
+ uint64_t shared_perm, Transaction *tran,
50
+ Error **errp)
51
+{
52
+ if (!bs->drv) {
53
+ return 0;
54
+ }
36
+ }
55
+
37
+
56
+ if (bs->drv->bdrv_check_perm) {
38
ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
57
+ int ret = bs->drv->bdrv_check_perm(bs, perm, shared_perm, errp);
39
- tran_finalize(tran, ret);
58
+ if (ret < 0) {
40
+
59
+ return ret;
41
+ if (local_tran) {
60
+ }
42
+ tran_finalize(local_tran, ret);
61
+ }
43
+ }
62
+
44
63
+ if (tran) {
45
return ret;
64
+ tran_add(tran, &bdrv_drv_set_perm_drv, bs);
46
}
65
+ }
47
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
66
+
48
67
+ return 0;
49
bdrv_child_set_perm(c, perm, shared, tran);
68
+}
50
69
+
51
- ret = bdrv_refresh_perms(c->bs, &local_err);
70
/*
52
+ ret = bdrv_refresh_perms(c->bs, tran, &local_err);
71
* Check whether permissions on this node can be changed in a way that
53
72
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
54
tran_finalize(tran, ret);
73
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
55
74
return 0;
56
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
57
goto out;
75
}
58
}
76
59
77
- if (drv->bdrv_check_perm) {
60
- ret = bdrv_refresh_perms(child_bs, errp);
78
- ret = drv->bdrv_check_perm(bs, cumulative_perms,
61
+ ret = bdrv_refresh_perms(child_bs, tran, errp);
79
- cumulative_shared_perms, errp);
62
80
- if (ret < 0) {
63
out:
81
- return ret;
64
tran_finalize(tran, ret);
82
- }
65
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
83
+ ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, NULL,
66
goto out;
84
+ errp);
85
+ if (ret < 0) {
86
+ return ret;
87
}
67
}
88
68
89
/* Drivers that never have children can omit .bdrv_child_perm() */
69
- ret = bdrv_refresh_perms(parent_bs, errp);
90
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_abort_perm_update(BlockDriverState *bs)
70
+ ret = bdrv_refresh_perms(parent_bs, tran, errp);
91
return;
71
if (ret < 0) {
72
goto out;
92
}
73
}
93
74
@@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child)
94
- if (drv->bdrv_abort_perm_update) {
75
* we're loosening restrictions. Errors of permission update are not
95
- drv->bdrv_abort_perm_update(bs);
76
* fatal in this case, ignore them.
96
- }
77
*/
97
+ bdrv_drv_set_perm_abort(bs);
78
- bdrv_refresh_perms(child_bs, NULL);
98
79
+ bdrv_refresh_perms(child_bs, NULL, NULL);
99
QLIST_FOREACH(c, &bs->children, next) {
80
100
bdrv_child_set_perm_abort(c);
81
/*
101
@@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
82
* When the parent requiring a non-default AioContext is removed, the
102
83
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
103
static void bdrv_node_set_perm(BlockDriverState *bs)
84
goto out;
104
{
105
- uint64_t cumulative_perms, cumulative_shared_perms;
106
BlockDriver *drv = bs->drv;
107
BdrvChild *c;
108
109
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_set_perm(BlockDriverState *bs)
110
return;
111
}
85
}
112
86
113
- bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
87
- ret = bdrv_refresh_perms(bs, errp);
114
-
88
+ ret = bdrv_refresh_perms(bs, tran, errp);
115
- /* Update this node */
89
out:
116
- if (drv->bdrv_set_perm) {
90
tran_finalize(tran, ret);
117
- drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
91
118
- }
92
@@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
119
+ bdrv_drv_set_perm_commit(bs);
93
goto out;
120
94
}
121
/* Drivers that never have children can omit .bdrv_child_perm() */
95
122
if (!drv->bdrv_child_perm) {
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) {
123
--
119
--
124
2.30.2
120
2.38.1
125
126
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vladimir.sementsov-ogievskiy@openvz.org>
2
2
3
Add new interface, allowing use of existing node list. It will be used
3
We are going to increase usage of collecting nodes in a list to then
4
to fix bdrv_replace_node() in the further commit.
4
update, and calling bdrv_topological_dfs() each time is not convenient,
5
and not correct as we are going to interleave graph modifying with
6
filling the node list.
5
7
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
So, let's switch to a function that takes any list of nodes, adds all
9
their subtrees and do topological sort. And finally, refresh
10
permissions.
11
12
While being here, make the function public, as we'll want to use it
13
from blockdev.c in near future.
14
15
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
16
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
17
Message-Id: <20221107163558.618889-5-vsementsov@yandex-team.ru>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-16-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
20
---
11
block.c | 106 +++++++++++++++++++++++++++++++++++++-------------------
21
block.c | 51 ++++++++++++++++++++++++++++++++-------------------
12
1 file changed, 71 insertions(+), 35 deletions(-)
22
1 file changed, 32 insertions(+), 19 deletions(-)
13
23
14
diff --git a/block.c b/block.c
24
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
26
--- a/block.c
17
+++ b/block.c
27
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
28
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q,
19
static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
20
uint64_t cumulative_perms,
21
uint64_t cumulative_shared_perms,
22
- GSList *ignore_children, Error **errp)
23
+ GSList *ignore_children,
24
+ Transaction *tran, Error **errp)
25
{
26
BlockDriver *drv = bs->drv;
27
BdrvChild *c;
28
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
29
return 0;
30
}
31
32
- ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, NULL,
33
+ ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, tran,
34
errp);
35
if (ret < 0) {
36
return ret;
37
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
38
bdrv_child_perm(bs, c->bs, c, c->role, q,
39
cumulative_perms, cumulative_shared_perms,
40
&cur_perm, &cur_shared);
41
- bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
42
+ bdrv_child_set_perm_safe(c, cur_perm, cur_shared, tran);
43
}
44
45
return 0;
29
return 0;
46
}
30
}
47
31
48
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
32
-static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
49
- uint64_t cumulative_perms,
33
- Transaction *tran, Error **errp)
50
- uint64_t cumulative_shared_perms,
51
- GSList *ignore_children, Error **errp)
52
+/*
34
+/*
53
+ * If use_cumulative_perms is true, use cumulative_perms and
35
+ * @list is a product of bdrv_topological_dfs() (may be called several times) -
54
+ * cumulative_shared_perms for first element of the list. Otherwise just refresh
36
+ * a topologically sorted subgraph.
55
+ * all permissions.
56
+ */
37
+ */
57
+static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
38
+static int bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q,
58
+ bool use_cumulative_perms,
39
+ Transaction *tran, Error **errp)
59
+ uint64_t cumulative_perms,
60
+ uint64_t cumulative_shared_perms,
61
+ GSList *ignore_children,
62
+ Transaction *tran, Error **errp)
63
{
40
{
64
int ret;
41
int ret;
65
- BlockDriverState *root = bs;
42
BlockDriverState *bs;
66
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, root);
43
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
67
+ BlockDriverState *bs;
68
69
- for ( ; list; list = list->next) {
70
+ if (use_cumulative_perms) {
71
bs = list->data;
72
73
- if (bs != root) {
74
- if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
75
- return -EINVAL;
76
- }
77
+ ret = bdrv_node_check_perm(bs, q, cumulative_perms,
78
+ cumulative_shared_perms,
79
+ ignore_children, tran, errp);
80
+ if (ret < 0) {
81
+ return ret;
82
+ }
83
84
- bdrv_get_cumulative_perm(bs, &cumulative_perms,
85
- &cumulative_shared_perms);
86
+ list = list->next;
87
+ }
88
+
89
+ for ( ; list; list = list->next) {
90
+ bs = list->data;
91
+
92
+ if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
93
+ return -EINVAL;
94
}
95
96
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
97
+ &cumulative_shared_perms);
98
+
99
ret = bdrv_node_check_perm(bs, q, cumulative_perms,
100
cumulative_shared_perms,
101
- ignore_children, errp);
102
+ ignore_children, tran, errp);
103
if (ret < 0) {
104
return ret;
105
}
106
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
107
return 0;
44
return 0;
108
}
45
}
109
46
110
+static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
47
+/*
111
+ uint64_t cumulative_perms,
48
+ * @list is any list of nodes. List is completed by all subtrees and
112
+ uint64_t cumulative_shared_perms,
49
+ * topologically sorted. It's not a problem if some node occurs in the @list
113
+ GSList *ignore_children, Error **errp)
50
+ * several times.
114
+{
51
+ */
115
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
116
+ return bdrv_check_perm_common(list, q, true, cumulative_perms,
117
+ cumulative_shared_perms, ignore_children,
118
+ NULL, errp);
119
+}
120
+
121
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
52
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
122
+ Transaction *tran, Error **errp)
53
+ Transaction *tran, Error **errp)
123
+{
54
+{
124
+ return bdrv_check_perm_common(list, q, false, 0, 0, NULL, tran, errp);
55
+ g_autoptr(GHashTable) found = g_hash_table_new(NULL, NULL);
125
+}
56
+ g_autoptr(GSList) refresh_list = NULL;
126
+
57
+
127
/*
58
+ for ( ; list; list = list->next) {
128
* Notifies drivers that after a previous bdrv_check_perm() call, the
59
+ refresh_list = bdrv_topological_dfs(refresh_list, found, list->data);
129
* permission update is not performed and any preparations made for it (e.g.
60
+ }
130
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_abort_perm_update(BlockDriverState *bs)
131
}
132
}
133
134
-static void bdrv_abort_perm_update(BlockDriverState *bs)
135
+static void bdrv_list_abort_perm_update(GSList *list)
136
{
137
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
138
-
139
for ( ; list; list = list->next) {
140
bdrv_node_abort_perm_update((BlockDriverState *)list->data);
141
}
142
}
143
144
+static void bdrv_abort_perm_update(BlockDriverState *bs)
145
+{
146
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
147
+ return bdrv_list_abort_perm_update(list);
148
+}
149
+
61
+
150
static void bdrv_node_set_perm(BlockDriverState *bs)
62
+ return bdrv_do_refresh_perms(refresh_list, q, tran, errp);
151
{
152
BlockDriver *drv = bs->drv;
153
@@ -XXX,XX +XXX,XX @@ static void bdrv_node_set_perm(BlockDriverState *bs)
154
}
155
}
156
157
-static void bdrv_set_perm(BlockDriverState *bs)
158
+static void bdrv_list_set_perm(GSList *list)
159
{
160
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
161
-
162
for ( ; list; list = list->next) {
163
bdrv_node_set_perm((BlockDriverState *)list->data);
164
}
165
}
166
167
+static void bdrv_set_perm(BlockDriverState *bs)
168
+{
169
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
170
+ return bdrv_list_set_perm(list);
171
+}
63
+}
172
+
64
+
173
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
65
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
174
uint64_t *shared_perm)
66
uint64_t *shared_perm)
175
{
67
{
176
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
68
@@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran,
177
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
69
tran = local_tran = tran_new();
70
}
71
72
- ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
73
+ ret = bdrv_do_refresh_perms(list, NULL, tran, errp);
74
75
if (local_tran) {
76
tran_finalize(local_tran, ret);
77
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
78
BlockReopenQueueEntry *bs_entry, *next;
79
AioContext *ctx;
80
Transaction *tran = tran_new();
81
- g_autoptr(GHashTable) found = NULL;
82
g_autoptr(GSList) refresh_list = NULL;
83
84
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
85
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
86
bs_entry->prepared = true;
87
}
88
89
- found = g_hash_table_new(NULL, NULL);
90
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
91
BDRVReopenState *state = &bs_entry->state;
92
93
- refresh_list = bdrv_topological_dfs(refresh_list, found, state->bs);
94
+ refresh_list = g_slist_prepend(refresh_list, state->bs);
95
if (state->old_backing_bs) {
96
- refresh_list = bdrv_topological_dfs(refresh_list, found,
97
- state->old_backing_bs);
98
+ refresh_list = g_slist_prepend(refresh_list, state->old_backing_bs);
99
}
100
if (state->old_file_bs) {
101
- refresh_list = bdrv_topological_dfs(refresh_list, found,
102
- state->old_file_bs);
103
+ refresh_list = g_slist_prepend(refresh_list, state->old_file_bs);
104
}
105
}
106
107
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
108
Error **errp)
109
{
110
Transaction *tran = tran_new();
111
- g_autoptr(GHashTable) found = NULL;
112
g_autoptr(GSList) refresh_list = NULL;
113
BlockDriverState *to_cow_parent = NULL;
114
int ret;
115
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
116
bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran);
117
}
118
119
- found = g_hash_table_new(NULL, NULL);
120
-
121
- refresh_list = bdrv_topological_dfs(refresh_list, found, to);
122
- refresh_list = bdrv_topological_dfs(refresh_list, found, from);
123
+ refresh_list = g_slist_prepend(refresh_list, to);
124
+ refresh_list = g_slist_prepend(refresh_list, from);
125
126
ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
127
if (ret < 0) {
128
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
178
{
129
{
179
int ret;
130
int ret;
180
- uint64_t perm, shared_perm;
131
Transaction *tran = tran_new();
181
+ Transaction *tran = tran_new();
132
- g_autoptr(GHashTable) found = NULL;
182
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
133
g_autoptr(GSList) refresh_list = NULL;
183
134
BlockDriverState *old_bs = child->bs;
184
- if (bdrv_parent_perms_conflict(bs, NULL, errp)) {
135
185
- return -EPERM;
136
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
186
- }
137
187
- bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
138
bdrv_replace_child_tran(child, new_bs, tran);
188
- ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp);
139
189
- if (ret < 0) {
140
- found = g_hash_table_new(NULL, NULL);
190
- bdrv_abort_perm_update(bs);
141
- refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs);
191
- return ret;
142
- refresh_list = bdrv_topological_dfs(refresh_list, found, new_bs);
192
- }
143
+ refresh_list = g_slist_prepend(refresh_list, old_bs);
193
- bdrv_set_perm(bs);
144
+ refresh_list = g_slist_prepend(refresh_list, new_bs);
194
+ ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
145
195
+ tran_finalize(tran, ret);
146
ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
196
147
197
- return 0;
198
+ return ret;
199
}
200
201
int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
202
--
148
--
203
2.30.2
149
2.38.1
204
205
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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.
2
3
3
Using bdrv_replace_node() for removing filter is not good enough: it
4
Because we increase bs->in_flight and bdrv_drained_begin() polls, the
4
keeps child reference of the filter, which may conflict with original
5
behaviour is unchanged.
5
top node during permission update.
6
6
7
Instead let's create new interface, which will do all graph
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
modifications first and then update permissions.
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
9
9
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Let's modify bdrv_replace_node_common(), allowing it additionally drop
10
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
11
backing chain child link pointing to new node. This is quite
11
Message-Id: <20221118174110.55183-2-kwolf@redhat.com>
12
appropriate for bdrv_drop_intermediate() and makes possible to add
13
new bdrv_drop_filter() as a simple wrapper.
14
15
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
16
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
17
Message-Id: <20210428151804.439460-24-vsementsov@virtuozzo.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
13
---
20
include/block/block.h | 1 +
14
block/qed.c | 20 +++++++++++++++++---
21
block.c | 43 +++++++++++++++++++++++++++++++++++++++----
15
1 file changed, 17 insertions(+), 3 deletions(-)
22
2 files changed, 40 insertions(+), 4 deletions(-)
23
16
24
diff --git a/include/block/block.h b/include/block/block.h
17
diff --git a/block/qed.c b/block/qed.c
25
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/block.h
19
--- a/block/qed.c
27
+++ b/include/block/block.h
20
+++ b/block/qed.c
28
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
21
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qed_unplug_allocating_write_reqs(BDRVQEDState *s)
29
Error **errp);
22
qemu_co_mutex_unlock(&s->table_lock);
30
BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
23
}
31
int flags, Error **errp);
24
32
+int bdrv_drop_filter(BlockDriverState *bs, Error **errp);
25
-static void coroutine_fn qed_need_check_timer_entry(void *opaque)
33
26
+static void coroutine_fn qed_need_check_timer(BDRVQEDState *s)
34
int bdrv_parse_aio(const char *mode, int *flags);
35
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
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 @@ static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
41
* format nodes (always .backing) and filter child for filters (may be .file or
42
* .backing)
43
*/
44
-__attribute__((unused))
45
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
46
Transaction *tran)
47
{
27
{
48
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
28
- BDRVQEDState *s = opaque;
49
*
50
* With auto_skip=false the error is returned if from has a parent which should
51
* not be updated.
52
+ *
53
+ * With @detach_subchain=true @to must be in a backing chain of @from. In this
54
+ * case backing link of the cow-parent of @to is removed.
55
*/
56
static int bdrv_replace_node_common(BlockDriverState *from,
57
BlockDriverState *to,
58
- bool auto_skip, Error **errp)
59
+ bool auto_skip, bool detach_subchain,
60
+ Error **errp)
61
{
62
Transaction *tran = tran_new();
63
g_autoptr(GHashTable) found = NULL;
64
g_autoptr(GSList) refresh_list = NULL;
65
+ BlockDriverState *to_cow_parent;
66
int ret;
29
int ret;
67
30
68
+ if (detach_subchain) {
31
trace_qed_need_check_timer_cb(s);
69
+ assert(bdrv_chain_contains(from, to));
32
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qed_need_check_timer_entry(void *opaque)
70
+ assert(from != to);
33
(void) ret;
71
+ for (to_cow_parent = from;
34
}
72
+ bdrv_filter_or_cow_bs(to_cow_parent) != to;
35
73
+ to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent))
36
+static void coroutine_fn qed_need_check_timer_entry(void *opaque)
74
+ {
37
+{
75
+ ;
38
+ BDRVQEDState *s = opaque;
76
+ }
77
+ }
78
+
39
+
79
/* Make sure that @from doesn't go away until we have successfully attached
40
+ qed_need_check_timer(opaque);
80
* all of its parents to @to. */
41
+ bdrv_dec_in_flight(s->bs);
81
bdrv_ref(from);
82
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
83
goto out;
84
}
85
86
+ if (detach_subchain) {
87
+ bdrv_remove_filter_or_cow_child(to_cow_parent, tran);
88
+ }
89
+
90
found = g_hash_table_new(NULL, NULL);
91
92
refresh_list = bdrv_topological_dfs(refresh_list, found, to);
93
@@ -XXX,XX +XXX,XX @@ out:
94
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
95
Error **errp)
96
{
97
- return bdrv_replace_node_common(from, to, true, errp);
98
+ return bdrv_replace_node_common(from, to, true, false, errp);
99
+}
42
+}
100
+
43
+
101
+int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
44
static void qed_need_check_timer_cb(void *opaque)
102
+{
45
{
103
+ return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true,
46
+ BDRVQEDState *s = opaque;
104
+ errp);
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);
105
}
51
}
106
52
107
/*
53
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs)
108
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
54
* header is flushed.
109
updated_children = g_slist_prepend(updated_children, c);
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);
110
}
64
}
111
65
}
112
- bdrv_replace_node_common(top, base, false, &local_err);
66
113
+ /*
114
+ * It seems correct to pass detach_subchain=true here, but it triggers
115
+ * one more yet not fixed bug, when due to nested aio_poll loop we switch to
116
+ * another drained section, which modify the graph (for example, removing
117
+ * the child, which we keep in updated_children list). So, it's a TODO.
118
+ *
119
+ * Note, bug triggered if pass detach_subchain=true here and run
120
+ * test-bdrv-drain. test_drop_intermediate_poll() test-case will crash.
121
+ * That's a FIXME.
122
+ */
123
+ bdrv_replace_node_common(top, base, false, false, &local_err);
124
if (local_err) {
125
error_report_err(local_err);
126
goto exit;
127
--
67
--
128
2.30.2
68
2.38.1
129
130
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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.
2
3
3
Add the test that shows that concept of ignore_children is incomplete.
4
This does almost the same as the existing logic in bdrv_drain_invoke(),
4
Actually, when we want to update something, ignoring permission of some
5
by creating and entering coroutines internally. However, since the test
5
existing BdrvChild, we should ignore also the propagated effect of this
6
case is by far the heaviest user of coroutine code in drain callbacks,
6
child to the other children. But that's not done. Better approach
7
it is preferable to have the complexity in the test case rather than the
7
(update permissions on already updated graph) will be implemented
8
drain core, which is already complicated enough without this.
8
later.
9
9
10
Now the test fails, so it's added with -d argument to not break make
10
The behaviour for bdrv_drain_begin() is unchanged because we increase
11
check.
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().
12
15
13
Test fails with
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
17
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
"Conflicts with use by fl1 as 'backing', which does not allow 'write' on base"
18
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
16
19
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
17
because when updating permissions we can ignore original top->fl1
20
Message-Id: <20221118174110.55183-3-kwolf@redhat.com>
18
BdrvChild. But we don't ignore exclusive write permission in fl1->base
19
BdrvChild, which is propagated. Correct thing to do is make graph
20
change first and then do permission update from the top node.
21
22
To run test do
23
24
./test-bdrv-graph-mod -d -p /bdrv-graph-mod/parallel-exclusive-write
25
26
from <build-directory>/tests.
27
28
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
29
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
30
Message-Id: <20210428151804.439460-2-vsementsov@virtuozzo.com>
31
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
32
---
22
---
33
tests/unit/test-bdrv-graph-mod.c | 70 +++++++++++++++++++++++++++++++-
23
tests/unit/test-bdrv-drain.c | 64 ++++++++++++++++++++++++++----------
34
1 file changed, 69 insertions(+), 1 deletion(-)
24
1 file changed, 46 insertions(+), 18 deletions(-)
35
25
36
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
26
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
37
index XXXXXXX..XXXXXXX 100644
27
index XXXXXXX..XXXXXXX 100644
38
--- a/tests/unit/test-bdrv-graph-mod.c
28
--- a/tests/unit/test-bdrv-drain.c
39
+++ b/tests/unit/test-bdrv-graph-mod.c
29
+++ b/tests/unit/test-bdrv-drain.c
40
@@ -XXX,XX +XXX,XX @@
30
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVTestState {
41
/*
31
bool sleep_in_drain_begin;
42
* Block node graph modifications tests
32
} BDRVTestState;
43
*
33
44
- * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved.
34
+static void coroutine_fn sleep_in_drain_begin(void *opaque)
45
+ * Copyright (c) 2019-2021 Virtuozzo International GmbH. All rights reserved.
46
*
47
* This program is free software; you can redistribute it and/or modify
48
* it under the terms of the GNU General Public License as published by
49
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_no_perm = {
50
.bdrv_child_perm = no_perm_default_perms,
51
};
52
53
+static void exclusive_write_perms(BlockDriverState *bs, BdrvChild *c,
54
+ BdrvChildRole role,
55
+ BlockReopenQueue *reopen_queue,
56
+ uint64_t perm, uint64_t shared,
57
+ uint64_t *nperm, uint64_t *nshared)
58
+{
35
+{
59
+ *nperm = BLK_PERM_WRITE;
36
+ BlockDriverState *bs = opaque;
60
+ *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
37
+
38
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
39
+ bdrv_dec_in_flight(bs);
61
+}
40
+}
62
+
41
+
63
+static BlockDriver bdrv_exclusive_writer = {
42
static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
64
+ .format_name = "exclusive-writer",
43
{
65
+ .bdrv_child_perm = exclusive_write_perms,
44
BDRVTestState *s = bs->opaque;
66
+};
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;
67
+
62
+
68
static BlockDriverState *no_perm_node(const char *name)
63
+ /* Keep waking io_co up until it is done */
69
{
64
+ while (s->io_co) {
70
return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort);
65
+ aio_co_wake(s->io_co);
71
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *pass_through_node(const char *name)
66
+ s->io_co = NULL;
72
BDRV_O_RDWR, &error_abort);
67
+ qemu_coroutine_yield();
73
}
68
+ }
74
69
+ s->drain_co = NULL;
75
+static BlockDriverState *exclusive_writer_node(const char *name)
70
+ bdrv_dec_in_flight(bs);
76
+{
77
+ return bdrv_new_open_driver(&bdrv_exclusive_writer, name,
78
+ BDRV_O_RDWR, &error_abort);
79
+}
71
+}
80
+
72
+
81
/*
73
/**
82
* test_update_perm_tree
74
* If .drain_count is 0, wake up .io_co if there is one; and set
83
*
75
* .was_drained.
84
@@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void)
76
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_replace_test_co_drain_begin(BlockDriverState *bs)
85
blk_unref(root);
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++;
86
}
95
}
87
96
88
+/*
97
+static void coroutine_fn bdrv_replace_test_read_entry(void *opaque)
89
+ * test_parallel_exclusive_write
90
+ *
91
+ * Check that when we replace node, old permissions of the node being removed
92
+ * doesn't break the replacement.
93
+ */
94
+static void test_parallel_exclusive_write(void)
95
+{
98
+{
96
+ BlockDriverState *top = exclusive_writer_node("top");
99
+ BlockDriverState *bs = opaque;
97
+ BlockDriverState *base = no_perm_node("base");
100
+ char data;
98
+ BlockDriverState *fl1 = pass_through_node("fl1");
101
+ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1);
99
+ BlockDriverState *fl2 = pass_through_node("fl2");
102
+ int ret;
100
+
103
+
101
+ /*
104
+ /* Queue a read request post-drain */
102
+ * bdrv_attach_child() eats child bs reference, so we need two @base
105
+ ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0);
103
+ * references for two filters:
106
+ g_assert(ret >= 0);
104
+ */
107
+ bdrv_dec_in_flight(bs);
105
+ bdrv_ref(base);
106
+
107
+ bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_DATA,
108
+ &error_abort);
109
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
110
+ &error_abort);
111
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
112
+ &error_abort);
113
+
114
+ bdrv_replace_node(fl1, fl2, &error_abort);
115
+
116
+ bdrv_unref(fl2);
117
+ bdrv_unref(top);
118
+}
108
+}
119
+
109
+
120
int main(int argc, char *argv[])
110
/**
121
{
111
* Reduce .drain_count, set .was_undrained once it reaches 0.
122
+ int i;
112
* If .drain_count reaches 0 and the node has a backing file, issue a
123
+ bool debug = false;
113
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs)
124
+
114
125
+ for (i = 1; i < argc; i++) {
115
g_assert(s->drain_count > 0);
126
+ if (!strcmp(argv[i], "-d")) {
116
if (!--s->drain_count) {
127
+ debug = true;
117
- int ret;
128
+ break;
118
-
129
+ }
119
s->was_undrained = true;
130
+ }
120
131
+
121
if (bs->backing) {
132
bdrv_init();
122
- char data;
133
qemu_init_main_loop(&error_abort);
123
- QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1);
134
124
-
135
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
125
- /* Queue a read request post-drain */
136
g_test_add_func("/bdrv-graph-mod/should-update-child",
126
- ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0);
137
test_should_update_child);
127
- g_assert(ret >= 0);
138
128
+ Coroutine *co = qemu_coroutine_create(bdrv_replace_test_read_entry,
139
+ if (debug) {
129
+ bs);
140
+ g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
130
+ bdrv_inc_in_flight(bs);
141
+ test_parallel_exclusive_write);
131
+ aio_co_enter(bdrv_get_aio_context(bs), co);
142
+ }
132
}
143
+
133
}
144
return g_test_run();
145
}
134
}
146
--
135
--
147
2.30.2
136
2.38.1
148
149
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
Polling during bdrv_drained_end() can be problematic (and in the future,
2
2
we may get cases for bdrv_drained_begin() where polling is forbidden,
3
Add additional check that node parents do not interfere with each
3
and we don't care about already in-flight requests, but just want to
4
other. This should not hurt existing callers and allows in further
4
prevent new requests from arriving).
5
patch use bdrv_refresh_perms() to update a subtree of changed
5
6
BdrvChild (check that change is correct).
6
The .bdrv_drained_begin/end callbacks running in a coroutine is the only
7
7
reason why we have to do this polling, so make them non-coroutine
8
New check will substitute bdrv_check_update_perm() in following
8
callbacks again. None of the callers actually yield any more.
9
permissions refactoring, so keep error messages the same to avoid
9
10
unit test result changes.
10
This means that bdrv_drained_end() effectively doesn't poll any more,
11
11
even if AIO_WAIT_WHILE() loops are still there (their condition is false
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
from the beginning). This is generally not a problem, but in
13
Reviewed-by: Alberto Garcia <berto@igalia.com>
13
test-bdrv-drain, some additional explicit aio_poll() calls need to be
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
added because the test case wants to verify the final state after BHs
15
Message-Id: <20210428151804.439460-10-vsementsov@virtuozzo.com>
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>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
23
---
18
block.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++---------
24
include/block/block_int-common.h | 10 ++++---
19
1 file changed, 54 insertions(+), 9 deletions(-)
25
block.c | 4 +--
20
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)(
21
diff --git a/block.c b/block.c
60
diff --git a/block.c b/block.c
22
index XXXXXXX..XXXXXXX 100644
61
index XXXXXXX..XXXXXXX 100644
23
--- a/block.c
62
--- a/block.c
24
+++ b/block.c
63
+++ b/block.c
25
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_writable(BlockDriverState *bs)
64
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
26
return bdrv_is_writable_after_reopen(bs, NULL);
65
assert(is_power_of_2(bs->bl.request_alignment));
27
}
66
28
67
for (i = 0; i < bs->quiesce_counter; i++) {
29
+static char *bdrv_child_user_desc(BdrvChild *c)
68
- if (drv->bdrv_co_drain_begin) {
30
+{
69
- drv->bdrv_co_drain_begin(bs);
31
+ if (c->klass->get_parent_desc) {
70
+ if (drv->bdrv_drain_begin) {
32
+ return c->klass->get_parent_desc(c);
71
+ drv->bdrv_drain_begin(bs);
33
+ }
72
}
34
+
73
}
35
+ return g_strdup("another user");
74
36
+}
75
diff --git a/block/io.c b/block/io.c
37
+
76
index XXXXXXX..XXXXXXX 100644
38
+static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
77
--- a/block/io.c
39
+{
78
+++ b/block/io.c
40
+ g_autofree char *user = NULL;
79
@@ -XXX,XX +XXX,XX @@ typedef struct {
41
+ g_autofree char *perm_names = NULL;
80
int *drained_end_counter;
42
+
81
} BdrvCoDrainData;
43
+ if ((b->perm & a->shared_perm) == b->perm) {
82
44
+ return true;
83
-static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
45
+ }
46
+
47
+ perm_names = bdrv_perm_names(b->perm & ~a->shared_perm);
48
+ user = bdrv_child_user_desc(a);
49
+ error_setg(errp, "Conflicts with use by %s as '%s', which does not "
50
+ "allow '%s' on %s",
51
+ user, a->name, perm_names, bdrv_get_node_name(b->bs));
52
+
53
+ return false;
54
+}
55
+
56
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
57
+{
58
+ BdrvChild *a, *b;
59
+
60
+ /*
61
+ * During the loop we'll look at each pair twice. That's correct because
62
+ * bdrv_a_allow_b() is asymmetric and we should check each pair in both
63
+ * directions.
64
+ */
65
+ QLIST_FOREACH(a, &bs->parents, next_parent) {
66
+ QLIST_FOREACH(b, &bs->parents, next_parent) {
67
+ if (a == b) {
68
+ continue;
69
+ }
70
+
71
+ if (!bdrv_a_allow_b(a, b, errp)) {
72
+ return true;
73
+ }
74
+ }
75
+ }
76
+
77
+ return false;
78
+}
79
+
80
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
81
BdrvChild *c, BdrvChildRole role,
82
BlockReopenQueue *reopen_queue,
83
@@ -XXX,XX +XXX,XX @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
84
*shared_perm = cumulative_shared_perms;
85
}
86
87
-static char *bdrv_child_user_desc(BdrvChild *c)
88
-{
84
-{
89
- if (c->klass->get_parent_desc) {
85
- BdrvCoDrainData *data = opaque;
90
- return c->klass->get_parent_desc(c);
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);
91
- }
92
- }
92
-
93
-
93
- return g_strdup("another user");
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);
94
-}
102
-}
95
-
103
-
96
char *bdrv_perm_names(uint64_t perm)
104
-/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
97
{
105
+/* Recursively call BlockDriver.bdrv_drain_begin/end callbacks */
98
struct perm_name {
106
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
99
@@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
107
int *drained_end_counter)
100
int ret;
108
{
101
uint64_t perm, shared_perm;
109
- BdrvCoDrainData *data;
102
110
-
103
+ if (bdrv_parent_perms_conflict(bs, errp)) {
111
- if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
104
+ return -EPERM;
112
- (!begin && !bs->drv->bdrv_co_drain_end)) {
105
+ }
113
+ if (!bs->drv || (begin && !bs->drv->bdrv_drain_begin) ||
106
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
114
+ (!begin && !bs->drv->bdrv_drain_end)) {
107
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp);
115
return;
108
if (ret < 0) {
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
};
109
--
284
--
110
2.30.2
285
2.38.1
111
112
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
drained_end_counter is unused now, nobody changes its value any more. It
2
can be removed.
2
3
3
Passing parent aio context is redundant, as child_class and parent
4
In cases where we had two almost identical functions that only differed
4
opaque pointer are enough to retrieve it. Drop the argument and use new
5
in whether the caller passes drained_end_counter, or whether they would
5
bdrv_child_get_parent_aio_context() interface.
6
poll for a local drained_end_counter to reach 0, these become a single
7
function.
6
8
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Message-Id: <20210428151804.439460-7-vsementsov@virtuozzo.com>
12
Message-Id: <20221118174110.55183-5-kwolf@redhat.com>
13
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
15
---
13
include/block/block_int.h | 1 -
16
include/block/block-io.h | 24 --------
14
block.c | 8 +++++---
17
include/block/block_int-common.h | 6 +-
15
block/block-backend.c | 4 ++--
18
block.c | 5 +-
16
blockjob.c | 3 +--
19
block/block-backend.c | 4 +-
17
4 files changed, 8 insertions(+), 8 deletions(-)
20
block/io.c | 98 ++++++++------------------------
21
blockjob.c | 2 +-
22
6 files changed, 30 insertions(+), 109 deletions(-)
18
23
19
diff --git a/include/block/block_int.h b/include/block/block_int.h
24
diff --git a/include/block/block-io.h b/include/block/block-io.h
20
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
21
--- a/include/block/block_int.h
26
--- a/include/block/block-io.h
22
+++ b/include/block/block_int.h
27
+++ b/include/block/block-io.h
23
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
28
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset,
24
const char *child_name,
29
int64_t bytes, BdrvRequestFlags read_flags,
25
const BdrvChildClass *child_class,
30
BdrvRequestFlags write_flags);
26
BdrvChildRole child_role,
31
27
- AioContext *ctx,
32
-/**
28
uint64_t perm, uint64_t shared_perm,
33
- * bdrv_drained_end_no_poll:
29
void *opaque, Error **errp);
34
- *
30
void bdrv_root_unref_child(BdrvChild *child);
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
31
diff --git a/block.c b/block.c
94
diff --git a/block.c b/block.c
32
index XXXXXXX..XXXXXXX 100644
95
index XXXXXXX..XXXXXXX 100644
33
--- a/block.c
96
--- a/block.c
34
+++ b/block.c
97
+++ b/block.c
35
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
98
@@ -XXX,XX +XXX,XX @@ static bool bdrv_child_cb_drained_poll(BdrvChild *child)
36
const char *child_name,
99
return bdrv_drain_poll(bs, false, NULL, false);
37
const BdrvChildClass *child_class,
100
}
38
BdrvChildRole child_role,
101
39
- AioContext *ctx,
102
-static void bdrv_child_cb_drained_end(BdrvChild *child,
40
uint64_t perm, uint64_t shared_perm,
103
- int *drained_end_counter)
41
void *opaque, Error **errp)
104
+static void bdrv_child_cb_drained_end(BdrvChild *child)
42
{
105
{
43
BdrvChild *child;
106
BlockDriverState *bs = child->opaque;
44
Error *local_err = NULL;
107
- bdrv_drained_end_no_poll(bs, drained_end_counter);
45
int ret;
108
+ bdrv_drained_end(bs);
46
+ AioContext *ctx;
109
}
47
110
48
ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
111
static int bdrv_child_cb_inactivate(BdrvChild *child)
49
if (ret < 0) {
50
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
51
.opaque = opaque,
52
};
53
54
+ ctx = bdrv_child_get_parent_aio_context(child);
55
+
56
/* If the AioContexts don't match, first try to move the subtree of
57
* child_bs into the AioContext of the new parent. If this doesn't work,
58
* try moving the parent into the AioContext of child_bs instead. */
59
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
60
perm, shared_perm, &perm, &shared_perm);
61
62
child = bdrv_root_attach_child(child_bs, child_name, child_class,
63
- child_role, bdrv_get_aio_context(parent_bs),
64
- perm, shared_perm, parent_bs, errp);
65
+ child_role, perm, shared_perm, parent_bs,
66
+ errp);
67
if (child == NULL) {
68
return NULL;
69
}
70
diff --git a/block/block-backend.c b/block/block-backend.c
112
diff --git a/block/block-backend.c b/block/block-backend.c
71
index XXXXXXX..XXXXXXX 100644
113
index XXXXXXX..XXXXXXX 100644
72
--- a/block/block-backend.c
114
--- a/block/block-backend.c
73
+++ b/block/block-backend.c
115
+++ b/block/block-backend.c
74
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
116
@@ -XXX,XX +XXX,XX @@ static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format,
75
117
}
76
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
118
static void blk_root_drained_begin(BdrvChild *child);
77
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
119
static bool blk_root_drained_poll(BdrvChild *child);
78
- blk->ctx, perm, BLK_PERM_ALL, blk, errp);
120
-static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter);
79
+ perm, BLK_PERM_ALL, blk, errp);
121
+static void blk_root_drained_end(BdrvChild *child);
80
if (!blk->root) {
122
81
blk_unref(blk);
123
static void blk_root_change_media(BdrvChild *child, bool load);
82
return NULL;
124
static void blk_root_resize(BdrvChild *child);
83
@@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
125
@@ -XXX,XX +XXX,XX @@ static bool blk_root_drained_poll(BdrvChild *child)
84
bdrv_ref(bs);
126
return busy || !!blk->in_flight;
85
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
127
}
86
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
128
87
- blk->ctx, blk->perm, blk->shared_perm,
129
-static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
88
+ blk->perm, blk->shared_perm,
130
+static void blk_root_drained_end(BdrvChild *child)
89
blk, errp);
131
{
90
if (blk->root == NULL) {
132
BlockBackend *blk = child->opaque;
91
return -EPERM;
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
}
92
diff --git a/blockjob.c b/blockjob.c
403
diff --git a/blockjob.c b/blockjob.c
93
index XXXXXXX..XXXXXXX 100644
404
index XXXXXXX..XXXXXXX 100644
94
--- a/blockjob.c
405
--- a/blockjob.c
95
+++ b/blockjob.c
406
+++ b/blockjob.c
96
@@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
407
@@ -XXX,XX +XXX,XX @@ static bool child_job_drained_poll(BdrvChild *c)
97
if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) {
408
}
98
aio_context_release(job->job.aio_context);
409
}
99
}
410
100
- c = bdrv_root_attach_child(bs, name, &child_job, 0,
411
-static void child_job_drained_end(BdrvChild *c, int *drained_end_counter)
101
- job->job.aio_context, perm, shared_perm, job,
412
+static void child_job_drained_end(BdrvChild *c)
102
+ c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job,
413
{
103
errp);
414
BlockJob *job = c->opaque;
104
if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) {
415
job_resume(&job->job);
105
aio_context_acquire(job->job.aio_context);
106
--
416
--
107
2.30.2
417
2.38.1
108
109
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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.
2
4
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
5
Message-Id: <20210428151804.439460-32-vsementsov@virtuozzo.com>
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>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
12
---
8
block.c | 103 --------------------------------------------------------
13
block/io.c | 23 ++++++-----------------
9
1 file changed, 103 deletions(-)
14
1 file changed, 6 insertions(+), 17 deletions(-)
10
15
11
diff --git a/block.c b/block.c
16
diff --git a/block/io.c b/block/io.c
12
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
13
--- a/block.c
18
--- a/block/io.c
14
+++ b/block.c
19
+++ b/block/io.c
15
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
20
@@ -XXX,XX +XXX,XX @@ typedef struct {
16
return 0;
21
bool ignore_bds_parents;
17
}
22
} BdrvCoDrainData;
18
23
19
-static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
24
-/* Recursively call BlockDriver.bdrv_drain_begin/end callbacks */
20
- uint64_t new_used_perm,
25
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
21
- uint64_t new_shared_perm,
22
- Error **errp);
23
-
24
typedef struct BlockReopenQueueEntry {
25
bool prepared;
26
bool perms_checked;
27
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
28
return 0;
29
}
30
31
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
32
- uint64_t cumulative_perms,
33
- uint64_t cumulative_shared_perms, Error **errp)
34
-{
26
-{
35
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
27
- if (!bs->drv || (begin && !bs->drv->bdrv_drain_begin) ||
36
- return bdrv_check_perm_common(list, q, true, cumulative_perms,
28
- (!begin && !bs->drv->bdrv_drain_end)) {
37
- cumulative_shared_perms, NULL, errp);
38
-}
39
-
40
static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
41
Transaction *tran, Error **errp)
42
{
43
return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
44
}
45
46
-/*
47
- * Notifies drivers that after a previous bdrv_check_perm() call, the
48
- * permission update is not performed and any preparations made for it (e.g.
49
- * taken file locks) need to be undone.
50
- */
51
-static void bdrv_node_abort_perm_update(BlockDriverState *bs)
52
-{
53
- BlockDriver *drv = bs->drv;
54
- BdrvChild *c;
55
-
56
- if (!drv) {
57
- return;
29
- return;
58
- }
30
- }
59
-
31
-
60
- bdrv_drv_set_perm_abort(bs);
32
- if (begin) {
61
-
33
- bs->drv->bdrv_drain_begin(bs);
62
- QLIST_FOREACH(c, &bs->children, next) {
34
- } else {
63
- bdrv_child_set_perm_abort(c);
35
- bs->drv->bdrv_drain_end(bs);
64
- }
36
- }
65
-}
37
-}
66
-
38
-
67
-static void bdrv_list_abort_perm_update(GSList *list)
39
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
68
-{
40
bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
69
- for ( ; list; list = list->next) {
41
BdrvChild *ignore_parent, bool ignore_bds_parents)
70
- bdrv_node_abort_perm_update((BlockDriverState *)list->data);
42
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
71
- }
43
}
72
-}
44
73
-
45
bdrv_parent_drained_begin(bs, parent, ignore_bds_parents);
74
-__attribute__((unused))
46
- bdrv_drain_invoke(bs, true);
75
-static void bdrv_abort_perm_update(BlockDriverState *bs)
47
+ if (bs->drv && bs->drv->bdrv_drain_begin) {
76
-{
48
+ bs->drv->bdrv_drain_begin(bs);
77
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
49
+ }
78
- return bdrv_list_abort_perm_update(list);
79
-}
80
-
81
static void bdrv_node_set_perm(BlockDriverState *bs)
82
{
83
BlockDriver *drv = bs->drv;
84
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
85
return g_string_free(result, FALSE);
86
}
50
}
87
51
88
-/*
52
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
89
- * Checks whether a new reference to @bs can be added if the new user requires
53
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
90
- * @new_used_perm/@new_shared_perm as its permissions. If @ignore_children is
54
assert(bs->quiesce_counter > 0);
91
- * set, the BdrvChild objects in this list are ignored in the calculations;
55
92
- * this allows checking permission updates for an existing reference.
56
/* Re-enable things in child-to-parent order */
93
- *
57
- bdrv_drain_invoke(bs, false);
94
- * Needs to be followed by a call to either bdrv_set_perm() or
58
+ if (bs->drv && bs->drv->bdrv_drain_end) {
95
- * bdrv_abort_perm_update(). */
59
+ bs->drv->bdrv_drain_end(bs);
96
-__attribute__((unused))
60
+ }
97
-static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
61
bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
98
- uint64_t new_used_perm,
62
99
- uint64_t new_shared_perm,
63
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
100
- Error **errp)
101
-{
102
- BdrvChild *c;
103
- uint64_t cumulative_perms = new_used_perm;
104
- uint64_t cumulative_shared_perms = new_shared_perm;
105
-
106
-
107
- /* There is no reason why anyone couldn't tolerate write_unchanged */
108
- assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
109
-
110
- QLIST_FOREACH(c, &bs->parents, next_parent) {
111
- if ((new_used_perm & c->shared_perm) != new_used_perm) {
112
- char *user = bdrv_child_user_desc(c);
113
- char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
114
-
115
- error_setg(errp, "Conflicts with use by %s as '%s', which does not "
116
- "allow '%s' on %s",
117
- user, c->name, perm_names, bdrv_get_node_name(c->bs));
118
- g_free(user);
119
- g_free(perm_names);
120
- return -EPERM;
121
- }
122
-
123
- if ((c->perm & new_shared_perm) != c->perm) {
124
- char *user = bdrv_child_user_desc(c);
125
- char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm);
126
-
127
- error_setg(errp, "Conflicts with use by %s as '%s', which uses "
128
- "'%s' on %s",
129
- user, c->name, perm_names, bdrv_get_node_name(c->bs));
130
- g_free(user);
131
- g_free(perm_names);
132
- return -EPERM;
133
- }
134
-
135
- cumulative_perms |= c->perm;
136
- cumulative_shared_perms &= c->shared_perm;
137
- }
138
-
139
- return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
140
- errp);
141
-}
142
143
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
144
{
145
--
64
--
146
2.30.2
65
2.38.1
147
148
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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.
2
5
3
Now, bdrv_node_check_perm() is called only with fresh cumulative
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
permissions, so its actually "refresh_perm".
7
Message-Id: <20221118174110.55183-7-kwolf@redhat.com>
5
8
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
6
Move permission calculation to the function. Also, drop unreachable
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
7
error message and rewrite the remaining one to be more generic (as now
8
we don't know which node is added and which was already here).
9
10
Add also Virtuozzo copyright, as big work is done at this point.
11
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Message-Id: <20210428151804.439460-37-vsementsov@virtuozzo.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
11
---
17
block.c | 38 +++++++++++---------------------------
12
block.c | 7 +++++--
18
tests/qemu-iotests/245 | 2 +-
13
1 file changed, 5 insertions(+), 2 deletions(-)
19
2 files changed, 12 insertions(+), 28 deletions(-)
20
14
21
diff --git a/block.c b/block.c
15
diff --git a/block.c b/block.c
22
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
23
--- a/block.c
17
--- a/block.c
24
+++ b/block.c
18
+++ b/block.c
25
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@ static bool bdrv_recurse_has_child(BlockDriverState *bs,
26
* QEMU System Emulator block driver
20
* bs_queue, or the existing bs_queue being used.
27
*
21
*
28
* Copyright (c) 2003 Fabrice Bellard
22
* bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
29
+ * Copyright (c) 2020 Virtuozzo International GmbH.
23
+ *
30
*
24
+ * To be called with bs->aio_context locked.
31
* Permission is hereby granted, free of charge, to any person obtaining a copy
25
*/
32
* of this software and associated documentation files (the "Software"), to deal
26
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
33
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
27
BlockDriverState *bs,
28
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
29
return bs_queue;
34
}
30
}
35
31
36
/*
32
+/* To be called with bs->aio_context locked */
37
- * Check whether permissions on this node can be changed in a way that
33
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
38
- * @cumulative_perms and @cumulative_shared_perms are the new cumulative
34
BlockDriverState *bs,
39
- * permissions of all its parents. This involves checking whether all necessary
35
QDict *options, bool keep_old_opts)
40
- * permission changes to child nodes can be performed.
36
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts,
41
- *
37
GLOBAL_STATE_CODE();
42
- * A call to this function must always be followed by a call to bdrv_set_perm()
38
43
- * or bdrv_abort_perm_update().
39
bdrv_subtree_drained_begin(bs);
44
+ * Refresh permissions in @bs subtree. The function is intended to be called
40
+ queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts);
45
+ * after some graph modification that was done without permission update.
46
*/
47
-static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
48
- uint64_t cumulative_perms,
49
- uint64_t cumulative_shared_perms,
50
- Transaction *tran, Error **errp)
51
+static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q,
52
+ Transaction *tran, Error **errp)
53
{
54
BlockDriver *drv = bs->drv;
55
BdrvChild *c;
56
int ret;
57
+ uint64_t cumulative_perms, cumulative_shared_perms;
58
+
41
+
59
+ bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
42
if (ctx != qemu_get_aio_context()) {
60
43
aio_context_release(ctx);
61
/* Write permissions never work with read-only images */
44
}
62
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
63
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
64
if (!bdrv_is_writable_after_reopen(bs, NULL)) {
65
error_setg(errp, "Block node is read-only");
66
} else {
67
- uint64_t current_perms, current_shared;
68
- bdrv_get_cumulative_perm(bs, &current_perms, &current_shared);
69
- if (current_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) {
70
- error_setg(errp, "Cannot make block node read-only, there is "
71
- "a writer on it");
72
- } else {
73
- error_setg(errp, "Cannot make block node read-only and create "
74
- "a writer on it");
75
- }
76
+ error_setg(errp, "Read-only block node '%s' cannot support "
77
+ "read-write users", bdrv_get_node_name(bs));
78
}
79
80
return -EPERM;
81
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
82
Transaction *tran, Error **errp)
83
{
84
int ret;
85
- uint64_t cumulative_perms, cumulative_shared_perms;
86
BlockDriverState *bs;
87
88
for ( ; list; list = list->next) {
89
@@ -XXX,XX +XXX,XX @@ static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
90
return -EINVAL;
91
}
92
93
- bdrv_get_cumulative_perm(bs, &cumulative_perms,
94
- &cumulative_shared_perms);
95
-
45
-
96
- ret = bdrv_node_check_perm(bs, q, cumulative_perms,
46
- queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts);
97
- cumulative_shared_perms,
47
ret = bdrv_reopen_multiple(queue, errp);
98
- tran, errp);
48
99
+ ret = bdrv_node_refresh_perm(bs, q, tran, errp);
49
if (ctx != qemu_get_aio_context()) {
100
if (ret < 0) {
101
return ret;
102
}
103
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
104
index XXXXXXX..XXXXXXX 100755
105
--- a/tests/qemu-iotests/245
106
+++ b/tests/qemu-iotests/245
107
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
108
# We can't reopen hd1 to read-only, as block-stream requires it to be
109
# read-write
110
self.reopen(opts['backing'], {'read-only': True},
111
- "Cannot make block node read-only, there is a writer on it")
112
+ "Read-only block node 'hd1' cannot support read-write users")
113
114
# We can't remove hd2 while the stream job is ongoing
115
opts['backing']['backing'] = None
116
--
50
--
117
2.30.2
51
2.38.1
118
119
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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
Split out no-perm part of bdrv_root_attach_child() into separate
6
Simplify the code by switching to draining the individual nodes that are
4
transaction action. bdrv_root_attach_child() now moves to new
7
already managed in the BlockReopenQueue anyway.
5
permission update paradigm: first update graph relations then update
6
permissions.
7
8
8
qsd-jobs test output updated. Seems now permission update goes in
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
another order. Still, the test comment say that we only want to check
10
Message-Id: <20221118174110.55183-8-kwolf@redhat.com>
10
that command doesn't crash, and it's still so.
11
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
11
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
12
Error message is a bit misleading as it looks like job was added first.
13
But actually in new paradigm of graph update we can't distinguish such
14
things. We should update the error message, but let's not do it now.
15
16
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Message-Id: <20210428151804.439460-19-vsementsov@virtuozzo.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
14
---
20
block.c | 190 ++++++++++++++++++--------
15
block.c | 16 +++++++++-------
21
tests/qemu-iotests/tests/qsd-jobs.out | 2 +-
16
block/replication.c | 6 ------
22
2 files changed, 137 insertions(+), 55 deletions(-)
17
blockdev.c | 13 -------------
18
3 files changed, 9 insertions(+), 26 deletions(-)
23
19
24
diff --git a/block.c b/block.c
20
diff --git a/block.c b/block.c
25
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
26
--- a/block.c
22
--- a/block.c
27
+++ b/block.c
23
+++ b/block.c
28
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
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);
29
}
72
}
73
- bdrv_subtree_drained_end(bs);
74
75
return ret;
30
}
76
}
31
77
diff --git a/block/replication.c b/block/replication.c
32
-/*
78
index XXXXXXX..XXXXXXX 100644
33
- * This function steals the reference to child_bs from the caller.
79
--- a/block/replication.c
34
- * That reference is later dropped by bdrv_root_unref_child().
80
+++ b/block/replication.c
35
- *
81
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
36
- * On failure NULL is returned, errp is set and the reference to
82
s->orig_secondary_read_only = bdrv_is_read_only(secondary_disk->bs);
37
- * child_bs is also dropped.
38
- *
39
- * The caller must hold the AioContext lock @child_bs, but not that of @ctx
40
- * (unless @child_bs is already in @ctx).
41
- */
42
-BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
43
- const char *child_name,
44
- const BdrvChildClass *child_class,
45
- BdrvChildRole child_role,
46
- uint64_t perm, uint64_t shared_perm,
47
- void *opaque, Error **errp)
48
+static void bdrv_remove_empty_child(BdrvChild *child)
49
{
50
- BdrvChild *child;
51
- Error *local_err = NULL;
52
- int ret;
53
- AioContext *ctx;
54
+ assert(!child->bs);
55
+ QLIST_SAFE_REMOVE(child, next);
56
+ g_free(child->name);
57
+ g_free(child);
58
+}
59
60
- ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
61
- if (ret < 0) {
62
- bdrv_abort_perm_update(child_bs);
63
- bdrv_unref(child_bs);
64
- return NULL;
65
+typedef struct BdrvAttachChildCommonState {
66
+ BdrvChild **child;
67
+ AioContext *old_parent_ctx;
68
+ AioContext *old_child_ctx;
69
+} BdrvAttachChildCommonState;
70
+
71
+static void bdrv_attach_child_common_abort(void *opaque)
72
+{
73
+ BdrvAttachChildCommonState *s = opaque;
74
+ BdrvChild *child = *s->child;
75
+ BlockDriverState *bs = child->bs;
76
+
77
+ bdrv_replace_child_noperm(child, NULL);
78
+
79
+ if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
80
+ bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort);
81
}
83
}
82
84
83
- child = g_new(BdrvChild, 1);
85
- bdrv_subtree_drained_begin(hidden_disk->bs);
84
- *child = (BdrvChild) {
86
- bdrv_subtree_drained_begin(secondary_disk->bs);
85
+ if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) {
86
+ GSList *ignore = g_slist_prepend(NULL, child);
87
+
88
+ child->klass->can_set_aio_ctx(child, s->old_parent_ctx, &ignore,
89
+ &error_abort);
90
+ g_slist_free(ignore);
91
+ ignore = g_slist_prepend(NULL, child);
92
+ child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore);
93
+
94
+ g_slist_free(ignore);
95
+ }
96
+
97
+ bdrv_unref(bs);
98
+ bdrv_remove_empty_child(child);
99
+ *s->child = NULL;
100
+}
101
+
102
+static TransactionActionDrv bdrv_attach_child_common_drv = {
103
+ .abort = bdrv_attach_child_common_abort,
104
+ .clean = g_free,
105
+};
106
+
107
+/*
108
+ * Common part of attaching bdrv child to bs or to blk or to job
109
+ */
110
+static int bdrv_attach_child_common(BlockDriverState *child_bs,
111
+ const char *child_name,
112
+ const BdrvChildClass *child_class,
113
+ BdrvChildRole child_role,
114
+ uint64_t perm, uint64_t shared_perm,
115
+ void *opaque, BdrvChild **child,
116
+ Transaction *tran, Error **errp)
117
+{
118
+ BdrvChild *new_child;
119
+ AioContext *parent_ctx;
120
+ AioContext *child_ctx = bdrv_get_aio_context(child_bs);
121
+
122
+ assert(child);
123
+ assert(*child == NULL);
124
+
125
+ new_child = g_new(BdrvChild, 1);
126
+ *new_child = (BdrvChild) {
127
.bs = NULL,
128
.name = g_strdup(child_name),
129
.klass = child_class,
130
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
131
.opaque = opaque,
132
};
133
134
- ctx = bdrv_child_get_parent_aio_context(child);
135
-
87
-
136
- /* If the AioContexts don't match, first try to move the subtree of
88
if (s->orig_hidden_read_only) {
137
+ /*
89
QDict *opts = qdict_new();
138
+ * If the AioContexts don't match, first try to move the subtree of
90
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
139
* child_bs into the AioContext of the new parent. If this doesn't work,
91
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
140
- * try moving the parent into the AioContext of child_bs instead. */
92
aio_context_acquire(ctx);
141
- if (bdrv_get_aio_context(child_bs) != ctx) {
142
- ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err);
143
+ * try moving the parent into the AioContext of child_bs instead.
144
+ */
145
+ parent_ctx = bdrv_child_get_parent_aio_context(new_child);
146
+ if (child_ctx != parent_ctx) {
147
+ Error *local_err = NULL;
148
+ int ret = bdrv_try_set_aio_context(child_bs, parent_ctx, &local_err);
149
+
150
if (ret < 0 && child_class->can_set_aio_ctx) {
151
- GSList *ignore = g_slist_prepend(NULL, child);
152
- ctx = bdrv_get_aio_context(child_bs);
153
- if (child_class->can_set_aio_ctx(child, ctx, &ignore, NULL)) {
154
+ GSList *ignore = g_slist_prepend(NULL, new_child);
155
+ if (child_class->can_set_aio_ctx(new_child, child_ctx, &ignore,
156
+ NULL))
157
+ {
158
error_free(local_err);
159
ret = 0;
160
g_slist_free(ignore);
161
- ignore = g_slist_prepend(NULL, child);
162
- child_class->set_aio_ctx(child, ctx, &ignore);
163
+ ignore = g_slist_prepend(NULL, new_child);
164
+ child_class->set_aio_ctx(new_child, child_ctx, &ignore);
165
}
166
g_slist_free(ignore);
167
}
168
+
169
if (ret < 0) {
170
error_propagate(errp, local_err);
171
- g_free(child);
172
- bdrv_abort_perm_update(child_bs);
173
- bdrv_unref(child_bs);
174
- return NULL;
175
+ bdrv_remove_empty_child(new_child);
176
+ return ret;
177
}
93
}
178
}
94
}
179
95
-
180
- /* This performs the matching bdrv_set_perm() for the above check. */
96
- bdrv_subtree_drained_end(hidden_disk->bs);
181
- bdrv_replace_child(child, child_bs);
97
- bdrv_subtree_drained_end(secondary_disk->bs);
182
+ bdrv_ref(child_bs);
183
+ bdrv_replace_child_noperm(new_child, child_bs);
184
185
+ *child = new_child;
186
+
187
+ BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1);
188
+ *s = (BdrvAttachChildCommonState) {
189
+ .child = child,
190
+ .old_parent_ctx = parent_ctx,
191
+ .old_child_ctx = child_ctx,
192
+ };
193
+ tran_add(tran, &bdrv_attach_child_common_drv, s);
194
+
195
+ return 0;
196
+}
197
+
198
+static void bdrv_detach_child(BdrvChild *child)
199
+{
200
+ bdrv_replace_child(child, NULL);
201
+ bdrv_remove_empty_child(child);
202
+}
203
+
204
+/*
205
+ * This function steals the reference to child_bs from the caller.
206
+ * That reference is later dropped by bdrv_root_unref_child().
207
+ *
208
+ * On failure NULL is returned, errp is set and the reference to
209
+ * child_bs is also dropped.
210
+ *
211
+ * The caller must hold the AioContext lock @child_bs, but not that of @ctx
212
+ * (unless @child_bs is already in @ctx).
213
+ */
214
+BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
215
+ const char *child_name,
216
+ const BdrvChildClass *child_class,
217
+ BdrvChildRole child_role,
218
+ uint64_t perm, uint64_t shared_perm,
219
+ void *opaque, Error **errp)
220
+{
221
+ int ret;
222
+ BdrvChild *child = NULL;
223
+ Transaction *tran = tran_new();
224
+
225
+ ret = bdrv_attach_child_common(child_bs, child_name, child_class,
226
+ child_role, perm, shared_perm, opaque,
227
+ &child, tran, errp);
228
+ if (ret < 0) {
229
+ bdrv_unref(child_bs);
230
+ return NULL;
231
+ }
232
+
233
+ ret = bdrv_refresh_perms(child_bs, errp);
234
+ tran_finalize(tran, ret);
235
+
236
+ bdrv_unref(child_bs);
237
return child;
238
}
98
}
239
99
240
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
100
static void backup_job_cleanup(BlockDriverState *bs)
241
return child;
101
diff --git a/blockdev.c b/blockdev.c
102
index XXXXXXX..XXXXXXX 100644
103
--- a/blockdev.c
104
+++ b/blockdev.c
105
@@ -XXX,XX +XXX,XX @@ fail:
106
void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
107
{
108
BlockReopenQueue *queue = NULL;
109
- GSList *drained = NULL;
110
- GSList *p;
111
112
/* Add each one of the BDS that we want to reopen to the queue */
113
for (; reopen_list != NULL; reopen_list = reopen_list->next) {
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);
123
}
124
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
125
126
fail:
127
bdrv_reopen_queue_free(queue);
128
- for (p = drained; p; p = p->next) {
129
- BlockDriverState *bs = p->data;
130
- AioContext *ctx = bdrv_get_aio_context(bs);
131
-
132
- aio_context_acquire(ctx);
133
- bdrv_subtree_drained_end(bs);
134
- aio_context_release(ctx);
135
- }
136
- g_slist_free(drained);
242
}
137
}
243
138
244
-static void bdrv_detach_child(BdrvChild *child)
139
void qmp_blockdev_del(const char *node_name, Error **errp)
245
-{
246
- QLIST_SAFE_REMOVE(child, next);
247
-
248
- bdrv_replace_child(child, NULL);
249
-
250
- g_free(child->name);
251
- g_free(child);
252
-}
253
-
254
/* Callers must ensure that child->frozen is false. */
255
void bdrv_root_unref_child(BdrvChild *child)
256
{
257
diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out
258
index XXXXXXX..XXXXXXX 100644
259
--- a/tests/qemu-iotests/tests/qsd-jobs.out
260
+++ b/tests/qemu-iotests/tests/qsd-jobs.out
261
@@ -XXX,XX +XXX,XX @@ QMP_VERSION
262
{"return": {}}
263
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
264
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
265
-{"error": {"class": "GenericError", "desc": "Conflicts with use by a block device as 'root', which uses 'write' on fmt_base"}}
266
+{"error": {"class": "GenericError", "desc": "Conflicts with use by stream job 'job0' as 'intermediate node', which does not allow 'write' on fmt_base"}}
267
{"return": {}}
268
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}}
269
*** done
270
--
140
--
271
2.30.2
141
2.38.1
272
273
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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
During reopen we may add backing bs from other aio context, which may
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
lead to changing original context of top bs.
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
5
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
6
We are going to move graph modification to prepare stage. So, it will
8
Message-Id: <20221118174110.55183-9-kwolf@redhat.com>
7
be possible that bdrv_flush() in bdrv_reopen_prepare called on bs in
8
non-original aio context, which we didn't aquire which leads to crash.
9
10
To avoid this problem move bdrv_flush() to be a separate reopen stage
11
before bdrv_reopen_prepare().
12
13
This doesn't seem correct to acquire only one aio context and not all
14
contexts participating in reopen. But it's not obvious how to do it
15
correctly, keeping in mind:
16
17
1. rules of bdrv_set_aio_context_ignore() that requires new_context
18
lock not being held
19
20
2. possible deadlocks because of holding all (or several?) AioContext
21
locks
22
23
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
24
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
25
Message-Id: <20210428151804.439460-30-vsementsov@virtuozzo.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
27
---
10
---
28
block.c | 14 ++++++++------
11
block.c | 4 ++--
29
1 file changed, 8 insertions(+), 6 deletions(-)
12
1 file changed, 2 insertions(+), 2 deletions(-)
30
13
31
diff --git a/block.c b/block.c
14
diff --git a/block.c b/block.c
32
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
33
--- a/block.c
16
--- a/block.c
34
+++ b/block.c
17
+++ b/block.c
35
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
18
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
36
19
GLOBAL_STATE_CODE();
37
assert(bs_queue != NULL);
20
38
21
bdrv_ref(top);
39
+ QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
22
- bdrv_subtree_drained_begin(top);
40
+ ret = bdrv_flush(bs_entry->state.bs);
23
+ bdrv_drained_begin(base);
41
+ if (ret < 0) {
24
42
+ error_setg_errno(errp, -ret, "Error flushing drive");
25
if (!top->drv || !base->drv) {
43
+ goto cleanup;
26
goto exit;
44
+ }
27
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
45
+ }
28
46
+
29
ret = 0;
47
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
30
exit:
48
assert(bs_entry->state.bs->quiesce_counter > 0);
31
- bdrv_subtree_drained_end(top);
49
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
32
+ bdrv_drained_end(base);
50
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
33
bdrv_unref(top);
51
bdrv_reopen_perm(queue, reopen_state->bs,
34
return ret;
52
&reopen_state->perm, &reopen_state->shared_perm);
35
}
53
54
- ret = bdrv_flush(reopen_state->bs);
55
- if (ret) {
56
- error_setg_errno(errp, -ret, "Error flushing drive");
57
- goto error;
58
- }
59
-
60
if (drv->bdrv_reopen_prepare) {
61
/*
62
* If a driver-specific option is missing, it means that we
63
--
36
--
64
2.30.2
37
2.38.1
65
66
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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.
2
4
3
Split out no-perm part of bdrv_set_backing_hd() as a separate
5
The block graph could change between these two points because
4
transaction action. Note the in case of existing BdrvChild we reuse it,
6
bdrv_set_backing_hd() first drains the parent node, which involved
5
not recreate, just to do less actions.
7
polling and can do anything.
6
8
7
We don't need to create extra reference to backing_hd as we don't lose
9
Subtree draining was an imperfect way to make this less likely (because
8
it in bdrv_attach_child().
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.
9
13
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
This replaces the subtree drain with a solution that simply drains the
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
parent node before we try to find the base node, and then call a version
12
Message-Id: <20210428151804.439460-29-vsementsov@virtuozzo.com>
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>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
27
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
28
---
15
block.c | 54 +++++++++++++++++++++++++++++++++++++-----------------
29
include/block/block-global-state.h | 3 +++
16
1 file changed, 37 insertions(+), 17 deletions(-)
30
block.c | 17 ++++++++++++++---
31
block/stream.c | 26 ++++++++++++++++----------
32
3 files changed, 33 insertions(+), 13 deletions(-)
17
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,
18
diff --git a/block.c b/block.c
48
diff --git a/block.c b/block.c
19
index XXXXXXX..XXXXXXX 100644
49
index XXXXXXX..XXXXXXX 100644
20
--- a/block.c
50
--- a/block.c
21
+++ b/block.c
51
+++ b/block.c
22
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
52
@@ -XXX,XX +XXX,XX @@ static int bdrv_set_backing_noperm(BlockDriverState *bs,
23
BdrvChild **child,
53
return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp);
24
Transaction *tran,
54
}
25
Error **errp);
55
26
+static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
27
+ Transaction *tran);
28
29
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
30
*queue, Error **errp);
31
@@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
32
* Sets the bs->backing link of a BDS. A new reference is created; callers
33
* which don't need their own reference any more must call bdrv_unref().
34
*/
35
-int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
56
-int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
36
- Error **errp)
57
- Error **errp)
37
+static int bdrv_set_backing_noperm(BlockDriverState *bs,
58
+int bdrv_set_backing_hd_drained(BlockDriverState *bs,
38
+ BlockDriverState *backing_hd,
59
+ BlockDriverState *backing_hd,
39
+ Transaction *tran, Error **errp)
60
+ Error **errp)
40
{
61
{
41
int ret = 0;
62
int ret;
42
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
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) {
43
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
71
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
44
return -EPERM;
72
ret = bdrv_refresh_perms(bs, tran, errp);
45
}
46
47
- if (backing_hd) {
48
- bdrv_ref(backing_hd);
49
- }
50
-
51
if (bs->backing) {
52
/* Cannot be frozen, we checked that above */
53
- bdrv_unref_child(bs, bs->backing);
54
- bs->backing = NULL;
55
+ bdrv_unset_inherits_from(bs, bs->backing, tran);
56
+ bdrv_remove_filter_or_cow_child(bs, tran);
57
}
58
59
if (!backing_hd) {
60
goto out;
61
}
62
63
- bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_of_bds,
64
- bdrv_backing_role(bs), errp);
65
- if (!bs->backing) {
66
- ret = -EPERM;
67
- goto out;
68
+ ret = bdrv_attach_child_noperm(bs, backing_hd, "backing",
69
+ &child_of_bds, bdrv_backing_role(bs),
70
+ &bs->backing, tran, errp);
71
+ if (ret < 0) {
72
+ return ret;
73
}
74
75
- /* If backing_hd was already part of bs's backing chain, and
76
+
77
+ /*
78
+ * If backing_hd was already part of bs's backing chain, and
79
* inherits_from pointed recursively to bs then let's update it to
80
- * point directly to bs (else it will become NULL). */
81
+ * point directly to bs (else it will become NULL).
82
+ */
83
if (update_inherits_from) {
84
- backing_hd->inherits_from = bs;
85
+ bdrv_set_inherits_from(backing_hd, bs, tran);
86
}
87
88
out:
73
out:
89
- bdrv_refresh_limits(bs, NULL, NULL);
74
tran_finalize(tran, ret);
90
+ bdrv_refresh_limits(bs, tran, NULL);
75
+ return ret;
91
+
92
+ return 0;
93
+}
76
+}
94
+
77
95
+int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
78
+int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
96
+ Error **errp)
79
+ Error **errp)
97
+{
80
+{
98
+ int ret;
81
+ int ret;
99
+ Transaction *tran = tran_new();
82
+ GLOBAL_STATE_CODE();
100
+
83
+
101
+ ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp);
84
+ bdrv_drained_begin(bs);
102
+ if (ret < 0) {
85
+ ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp);
103
+ goto out;
86
bdrv_drained_end(bs);
104
+ }
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);
105
+
121
+
106
+ ret = bdrv_refresh_perms(bs, errp);
122
+ /*
107
+out:
123
+ * This call will do I/O, so the graph can change again from here on.
108
+ tran_finalize(tran, ret);
124
+ * We have already completed the graph change, so we are not in danger
109
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);
110
return ret;
139
return ret;
111
}
140
}
141
112
--
142
--
113
2.30.2
143
2.38.1
114
115
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
Subtree drains are not used any more. Remove them.
2
2
3
Move bdrv_reopen_multiple to new paradigm of permission update:
3
After this, BdrvChildClass.attach/detach() don't poll any more.
4
first update graph relations, then do refresh the permissions.
5
4
6
We have to modify reopen process in file-posix driver: with new scheme
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
we don't have prepared permissions in raw_reopen_prepare(), so we
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
8
should reconfigure fd in raw_check_perm(). Still this seems more native
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
9
and simple anyway.
8
Message-Id: <20221118174110.55183-11-kwolf@redhat.com>
10
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20210428151804.439460-31-vsementsov@virtuozzo.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
10
---
16
include/block/block.h | 3 +-
11
include/block/block-io.h | 18 +--
17
block.c | 187 ++++++++++++------------------------------
12
include/block/block_int-common.h | 1 -
18
block/file-posix.c | 91 +++++++-------------
13
include/block/block_int-io.h | 12 --
19
3 files changed, 84 insertions(+), 197 deletions(-)
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(-)
20
18
21
diff --git a/include/block/block.h b/include/block/block.h
19
diff --git a/include/block/block-io.h b/include/block/block-io.h
22
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block.h
21
--- a/include/block/block-io.h
24
+++ b/include/block/block.h
22
+++ b/include/block/block-io.h
25
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState {
23
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c);
26
BlockdevDetectZeroesOptions detect_zeroes;
24
/**
27
bool backing_missing;
25
* bdrv_drain_poll:
28
bool replace_backing_bs; /* new_backing_bs is ignored if this is false */
26
*
29
- BlockDriverState *new_backing_bs; /* If NULL then detach the current bs */
27
- * Poll for pending requests in @bs, its parents (except for @ignore_parent),
30
- uint64_t perm, shared_perm;
28
- * and if @recursive is true its children as well (used for subtree drain).
31
+ BlockDriverState *old_backing_bs; /* keep pointer for permissions update */
29
+ * Poll for pending requests in @bs and its parents (except for @ignore_parent).
32
QDict *options;
30
*
33
QDict *explicit_options;
31
* If @ignore_bds_parents is true, parents that are BlockDriverStates must
34
void *opaque;
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 */
35
diff --git a/block.c b/block.c
100
diff --git a/block.c b/block.c
36
index XXXXXXX..XXXXXXX 100644
101
index XXXXXXX..XXXXXXX 100644
37
--- a/block.c
102
--- a/block.c
38
+++ b/block.c
103
+++ b/block.c
39
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
104
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_begin(BdrvChild *child)
40
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
105
static bool bdrv_child_cb_drained_poll(BdrvChild *child)
41
Transaction *tran);
106
{
42
107
BlockDriverState *bs = child->opaque;
43
-static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
108
- return bdrv_drain_poll(bs, false, NULL, false);
44
- *queue, Error **errp);
109
+ return bdrv_drain_poll(bs, NULL, false);
45
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
110
}
46
+ BlockReopenQueue *queue,
111
47
+ Transaction *set_backings_tran, Error **errp);
112
static void bdrv_child_cb_drained_end(BdrvChild *child)
48
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
113
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child)
49
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
114
assert(!bs->file);
50
115
bs->file = child;
51
@@ -XXX,XX +XXX,XX @@ static void bdrv_list_abort_perm_update(GSList *list)
116
}
52
}
117
-
53
}
118
- bdrv_apply_subtree_drain(child, bs);
54
119
}
55
+__attribute__((unused))
120
56
static void bdrv_abort_perm_update(BlockDriverState *bs)
121
static void bdrv_child_cb_detach(BdrvChild *child)
57
{
122
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_detach(BdrvChild *child)
58
g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
123
bdrv_backing_detach(child);
59
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
124
}
60
*
125
61
* Needs to be followed by a call to either bdrv_set_perm() or
126
- bdrv_unapply_subtree_drain(child, bs);
62
* bdrv_abort_perm_update(). */
127
-
63
+__attribute__((unused))
128
assert_bdrv_graph_writable(bs);
64
static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
129
QLIST_REMOVE(child, next);
65
uint64_t new_used_perm,
130
if (child == bs->backing) {
66
uint64_t new_shared_perm,
131
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
67
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
132
}
68
bs_entry->state.explicit_options = explicit_options;
133
69
bs_entry->state.flags = flags;
134
if (old_bs) {
70
135
- /* Detach first so that the recursive drain sections coming from @child
71
- /* This needs to be overwritten in bdrv_reopen_prepare() */
136
- * are already gone and we only end the drain sections that came from
72
- bs_entry->state.perm = UINT64_MAX;
137
- * elsewhere. */
73
- bs_entry->state.shared_perm = 0;
138
if (child->klass->detach) {
74
-
139
child->klass->detach(child);
75
/*
76
* If keep_old_opts is false then it means that unspecified
77
* options must be reset to their original value. We don't allow
78
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
79
{
80
int ret = -1;
81
BlockReopenQueueEntry *bs_entry, *next;
82
+ Transaction *tran = tran_new();
83
+ g_autoptr(GHashTable) found = NULL;
84
+ g_autoptr(GSList) refresh_list = NULL;
85
86
assert(bs_queue != NULL);
87
88
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
89
90
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
91
assert(bs_entry->state.bs->quiesce_counter > 0);
92
- if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
93
- goto cleanup;
94
+ ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp);
95
+ if (ret < 0) {
96
+ goto abort;
97
}
140
}
98
bs_entry->prepared = true;
141
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
99
}
142
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
100
143
101
+ found = g_hash_table_new(NULL, NULL);
144
/*
102
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
145
- * Detaching the old node may have led to the new node's
103
BDRVReopenState *state = &bs_entry->state;
146
- * quiesce_counter having been decreased. Not a problem, we
104
- ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
147
- * just need to recognize this here and then invoke
105
- state->shared_perm, errp);
148
- * drained_end appropriately more often.
106
- if (ret < 0) {
149
+ * Polling in bdrv_parent_drained_begin_single() may have led to the new
107
- goto cleanup_perm;
150
+ * node's quiesce_counter having been decreased. Not a problem, we just
108
- }
151
+ * need to recognize this here and then invoke drained_end appropriately
109
- /* Check if new_backing_bs would accept the new permissions */
152
+ * more often.
110
- if (state->replace_backing_bs && state->new_backing_bs) {
153
*/
111
- uint64_t nperm, nshared;
154
assert(new_bs->quiesce_counter <= new_bs_quiesce_counter);
112
- bdrv_child_perm(state->bs, state->new_backing_bs,
155
drain_saldo += new_bs->quiesce_counter - new_bs_quiesce_counter;
113
- NULL, bdrv_backing_role(state->bs),
156
114
- bs_queue, state->perm, state->shared_perm,
157
- /* Attach only after starting new drained sections, so that recursive
115
- &nperm, &nshared);
158
- * drain sections coming from @child don't get an extra .drained_begin
116
- ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
159
- * callback. */
117
- nperm, nshared, errp);
160
if (child->klass->attach) {
118
- if (ret < 0) {
161
child->klass->attach(child);
119
- goto cleanup_perm;
120
- }
121
+
122
+ refresh_list = bdrv_topological_dfs(refresh_list, found, state->bs);
123
+ if (state->old_backing_bs) {
124
+ refresh_list = bdrv_topological_dfs(refresh_list, found,
125
+ state->old_backing_bs);
126
}
162
}
127
- bs_entry->perms_checked = true;
163
diff --git a/block/io.c b/block/io.c
128
+ }
164
index XXXXXXX..XXXXXXX 100644
129
+
165
--- a/block/io.c
130
+ /*
166
+++ b/block/io.c
131
+ * Note that file-posix driver rely on permission update done during reopen
167
@@ -XXX,XX +XXX,XX @@ typedef struct {
132
+ * (even if no permission changed), because it wants "new" permissions for
168
BlockDriverState *bs;
133
+ * reconfiguring the fd and that's why it does it in raw_check_perm(), not
169
bool done;
134
+ * in raw_reopen_prepare() which is called with "old" permissions.
170
bool begin;
135
+ */
171
- bool recursive;
136
+ ret = bdrv_list_refresh_perms(refresh_list, bs_queue, tran, errp);
172
bool poll;
137
+ if (ret < 0) {
173
BdrvChild *parent;
138
+ goto abort;
174
bool ignore_bds_parents;
139
}
175
} BdrvCoDrainData;
140
176
141
/*
177
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
142
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
178
-bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
143
bdrv_reopen_commit(&bs_entry->state);
179
- BdrvChild *ignore_parent, bool ignore_bds_parents)
144
}
180
+bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent,
145
181
+ bool ignore_bds_parents)
146
- ret = 0;
182
{
147
-cleanup_perm:
183
- BdrvChild *child, *next;
148
- QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
184
IO_OR_GS_CODE();
149
- BDRVReopenState *state = &bs_entry->state;
185
150
-
186
if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) {
151
- if (!bs_entry->perms_checked) {
187
@@ -XXX,XX +XXX,XX @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
152
- continue;
188
return true;
153
- }
189
}
154
-
190
155
- if (ret == 0) {
191
- if (recursive) {
156
- uint64_t perm, shared;
192
- assert(!ignore_bds_parents);
157
+ tran_commit(tran);
193
- QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
158
194
- if (bdrv_drain_poll(child->bs, recursive, child, false)) {
159
- bdrv_get_cumulative_perm(state->bs, &perm, &shared);
195
- return true;
160
- assert(perm == state->perm);
161
- assert(shared == state->shared_perm);
162
+ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
163
+ BlockDriverState *bs = bs_entry->state.bs;
164
165
- bdrv_set_perm(state->bs);
166
- } else {
167
- bdrv_abort_perm_update(state->bs);
168
- if (state->replace_backing_bs && state->new_backing_bs) {
169
- bdrv_abort_perm_update(state->new_backing_bs);
170
- }
171
+ if (bs->drv->bdrv_reopen_commit_post) {
172
+ bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
173
}
174
}
175
176
- if (ret == 0) {
177
- QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
178
- BlockDriverState *bs = bs_entry->state.bs;
179
+ ret = 0;
180
+ goto cleanup;
181
182
- if (bs->drv->bdrv_reopen_commit_post)
183
- bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
184
+abort:
185
+ tran_abort(tran);
186
+ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
187
+ if (bs_entry->prepared) {
188
+ bdrv_reopen_abort(&bs_entry->state);
189
}
190
+ qobject_unref(bs_entry->state.explicit_options);
191
+ qobject_unref(bs_entry->state.options);
192
}
193
+
194
cleanup:
195
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
196
- if (ret) {
197
- if (bs_entry->prepared) {
198
- bdrv_reopen_abort(&bs_entry->state);
199
- }
200
- qobject_unref(bs_entry->state.explicit_options);
201
- qobject_unref(bs_entry->state.options);
202
- }
203
- if (bs_entry->state.new_backing_bs) {
204
- bdrv_unref(bs_entry->state.new_backing_bs);
205
- }
206
g_free(bs_entry);
207
}
208
g_free(bs_queue);
209
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
210
return ret;
211
}
212
213
-static BlockReopenQueueEntry *find_parent_in_reopen_queue(BlockReopenQueue *q,
214
- BdrvChild *c)
215
-{
216
- BlockReopenQueueEntry *entry;
217
-
218
- QTAILQ_FOREACH(entry, q, entry) {
219
- BlockDriverState *bs = entry->state.bs;
220
- BdrvChild *child;
221
-
222
- QLIST_FOREACH(child, &bs->children, next) {
223
- if (child == c) {
224
- return entry;
225
- }
196
- }
226
- }
197
- }
227
- }
198
- }
228
-
199
-
229
- return NULL;
200
return false;
230
-}
201
}
231
-
202
232
-static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
203
-static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive,
233
- uint64_t *perm, uint64_t *shared)
204
+static bool bdrv_drain_poll_top_level(BlockDriverState *bs,
234
-{
205
BdrvChild *ignore_parent)
235
- BdrvChild *c;
206
{
236
- BlockReopenQueueEntry *parent;
207
- return bdrv_drain_poll(bs, recursive, ignore_parent, false);
237
- uint64_t cumulative_perms = 0;
208
+ return bdrv_drain_poll(bs, ignore_parent, false);
238
- uint64_t cumulative_shared_perms = BLK_PERM_ALL;
209
}
239
-
210
240
- QLIST_FOREACH(c, &bs->parents, next_parent) {
211
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
241
- parent = find_parent_in_reopen_queue(q, c);
212
- BdrvChild *parent, bool ignore_bds_parents,
242
- if (!parent) {
213
- bool poll);
243
- cumulative_perms |= c->perm;
214
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
244
- cumulative_shared_perms &= c->shared_perm;
215
- BdrvChild *parent, bool ignore_bds_parents);
245
- } else {
216
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
246
- uint64_t nperm, nshared;
217
+ bool ignore_bds_parents, bool poll);
247
-
218
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
248
- bdrv_child_perm(parent->state.bs, bs, c, c->role, q,
219
+ bool ignore_bds_parents);
249
- parent->state.perm, parent->state.shared_perm,
220
250
- &nperm, &nshared);
221
static void bdrv_co_drain_bh_cb(void *opaque)
251
-
222
{
252
- cumulative_perms |= nperm;
223
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
253
- cumulative_shared_perms &= nshared;
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);
254
- }
283
- }
255
- }
284
- }
256
- *perm = cumulative_perms;
285
-
257
- *shared = cumulative_shared_perms;
286
/*
258
-}
287
* Wait for drained requests to finish.
259
-
288
*
260
static bool bdrv_reopen_can_attach(BlockDriverState *parent,
289
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
261
BdrvChild *child,
290
*/
262
BlockDriverState *new_child,
291
if (poll) {
263
@@ -XXX,XX +XXX,XX @@ static bool bdrv_reopen_can_attach(BlockDriverState *parent,
292
assert(!ignore_bds_parents);
264
* Return 0 on success, otherwise return < 0 and set @errp.
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.
265
*/
314
*/
266
static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
315
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
267
+ Transaction *set_backings_tran,
316
- BdrvChild *parent, bool ignore_bds_parents)
268
Error **errp)
317
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
269
{
318
+ bool ignore_bds_parents)
270
BlockDriverState *bs = reopen_state->bs;
319
{
271
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
320
- BdrvChild *child;
272
321
int old_quiesce_counter;
273
/* If we want to replace the backing file we need some extra checks */
322
274
if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) {
323
if (qemu_in_coroutine()) {
275
+ int ret;
324
- bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents,
276
+
325
- false);
277
/* Check for implicit nodes between bs and its backing file */
326
+ bdrv_co_yield_to_drain(bs, false, parent, ignore_bds_parents, false);
278
if (bs != overlay_bs) {
327
return;
279
error_setg(errp, "Cannot change backing link if '%s' has "
328
}
280
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
329
assert(bs->quiesce_counter > 0);
281
return -EPERM;
330
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
282
}
331
if (old_quiesce_counter == 1) {
283
reopen_state->replace_backing_bs = true;
332
aio_enable_external(bdrv_get_aio_context(bs));
284
- if (new_backing_bs) {
333
}
285
- bdrv_ref(new_backing_bs);
334
-
286
- reopen_state->new_backing_bs = new_backing_bs;
335
- if (recursive) {
287
+ reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
336
- assert(!ignore_bds_parents);
288
+ ret = bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran,
337
- bs->recursive_quiesce_counter--;
289
+ errp);
338
- QLIST_FOREACH(child, &bs->children, next) {
290
+ if (ret < 0) {
339
- bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents);
291
+ return ret;
292
}
293
}
294
295
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
296
*
297
*/
298
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
299
- BlockReopenQueue *queue, Error **errp)
300
+ BlockReopenQueue *queue,
301
+ Transaction *set_backings_tran, Error **errp)
302
{
303
int ret = -1;
304
int old_flags;
305
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
306
goto error;
307
}
308
309
- /* Calculate required permissions after reopening */
310
- bdrv_reopen_perm(queue, reopen_state->bs,
311
- &reopen_state->perm, &reopen_state->shared_perm);
312
-
313
if (drv->bdrv_reopen_prepare) {
314
/*
315
* If a driver-specific option is missing, it means that we
316
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
317
* either a reference to an existing node (using its node name)
318
* or NULL to simply detach the current backing file.
319
*/
320
- ret = bdrv_reopen_parse_backing(reopen_state, errp);
321
+ ret = bdrv_reopen_parse_backing(reopen_state, set_backings_tran, errp);
322
if (ret < 0) {
323
goto error;
324
}
325
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
326
qdict_del(bs->explicit_options, child->name);
327
qdict_del(bs->options, child->name);
328
}
329
-
330
- /*
331
- * Change the backing file if a new one was specified. We do this
332
- * after updating bs->options, so bdrv_refresh_filename() (called
333
- * from bdrv_set_backing_hd()) has the new values.
334
- */
335
- if (reopen_state->replace_backing_bs) {
336
- BlockDriverState *old_backing_bs = child_bs(bs->backing);
337
- assert(!old_backing_bs || !old_backing_bs->implicit);
338
- /* Abort the permission update on the backing bs we're detaching */
339
- if (old_backing_bs) {
340
- bdrv_abort_perm_update(old_backing_bs);
341
- }
342
- bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
343
- }
344
-
345
bdrv_refresh_limits(bs, NULL, NULL);
346
}
347
348
diff --git a/block/file-posix.c b/block/file-posix.c
349
index XXXXXXX..XXXXXXX 100644
350
--- a/block/file-posix.c
351
+++ b/block/file-posix.c
352
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
353
} BDRVRawState;
354
355
typedef struct BDRVRawReopenState {
356
- int fd;
357
int open_flags;
358
bool drop_cache;
359
bool check_cache_dropped;
360
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
361
BDRVRawReopenState *rs;
362
QemuOpts *opts;
363
int ret;
364
- Error *local_err = NULL;
365
366
assert(state != NULL);
367
assert(state->bs != NULL);
368
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
369
* bdrv_reopen_prepare() will detect changes and complain. */
370
qemu_opts_to_qdict(opts, state->options);
371
372
- rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
373
- state->perm, true, &local_err);
374
- if (local_err) {
375
- error_propagate(errp, local_err);
376
- ret = -1;
377
- goto out;
378
- }
379
-
380
- /* Fail already reopen_prepare() if we can't get a working O_DIRECT
381
- * alignment with the new fd. */
382
- if (rs->fd != -1) {
383
- raw_probe_alignment(state->bs, rs->fd, &local_err);
384
- if (local_err) {
385
- error_propagate(errp, local_err);
386
- ret = -EINVAL;
387
- goto out_fd;
388
- }
340
- }
389
- }
341
- }
390
+ /*
342
}
391
+ * As part of reopen prepare we also want to create new fd by
343
392
+ * raw_reconfigure_getfd(). But it wants updated "perm", when in
344
void bdrv_drained_end(BlockDriverState *bs)
393
+ * bdrv_reopen_multiple() .bdrv_reopen_prepare() callback called prior to
345
{
394
+ * permission update. Happily, permission update is always a part (a seprate
346
IO_OR_GS_CODE();
395
+ * stage) of bdrv_reopen_multiple() so we can rely on this fact and
347
- bdrv_do_drained_end(bs, false, NULL, false);
396
+ * reconfigure fd in raw_check_perm().
348
-}
397
+ */
349
-
398
350
-void bdrv_subtree_drained_end(BlockDriverState *bs)
399
s->reopen_state = state;
351
-{
400
ret = 0;
352
- IO_OR_GS_CODE();
401
-out_fd:
353
- bdrv_do_drained_end(bs, true, NULL, false);
402
- if (ret < 0) {
354
-}
403
- qemu_close(rs->fd);
355
-
404
- rs->fd = -1;
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);
405
- }
363
- }
406
+
364
-}
407
out:
365
-
408
qemu_opts_del(opts);
366
-void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
409
return ret;
367
-{
410
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
368
- int i;
411
s->drop_cache = rs->drop_cache;
369
- IO_OR_GS_CODE();
412
s->check_cache_dropped = rs->check_cache_dropped;
370
-
413
s->open_flags = rs->open_flags;
371
- for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
414
-
372
- bdrv_do_drained_end(child->bs, true, child, false);
415
- qemu_close(s->fd);
373
- }
416
- s->fd = rs->fd;
374
+ bdrv_do_drained_end(bs, NULL, false);
417
-
375
}
418
g_free(state->opaque);
376
419
state->opaque = NULL;
377
void bdrv_drain(BlockDriverState *bs)
420
378
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_all_poll(void)
421
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_abort(BDRVReopenState *state)
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);
422
return;
393
return;
423
}
394
}
424
395
425
- if (rs->fd >= 0) {
396
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
426
- qemu_close(rs->fd);
397
AioContext *aio_context = bdrv_get_aio_context(bs);
427
- rs->fd = -1;
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);
428
- }
696
- }
429
g_free(state->opaque);
697
}
430
state->opaque = NULL;
698
431
699
static void test_blockjob_drain_all(void)
432
@@ -XXX,XX +XXX,XX @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
700
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_drain(void)
433
Error **errp)
701
test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_SUCCESS);
434
{
702
}
435
BDRVRawState *s = bs->opaque;
703
436
- BDRVRawReopenState *rs = NULL;
704
-static void test_blockjob_drain_subtree(void)
437
+ int input_flags = s->reopen_state ? s->reopen_state->flags : bs->open_flags;
705
-{
438
int open_flags;
706
- test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_SUCCESS);
439
int ret;
707
-}
440
708
-
441
- if (s->perm_change_fd) {
709
static void test_blockjob_error_drain_all(void)
442
+ /* We may need a new fd if auto-read-only switches the mode */
710
{
443
+ ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm,
711
test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_RUN);
444
+ false, errp);
712
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_error_drain(void)
445
+ if (ret < 0) {
713
test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_PREPARE);
446
+ return ret;
714
}
447
+ } else if (ret != s->fd) {
715
448
+ Error *local_err = NULL;
716
-static void test_blockjob_error_drain_subtree(void)
449
+
717
-{
450
/*
718
- test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_RUN);
451
- * In the context of reopen, this function may be called several times
719
- test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_PREPARE);
452
- * (directly and recursively while change permissions of the parent).
720
-}
453
- * This is even true for children that don't inherit from the original
721
-
454
- * reopen node, so s->reopen_state is not set.
722
static void test_blockjob_iothread_drain_all(void)
455
- *
723
{
456
- * Ignore all but the first call.
724
test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_SUCCESS);
457
+ * Fail already check_perm() if we can't get a working O_DIRECT
725
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_iothread_drain(void)
458
+ * alignment with the new fd.
726
test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_SUCCESS);
459
*/
727
}
460
- return 0;
728
461
- }
729
-static void test_blockjob_iothread_drain_subtree(void)
462
-
730
-{
463
- if (s->reopen_state) {
731
- test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_SUCCESS);
464
- /* We already have a new file descriptor to set permissions for */
732
-}
465
- assert(s->reopen_state->perm == perm);
733
-
466
- assert(s->reopen_state->shared_perm == shared);
734
static void test_blockjob_iothread_error_drain_all(void)
467
- rs = s->reopen_state->opaque;
735
{
468
- s->perm_change_fd = rs->fd;
736
test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_RUN);
469
- s->perm_change_flags = rs->open_flags;
737
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_iothread_error_drain(void)
470
- } else {
738
test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_PREPARE);
471
- /* We may need a new fd if auto-read-only switches the mode */
739
}
472
- ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm,
740
473
- false, errp);
741
-static void test_blockjob_iothread_error_drain_subtree(void)
474
- if (ret < 0) {
742
-{
475
- return ret;
743
- test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_RUN);
476
- } else if (ret != s->fd) {
744
- test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_PREPARE);
477
- s->perm_change_fd = ret;
745
-}
478
- s->perm_change_flags = open_flags;
746
-
479
+ raw_probe_alignment(bs, ret, &local_err);
747
480
+ if (local_err) {
748
typedef struct BDRVTestTopState {
481
+ error_propagate(errp, local_err);
749
BdrvChild *wait_child;
482
+ return -EINVAL;
750
@@ -XXX,XX +XXX,XX @@ static void do_test_delete_by_drain(bool detach_instead_of_delete,
483
}
751
bdrv_drain(child_bs);
484
+
752
bdrv_unref(child_bs);
485
+ s->perm_change_fd = ret;
753
break;
486
+ s->perm_change_flags = open_flags;
754
- case BDRV_SUBTREE_DRAIN:
487
}
755
- /* Would have to ref/unref bs here for !detach_instead_of_delete, but
488
756
- * then the whole test becomes pointless because the graph changes
489
/* Prepare permissions on old fd to avoid conflicts between old and new,
757
- * don't occur during the drain any more. */
490
@@ -XXX,XX +XXX,XX @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
758
- assert(detach_instead_of_delete);
491
return 0;
759
- bdrv_subtree_drained_begin(bs);
492
760
- bdrv_subtree_drained_end(bs);
493
fail:
761
- break;
494
- if (s->perm_change_fd && !s->reopen_state) {
762
case BDRV_DRAIN_ALL:
495
+ if (s->perm_change_fd) {
763
bdrv_drain_all_begin();
496
qemu_close(s->perm_change_fd);
764
bdrv_drain_all_end();
497
}
765
@@ -XXX,XX +XXX,XX @@ static void test_detach_by_drain(void)
498
s->perm_change_fd = 0;
766
do_test_delete_by_drain(true, BDRV_DRAIN);
499
@@ -XXX,XX +XXX,XX @@ static void raw_abort_perm_update(BlockDriverState *bs)
767
}
500
768
501
/* For reopen, .bdrv_reopen_abort is called afterwards and will close
769
-static void test_detach_by_drain_subtree(void)
502
* the file descriptor. */
770
-{
503
- if (s->perm_change_fd && !s->reopen_state) {
771
- do_test_delete_by_drain(true, BDRV_SUBTREE_DRAIN);
504
+ if (s->perm_change_fd) {
772
-}
505
qemu_close(s->perm_change_fd);
773
-
506
}
774
507
s->perm_change_fd = 0;
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
508
--
879
--
509
2.30.2
880
2.38.1
510
511
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
We only need to call both the BlockDriver's callback and the parent
2
2
callbacks when going from undrained to drained or vice versa. A second
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
3
drain section doesn't make a difference for the driver or the parent,
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
they weren't supposed to send new requests before and after the second
5
Message-Id: <20210428151804.439460-23-vsementsov@virtuozzo.com>
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>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
32
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
33
---
8
block.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
34
include/block/block_int-common.h | 8 ++++----
9
1 file changed, 82 insertions(+), 2 deletions(-)
35
block.c | 25 +++++++------------------
10
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;
11
diff --git a/block.c b/block.c
62
diff --git a/block.c b/block.c
12
index XXXXXXX..XXXXXXX 100644
63
index XXXXXXX..XXXXXXX 100644
13
--- a/block.c
64
--- a/block.c
14
+++ b/block.c
65
+++ b/block.c
15
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
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++;
16
}
123
}
17
}
124
}
18
125
19
+static void bdrv_child_free(void *opaque)
126
diff --git a/block/io.c b/block/io.c
20
+{
127
index XXXXXXX..XXXXXXX 100644
21
+ BdrvChild *c = opaque;
128
--- a/block/io.c
22
+
129
+++ b/block/io.c
23
+ g_free(c->name);
130
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c)
24
+ g_free(c);
25
+}
26
+
27
static void bdrv_remove_empty_child(BdrvChild *child)
28
{
131
{
29
assert(!child->bs);
132
IO_OR_GS_CODE();
30
QLIST_SAFE_REMOVE(child, next);
133
31
- g_free(child->name);
134
- assert(c->parent_quiesce_counter > 0);
32
- g_free(child);
135
- c->parent_quiesce_counter--;
33
+ bdrv_child_free(child);
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
}
34
}
169
}
35
170
36
typedef struct BdrvAttachChildCommonState {
171
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
37
@@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
172
assert(bs->quiesce_counter > 0);
38
return ret;
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
}
39
}
190
}
40
191
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
41
+typedef struct BdrvRemoveFilterOrCowChild {
192
index XXXXXXX..XXXXXXX 100644
42
+ BdrvChild *child;
193
--- a/tests/unit/test-bdrv-drain.c
43
+ bool is_backing;
194
+++ b/tests/unit/test-bdrv-drain.c
44
+} BdrvRemoveFilterOrCowChild;
195
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_common(enum drain_type drain_type, bool recursive)
45
+
196
46
+static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
197
do_drain_begin(drain_type, bs);
47
+{
198
48
+ BdrvRemoveFilterOrCowChild *s = opaque;
199
- g_assert_cmpint(bs->quiesce_counter, ==, 1);
49
+ BlockDriverState *parent_bs = s->child->opaque;
200
+ if (drain_type == BDRV_DRAIN_ALL) {
50
+
201
+ g_assert_cmpint(bs->quiesce_counter, ==, 2);
51
+ QLIST_INSERT_HEAD(&parent_bs->children, s->child, next);
52
+ if (s->is_backing) {
53
+ parent_bs->backing = s->child;
54
+ } else {
202
+ } else {
55
+ parent_bs->file = s->child;
203
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
56
+ }
204
+ }
57
+
205
g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
58
+ /*
206
59
+ * We don't have to restore child->bs here to undo bdrv_replace_child()
207
do_drain_end(drain_type, bs);
60
+ * because that function is transactionable and it registered own completion
208
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
61
+ * entries in @tran, so .abort() for bdrv_replace_child_safe() will be
209
62
+ * called automatically.
210
for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
63
+ */
211
for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
64
+}
212
- int backing_quiesce = (outer != BDRV_DRAIN) +
65
+
213
- (inner != BDRV_DRAIN);
66
+static void bdrv_remove_filter_or_cow_child_commit(void *opaque)
214
+ int backing_quiesce = (outer == BDRV_DRAIN_ALL) +
67
+{
215
+ (inner == BDRV_DRAIN_ALL);
68
+ BdrvRemoveFilterOrCowChild *s = opaque;
216
69
+
217
g_assert_cmpint(bs->quiesce_counter, ==, 0);
70
+ bdrv_child_free(s->child);
218
g_assert_cmpint(backing->quiesce_counter, ==, 0);
71
+}
219
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
72
+
220
do_drain_begin(outer, bs);
73
+static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
221
do_drain_begin(inner, bs);
74
+ .abort = bdrv_remove_filter_or_cow_child_abort,
222
75
+ .commit = bdrv_remove_filter_or_cow_child_commit,
223
- g_assert_cmpint(bs->quiesce_counter, ==, 2);
76
+ .clean = g_free,
224
+ g_assert_cmpint(bs->quiesce_counter, ==, 2 + !!backing_quiesce);
77
+};
225
g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
78
+
226
- g_assert_cmpint(s->drain_count, ==, 2);
79
+/*
227
- g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce);
80
+ * A function to remove backing-chain child of @bs if exists: cow child for
228
+ g_assert_cmpint(s->drain_count, ==, 1);
81
+ * format nodes (always .backing) and filter child for filters (may be .file or
229
+ g_assert_cmpint(backing_s->drain_count, ==, !!backing_quiesce);
82
+ * .backing)
230
83
+ */
231
do_drain_end(inner, bs);
84
+__attribute__((unused))
232
do_drain_end(outer, bs);
85
+static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
86
+ Transaction *tran)
87
+{
88
+ BdrvRemoveFilterOrCowChild *s;
89
+ BdrvChild *child = bdrv_filter_or_cow_child(bs);
90
+
91
+ if (!child) {
92
+ return;
93
+ }
94
+
95
+ if (child->bs) {
96
+ bdrv_replace_child_safe(child, NULL, tran);
97
+ }
98
+
99
+ s = g_new(BdrvRemoveFilterOrCowChild, 1);
100
+ *s = (BdrvRemoveFilterOrCowChild) {
101
+ .child = child,
102
+ .is_backing = (child == bs->backing),
103
+ };
104
+ tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
105
+
106
+ QLIST_SAFE_REMOVE(child, next);
107
+ if (s->is_backing) {
108
+ bs->backing = NULL;
109
+ } else {
110
+ bs->file = NULL;
111
+ }
112
+}
113
+
114
static int bdrv_replace_node_noperm(BlockDriverState *from,
115
BlockDriverState *to,
116
bool auto_skip, Transaction *tran,
117
--
233
--
118
2.30.2
234
2.38.1
119
120
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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.
2
4
3
These functions are called only from bdrv_reopen_multiple() in block.c.
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
No reason to publish them.
6
Message-Id: <20221118174110.55183-13-kwolf@redhat.com>
5
7
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20210428151804.439460-8-vsementsov@virtuozzo.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
10
---
12
include/block/block.h | 4 ----
11
include/block/block-io.h | 3 +--
13
block.c | 13 +++++++++----
12
block.c | 2 +-
14
2 files changed, 9 insertions(+), 8 deletions(-)
13
block/io.c | 58 +++++++++++++++-------------------------
14
3 files changed, 24 insertions(+), 39 deletions(-)
15
15
16
diff --git a/include/block/block.h b/include/block/block.h
16
diff --git a/include/block/block-io.h b/include/block/block-io.h
17
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/block/block.h
18
--- a/include/block/block-io.h
19
+++ b/include/block/block.h
19
+++ b/include/block/block-io.h
20
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
21
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
21
* Quiesces a BDS like bdrv_drained_begin(), but does not wait for already
22
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
22
* running requests to complete.
23
Error **errp);
23
*/
24
-int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
24
-void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
25
- BlockReopenQueue *queue, Error **errp);
25
- BdrvChild *parent, bool ignore_bds_parents);
26
-void bdrv_reopen_commit(BDRVReopenState *reopen_state);
26
+void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent);
27
-void bdrv_reopen_abort(BDRVReopenState *reopen_state);
27
28
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
28
/**
29
int64_t bytes, BdrvRequestFlags flags);
29
* bdrv_drained_end:
30
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags);
31
diff --git a/block.c b/block.c
30
diff --git a/block.c b/block.c
32
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
33
--- a/block.c
32
--- a/block.c
34
+++ b/block.c
33
+++ b/block.c
35
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
34
@@ -XXX,XX +XXX,XX @@ static char *bdrv_child_get_parent_desc(BdrvChild *c)
36
BdrvChildRole child_role,
35
static void bdrv_child_cb_drained_begin(BdrvChild *child)
37
Error **errp);
36
{
38
37
BlockDriverState *bs = child->opaque;
39
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
38
- bdrv_do_drained_begin_quiesce(bs, NULL, false);
40
+ *queue, Error **errp);
39
+ bdrv_do_drained_begin_quiesce(bs, NULL);
41
+static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
40
}
42
+static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
41
43
+
42
static bool bdrv_child_cb_drained_poll(BdrvChild *child)
44
/* If non-zero, use only whitelisted block drivers */
43
diff --git a/block/io.c b/block/io.c
45
static int use_bdrv_whitelist;
44
index XXXXXXX..XXXXXXX 100644
46
45
--- a/block/io.c
47
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
46
+++ b/block/io.c
48
* commit() for any other BDS that have been left in a prepare() state
47
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs);
49
*
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.
50
*/
187
*/
51
-int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
188
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
52
- Error **errp)
189
- bool ignore_bds_parents)
53
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
190
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
54
+ BlockReopenQueue *queue, Error **errp)
191
{
55
{
192
int old_quiesce_counter;
56
int ret = -1;
193
57
int old_flags;
194
if (qemu_in_coroutine()) {
58
@@ -XXX,XX +XXX,XX @@ error:
195
- bdrv_co_yield_to_drain(bs, false, parent, ignore_bds_parents, false);
59
* makes them final by swapping the staging BlockDriverState contents into
196
+ bdrv_co_yield_to_drain(bs, false, parent, false);
60
* the active BlockDriverState contents.
197
return;
61
*/
198
}
62
-void bdrv_reopen_commit(BDRVReopenState *reopen_state)
199
assert(bs->quiesce_counter > 0);
63
+static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
200
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
64
{
201
if (bs->drv && bs->drv->bdrv_drain_end) {
65
BlockDriver *drv;
202
bs->drv->bdrv_drain_end(bs);
66
BlockDriverState *bs;
203
}
67
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
204
- /* TODO Remove ignore_bds_parents, we don't consider it any more */
68
* Abort the reopen, and delete and free the staged changes in
205
- bdrv_parent_drained_end(bs, parent, false);
69
* reopen_state
206
-
70
*/
207
+ bdrv_parent_drained_end(bs, parent);
71
-void bdrv_reopen_abort(BDRVReopenState *reopen_state)
208
aio_enable_external(bdrv_get_aio_context(bs));
72
+static void bdrv_reopen_abort(BDRVReopenState *reopen_state)
209
}
73
{
210
}
74
BlockDriver *drv;
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
}
75
255
76
--
256
--
77
2.30.2
257
2.38.1
78
79
diff view generated by jsdifflib
1
virtio_add_queue() aborts when queue_size > VIRTQUEUE_MAX_SIZE, so
1
The next patch adds a parent drain to bdrv_attach_child_common(), which
2
vhost_user_blk_device_realize() should check this before calling it.
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.
3
5
4
Simple reproducer:
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.
5
13
6
qemu-system-x86_64 \
14
As a nice side effect, the structure of bdrv_do_drained_begin() is now
7
-chardev null,id=foo \
15
symmetrical with bdrv_do_drained_end().
8
-device vhost-user-blk-pci,queue-size=4096,chardev=foo
9
16
10
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1935014
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20210413165654.50810-1-kwolf@redhat.com>
18
Message-Id: <20221118174110.55183-14-kwolf@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
14
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
20
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
16
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
22
---
19
hw/block/vhost-user-blk.c | 5 +++++
23
block/io.c | 25 ++++++++++++-------------
20
1 file changed, 5 insertions(+)
24
1 file changed, 12 insertions(+), 13 deletions(-)
21
25
22
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
26
diff --git a/block/io.c b/block/io.c
23
index XXXXXXX..XXXXXXX 100644
27
index XXXXXXX..XXXXXXX 100644
24
--- a/hw/block/vhost-user-blk.c
28
--- a/block/io.c
25
+++ b/hw/block/vhost-user-blk.c
29
+++ b/block/io.c
26
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
30
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
27
error_setg(errp, "vhost-user-blk: queue size must be non-zero");
28
return;
29
}
31
}
30
+ if (s->queue_size > VIRTQUEUE_MAX_SIZE) {
32
}
31
+ error_setg(errp, "vhost-user-blk: queue size must not exceed %d",
33
32
+ VIRTQUEUE_MAX_SIZE);
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);
33
+ return;
43
+ return;
34
+ }
44
+ }
35
45
36
if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
46
/* Stop things in parent-to-child order */
37
return;
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();
38
--
78
--
39
2.30.2
79
2.38.1
40
41
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
In order to make sure that bdrv_replace_child_noperm() doesn't have to
2
2
poll any more, get rid of the bdrv_parent_drained_begin_single() call.
3
inore_children thing doesn't help to track all propagated permissions
3
4
of children we want to ignore. The simplest way to correctly update
4
This is possible now because we can require that the parent is already
5
permissions is update graph first and then do permission update. In
5
drained through the child in question when the function is called and we
6
this case we just referesh permissions for the whole subgraph (in
6
don't call the parent drain callbacks more than once.
7
topological-sort defined order) and everything is correctly calculated
7
8
automatically without any ignore_children.
8
The additional drain calls needed in callers cause the test case to run
9
9
its code in the drain handler too early (bdrv_attach_child() drains
10
So, refactor bdrv_replace_node_common to first do graph update and then
10
now), so modify it to only enable the code after the test setup has
11
refresh the permissions.
11
completed.
12
12
13
Test test_parallel_exclusive_write() now pass, so move it out of
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
debugging "if".
14
Message-Id: <20221118174110.55183-15-kwolf@redhat.com>
15
15
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
16
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
16
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
17
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
18
Message-Id: <20210428151804.439460-18-vsementsov@virtuozzo.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
18
---
21
block.c | 43 +++++++++++++-------------------
19
include/block/block-io.h | 8 +++
22
tests/unit/test-bdrv-graph-mod.c | 4 +--
20
block.c | 103 ++++++++++++++++++++++++++++++-----
23
2 files changed, 20 insertions(+), 27 deletions(-)
21
block/io.c | 2 +-
24
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
*
25
diff --git a/block.c b/block.c
44
diff --git a/block.c b/block.c
26
index XXXXXXX..XXXXXXX 100644
45
index XXXXXXX..XXXXXXX 100644
27
--- a/block.c
46
--- a/block.c
28
+++ b/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
}
29
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = {
69
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = {
30
*
70
*
31
* Note: real unref of old_bs is done only on commit.
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.
32
*/
77
*/
33
-__attribute__((unused))
78
static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
34
static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
35
Transaction *tran)
79
Transaction *tran)
36
{
80
{
37
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
81
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
38
bool auto_skip, Error **errp)
82
+
39
{
83
+ assert(child->quiesced_parent);
40
BdrvChild *c, *next;
84
+ assert(!new_bs || new_bs->quiesce_counter);
41
- GSList *list = NULL, *p;
85
+
42
- uint64_t perm = 0, shared = BLK_PERM_ALL;
86
*s = (BdrvReplaceChildState) {
43
+ Transaction *tran = tran_new();
87
.child = child,
44
+ g_autoptr(GHashTable) found = NULL;
88
.old_bs = child->bs,
45
+ g_autoptr(GSList) refresh_list = NULL;
89
@@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
46
int ret;
90
return permissions[qapi_perm];
47
91
}
48
/* Make sure that @from doesn't go away until we have successfully attached
92
49
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
93
+/*
50
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
94
+ * Replaces the node that a BdrvChild points to without updating permissions.
51
bdrv_drained_begin(from);
95
+ *
52
96
+ * If @new_bs is non-NULL, the parent of @child must already be drained through
53
- /* Put all parents into @list and calculate their cumulative permissions */
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
+
54
+ /*
109
+ /*
55
+ * Do the replacement without permission update.
110
+ * If we want to change the BdrvChild to point to a drained node as its new
56
+ * Replacement may influence the permissions, we should calculate new
111
+ * child->bs, we need to make sure that its new parent is drained, too. In
57
+ * permissions based on new graph. If we fail, we'll roll-back the
112
+ * other words, either child->quiesce_parent must already be true or we must
58
+ * replacement.
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.
59
+ */
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
+
60
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
223
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
61
assert(c->bs == from);
224
assert(c->bs == from);
62
if (!should_update_child(c, to)) {
225
if (!should_update_child(c, to)) {
63
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
226
diff --git a/block/io.c b/block/io.c
64
c->name, from->node_name);
227
index XXXXXXX..XXXXXXX 100644
65
goto out;
228
--- a/block/io.c
66
}
229
+++ b/block/io.c
67
- list = g_slist_prepend(list, c);
230
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
68
- perm |= c->perm;
231
}
69
- shared &= c->shared_perm;
232
}
70
+ bdrv_replace_child_safe(c, to, tran);
233
71
}
234
-static bool bdrv_parent_drained_poll_single(BdrvChild *c)
72
235
+bool bdrv_parent_drained_poll_single(BdrvChild *c)
73
- /* Check whether the required permissions can be granted on @to, ignoring
236
{
74
- * all BdrvChild in @list so that they can't block themselves. */
237
if (c->klass->drained_poll) {
75
- ret = bdrv_check_update_perm(to, NULL, perm, shared, list, errp);
238
return c->klass->drained_poll(c);
76
- if (ret < 0) {
239
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
77
- bdrv_abort_perm_update(to);
240
index XXXXXXX..XXXXXXX 100644
78
- goto out;
241
--- a/tests/unit/test-bdrv-drain.c
79
- }
242
+++ b/tests/unit/test-bdrv-drain.c
80
+ found = g_hash_table_new(NULL, NULL);
243
@@ -XXX,XX +XXX,XX @@ static void test_drop_intermediate_poll(void)
81
244
82
- /* Now actually perform the change. We performed the permission check for
245
83
- * all elements of @list at once, so set the permissions all at once at the
246
typedef struct BDRVReplaceTestState {
84
- * very end. */
247
+ bool setup_completed;
85
- for (p = list; p != NULL; p = p->next) {
248
bool was_drained;
86
- c = p->data;
249
bool was_undrained;
87
+ refresh_list = bdrv_topological_dfs(refresh_list, found, to);
250
bool has_read;
88
+ refresh_list = bdrv_topological_dfs(refresh_list, found, from);
251
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_test_drain_begin(BlockDriverState *bs)
89
252
{
90
- bdrv_ref(to);
253
BDRVReplaceTestState *s = bs->opaque;
91
- bdrv_replace_child_noperm(c, to);
254
92
- bdrv_unref(from);
255
+ if (!s->setup_completed) {
93
+ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
256
+ return;
94
+ if (ret < 0) {
257
+ }
95
+ goto out;
258
+
96
}
259
if (!s->drain_count) {
97
260
s->drain_co = qemu_coroutine_create(bdrv_replace_test_drain_co, bs);
98
- bdrv_set_perm(to);
261
bdrv_inc_in_flight(bs);
99
-
262
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_test_drain_end(BlockDriverState *bs)
100
ret = 0;
263
{
101
264
BDRVReplaceTestState *s = bs->opaque;
102
out:
265
103
- g_slist_free(list);
266
+ if (!s->setup_completed) {
104
+ tran_finalize(tran, ret);
267
+ return;
105
+
268
+ }
106
bdrv_drained_end(from);
269
+
107
bdrv_unref(from);
270
g_assert(s->drain_count > 0);
108
271
if (!--s->drain_count) {
109
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
272
s->was_undrained = true;
110
index XXXXXXX..XXXXXXX 100644
273
@@ -XXX,XX +XXX,XX @@ static void do_test_replace_child_mid_drain(int old_drain_count,
111
--- a/tests/unit/test-bdrv-graph-mod.c
274
bdrv_ref(old_child_bs);
112
+++ b/tests/unit/test-bdrv-graph-mod.c
275
bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds,
113
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
276
BDRV_CHILD_COW, &error_abort);
114
test_should_update_child);
277
+ parent_s->setup_completed = true;
115
g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
278
116
test_parallel_perm_update);
279
for (i = 0; i < old_drain_count; i++) {
117
+ g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
280
bdrv_drained_begin(old_child_bs);
118
+ test_parallel_exclusive_write);
119
120
if (debug) {
121
- g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
122
- test_parallel_exclusive_write);
123
g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
124
test_append_greedy_filter);
125
}
126
--
281
--
127
2.30.2
282
2.38.1
128
129
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
All callers of bdrv_parent_drained_begin_single() pass poll=false now,
2
so we don't need the parameter any more.
2
3
3
To be used in the further commit.
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
5
Message-Id: <20221118174110.55183-16-kwolf@redhat.com>
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
7
Message-Id: <20210428151804.439460-27-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
9
---
10
block.c | 46 ++++++++++++++++++++++++++++++++++++++++++----
10
include/block/block-io.h | 5 ++---
11
1 file changed, 42 insertions(+), 4 deletions(-)
11
block.c | 4 ++--
12
block/io.c | 8 ++------
13
3 files changed, 6 insertions(+), 11 deletions(-)
12
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:
13
diff --git a/block.c b/block.c
32
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
34
--- a/block.c
16
+++ b/block.c
35
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child)
36
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque)
18
bdrv_unref(child_bs);
37
* new_bs drained when calling bdrv_replace_child_tran() is not a
19
}
38
* requirement any more.
20
39
*/
21
+typedef struct BdrvSetInheritsFrom {
40
- bdrv_parent_drained_begin_single(s->child, false);
22
+ BlockDriverState *bs;
41
+ bdrv_parent_drained_begin_single(s->child);
23
+ BlockDriverState *old_inherits_from;
42
assert(!bdrv_parent_drained_poll_single(s->child));
24
+} BdrvSetInheritsFrom;
43
}
25
+
44
assert(s->child->quiesced_parent);
26
+static void bdrv_set_inherits_from_abort(void *opaque)
45
@@ -XXX,XX +XXX,XX @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs,
27
+{
46
* a problem, we already did this), but it will still poll until the parent
28
+ BdrvSetInheritsFrom *s = opaque;
47
* is fully quiesced, so it will not be negatively affected either.
29
+
48
*/
30
+ s->bs->inherits_from = s->old_inherits_from;
49
- bdrv_parent_drained_begin_single(new_child, false);
31
+}
50
+ bdrv_parent_drained_begin_single(new_child);
32
+
51
bdrv_replace_child_noperm(new_child, child_bs);
33
+static TransactionActionDrv bdrv_set_inherits_from_drv = {
52
34
+ .abort = bdrv_set_inherits_from_abort,
53
BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1);
35
+ .clean = g_free,
54
diff --git a/block/io.c b/block/io.c
36
+};
55
index XXXXXXX..XXXXXXX 100644
37
+
56
--- a/block/io.c
38
+/* @tran is allowed to be NULL. In this case no rollback is possible */
57
+++ b/block/io.c
39
+static void bdrv_set_inherits_from(BlockDriverState *bs,
58
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
40
+ BlockDriverState *new_inherits_from,
59
if (c == ignore) {
41
+ Transaction *tran)
60
continue;
42
+{
43
+ if (tran) {
44
+ BdrvSetInheritsFrom *s = g_new(BdrvSetInheritsFrom, 1);
45
+
46
+ *s = (BdrvSetInheritsFrom) {
47
+ .bs = bs,
48
+ .old_inherits_from = bs->inherits_from,
49
+ };
50
+
51
+ tran_add(tran, &bdrv_set_inherits_from_drv, s);
52
+ }
53
+
54
+ bs->inherits_from = new_inherits_from;
55
+}
56
+
57
/**
58
* Clear all inherits_from pointers from children and grandchildren of
59
* @root that point to @root, where necessary.
60
+ * @tran is allowed to be NULL. In this case no rollback is possible
61
*/
62
-static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child)
63
+static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child,
64
+ Transaction *tran)
65
{
66
BdrvChild *c;
67
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child)
69
}
70
}
61
}
71
if (c == NULL) {
62
- bdrv_parent_drained_begin_single(c, false);
72
- child->bs->inherits_from = NULL;
63
+ bdrv_parent_drained_begin_single(c);
73
+ bdrv_set_inherits_from(child->bs, NULL, tran);
74
}
75
}
76
77
QLIST_FOREACH(c, &child->bs->children, next) {
78
- bdrv_unset_inherits_from(root, c);
79
+ bdrv_unset_inherits_from(root, c, tran);
80
}
64
}
81
}
65
}
82
66
83
@@ -XXX,XX +XXX,XX @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
67
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore,
84
return;
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);
85
}
81
}
86
82
- if (poll) {
87
- bdrv_unset_inherits_from(parent, child);
83
- AIO_WAIT_WHILE(ctx, bdrv_parent_drained_poll_single(c));
88
+ bdrv_unset_inherits_from(parent, child, NULL);
84
- }
89
bdrv_root_unref_child(child);
90
}
85
}
91
86
87
static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
92
--
88
--
93
2.30.2
89
2.38.1
94
95
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Split no-perm part of bdrv_attach_child as separate transaction action.
3
bdrv_common_block_status_above() is a g_c_w, and it is being called by
4
It will be used in later commits.
4
many "wrapper" functions like bdrv_is_allocated(),
5
bdrv_is_allocated_above() and bdrv_block_status_above().
5
6
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-20-vsementsov@virtuozzo.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
14
Message-Id: <20221128142337.657646-2-eesposit@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
16
---
11
block.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++-----------
17
include/block/block-io.h | 15 +++++++++++
12
1 file changed, 58 insertions(+), 13 deletions(-)
18
block/io.c | 58 +++++++++++++++++++++++++++++++++++++---
19
2 files changed, 70 insertions(+), 3 deletions(-)
13
20
14
diff --git a/block.c b/block.c
21
diff --git a/include/block/block-io.h b/include/block/block-io.h
15
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
23
--- a/include/block/block-io.h
17
+++ b/block.c
24
+++ b/include/block/block-io.h
18
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
25
@@ -XXX,XX +XXX,XX @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
19
26
int bdrv_block_status(BlockDriverState *bs, int64_t offset,
20
static void bdrv_replace_child_noperm(BdrvChild *child,
27
int64_t bytes, int64_t *pnum, int64_t *map,
21
BlockDriverState *new_bs);
28
BlockDriverState **file);
22
+static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
29
+
23
+ BlockDriverState *child_bs,
30
+int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs,
24
+ const char *child_name,
31
+ BlockDriverState *base,
25
+ const BdrvChildClass *child_class,
32
+ int64_t offset, int64_t bytes,
26
+ BdrvChildRole child_role,
33
+ int64_t *pnum, int64_t *map,
27
+ BdrvChild **child,
34
+ BlockDriverState **file);
28
+ Transaction *tran,
35
int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base,
29
+ Error **errp);
36
int64_t offset, int64_t bytes, int64_t *pnum,
30
37
int64_t *map, BlockDriverState **file);
31
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
38
+
32
*queue, Error **errp);
39
+int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset,
33
@@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
40
+ int64_t bytes, int64_t *pnum);
34
return 0;
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;
35
}
61
}
36
62
37
+static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
63
+int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs,
38
+ BlockDriverState *child_bs,
64
+ BlockDriverState *base,
39
+ const char *child_name,
65
+ int64_t offset, int64_t bytes,
40
+ const BdrvChildClass *child_class,
66
+ int64_t *pnum, int64_t *map,
41
+ BdrvChildRole child_role,
67
+ BlockDriverState **file)
42
+ BdrvChild **child,
68
+{
43
+ Transaction *tran,
69
+ IO_CODE();
44
+ Error **errp)
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)
45
+{
83
+{
46
+ int ret;
84
+ int ret;
47
+ uint64_t perm, shared_perm;
85
+ int64_t dummy;
86
+ IO_CODE();
48
+
87
+
49
+ assert(parent_bs->drv);
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
+}
50
+
96
+
51
+ bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
97
int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
52
+ bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
98
int64_t *pnum)
53
+ perm, shared_perm, &perm, &shared_perm);
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();
54
+
113
+
55
+ ret = bdrv_attach_child_common(child_bs, child_name, child_class,
114
+ ret = bdrv_co_common_block_status_above(top, base, include_base, false,
56
+ child_role, perm, shared_perm, parent_bs,
115
+ offset, bytes, pnum, NULL, NULL,
57
+ child, tran, errp);
116
+ &depth);
58
+ if (ret < 0) {
117
+ if (ret < 0) {
59
+ return ret;
118
+ return ret;
60
+ }
119
+ }
61
+
120
+
62
+ QLIST_INSERT_HEAD(&parent_bs->children, *child, next);
121
+ if (ret & BDRV_BLOCK_ALLOCATED) {
63
+ /*
122
+ return depth;
64
+ * child is removed in bdrv_attach_child_common_abort(), so don't care to
123
+ }
65
+ * abort this change separately.
66
+ */
67
+
68
+ return 0;
124
+ return 0;
69
+}
125
+}
70
+
126
+
71
static void bdrv_detach_child(BdrvChild *child)
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)
72
{
132
{
73
bdrv_replace_child(child, NULL);
133
int depth;
74
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
134
- int ret = bdrv_common_block_status_above(top, base, include_base, false,
75
BdrvChildRole child_role,
135
- offset, bytes, pnum, NULL, NULL,
76
Error **errp)
136
- &depth);
77
{
78
- BdrvChild *child;
79
- uint64_t perm, shared_perm;
80
-
81
- bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
82
+ int ret;
137
+ int ret;
83
+ BdrvChild *child = NULL;
138
IO_CODE();
84
+ Transaction *tran = tran_new();
139
+
85
140
+ ret = bdrv_common_block_status_above(top, base, include_base, false,
86
- assert(parent_bs->drv);
141
+ offset, bytes, pnum, NULL, NULL,
87
- bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
142
+ &depth);
88
- perm, shared_perm, &perm, &shared_perm);
143
if (ret < 0) {
89
+ ret = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class,
144
return ret;
90
+ child_role, &child, tran, errp);
91
+ if (ret < 0) {
92
+ goto out;
93
+ }
94
95
- child = bdrv_root_attach_child(child_bs, child_name, child_class,
96
- child_role, perm, shared_perm, parent_bs,
97
- errp);
98
- if (child == NULL) {
99
- return NULL;
100
+ ret = bdrv_refresh_perms(parent_bs, errp);
101
+ if (ret < 0) {
102
+ goto out;
103
}
145
}
104
105
- QLIST_INSERT_HEAD(&parent_bs->children, child, next);
106
+out:
107
+ tran_finalize(tran, ret);
108
+
109
+ bdrv_unref(child_bs);
110
+
111
return child;
112
}
113
114
--
146
--
115
2.30.2
147
2.38.1
116
117
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
bdrv_append() is not quite good for inserting filters: it does extra
3
These functions end up calling bdrv_common_block_status_above(), a
4
permission update in intermediate state, where filter get it filtered
4
generated_co_wrapper function.
5
child but is not yet replace it in a backing chain.
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.
6
10
7
Some filters (for example backup-top) may want permissions even when
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
8
have no parents. And described intermediate state becomes invalid.
12
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
9
10
That's (half a) reason, why we need "inactive" state for backup-top
11
filter.
12
13
bdrv_append() will be improved later, now let's add a unit test.
14
15
Now test fails, so it runs only with -d flag. To run do
16
17
./test-bdrv-graph-mod -d -p /bdrv-graph-mod/append-greedy-filter
18
19
from <build-directory>/tests.
20
21
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
22
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
23
Message-Id: <20210428151804.439460-4-vsementsov@virtuozzo.com>
14
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Message-Id: <20221128142337.657646-3-eesposit@redhat.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
17
---
26
tests/unit/test-bdrv-graph-mod.c | 33 ++++++++++++++++++++++++++++++++
18
include/block/block-copy.h | 5 +++--
27
1 file changed, 33 insertions(+)
19
block/block-copy.c | 21 ++++++++++++---------
20
2 files changed, 15 insertions(+), 11 deletions(-)
28
21
29
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
22
diff --git a/include/block/block-copy.h b/include/block/block-copy.h
30
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
31
--- a/tests/unit/test-bdrv-graph-mod.c
24
--- a/include/block/block-copy.h
32
+++ b/tests/unit/test-bdrv-graph-mod.c
25
+++ b/include/block/block-copy.h
33
@@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void)
26
@@ -XXX,XX +XXX,XX @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm);
34
bdrv_unref(top);
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;
35
}
44
}
36
45
37
+/*
46
-static int block_copy_block_status(BlockCopyState *s, int64_t offset,
38
+ * It's possible that filter required permissions allows to insert it to backing
47
- int64_t bytes, int64_t *pnum)
39
+ * chain, like:
48
+static coroutine_fn int block_copy_block_status(BlockCopyState *s,
40
+ *
49
+ int64_t offset,
41
+ * 1. [top] -> [filter] -> [base]
50
+ int64_t bytes, int64_t *pnum)
42
+ *
43
+ * but doesn't allow to add it as a branch:
44
+ *
45
+ * 2. [filter] --\
46
+ * v
47
+ * [top] -> [base]
48
+ *
49
+ * So, inserting such filter should do all graph modifications and only then
50
+ * update permissions. If we try to go through intermediate state [2] and update
51
+ * permissions on it we'll fail.
52
+ *
53
+ * Let's check that bdrv_append() can append such a filter.
54
+ */
55
+static void test_append_greedy_filter(void)
56
+{
57
+ BlockDriverState *top = exclusive_writer_node("top");
58
+ BlockDriverState *base = no_perm_node("base");
59
+ BlockDriverState *fl = exclusive_writer_node("fl1");
60
+
61
+ bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_COW,
62
+ &error_abort);
63
+
64
+ bdrv_append(fl, base, &error_abort);
65
+ bdrv_unref(top);
66
+}
67
+
68
int main(int argc, char *argv[])
69
{
51
{
70
int i;
52
int64_t num;
71
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
53
BlockDriverState *base;
72
test_parallel_exclusive_write);
54
@@ -XXX,XX +XXX,XX @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset,
73
g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
55
base = NULL;
74
test_parallel_perm_update);
75
+ g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
76
+ test_append_greedy_filter);
77
}
56
}
78
57
79
return g_test_run();
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;
80
--
98
--
81
2.30.2
99
2.38.1
82
83
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Each of them has only one caller. Open-coding simplifies further
3
These functions end up calling bdrv_*() implemented as generated_co_wrapper
4
pemission-update system changes.
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.
5
10
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20210428151804.439460-13-vsementsov@virtuozzo.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>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
17
---
12
block.c | 59 +++++++++++++++++----------------------------------------
18
nbd/server.c | 29 ++++++++++++++++-------------
13
1 file changed, 17 insertions(+), 42 deletions(-)
19
1 file changed, 16 insertions(+), 13 deletions(-)
14
20
15
diff --git a/block.c b/block.c
21
diff --git a/nbd/server.c b/nbd/server.c
16
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
17
--- a/block.c
23
--- a/nbd/server.c
18
+++ b/block.c
24
+++ b/nbd/server.c
19
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
25
@@ -XXX,XX +XXX,XX @@ static int nbd_extent_array_add(NBDExtentArray *ea,
20
return 0;
26
return 0;
21
}
27
}
22
28
23
-static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
29
-static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
24
- uint64_t perm, uint64_t shared,
30
- uint64_t bytes, NBDExtentArray *ea)
25
- GSList *ignore_children, Error **errp);
31
+static int coroutine_fn blockstatus_to_extents(BlockDriverState *bs,
26
-static void bdrv_child_abort_perm_update(BdrvChild *c);
32
+ uint64_t offset, uint64_t bytes,
27
-static void bdrv_child_set_perm(BdrvChild *c);
33
+ NBDExtentArray *ea)
28
+static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
34
{
29
+ uint64_t new_used_perm,
35
while (bytes) {
30
+ uint64_t new_shared_perm,
36
uint32_t flags;
31
+ GSList *ignore_children,
37
int64_t num;
32
+ Error **errp);
38
- int ret = bdrv_block_status_above(bs, NULL, offset, bytes, &num,
33
39
- NULL, NULL);
34
typedef struct BlockReopenQueueEntry {
40
+ int ret = bdrv_co_block_status_above(bs, NULL, offset, bytes, &num,
35
bool prepared;
41
+ NULL, NULL);
36
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
42
37
/* Check all children */
38
QLIST_FOREACH(c, &bs->children, next) {
39
uint64_t cur_perm, cur_shared;
40
+ GSList *cur_ignore_children;
41
42
bdrv_child_perm(bs, c->bs, c, c->role, q,
43
cumulative_perms, cumulative_shared_perms,
44
&cur_perm, &cur_shared);
45
- ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared, ignore_children,
46
- errp);
47
+
48
+ cur_ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
49
+ ret = bdrv_check_update_perm(c->bs, q, cur_perm, cur_shared,
50
+ cur_ignore_children, errp);
51
+ g_slist_free(cur_ignore_children);
52
if (ret < 0) {
43
if (ret < 0) {
53
return ret;
44
return ret;
54
}
45
@@ -XXX,XX +XXX,XX @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
55
+
56
+ bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
57
}
58
59
return 0;
46
return 0;
60
@@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
61
}
62
63
QLIST_FOREACH(c, &bs->children, next) {
64
- bdrv_child_abort_perm_update(c);
65
+ bdrv_child_set_perm_abort(c);
66
+ bdrv_abort_perm_update(c->bs);
67
}
68
}
47
}
69
48
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs)
49
-static int blockalloc_to_extents(BlockDriverState *bs, uint64_t offset,
71
50
- uint64_t bytes, NBDExtentArray *ea)
72
/* Update all children */
51
+static int coroutine_fn blockalloc_to_extents(BlockDriverState *bs,
73
QLIST_FOREACH(c, &bs->children, next) {
52
+ uint64_t offset, uint64_t bytes,
74
- bdrv_child_set_perm(c);
53
+ NBDExtentArray *ea)
75
+ bdrv_child_set_perm_commit(c);
54
{
76
+ bdrv_set_perm(c->bs);
55
while (bytes) {
77
}
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,
78
}
65
}
79
66
80
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
67
/* Get block status from the exported device and send it to the client */
81
ignore_children, errp);
68
-static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
82
}
69
- BlockDriverState *bs, uint64_t offset,
83
70
- uint32_t length, bool dont_fragment,
84
-/* Needs to be followed by a call to either bdrv_child_set_perm() or
71
- bool last, uint32_t context_id,
85
- * bdrv_child_abort_perm_update(). */
72
- Error **errp)
86
-static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
73
+static int
87
- uint64_t perm, uint64_t shared,
74
+coroutine_fn nbd_co_send_block_status(NBDClient *client, uint64_t handle,
88
- GSList *ignore_children, Error **errp)
75
+ BlockDriverState *bs, uint64_t offset,
89
-{
76
+ uint32_t length, bool dont_fragment,
90
- int ret;
77
+ bool last, uint32_t context_id,
91
-
78
+ Error **errp)
92
- ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
93
- ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp);
94
- g_slist_free(ignore_children);
95
-
96
- if (ret < 0) {
97
- return ret;
98
- }
99
-
100
- bdrv_child_set_perm_safe(c, perm, shared, NULL);
101
-
102
- return 0;
103
-}
104
-
105
-static void bdrv_child_set_perm(BdrvChild *c)
106
-{
107
- bdrv_child_set_perm_commit(c);
108
- bdrv_set_perm(c->bs);
109
-}
110
-
111
-static void bdrv_child_abort_perm_update(BdrvChild *c)
112
-{
113
- bdrv_child_set_perm_abort(c);
114
- bdrv_abort_perm_update(c->bs);
115
-}
116
-
117
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
118
{
79
{
119
int ret;
80
int ret;
81
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
120
--
82
--
121
2.30.2
83
2.38.1
122
123
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Split out non-recursive parts, and refactor as block graph transaction
3
Avoid mixing bdrv_* functions with blk_*, so create blk_* counterparts
4
action.
4
for bdrv_block_status_above and bdrv_is_allocated_above.
5
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Note that since blk_co_block_status_above only calls the g_c_w function
7
bdrv_common_block_status_above and is marked as coroutine_fn, call
8
directly bdrv_co_common_block_status_above() to avoid using a g_c_w.
9
Same applies to blk_co_is_allocated_above.
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
13
Message-Id: <20221128142337.657646-5-eesposit@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-11-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
16
---
11
block.c | 79 ++++++++++++++++++++++++++++++++++++++++++---------------
17
include/sysemu/block-backend-io.h | 9 +++++++++
12
1 file changed, 59 insertions(+), 20 deletions(-)
18
block/block-backend.c | 21 ++++++++++++++++++++
19
block/commit.c | 4 ++--
20
nbd/server.c | 32 +++++++++++++++----------------
21
4 files changed, 48 insertions(+), 18 deletions(-)
13
22
14
diff --git a/block.c b/block.c
23
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
15
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
25
--- a/include/sysemu/block-backend-io.h
17
+++ b/block.c
26
+++ b/include/sysemu/block-backend-io.h
18
@@ -XXX,XX +XXX,XX @@
27
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
19
#include "qemu/timer.h"
28
int64_t bytes, BdrvRequestFlags read_flags,
20
#include "qemu/cutils.h"
29
BdrvRequestFlags write_flags);
21
#include "qemu/id.h"
30
22
+#include "qemu/transactions.h"
31
+int coroutine_fn blk_co_block_status_above(BlockBackend *blk,
23
#include "block/coroutines.h"
32
+ BlockDriverState *base,
24
33
+ int64_t offset, int64_t bytes,
25
#ifdef CONFIG_BSD
34
+ int64_t *pnum, int64_t *map,
26
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
35
+ BlockDriverState **file);
27
}
36
+int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk,
37
+ BlockDriverState *base,
38
+ bool include_base, int64_t offset,
39
+ int64_t bytes, int64_t *pnum);
40
41
/*
42
* "I/O or GS" API functions. These functions can run without
43
diff --git a/block/block-backend.c b/block/block-backend.c
44
index XXXXXXX..XXXXXXX 100644
45
--- a/block/block-backend.c
46
+++ b/block/block-backend.c
47
@@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
48
return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags);
28
}
49
}
29
50
30
+static void bdrv_child_set_perm_commit(void *opaque)
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)
31
+{
56
+{
32
+ BdrvChild *c = opaque;
57
+ IO_CODE();
33
+
58
+ return bdrv_co_block_status_above(blk_bs(blk), base, offset, bytes, pnum,
34
+ c->has_backup_perm = false;
59
+ map, file);
35
+}
60
+}
36
+
61
+
37
+static void bdrv_child_set_perm_abort(void *opaque)
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)
38
+{
66
+{
39
+ BdrvChild *c = opaque;
67
+ IO_CODE();
40
+ /*
68
+ return bdrv_co_is_allocated_above(blk_bs(blk), base, include_base, offset,
41
+ * We may have child->has_backup_perm unset at this point, as in case of
69
+ bytes, pnum);
42
+ * _check_ stage of permission update failure we may _check_ not the whole
43
+ * subtree. Still, _abort_ is called on the whole subtree anyway.
44
+ */
45
+ if (c->has_backup_perm) {
46
+ c->perm = c->backup_perm;
47
+ c->shared_perm = c->backup_shared_perm;
48
+ c->has_backup_perm = false;
49
+ }
50
+}
70
+}
51
+
71
+
52
+static TransactionActionDrv bdrv_child_set_pem_drv = {
72
typedef struct BlkRwCo {
53
+ .abort = bdrv_child_set_perm_abort,
73
BlockBackend *blk;
54
+ .commit = bdrv_child_set_perm_commit,
74
int64_t offset;
55
+};
75
diff --git a/block/commit.c b/block/commit.c
56
+
76
index XXXXXXX..XXXXXXX 100644
57
+/*
77
--- a/block/commit.c
58
+ * With tran=NULL needs to be followed by direct call to either
78
+++ b/block/commit.c
59
+ * bdrv_child_set_perm_commit() or bdrv_child_set_perm_abort().
79
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
60
+ *
80
break;
61
+ * With non-NULL tran needs to be followed by tran_abort() or tran_commit()
81
}
62
+ * instead.
82
/* Copy if allocated above the base */
63
+ */
83
- ret = bdrv_is_allocated_above(blk_bs(s->top), s->base_overlay, true,
64
+static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
84
- offset, COMMIT_BUFFER_SIZE, &n);
65
+ uint64_t shared, Transaction *tran)
85
+ ret = blk_co_is_allocated_above(s->top, s->base_overlay, true,
66
+{
86
+ offset, COMMIT_BUFFER_SIZE, &n);
67
+ if (!c->has_backup_perm) {
87
copy = (ret > 0);
68
+ c->has_backup_perm = true;
88
trace_commit_one_iteration(s, offset, n, ret);
69
+ c->backup_perm = c->perm;
89
if (copy) {
70
+ c->backup_shared_perm = c->shared_perm;
90
diff --git a/nbd/server.c b/nbd/server.c
71
+ }
91
index XXXXXXX..XXXXXXX 100644
72
+ /*
92
--- a/nbd/server.c
73
+ * Note: it's OK if c->has_backup_perm was already set, as we can find the
93
+++ b/nbd/server.c
74
+ * same c twice during check_perm procedure
94
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_co_send_structured_error(NBDClient *client,
75
+ */
95
}
76
+
96
77
+ c->perm = perm;
97
/* Do a sparse read and send the structured reply to the client.
78
+ c->shared_perm = shared;
98
- * Returns -errno if sending fails. bdrv_block_status_above() failure is
79
+
99
+ * Returns -errno if sending fails. blk_co_block_status_above() failure is
80
+ if (tran) {
100
* reported to the client, at which point this function succeeds.
81
+ tran_add(tran, &bdrv_child_set_pem_drv, c);
101
*/
82
+ }
102
static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
83
+}
103
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
84
+
104
85
/*
105
while (progress < size) {
86
* Check whether permissions on this node can be changed in a way that
106
int64_t pnum;
87
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
107
- int status = bdrv_block_status_above(blk_bs(exp->common.blk), NULL,
88
@@ -XXX,XX +XXX,XX @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
108
- offset + progress,
89
return ret;
109
- size - progress, &pnum, NULL,
90
}
110
- NULL);
91
111
+ int status = blk_co_block_status_above(exp->common.blk, NULL,
92
- if (!c->has_backup_perm) {
112
+ offset + progress,
93
- c->has_backup_perm = true;
113
+ size - progress, &pnum, NULL,
94
- c->backup_perm = c->perm;
114
+ NULL);
95
- c->backup_shared_perm = c->shared_perm;
115
bool final;
96
- }
116
97
- /*
117
if (status < 0) {
98
- * Note: it's OK if c->has_backup_perm was already set, as we can find the
118
@@ -XXX,XX +XXX,XX @@ static int nbd_extent_array_add(NBDExtentArray *ea,
99
- * same child twice during check_perm procedure
100
- */
101
-
102
- c->perm = perm;
103
- c->shared_perm = shared;
104
+ bdrv_child_set_perm_safe(c, perm, shared, NULL);
105
106
return 0;
119
return 0;
107
}
120
}
108
121
109
static void bdrv_child_set_perm(BdrvChild *c)
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)
110
{
126
{
111
- c->has_backup_perm = false;
127
while (bytes) {
112
-
128
uint32_t flags;
113
+ bdrv_child_set_perm_commit(c);
129
int64_t num;
114
bdrv_set_perm(c->bs);
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;
115
}
139
}
116
140
117
static void bdrv_child_abort_perm_update(BdrvChild *c)
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)
118
{
145
{
119
- if (c->has_backup_perm) {
146
while (bytes) {
120
- c->perm = c->backup_perm;
147
int64_t num;
121
- c->shared_perm = c->backup_shared_perm;
148
- int ret = bdrv_co_is_allocated_above(bs, NULL, false, offset, bytes,
122
- c->has_backup_perm = false;
149
- &num);
123
- }
150
+ int ret = blk_co_is_allocated_above(blk, NULL, false, offset, bytes,
124
-
151
+ &num);
125
+ bdrv_child_set_perm_abort(c);
152
126
bdrv_abort_perm_update(c->bs);
153
if (ret < 0) {
127
}
154
return ret;
128
155
@@ -XXX,XX +XXX,XX @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
156
/* Get block status from the exported device and send it to the client */
157
static int
158
coroutine_fn nbd_co_send_block_status(NBDClient *client, uint64_t handle,
159
- BlockDriverState *bs, uint64_t offset,
160
+ BlockBackend *blk, uint64_t offset,
161
uint32_t length, bool dont_fragment,
162
bool last, uint32_t context_id,
163
Error **errp)
164
@@ -XXX,XX +XXX,XX @@ coroutine_fn nbd_co_send_block_status(NBDClient *client, uint64_t handle,
165
g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
166
167
if (context_id == NBD_META_ID_BASE_ALLOCATION) {
168
- ret = blockstatus_to_extents(bs, offset, length, ea);
169
+ ret = blockstatus_to_extents(blk, offset, length, ea);
170
} else {
171
- ret = blockalloc_to_extents(bs, offset, length, ea);
172
+ ret = blockalloc_to_extents(blk, offset, length, ea);
173
}
174
if (ret < 0) {
175
return nbd_co_send_structured_error(
176
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
177
178
if (client->export_meta.base_allocation) {
179
ret = nbd_co_send_block_status(client, request->handle,
180
- blk_bs(exp->common.blk),
181
+ exp->common.blk,
182
request->from,
183
request->len, dont_fragment,
184
!--contexts_remaining,
185
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
186
187
if (client->export_meta.allocation_depth) {
188
ret = nbd_co_send_block_status(client, request->handle,
189
- blk_bs(exp->common.blk),
190
+ exp->common.blk,
191
request->from, request->len,
192
dont_fragment,
193
!--contexts_remaining,
129
--
194
--
130
2.30.2
195
2.38.1
131
132
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: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
We are going to drop recursive bdrv_child_* functions, so stop use them
3
We know that the string will stay around until the function
4
in bdrv_child_try_set_perm() as a first step.
4
returns, and the parameter of drv->bdrv_co_create_opts is const char*,
5
so it must not be modified either.
5
6
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Suggested-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-12-vsementsov@virtuozzo.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
11
Message-Id: <20221128142337.657646-7-eesposit@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
13
---
11
block.c | 14 ++++++++------
14
block.c | 7 ++-----
12
1 file changed, 8 insertions(+), 6 deletions(-)
15
1 file changed, 2 insertions(+), 5 deletions(-)
13
16
14
diff --git a/block.c b/block.c
17
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
19
--- a/block.c
17
+++ b/block.c
20
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
21
@@ -XXX,XX +XXX,XX @@ int bdrv_create(BlockDriver *drv, const char* filename,
19
Error **errp)
22
Coroutine *co;
20
{
23
CreateCo cco = {
21
Error *local_err = NULL;
24
.drv = drv,
22
+ Transaction *tran = tran_new();
25
- .filename = g_strdup(filename),
23
int ret;
26
+ .filename = filename,
24
27
.opts = opts,
25
- ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, &local_err);
28
.ret = NOT_DONE,
26
+ bdrv_child_set_perm_safe(c, perm, shared, tran);
29
.err = NULL,
27
+
30
@@ -XXX,XX +XXX,XX @@ int bdrv_create(BlockDriver *drv, const char* filename,
28
+ ret = bdrv_refresh_perms(c->bs, &local_err);
31
29
+
32
if (!drv->bdrv_co_create_opts) {
30
+ tran_finalize(tran, ret);
33
error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
31
+
34
- ret = -ENOTSUP;
32
if (ret < 0) {
35
- goto out;
33
- bdrv_child_abort_perm_update(c);
36
+ return -ENOTSUP;
34
if ((perm & ~c->perm) || (c->shared_perm & ~shared)) {
37
}
35
/* tighten permissions */
38
36
error_propagate(errp, local_err);
39
if (qemu_in_coroutine()) {
37
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
40
@@ -XXX,XX +XXX,XX @@ int bdrv_create(BlockDriver *drv, const char* filename,
38
error_free(local_err);
39
ret = 0;
40
}
41
}
41
- return ret;
42
}
42
}
43
43
44
- bdrv_child_set_perm(c);
44
-out:
45
-
45
- g_free(cco.filename);
46
- return 0;
46
return ret;
47
+ return ret;
48
}
47
}
49
48
50
int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp)
51
--
49
--
52
2.30.2
50
2.38.1
53
54
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
bdrv_append is not very good for inserting filters: it does extra
3
Call two different functions depending on whether bdrv_create
4
permission update as part of bdrv_set_backing_hd(). During this update
4
is in coroutine or not, following the same pattern as
5
filter may conflict with other parents of top_bs.
5
generated_co_wrapper functions.
6
6
7
Instead, let's first do all graph modifications and after it update
7
This allows to also call the coroutine function directly,
8
permissions.
8
without using CreateCo or relying in bdrv_create().
9
9
10
append-greedy-filter test-case in test-bdrv-graph-mod is now works, so
10
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
11
move it out of debug option.
12
13
Note: bdrv_append() is still only works for backing-child based
14
filters. It's something to improve later.
15
16
Note2: we use the fact that bdrv_append() is used to append new nodes,
17
without backing child, so we don't need frozen check and inherits_from
18
logic from bdrv_set_backing_hd().
19
20
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
21
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
22
Message-Id: <20210428151804.439460-22-vsementsov@virtuozzo.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
13
Message-Id: <20221128142337.657646-8-eesposit@redhat.com>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
24
---
15
---
25
block.c | 27 ++++++++++++++++++++-------
16
block.c | 69 ++++++++++++++++++++++++++++-----------------------------
26
tests/unit/test-bdrv-graph-mod.c | 17 ++---------------
17
1 file changed, 34 insertions(+), 35 deletions(-)
27
2 files changed, 22 insertions(+), 22 deletions(-)
28
18
29
diff --git a/block.c b/block.c
19
diff --git a/block.c b/block.c
30
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
21
--- a/block.c
32
+++ b/block.c
22
+++ b/block.c
33
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
23
@@ -XXX,XX +XXX,XX @@ typedef struct CreateCo {
34
* This will modify the BlockDriverState fields, and swap contents
24
Error *err;
35
* between bs_new and bs_top. Both bs_new and bs_top are modified.
25
} CreateCo;
36
*
26
37
- * bs_new must not be attached to a BlockBackend.
27
-static void coroutine_fn bdrv_create_co_entry(void *opaque)
38
+ * bs_new must not be attached to a BlockBackend and must not have backing
28
+static int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
39
+ * child.
29
+ QemuOpts *opts, Error **errp)
40
*
41
* This function does not create any image files.
42
*/
43
int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
44
Error **errp)
45
{
30
{
46
- int ret = bdrv_set_backing_hd(bs_new, bs_top, errp);
31
- Error *local_err = NULL;
47
+ int ret;
32
int ret;
48
+ Transaction *tran = tran_new();
33
+ GLOBAL_STATE_CODE();
34
+ ERRP_GUARD();
35
36
+ if (!drv->bdrv_co_create_opts) {
37
+ error_setg(errp, "Driver '%s' does not support image creation",
38
+ drv->format_name);
39
+ return -ENOTSUP;
40
+ }
49
+
41
+
50
+ assert(!bs_new->backing);
42
+ ret = drv->bdrv_co_create_opts(drv, filename, opts, errp);
51
+
43
+ if (ret < 0 && !*errp) {
52
+ ret = bdrv_attach_child_noperm(bs_new, bs_top, "backing",
44
+ error_setg_errno(errp, -ret, "Could not create image");
53
+ &child_of_bds, bdrv_backing_role(bs_new),
45
+ }
54
+ &bs_new->backing, tran, errp);
55
if (ret < 0) {
56
- return ret;
57
+ goto out;
58
}
59
60
- ret = bdrv_replace_node(bs_top, bs_new, errp);
61
+ ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp);
62
if (ret < 0) {
63
- bdrv_set_backing_hd(bs_new, NULL, &error_abort);
64
- return ret;
65
+ goto out;
66
}
67
68
- return 0;
69
+ ret = bdrv_refresh_perms(bs_new, errp);
70
+out:
71
+ tran_finalize(tran, ret);
72
+
73
+ bdrv_refresh_limits(bs_top, NULL);
74
+
46
+
75
+ return ret;
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();
76
}
62
}
77
63
78
static void bdrv_delete(BlockDriverState *bs)
64
int bdrv_create(BlockDriver *drv, const char* filename,
79
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
65
QemuOpts *opts, Error **errp)
80
index XXXXXXX..XXXXXXX 100644
81
--- a/tests/unit/test-bdrv-graph-mod.c
82
+++ b/tests/unit/test-bdrv-graph-mod.c
83
@@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void)
84
85
int main(int argc, char *argv[])
86
{
66
{
87
- int i;
67
- int ret;
88
- bool debug = false;
89
-
68
-
90
- for (i = 1; i < argc; i++) {
69
GLOBAL_STATE_CODE();
91
- if (!strcmp(argv[i], "-d")) {
70
92
- debug = true;
71
- Coroutine *co;
93
- break;
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;
106
}
107
-
108
- ret = cco.ret;
109
- if (ret < 0) {
110
- if (cco.err) {
111
- error_propagate(errp, cco.err);
112
- } else {
113
- error_setg_errno(errp, -ret, "Could not create image");
94
- }
114
- }
95
- }
115
- }
96
-
116
-
97
bdrv_init();
117
- return ret;
98
qemu_init_main_loop(&error_abort);
99
100
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
101
test_parallel_perm_update);
102
g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
103
test_parallel_exclusive_write);
104
-
105
- if (debug) {
106
- g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
107
- test_append_greedy_filter);
108
- }
109
+ g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
110
+ test_append_greedy_filter);
111
112
return g_test_run();
113
}
118
}
119
120
/**
114
--
121
--
115
2.30.2
122
2.38.1
116
117
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Old interfaces dropped, nobody directly calls
3
It is always called in coroutine_fn callbacks, therefore
4
bdrv_child_set_perm_abort() and bdrv_child_set_perm_commit(), so we can
4
it can directly call bdrv_co_create().
5
use personal state structure for the action and stop exploiting
5
6
BdrvChild structure. Also, drop "_safe" suffix which is redundant now.
6
Rename it to bdrv_co_create_file too.
7
7
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20210428151804.439460-35-vsementsov@virtuozzo.com>
10
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
11
Message-Id: <20221128142337.657646-9-eesposit@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
---
13
include/block/block_int.h | 5 ----
14
include/block/block-global-state.h | 3 ++-
14
block.c | 63 ++++++++++++++-------------------------
15
block.c | 5 +++--
15
2 files changed, 22 insertions(+), 46 deletions(-)
16
block/crypto.c | 2 +-
16
17
block/parallels.c | 2 +-
17
diff --git a/include/block/block_int.h b/include/block/block_int.h
18
block/qcow.c | 2 +-
18
index XXXXXXX..XXXXXXX 100644
19
block/qcow2.c | 4 ++--
19
--- a/include/block/block_int.h
20
block/qed.c | 2 +-
20
+++ b/include/block/block_int.h
21
block/raw-format.c | 2 +-
21
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
22
block/vdi.c | 2 +-
22
*/
23
block/vhdx.c | 2 +-
23
uint64_t shared_perm;
24
block/vmdk.c | 2 +-
24
25
block/vpc.c | 2 +-
25
- /* backup of permissions during permission update procedure */
26
12 files changed, 16 insertions(+), 14 deletions(-)
26
- bool has_backup_perm;
27
27
- uint64_t backup_perm;
28
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
28
- uint64_t backup_shared_perm;
29
index XXXXXXX..XXXXXXX 100644
29
-
30
--- a/include/block/block-global-state.h
30
/*
31
+++ b/include/block/block-global-state.h
31
* This link is frozen: the child can neither be replaced nor
32
@@ -XXX,XX +XXX,XX @@ BlockDriver *bdrv_find_protocol(const char *filename,
32
* detached from the parent.
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,
33
diff --git a/block.c b/block.c
42
diff --git a/block.c b/block.c
34
index XXXXXXX..XXXXXXX 100644
43
index XXXXXXX..XXXXXXX 100644
35
--- a/block.c
44
--- a/block.c
36
+++ b/block.c
45
+++ b/block.c
37
@@ -XXX,XX +XXX,XX @@ static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found,
46
@@ -XXX,XX +XXX,XX @@ out:
38
return g_slist_prepend(list, bs);
47
return ret;
39
}
48
}
40
49
41
-static void bdrv_child_set_perm_commit(void *opaque)
50
-int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
42
-{
51
+int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts,
43
- BdrvChild *c = opaque;
52
+ Error **errp)
44
-
45
- c->has_backup_perm = false;
46
-}
47
+typedef struct BdrvChildSetPermState {
48
+ BdrvChild *child;
49
+ uint64_t old_perm;
50
+ uint64_t old_shared_perm;
51
+} BdrvChildSetPermState;
52
53
static void bdrv_child_set_perm_abort(void *opaque)
54
{
53
{
55
- BdrvChild *c = opaque;
54
QemuOpts *protocol_opts;
56
- /*
55
BlockDriver *drv;
57
- * We may have child->has_backup_perm unset at this point, as in case of
56
@@ -XXX,XX +XXX,XX @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
58
- * _check_ stage of permission update failure we may _check_ not the whole
57
goto out;
59
- * subtree. Still, _abort_ is called on the whole subtree anyway.
58
}
60
- */
59
61
- if (c->has_backup_perm) {
60
- ret = bdrv_create(drv, filename, protocol_opts, errp);
62
- c->perm = c->backup_perm;
61
+ ret = bdrv_co_create(drv, filename, protocol_opts, errp);
63
- c->shared_perm = c->backup_shared_perm;
62
out:
64
- c->has_backup_perm = false;
63
qemu_opts_del(protocol_opts);
65
- }
64
qobject_unref(qdict);
66
+ BdrvChildSetPermState *s = opaque;
65
diff --git a/block/crypto.c b/block/crypto.c
67
+
66
index XXXXXXX..XXXXXXX 100644
68
+ s->child->perm = s->old_perm;
67
--- a/block/crypto.c
69
+ s->child->shared_perm = s->old_shared_perm;
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);
70
}
149
}
71
150
72
static TransactionActionDrv bdrv_child_set_pem_drv = {
151
static int raw_open(BlockDriverState *bs, QDict *options, int flags,
73
.abort = bdrv_child_set_perm_abort,
152
diff --git a/block/vdi.c b/block/vdi.c
74
- .commit = bdrv_child_set_perm_commit,
153
index XXXXXXX..XXXXXXX 100644
75
+ .clean = g_free,
154
--- a/block/vdi.c
76
};
155
+++ b/block/vdi.c
77
156
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(BlockDriver *drv,
78
-/*
157
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
79
- * With tran=NULL needs to be followed by direct call to either
158
80
- * bdrv_child_set_perm_commit() or bdrv_child_set_perm_abort().
159
/* Create and open the file (protocol layer) */
81
- *
160
- ret = bdrv_create_file(filename, opts, errp);
82
- * With non-NULL tran needs to be followed by tran_abort() or tran_commit()
161
+ ret = bdrv_co_create_file(filename, opts, errp);
83
- * instead.
162
if (ret < 0) {
84
- */
163
goto done;
85
-static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
164
}
86
- uint64_t shared, Transaction *tran)
165
diff --git a/block/vhdx.c b/block/vhdx.c
87
+static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm,
166
index XXXXXXX..XXXXXXX 100644
88
+ uint64_t shared, Transaction *tran)
167
--- a/block/vhdx.c
89
{
168
+++ b/block/vhdx.c
90
- if (!c->has_backup_perm) {
169
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(BlockDriver *drv,
91
- c->has_backup_perm = true;
170
}
92
- c->backup_perm = c->perm;
171
93
- c->backup_shared_perm = c->shared_perm;
172
/* Create and open the file (protocol layer) */
94
- }
173
- ret = bdrv_create_file(filename, opts, errp);
95
- /*
174
+ ret = bdrv_co_create_file(filename, opts, errp);
96
- * Note: it's OK if c->has_backup_perm was already set, as we can find the
175
if (ret < 0) {
97
- * same c twice during check_perm procedure
176
goto fail;
98
- */
177
}
99
+ BdrvChildSetPermState *s = g_new(BdrvChildSetPermState, 1);
178
diff --git a/block/vmdk.c b/block/vmdk.c
100
+
179
index XXXXXXX..XXXXXXX 100644
101
+ *s = (BdrvChildSetPermState) {
180
--- a/block/vmdk.c
102
+ .child = c,
181
+++ b/block/vmdk.c
103
+ .old_perm = c->perm,
182
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vmdk_create_extent(const char *filename,
104
+ .old_shared_perm = c->shared_perm,
105
+ };
106
107
c->perm = perm;
108
c->shared_perm = shared;
109
110
- if (tran) {
111
- tran_add(tran, &bdrv_child_set_pem_drv, c);
112
- }
113
+ tran_add(tran, &bdrv_child_set_pem_drv, s);
114
}
115
116
static void bdrv_drv_set_perm_commit(void *opaque)
117
@@ -XXX,XX +XXX,XX @@ static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
118
bdrv_child_perm(bs, c->bs, c, c->role, q,
119
cumulative_perms, cumulative_shared_perms,
120
&cur_perm, &cur_shared);
121
- bdrv_child_set_perm_safe(c, cur_perm, cur_shared, tran);
122
+ bdrv_child_set_perm(c, cur_perm, cur_shared, tran);
123
}
124
125
return 0;
126
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
127
Transaction *tran = tran_new();
128
int ret;
183
int ret;
129
184
BlockBackend *blk = NULL;
130
- bdrv_child_set_perm_safe(c, perm, shared, tran);
185
131
+ bdrv_child_set_perm(c, perm, shared, tran);
186
- ret = bdrv_create_file(filename, opts, errp);
132
187
+ ret = bdrv_co_create_file(filename, opts, errp);
133
ret = bdrv_refresh_perms(c->bs, &local_err);
188
if (ret < 0) {
134
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
}
135
--
204
--
136
2.30.2
205
2.38.1
137
138
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
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Split part of bdrv_replace_node_common() to be used separately.
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.
4
6
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20210428151804.439460-21-vsementsov@virtuozzo.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
10
Message-Id: <20221128142337.657646-14-eesposit@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
12
---
10
block.c | 50 +++++++++++++++++++++++++++++++-------------------
13
include/block/block-global-state.h | 8 ++++--
11
1 file changed, 31 insertions(+), 19 deletions(-)
14
block.c | 41 ++----------------------------
15
2 files changed, 8 insertions(+), 41 deletions(-)
12
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
13
diff --git a/block.c b/block.c
36
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
37
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
38
--- a/block.c
16
+++ b/block.c
39
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
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,
18
return ret;
52
return ret;
19
}
53
}
20
54
21
+static int bdrv_replace_node_noperm(BlockDriverState *from,
55
-static void coroutine_fn bdrv_create_co_entry(void *opaque)
22
+ BlockDriverState *to,
56
-{
23
+ bool auto_skip, Transaction *tran,
57
- CreateCo *cco = opaque;
24
+ Error **errp)
58
- GLOBAL_STATE_CODE();
25
+{
59
-
26
+ BdrvChild *c, *next;
60
- cco->ret = bdrv_co_create(cco->drv, cco->filename, cco->opts, &cco->err);
27
+
61
- aio_wait_kick();
28
+ QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
62
-}
29
+ assert(c->bs == from);
63
-
30
+ if (!should_update_child(c, to)) {
64
-int bdrv_create(BlockDriver *drv, const char* filename,
31
+ if (auto_skip) {
65
- QemuOpts *opts, Error **errp)
32
+ continue;
66
-{
33
+ }
67
- GLOBAL_STATE_CODE();
34
+ error_setg(errp, "Should not change '%s' link to '%s'",
68
-
35
+ c->name, from->node_name);
69
- if (qemu_in_coroutine()) {
36
+ return -EINVAL;
70
- /* Fast-path if already in coroutine context */
37
+ }
71
- return bdrv_co_create(drv, filename, opts, errp);
38
+ if (c->frozen) {
72
- } else {
39
+ error_setg(errp, "Cannot change '%s' link to '%s'",
73
- Coroutine *co;
40
+ c->name, from->node_name);
74
- CreateCo cco = {
41
+ return -EPERM;
75
- .drv = drv,
42
+ }
76
- .filename = filename,
43
+ bdrv_replace_child_safe(c, to, tran);
77
- .opts = opts,
44
+ }
78
- .ret = NOT_DONE,
45
+
79
- .err = NULL,
46
+ return 0;
80
- };
47
+}
81
-
48
+
82
- co = qemu_coroutine_create(bdrv_create_co_entry, &cco);
49
/*
83
- qemu_coroutine_enter(co);
50
* With auto_skip=true bdrv_replace_node_common skips updating from parents
84
- while (cco.ret == NOT_DONE) {
51
* if it creates a parent-child relation loop or if parent is block-job.
85
- aio_poll(qemu_get_aio_context(), true);
52
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
53
BlockDriverState *to,
54
bool auto_skip, Error **errp)
55
{
56
- BdrvChild *c, *next;
57
Transaction *tran = tran_new();
58
g_autoptr(GHashTable) found = NULL;
59
g_autoptr(GSList) refresh_list = NULL;
60
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from,
61
* permissions based on new graph. If we fail, we'll roll-back the
62
* replacement.
63
*/
64
- QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
65
- assert(c->bs == from);
66
- if (!should_update_child(c, to)) {
67
- if (auto_skip) {
68
- continue;
69
- }
70
- ret = -EINVAL;
71
- error_setg(errp, "Should not change '%s' link to '%s'",
72
- c->name, from->node_name);
73
- goto out;
74
- }
86
- }
75
- if (c->frozen) {
87
- error_propagate(errp, cco.err);
76
- ret = -EPERM;
88
- return cco.ret;
77
- error_setg(errp, "Cannot change '%s' link to '%s'",
89
- }
78
- c->name, from->node_name);
90
-}
79
- goto out;
91
-
80
- }
92
/**
81
- bdrv_replace_child_safe(c, to, tran);
93
* Helper function for bdrv_create_file_fallback(): Resize @blk to at
82
+ ret = bdrv_replace_node_noperm(from, to, auto_skip, tran, errp);
94
* least the given @minimum_size.
83
+ if (ret < 0) {
84
+ goto out;
85
}
86
87
found = g_hash_table_new(NULL, NULL);
88
--
95
--
89
2.30.2
96
2.38.1
90
91
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Add new handler to get aio context and implement it in all child
3
bdrv_can_store_new_dirty_bitmap and bdrv_remove_persistent_dirty_bitmap
4
classes. Add corresponding public interface to be used soon.
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.
5
9
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20210428151804.439460-6-vsementsov@virtuozzo.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
13
Message-Id: <20221128142337.657646-15-eesposit@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
15
---
12
include/block/block.h | 2 ++
16
include/block/block-common.h | 5 +-
13
include/block/block_int.h | 2 ++
17
include/block/block-io.h | 10 +++-
14
block.c | 13 +++++++++++++
18
include/block/dirty-bitmap.h | 10 +++-
15
block/block-backend.c | 9 +++++++++
19
block/dirty-bitmap.c | 88 +-----------------------------------
16
blockjob.c | 8 ++++++++
20
block/meson.build | 1 +
17
5 files changed, 34 insertions(+)
21
5 files changed, 22 insertions(+), 92 deletions(-)
18
22
19
diff --git a/include/block/block.h b/include/block/block.h
23
diff --git a/include/block/block-common.h b/include/block/block-common.h
20
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
21
--- a/include/block/block.h
25
--- a/include/block/block-common.h
22
+++ b/include/block/block.h
26
+++ b/include/block/block-common.h
23
@@ -XXX,XX +XXX,XX @@ bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
27
@@ -XXX,XX +XXX,XX @@
24
GSList **ignore, Error **errp);
28
#include "qemu/iov.h"
25
bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx,
29
#include "qemu/coroutine.h"
26
GSList **ignore, Error **errp);
30
#include "block/accounting.h"
27
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c);
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"
28
+
42
+
29
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
43
/* block.c */
30
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
44
typedef struct BlockDriver BlockDriver;
31
45
typedef struct BdrvChild BdrvChild;
32
diff --git a/include/block/block_int.h b/include/block/block_int.h
46
diff --git a/include/block/block-io.h b/include/block/block-io.h
33
index XXXXXXX..XXXXXXX 100644
47
index XXXXXXX..XXXXXXX 100644
34
--- a/include/block/block_int.h
48
--- a/include/block/block-io.h
35
+++ b/include/block/block_int.h
49
+++ b/include/block/block-io.h
36
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
50
@@ -XXX,XX +XXX,XX @@ AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c);
37
bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx,
51
void bdrv_io_plug(BlockDriverState *bs);
38
GSList **ignore, Error **errp);
52
void bdrv_io_unplug(BlockDriverState *bs);
39
void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore);
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);
40
+
77
+
41
+ AioContext *(*get_parent_aio_context)(BdrvChild *child);
78
+int coroutine_fn bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs,
42
};
79
+ const char *name,
43
80
+ Error **errp);
44
extern const BdrvChildClass child_of_bds;
81
+int co_wrapper bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
45
diff --git a/block.c b/block.c
82
+ const char *name,
46
index XXXXXXX..XXXXXXX 100644
83
+ Error **errp);
47
--- a/block.c
84
+
48
+++ b/block.c
85
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
49
@@ -XXX,XX +XXX,XX @@ static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
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,
50
return 0;
102
return 0;
51
}
103
}
52
104
53
+static AioContext *bdrv_child_cb_get_parent_aio_context(BdrvChild *c)
105
-typedef struct BdrvRemovePersistentDirtyBitmapCo {
54
+{
106
- BlockDriverState *bs;
55
+ BlockDriverState *bs = c->opaque;
107
- const char *name;
56
+
108
- Error **errp;
57
+ return bdrv_get_aio_context(bs);
109
- int ret;
58
+}
110
-} BdrvRemovePersistentDirtyBitmapCo;
59
+
111
-
60
const BdrvChildClass child_of_bds = {
112
-static void coroutine_fn
61
.parent_is_bds = true,
113
-bdrv_co_remove_persistent_dirty_bitmap_entry(void *opaque)
62
.get_parent_desc = bdrv_child_get_parent_desc,
114
-{
63
@@ -XXX,XX +XXX,XX @@ const BdrvChildClass child_of_bds = {
115
- BdrvRemovePersistentDirtyBitmapCo *s = opaque;
64
.can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
116
-
65
.set_aio_ctx = bdrv_child_cb_set_aio_ctx,
117
- s->ret = bdrv_co_remove_persistent_dirty_bitmap(s->bs, s->name, s->errp);
66
.update_filename = bdrv_child_cb_update_filename,
118
- aio_wait_kick();
67
+ .get_parent_aio_context = bdrv_child_cb_get_parent_aio_context,
119
-}
68
};
120
-
69
121
-int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
70
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c)
122
- Error **errp)
71
+{
123
-{
72
+ return c->klass->get_parent_aio_context(c);
124
- if (qemu_in_coroutine()) {
73
+}
125
- return bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp);
74
+
126
- } else {
75
static int bdrv_open_flags(BlockDriverState *bs, int flags)
127
- Coroutine *co;
76
{
128
- BdrvRemovePersistentDirtyBitmapCo s = {
77
int open_flags = flags;
129
- .bs = bs,
78
diff --git a/block/block-backend.c b/block/block-backend.c
130
- .name = name,
79
index XXXXXXX..XXXXXXX 100644
131
- .errp = errp,
80
--- a/block/block-backend.c
132
- .ret = -EINPROGRESS,
81
+++ b/block/block-backend.c
133
- };
82
@@ -XXX,XX +XXX,XX @@ static void blk_root_detach(BdrvChild *child)
134
-
83
}
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;
84
}
149
}
85
150
86
+static AioContext *blk_root_get_parent_aio_context(BdrvChild *c)
151
-static bool coroutine_fn
87
+{
152
+bool coroutine_fn
88
+ BlockBackend *blk = c->opaque;
153
bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
89
+
154
uint32_t granularity, Error **errp)
90
+ return blk_get_aio_context(blk);
155
{
91
+}
156
@@ -XXX,XX +XXX,XX @@ bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
92
+
157
return drv->bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp);
93
static const BdrvChildClass child_root = {
94
.inherit_options = blk_root_inherit_options,
95
96
@@ -XXX,XX +XXX,XX @@ static const BdrvChildClass child_root = {
97
98
.can_set_aio_ctx = blk_root_can_set_aio_ctx,
99
.set_aio_ctx = blk_root_set_aio_ctx,
100
+
101
+ .get_parent_aio_context = blk_root_get_parent_aio_context,
102
};
103
104
/*
105
diff --git a/blockjob.c b/blockjob.c
106
index XXXXXXX..XXXXXXX 100644
107
--- a/blockjob.c
108
+++ b/blockjob.c
109
@@ -XXX,XX +XXX,XX @@ static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx,
110
job->job.aio_context = ctx;
111
}
158
}
112
159
113
+static AioContext *child_job_get_parent_aio_context(BdrvChild *c)
160
-typedef struct BdrvCanStoreNewDirtyBitmapCo {
114
+{
161
- BlockDriverState *bs;
115
+ BlockJob *job = c->opaque;
162
- const char *name;
116
+
163
- uint32_t granularity;
117
+ return job->job.aio_context;
164
- Error **errp;
118
+}
165
- bool ret;
119
+
166
-
120
static const BdrvChildClass child_job = {
167
- bool in_progress;
121
.get_parent_desc = child_job_get_parent_desc,
168
-} BdrvCanStoreNewDirtyBitmapCo;
122
.drained_begin = child_job_drained_begin,
169
-
123
@@ -XXX,XX +XXX,XX @@ static const BdrvChildClass child_job = {
170
-static void coroutine_fn bdrv_co_can_store_new_dirty_bitmap_entry(void *opaque)
124
.can_set_aio_ctx = child_job_can_set_aio_ctx,
171
-{
125
.set_aio_ctx = child_job_set_aio_ctx,
172
- BdrvCanStoreNewDirtyBitmapCo *s = opaque;
126
.stay_at_node = true,
173
-
127
+ .get_parent_aio_context = child_job_get_parent_aio_context,
174
- s->ret = bdrv_co_can_store_new_dirty_bitmap(s->bs, s->name, s->granularity,
128
};
175
- s->errp);
129
176
- s->in_progress = false;
130
void block_job_remove_all_bdrv(BlockJob *job)
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'
131
--
220
--
132
2.30.2
221
2.38.1
133
134
diff view generated by jsdifflib
1
Normally, blk_new_open() just shares all permissions. This was fine
1
Provide a separate function that just quiesces the users of a node to
2
originally when permissions only protected against uses in the same
2
prevent new requests from coming in, but without waiting for the already
3
process because no other part of the code would actually get to access
3
in-flight I/O to complete.
4
the block nodes opened with blk_new_open(). However, since we use it for
5
file locking now, unsharing permissions becomes desirable.
6
4
7
Add a new BDRV_O_NO_SHARE flag that is used in blk_new_open() to unshare
5
This function can be used in contexts where polling is not allowed.
8
any permissions that can be unshared.
9
6
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20210422164344.283389-2-kwolf@redhat.com>
8
Message-Id: <20221207131838.239125-2-kwolf@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
11
---
16
include/block/block.h | 1 +
12
include/block/block-global-state.h | 1 +
17
block/block-backend.c | 19 +++++++++++++------
13
block/io.c | 19 +++++++++++++------
18
2 files changed, 14 insertions(+), 6 deletions(-)
14
2 files changed, 14 insertions(+), 6 deletions(-)
19
15
20
diff --git a/include/block/block.h b/include/block/block.h
16
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
21
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
22
--- a/include/block/block.h
18
--- a/include/block/block-global-state.h
23
+++ b/include/block/block.h
19
+++ b/include/block/block-global-state.h
24
@@ -XXX,XX +XXX,XX @@ typedef struct HDGeometry {
20
@@ -XXX,XX +XXX,XX @@ int bdrv_inactivate_all(void);
25
uint32_t cylinders;
21
int bdrv_flush_all(void);
26
} HDGeometry;
22
void bdrv_close_all(void);
27
23
void bdrv_drain_all_begin(void);
28
+#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */
24
+void bdrv_drain_all_begin_nopoll(void);
29
#define BDRV_O_RDWR 0x0002
25
void bdrv_drain_all_end(void);
30
#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */
26
void bdrv_drain_all(void);
31
#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */
27
32
diff --git a/block/block-backend.c b/block/block-backend.c
28
diff --git a/block/io.c b/block/io.c
33
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
34
--- a/block/block-backend.c
30
--- a/block/io.c
35
+++ b/block/block-backend.c
31
+++ b/block/io.c
36
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
32
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_all_poll(void)
37
BlockBackend *blk;
33
* NOTE: no new block jobs or BlockDriverStates can be created between
38
BlockDriverState *bs;
34
* the bdrv_drain_all_begin() and bdrv_drain_all_end() calls.
39
uint64_t perm = 0;
35
*/
40
+ uint64_t shared = BLK_PERM_ALL;
36
-void bdrv_drain_all_begin(void)
41
37
+void bdrv_drain_all_begin_nopoll(void)
42
- /* blk_new_open() is mainly used in .bdrv_create implementations and the
38
{
43
- * tools where sharing isn't a concern because the BDS stays private, so we
39
BlockDriverState *bs = NULL;
44
- * just request permission according to the flags.
40
GLOBAL_STATE_CODE();
45
+ /*
41
46
+ * blk_new_open() is mainly used in .bdrv_create implementations and the
42
- if (qemu_in_coroutine()) {
47
+ * tools where sharing isn't a major concern because the BDS stays private
43
- bdrv_co_yield_to_drain(NULL, true, NULL, true);
48
+ * and the file is generally not supposed to be used by a second process,
44
- return;
49
+ * so we just request permission according to the flags.
45
- }
50
*
46
-
51
* The exceptions are xen_disk and blockdev_init(); in these cases, the
47
/*
52
* caller of blk_new_open() doesn't make use of the permissions, but they
48
* bdrv queue is managed by record/replay,
53
* shouldn't hurt either. We can still share everything here because the
49
* waiting for finishing the I/O requests may
54
- * guest devices will add their own blockers if they can't share. */
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
55
+ * guest devices will add their own blockers if they can't share.
51
bdrv_do_drained_begin(bs, NULL, false);
56
+ */
52
aio_context_release(aio_context);
57
if ((flags & BDRV_O_NO_IO) == 0) {
58
perm |= BLK_PERM_CONSISTENT_READ;
59
if (flags & BDRV_O_RDWR) {
60
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
61
if (flags & BDRV_O_RESIZE) {
62
perm |= BLK_PERM_RESIZE;
63
}
53
}
64
+ if (flags & BDRV_O_NO_SHARE) {
54
+}
65
+ shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
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;
66
+ }
63
+ }
67
64
+
68
- blk = blk_new(qemu_get_aio_context(), perm, BLK_PERM_ALL);
65
+ bdrv_drain_all_begin_nopoll();
69
+ blk = blk_new(qemu_get_aio_context(), perm, shared);
66
70
bs = bdrv_open(filename, reference, options, flags, errp);
67
/* Now poll the in-flight requests */
71
if (!bs) {
68
AIO_WAIT_WHILE(NULL, bdrv_drain_all_poll());
72
blk_unref(blk);
73
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
74
75
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
76
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
77
- perm, BLK_PERM_ALL, blk, errp);
78
+ perm, shared, blk, errp);
79
if (!blk->root) {
80
blk_unref(blk);
81
return NULL;
82
--
69
--
83
2.30.2
70
2.38.1
84
85
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Add simple transaction API to use in further update of block graph
3
Block layer graph operations are always run under BQL in the main loop.
4
operations.
4
This is proved by the assertion qemu_in_main_thread() and its wrapper
5
5
macro GLOBAL_STATE_CODE.
6
Supposed usage is:
6
7
7
However, there are also concurrent coroutines running in other iothreads
8
- "prepare" is main function of the action and it should make the main
8
that always try to traverse the graph. Currently this is protected
9
effect of the action to be visible for the following actions, keeping
9
(among various other things) by the AioContext lock, but once this is
10
possibility of roll-back, saving necessary things in action state,
10
removed, we need to make sure that reads do not happen while modifying
11
which is prepended to the action list (to do that, prepare func
11
the graph.
12
should call tran_add()). So, driver struct doesn't include "prepare"
12
13
field, as it is supposed to be called directly.
13
We distinguish between writer (main loop, under BQL) that modifies the
14
14
graph, and readers (all other coroutines running in various AioContext),
15
- commit/rollback is supposed to be called for the list of action
15
that go through the graph edges, reading ->parents and->children.
16
states, to commit/rollback all the actions in reverse order
16
17
17
The writer (main loop) has "exclusive" access, so it first waits for any
18
- When possible "commit" should not make visible effect for other
18
current read to finish, and then prevents incoming ones from entering
19
actions, which make possible transparent logical interaction between
19
while it has the exclusive access.
20
actions.
20
21
21
The readers (coroutines in multiple AioContext) are free to access the
22
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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>
23
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
40
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
24
Message-Id: <20210428151804.439460-9-vsementsov@virtuozzo.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
41
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
---
42
---
27
include/qemu/transactions.h | 63 ++++++++++++++++++++++++
43
include/block/aio.h | 9 ++
28
util/transactions.c | 96 +++++++++++++++++++++++++++++++++++++
44
include/block/block_int.h | 1 +
29
MAINTAINERS | 6 +++
45
include/block/graph-lock.h | 139 ++++++++++++++++++++
30
util/meson.build | 1 +
46
block/graph-lock.c | 261 +++++++++++++++++++++++++++++++++++++
31
4 files changed, 166 insertions(+)
47
block/meson.build | 1 +
32
create mode 100644 include/qemu/transactions.h
48
5 files changed, 411 insertions(+)
33
create mode 100644 util/transactions.c
49
create mode 100644 include/block/graph-lock.h
34
50
create mode 100644 block/graph-lock.c
35
diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h
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
36
new file mode 100644
92
new file mode 100644
37
index XXXXXXX..XXXXXXX
93
index XXXXXXX..XXXXXXX
38
--- /dev/null
94
--- /dev/null
39
+++ b/include/qemu/transactions.h
95
+++ b/include/block/graph-lock.h
40
@@ -XXX,XX +XXX,XX @@
96
@@ -XXX,XX +XXX,XX @@
41
+/*
97
+/*
42
+ * Simple transactions API
98
+ * Graph lock: rwlock to protect block layer graph manipulations (add/remove
43
+ *
99
+ * edges and nodes)
44
+ * Copyright (c) 2021 Virtuozzo International GmbH.
100
+ *
45
+ *
101
+ * Copyright (c) 2022 Red Hat
46
+ * Author:
102
+ *
47
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
103
+ * This library is free software; you can redistribute it and/or
48
+ *
104
+ * modify it under the terms of the GNU Lesser General Public
49
+ * This program is free software; you can redistribute it and/or modify
105
+ * License as published by the Free Software Foundation; either
50
+ * it under the terms of the GNU General Public License as published by
106
+ * version 2.1 of the License, or (at your option) any later version.
51
+ * the Free Software Foundation; either version 2 of the License, or
107
+ *
52
+ * (at your option) any later version.
108
+ * This library is distributed in the hope that it will be useful,
53
+ *
54
+ * This program is distributed in the hope that it will be useful,
55
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
109
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
56
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
110
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
57
+ * GNU General Public License for more details.
111
+ * Lesser General Public License for more details.
58
+ *
112
+ *
59
+ * You should have received a copy of the GNU General Public License
113
+ * You should have received a copy of the GNU Lesser General Public
60
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
114
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
61
+ *
115
+ */
62
+ *
116
+#ifndef GRAPH_LOCK_H
63
+ * = Generic transaction API =
117
+#define GRAPH_LOCK_H
64
+ *
118
+
65
+ * The intended usage is the following: you create "prepare" functions, which
119
+#include "qemu/osdep.h"
66
+ * represents the actions. They will usually have Transaction* argument, and
120
+
67
+ * call tran_add() to register finalization callbacks. For finalization
121
+#include "qemu/coroutine.h"
68
+ * callbacks, prepare corresponding TransactionActionDrv structures.
122
+
69
+ *
123
+/**
70
+ * Then, when you need to make a transaction, create an empty Transaction by
124
+ * Graph Lock API
71
+ * tran_create(), call your "prepare" functions on it, and finally call
125
+ * This API provides a rwlock used to protect block layer
72
+ * tran_abort() or tran_commit() to finalize the transaction by corresponding
126
+ * graph modifications like edge (BdrvChild) and node (BlockDriverState)
73
+ * finalization actions in reverse order.
127
+ * addition and removal.
74
+ */
128
+ * Currently we have 1 writer only, the Main loop, and many
75
+
129
+ * readers, mostly coroutines running in other AioContext thus other threads.
76
+#ifndef QEMU_TRANSACTIONS_H
130
+ *
77
+#define QEMU_TRANSACTIONS_H
131
+ * We distinguish between writer (main loop, under BQL) that modifies the
78
+
132
+ * graph, and readers (all other coroutines running in various AioContext),
79
+#include <gmodule.h>
133
+ * that go through the graph edges, reading
80
+
134
+ * BlockDriverState ->parents and->children.
81
+typedef struct TransactionActionDrv {
135
+ *
82
+ void (*abort)(void *opaque);
136
+ * The writer (main loop) has an "exclusive" access, so it first waits for
83
+ void (*commit)(void *opaque);
137
+ * current read to finish, and then prevents incoming ones from
84
+ void (*clean)(void *opaque);
138
+ * entering while it has the exclusive access.
85
+} TransactionActionDrv;
139
+ *
86
+
140
+ * The readers (coroutines in multiple AioContext) are free to
87
+typedef struct Transaction Transaction;
141
+ * access the graph as long the writer is not modifying the graph.
88
+
142
+ * In case it is, they go in a CoQueue and sleep until the writer
89
+Transaction *tran_new(void);
143
+ * is done.
90
+void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque);
144
+ *
91
+void tran_abort(Transaction *tran);
145
+ * If a coroutine changes AioContext, the counter in the original and new
92
+void tran_commit(Transaction *tran);
146
+ * AioContext are left intact, since the writer does not care where is the
93
+
147
+ * reader, but only if there is one.
94
+static inline void tran_finalize(Transaction *tran, int ret)
148
+ * As a result, some AioContexts might have a negative reader count, to
95
+{
149
+ * balance the positive count of the AioContext that took the lock.
96
+ if (ret < 0) {
150
+ * This also means that when an AioContext is deleted it may have a nonzero
97
+ tran_abort(tran);
151
+ * reader count. In that case we transfer the count to a global shared counter
98
+ } else {
152
+ * so that the writer is always aware of all readers.
99
+ tran_commit(tran);
153
+ */
100
+ }
154
+typedef struct BdrvGraphRWlock BdrvGraphRWlock;
101
+}
155
+
102
+
156
+/*
103
+#endif /* QEMU_TRANSACTIONS_H */
157
+ * register_aiocontext:
104
diff --git a/util/transactions.c b/util/transactions.c
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
105
new file mode 100644
237
new file mode 100644
106
index XXXXXXX..XXXXXXX
238
index XXXXXXX..XXXXXXX
107
--- /dev/null
239
--- /dev/null
108
+++ b/util/transactions.c
240
+++ b/block/graph-lock.c
109
@@ -XXX,XX +XXX,XX @@
241
@@ -XXX,XX +XXX,XX @@
110
+/*
242
+/*
111
+ * Simple transactions API
243
+ * Graph lock: rwlock to protect block layer graph manipulations (add/remove
112
+ *
244
+ * edges and nodes)
113
+ * Copyright (c) 2021 Virtuozzo International GmbH.
245
+ *
114
+ *
246
+ * Copyright (c) 2022 Red Hat
115
+ * Author:
247
+ *
116
+ * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
248
+ * This library is free software; you can redistribute it and/or
117
+ *
249
+ * modify it under the terms of the GNU Lesser General Public
118
+ * This program is free software; you can redistribute it and/or modify
250
+ * License as published by the Free Software Foundation; either
119
+ * it under the terms of the GNU General Public License as published by
251
+ * version 2.1 of the License, or (at your option) any later version.
120
+ * the Free Software Foundation; either version 2 of the License, or
252
+ *
121
+ * (at your option) any later version.
253
+ * This library is distributed in the hope that it will be useful,
122
+ *
123
+ * This program is distributed in the hope that it will be useful,
124
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
254
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
125
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
255
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
126
+ * GNU General Public License for more details.
256
+ * Lesser General Public License for more details.
127
+ *
257
+ *
128
+ * You should have received a copy of the GNU General Public License
258
+ * You should have received a copy of the GNU Lesser General Public
129
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
259
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
130
+ */
260
+ */
131
+
261
+
132
+#include "qemu/osdep.h"
262
+#include "qemu/osdep.h"
133
+
263
+#include "qemu/main-loop.h"
134
+#include "qemu/transactions.h"
264
+#include "block/graph-lock.h"
135
+#include "qemu/queue.h"
265
+#include "block/block.h"
136
+
266
+#include "block/block_int.h"
137
+typedef struct TransactionAction {
267
+
138
+ TransactionActionDrv *drv;
268
+/* Protects the list of aiocontext and orphaned_reader_count */
139
+ void *opaque;
269
+static QemuMutex aio_context_list_lock;
140
+ QSLIST_ENTRY(TransactionAction) entry;
270
+
141
+} TransactionAction;
271
+/* Written and read with atomic operations. */
142
+
272
+static int has_writer;
143
+struct Transaction {
273
+
144
+ QSLIST_HEAD(, TransactionAction) actions;
274
+/*
275
+ * A reader coroutine could move from an AioContext to another.
276
+ * If this happens, there is no problem from the point of view of
277
+ * counters. The problem is that the total count becomes
278
+ * unbalanced if one of the two AioContexts gets deleted.
279
+ * The count of readers must remain correct, so the AioContext's
280
+ * balance is transferred to this glboal variable.
281
+ * Protected by aio_context_list_lock.
282
+ */
283
+static uint32_t orphaned_reader_count;
284
+
285
+/* Queue of readers waiting for the writer to finish */
286
+static CoQueue reader_queue;
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;
145
+};
297
+};
146
+
298
+
147
+Transaction *tran_new(void)
299
+/*
148
+{
300
+ * List of BdrvGraphRWlock. This list ensures that each BdrvGraphRWlock
149
+ Transaction *tran = g_new(Transaction, 1);
301
+ * can safely modify only its own counter, avoid reading/writing
150
+
302
+ * others and thus improving performances by avoiding cacheline bounces.
151
+ QSLIST_INIT(&tran->actions);
303
+ */
152
+
304
+static QTAILQ_HEAD(, BdrvGraphRWlock) aio_context_list =
153
+ return tran;
305
+ QTAILQ_HEAD_INITIALIZER(aio_context_list);
154
+}
306
+
155
+
307
+static void __attribute__((__constructor__)) bdrv_init_graph_lock(void)
156
+void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque)
308
+{
157
+{
309
+ qemu_mutex_init(&aio_context_list_lock);
158
+ TransactionAction *act;
310
+ qemu_co_queue_init(&reader_queue);
159
+
311
+}
160
+ act = g_new(TransactionAction, 1);
312
+
161
+ *act = (TransactionAction) {
313
+void register_aiocontext(AioContext *ctx)
162
+ .drv = drv,
314
+{
163
+ .opaque = opaque
315
+ ctx->bdrv_graph = g_new0(BdrvGraphRWlock, 1);
164
+ };
316
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
165
+
317
+ assert(ctx->bdrv_graph->reader_count == 0);
166
+ QSLIST_INSERT_HEAD(&tran->actions, act, entry);
318
+ QTAILQ_INSERT_TAIL(&aio_context_list, ctx->bdrv_graph, next_aio);
167
+}
319
+}
168
+
320
+
169
+void tran_abort(Transaction *tran)
321
+void unregister_aiocontext(AioContext *ctx)
170
+{
322
+{
171
+ TransactionAction *act, *next;
323
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
172
+
324
+ orphaned_reader_count += ctx->bdrv_graph->reader_count;
173
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
325
+ QTAILQ_REMOVE(&aio_context_list, ctx->bdrv_graph, next_aio);
174
+ if (act->drv->abort) {
326
+ g_free(ctx->bdrv_graph);
175
+ act->drv->abort(act->opaque);
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);
340
+ }
341
+
342
+ /* shouldn't overflow unless there are 2^31 readers */
343
+ assert((int32_t)rd >= 0);
344
+ return rd;
345
+}
346
+
347
+void bdrv_graph_wrlock(void)
348
+{
349
+ GLOBAL_STATE_CODE();
350
+ assert(!qatomic_read(&has_writer));
351
+
352
+ /* Make sure that constantly arriving new I/O doesn't cause starvation */
353
+ bdrv_drain_all_begin_nopoll();
354
+
355
+ /*
356
+ * reader_count == 0: this means writer will read has_reader as 1
357
+ * reader_count >= 1: we don't know if writer read has_writer == 0 or 1,
358
+ * but we need to wait.
359
+ * Wait by allowing other coroutine (and possible readers) to continue.
360
+ */
361
+ do {
362
+ /*
363
+ * has_writer must be 0 while polling, otherwise we get a deadlock if
364
+ * any callback involved during AIO_WAIT_WHILE() tries to acquire the
365
+ * reader lock.
366
+ */
367
+ qatomic_set(&has_writer, 0);
368
+ AIO_WAIT_WHILE(qemu_get_aio_context(), reader_count() >= 1);
369
+ qatomic_set(&has_writer, 1);
370
+
371
+ /*
372
+ * We want to only check reader_count() after has_writer = 1 is visible
373
+ * to other threads. That way no more readers can sneak in after we've
374
+ * determined reader_count() == 0.
375
+ */
376
+ smp_mb();
377
+ } while (reader_count() >= 1);
378
+
379
+ bdrv_drain_all_end();
380
+}
381
+
382
+void bdrv_graph_wrunlock(void)
383
+{
384
+ GLOBAL_STATE_CODE();
385
+ QEMU_LOCK_GUARD(&aio_context_list_lock);
386
+ assert(qatomic_read(&has_writer));
387
+
388
+ /*
389
+ * No need for memory barriers, this works in pair with
390
+ * the slow path of rdlock() and both take the lock.
391
+ */
392
+ qatomic_store_release(&has_writer, 0);
393
+
394
+ /* Wake up all coroutine that are waiting to read the graph */
395
+ qemu_co_enter_all(&reader_queue, &aio_context_list_lock);
396
+}
397
+
398
+void coroutine_fn bdrv_graph_co_rdlock(void)
399
+{
400
+ BdrvGraphRWlock *bdrv_graph;
401
+ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
402
+
403
+ /* Do not lock if in main thread */
404
+ if (qemu_in_main_thread()) {
405
+ return;
406
+ }
407
+
408
+ for (;;) {
409
+ qatomic_set(&bdrv_graph->reader_count,
410
+ bdrv_graph->reader_count + 1);
411
+ /* make sure writer sees reader_count before we check has_writer */
412
+ smp_mb();
413
+
414
+ /*
415
+ * has_writer == 0: this means writer will read reader_count as >= 1
416
+ * has_writer == 1: we don't know if writer read reader_count == 0
417
+ * or > 0, but we need to wait anyways because
418
+ * it will write.
419
+ */
420
+ if (!qatomic_read(&has_writer)) {
421
+ break;
176
+ }
422
+ }
177
+
423
+
178
+ if (act->drv->clean) {
424
+ /*
179
+ act->drv->clean(act->opaque);
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);
180
+ }
462
+ }
181
+
182
+ g_free(act);
183
+ }
463
+ }
184
+
464
+}
185
+ g_free(tran);
465
+
186
+}
466
+void coroutine_fn bdrv_graph_co_rdunlock(void)
187
+
467
+{
188
+void tran_commit(Transaction *tran)
468
+ BdrvGraphRWlock *bdrv_graph;
189
+{
469
+ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph;
190
+ TransactionAction *act, *next;
470
+
191
+
471
+ /* Do not lock if in main thread */
192
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
472
+ if (qemu_in_main_thread()) {
193
+ if (act->drv->commit) {
473
+ return;
194
+ act->drv->commit(act->opaque);
195
+ }
196
+
197
+ if (act->drv->clean) {
198
+ act->drv->clean(act->opaque);
199
+ }
200
+
201
+ g_free(act);
202
+ }
474
+ }
203
+
475
+
204
+ g_free(tran);
476
+ qatomic_store_release(&bdrv_graph->reader_count,
205
+}
477
+ bdrv_graph->reader_count - 1);
206
diff --git a/MAINTAINERS b/MAINTAINERS
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
207
index XXXXXXX..XXXXXXX 100644
504
index XXXXXXX..XXXXXXX 100644
208
--- a/MAINTAINERS
505
--- a/block/meson.build
209
+++ b/MAINTAINERS
506
+++ b/block/meson.build
210
@@ -XXX,XX +XXX,XX @@ M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
507
@@ -XXX,XX +XXX,XX @@ block_ss.add(files(
211
S: Maintained
508
'blkverify.c',
212
F: scripts/simplebench/
509
'block-backend.c',
213
510
'block-copy.c',
214
+Transactions helper
511
+ 'graph-lock.c',
215
+M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
512
'commit.c',
216
+S: Maintained
513
'copy-on-read.c',
217
+F: include/qemu/transactions.h
514
'preallocate.c',
218
+F: util/transactions.c
219
+
220
QAPI
221
M: Markus Armbruster <armbru@redhat.com>
222
M: Michael Roth <michael.roth@amd.com>
223
diff --git a/util/meson.build b/util/meson.build
224
index XXXXXXX..XXXXXXX 100644
225
--- a/util/meson.build
226
+++ b/util/meson.build
227
@@ -XXX,XX +XXX,XX @@ util_ss.add(files('qsp.c'))
228
util_ss.add(files('range.c'))
229
util_ss.add(files('stats64.c'))
230
util_ss.add(files('systemd.c'))
231
+util_ss.add(files('transactions.c'))
232
util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c'))
233
util_ss.add(files('guest-random.c'))
234
util_ss.add(files('yank.c'))
235
--
515
--
236
2.30.2
516
2.38.1
237
238
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Add test to show that simple DFS recursion order is not correct for
3
Similar to the implementation in lockable.h, implement macros to
4
permission update. Correct order is topological-sort order, which will
4
automatically take and release the rdlock.
5
be introduced later.
6
5
7
Consider the block driver which has two filter children: one active
6
Create the empty GraphLockable and GraphLockableMainloop structs only to
8
with exclusive write access and one inactive with no specific
7
use it as a type for G_DEFINE_AUTOPTR_CLEANUP_FUNC.
9
permissions.
10
8
11
And, these two children has a common base child, like this:
9
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
┌─────┐ ┌──────┐
11
Message-Id: <20221207131838.239125-4-kwolf@redhat.com>
14
│ fl2 │ ◀── │ top │
12
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
15
└─────┘ └──────┘
16
│ │
17
│ │ w
18
│ ▼
19
│ ┌──────┐
20
│ │ fl1 │
21
│ └──────┘
22
│ │
23
│ │ w
24
│ ▼
25
│ ┌──────┐
26
└───────▶ │ base │
27
└──────┘
28
29
So, exclusive write is propagated.
30
31
Assume, we want to make fl2 active instead of fl1.
32
So, we set some option for top driver and do permission update.
33
34
If permission update (remember, it's DFS) goes first through
35
top->fl1->base branch it will succeed: it firstly drop exclusive write
36
permissions and than apply them for another BdrvChildren.
37
But if permission update goes first through top->fl2->base branch it
38
will fail, as when we try to update fl2->base child, old not yet
39
updated fl1->base child will be in conflict.
40
41
Now test fails, so it runs only with -d flag. To run do
42
43
./test-bdrv-graph-mod -d -p /bdrv-graph-mod/parallel-perm-update
44
45
from <build-directory>/tests.
46
47
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
48
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
49
Message-Id: <20210428151804.439460-3-vsementsov@virtuozzo.com>
50
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
51
---
14
---
52
tests/unit/test-bdrv-graph-mod.c | 116 +++++++++++++++++++++++++++++++
15
include/block/graph-lock.h | 66 ++++++++++++++++++++++++++++++++++++++
53
1 file changed, 116 insertions(+)
16
1 file changed, 66 insertions(+)
54
17
55
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
18
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
56
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
57
--- a/tests/unit/test-bdrv-graph-mod.c
20
--- a/include/block/graph-lock.h
58
+++ b/tests/unit/test-bdrv-graph-mod.c
21
+++ b/include/block/graph-lock.h
59
@@ -XXX,XX +XXX,XX @@ static void test_parallel_exclusive_write(void)
22
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_graph_co_rdunlock(void);
60
bdrv_unref(top);
23
void bdrv_graph_rdlock_main_loop(void);
61
}
24
void bdrv_graph_rdunlock_main_loop(void);
62
25
63
+static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c,
26
+typedef struct GraphLockable { } GraphLockable;
64
+ BdrvChildRole role,
27
+
65
+ BlockReopenQueue *reopen_queue,
28
+/*
66
+ uint64_t perm, uint64_t shared,
29
+ * In C, compound literals have the lifetime of an automatic variable.
67
+ uint64_t *nperm, uint64_t *nshared)
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)
68
+{
36
+{
69
+ if (bs->file && c == bs->file) {
37
+ bdrv_graph_co_rdlock();
70
+ *nperm = BLK_PERM_WRITE;
38
+ return x;
71
+ *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
72
+ } else {
73
+ *nperm = 0;
74
+ *nshared = BLK_PERM_ALL;
75
+ }
76
+}
39
+}
77
+
40
+
78
+static BlockDriver bdrv_write_to_file = {
41
+static inline void graph_lockable_auto_unlock(GraphLockable *x)
79
+ .format_name = "tricky-perm",
42
+{
80
+ .bdrv_child_perm = write_to_file_perms,
43
+ bdrv_graph_co_rdunlock();
81
+};
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_())
82
+
60
+
83
+
61
+
62
+typedef struct GraphLockableMainloop { } GraphLockableMainloop;
63
+
84
+/*
64
+/*
85
+ * The following test shows that topological-sort order is required for
65
+ * In C, compound literals have the lifetime of an automatic variable.
86
+ * permission update, simple DFS is not enough.
66
+ * In C++ it would be different, but then C++ wouldn't need QemuLockable
87
+ *
67
+ * either...
88
+ * Consider the block driver which has two filter children: one active
89
+ * with exclusive write access and one inactive with no specific
90
+ * permissions.
91
+ *
92
+ * And, these two children has a common base child, like this:
93
+ *
94
+ * ┌─────┐ ┌──────┐
95
+ * │ fl2 │ ◀── │ top │
96
+ * └─────┘ └──────┘
97
+ * │ │
98
+ * │ │ w
99
+ * │ ▼
100
+ * │ ┌──────┐
101
+ * │ │ fl1 │
102
+ * │ └──────┘
103
+ * │ │
104
+ * │ │ w
105
+ * │ ▼
106
+ * │ ┌──────┐
107
+ * └───────▶ │ base │
108
+ * └──────┘
109
+ *
110
+ * So, exclusive write is propagated.
111
+ *
112
+ * Assume, we want to make fl2 active instead of fl1.
113
+ * So, we set some option for top driver and do permission update.
114
+ *
115
+ * With simple DFS, if permission update goes first through
116
+ * top->fl1->base branch it will succeed: it firstly drop exclusive write
117
+ * permissions and than apply them for another BdrvChildren.
118
+ * But if permission update goes first through top->fl2->base branch it
119
+ * will fail, as when we try to update fl2->base child, old not yet
120
+ * updated fl1->base child will be in conflict.
121
+ *
122
+ * With topological-sort order we always update parents before children, so fl1
123
+ * and fl2 are both updated when we update base and there is no conflict.
124
+ */
68
+ */
125
+static void test_parallel_perm_update(void)
69
+#define GMLML_OBJ_() (&(GraphLockableMainloop) { })
70
+
71
+static inline GraphLockableMainloop *
72
+graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x)
126
+{
73
+{
127
+ BlockDriverState *top = no_perm_node("top");
74
+ bdrv_graph_rdlock_main_loop();
128
+ BlockDriverState *tricky =
75
+ return x;
129
+ bdrv_new_open_driver(&bdrv_write_to_file, "tricky", BDRV_O_RDWR,
130
+ &error_abort);
131
+ BlockDriverState *base = no_perm_node("base");
132
+ BlockDriverState *fl1 = pass_through_node("fl1");
133
+ BlockDriverState *fl2 = pass_through_node("fl2");
134
+ BdrvChild *c_fl1, *c_fl2;
135
+
136
+ /*
137
+ * bdrv_attach_child() eats child bs reference, so we need two @base
138
+ * references for two filters:
139
+ */
140
+ bdrv_ref(base);
141
+
142
+ bdrv_attach_child(top, tricky, "file", &child_of_bds, BDRV_CHILD_DATA,
143
+ &error_abort);
144
+ c_fl1 = bdrv_attach_child(tricky, fl1, "first", &child_of_bds,
145
+ BDRV_CHILD_FILTERED, &error_abort);
146
+ c_fl2 = bdrv_attach_child(tricky, fl2, "second", &child_of_bds,
147
+ BDRV_CHILD_FILTERED, &error_abort);
148
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
149
+ &error_abort);
150
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
151
+ &error_abort);
152
+
153
+ /* Select fl1 as first child to be active */
154
+ tricky->file = c_fl1;
155
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
156
+
157
+ assert(c_fl1->perm & BLK_PERM_WRITE);
158
+ assert(!(c_fl2->perm & BLK_PERM_WRITE));
159
+
160
+ /* Now, try to switch active child and update permissions */
161
+ tricky->file = c_fl2;
162
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
163
+
164
+ assert(c_fl2->perm & BLK_PERM_WRITE);
165
+ assert(!(c_fl1->perm & BLK_PERM_WRITE));
166
+
167
+ /* Switch once more, to not care about real child order in the list */
168
+ tricky->file = c_fl1;
169
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
170
+
171
+ assert(c_fl1->perm & BLK_PERM_WRITE);
172
+ assert(!(c_fl2->perm & BLK_PERM_WRITE));
173
+
174
+ bdrv_unref(top);
175
+}
76
+}
176
+
77
+
177
int main(int argc, char *argv[])
78
+static inline void
178
{
79
+graph_lockable_auto_unlock_mainloop(GraphLockableMainloop *x)
179
int i;
80
+{
180
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
81
+ bdrv_graph_rdunlock_main_loop();
181
if (debug) {
82
+}
182
g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
83
+
183
test_parallel_exclusive_write);
84
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockableMainloop,
184
+ g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
85
+ graph_lockable_auto_unlock_mainloop)
185
+ test_parallel_perm_update);
86
+
186
}
87
+#define GRAPH_RDLOCK_GUARD_MAINLOOP(x) \
187
88
+ g_autoptr(GraphLockableMainloop) \
188
return g_test_run();
89
+ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \
90
+ graph_lockable_auto_lock_mainloop(GMLML_OBJ_())
91
+
92
#endif /* GRAPH_LOCK_H */
93
189
--
94
--
190
2.30.2
95
2.38.1
191
192
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
New patch
1
This enables clang's thread safety analysis (TSA), which we'll use to
2
statically check the block graph locking.
1
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Message-Id: <20221207131838.239125-9-kwolf@redhat.com>
6
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
configure | 1 +
10
1 file changed, 1 insertion(+)
11
12
diff --git a/configure b/configure
13
index XXXXXXX..XXXXXXX 100755
14
--- a/configure
15
+++ b/configure
16
@@ -XXX,XX +XXX,XX @@ add_to warn_flags -Wnested-externs
17
add_to warn_flags -Wendif-labels
18
add_to warn_flags -Wexpansion-to-defined
19
add_to warn_flags -Wimplicit-fallthrough=2
20
+add_to warn_flags -Wthread-safety
21
22
nowarn_flags=
23
add_to nowarn_flags -Wno-initializer-overrides
24
--
25
2.38.1
diff view generated by jsdifflib
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: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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
We have too much comments for this feature. It seems better just don't
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
do it. Most of real users (tests don't count) have to create additional
7
Message-Id: <20221207131838.239125-11-kwolf@redhat.com>
5
reference.
8
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
7
Drop also comment in external_snapshot_prepare:
8
- bdrv_append doesn't "remove" old bs in common sense, it sounds
9
strange
10
- the fact that bdrv_append can fail is obvious from the context
11
- the fact that we must rollback all changes in transaction abort is
12
known (it's the direct role of abort)
13
14
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
15
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
16
Message-Id: <20210428151804.439460-5-vsementsov@virtuozzo.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
10
---
19
block.c | 25 +++----------------------
11
block.c | 4 ++++
20
block/backup-top.c | 1 -
12
blockdev.c | 4 ++++
21
block/commit.c | 1 +
13
2 files changed, 8 insertions(+)
22
block/mirror.c | 3 ---
23
blockdev.c | 4 ----
24
tests/unit/test-bdrv-drain.c | 2 +-
25
tests/unit/test-bdrv-graph-mod.c | 3 +++
26
7 files changed, 8 insertions(+), 31 deletions(-)
27
14
28
diff --git a/block.c b/block.c
15
diff --git a/block.c b/block.c
29
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
30
--- a/block.c
17
--- a/block.c
31
+++ b/block.c
18
+++ b/block.c
32
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
19
@@ -XXX,XX +XXX,XX @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs)
33
goto out;
20
return true;
34
}
35
36
- /* bdrv_append() consumes a strong reference to bs_snapshot
37
- * (i.e. it will call bdrv_unref() on it) even on error, so in
38
- * order to be able to return one, we have to increase
39
- * bs_snapshot's refcount here */
40
- bdrv_ref(bs_snapshot);
41
ret = bdrv_append(bs_snapshot, bs, errp);
42
if (ret < 0) {
43
bs_snapshot = NULL;
44
@@ -XXX,XX +XXX,XX @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
45
* bs_new must not be attached to a BlockBackend.
46
*
47
* This function does not create any image files.
48
- *
49
- * bdrv_append() takes ownership of a bs_new reference and unrefs it because
50
- * that's what the callers commonly need. bs_new will be referenced by the old
51
- * parents of bs_top after bdrv_append() returns. If the caller needs to keep a
52
- * reference of its own, it must call bdrv_ref().
53
*/
54
int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
55
Error **errp)
56
{
57
int ret = bdrv_set_backing_hd(bs_new, bs_top, errp);
58
if (ret < 0) {
59
- goto out;
60
+ return ret;
61
}
62
63
ret = bdrv_replace_node(bs_top, bs_new, errp);
64
if (ret < 0) {
65
bdrv_set_backing_hd(bs_new, NULL, &error_abort);
66
- goto out;
67
+ return ret;
68
}
69
70
- ret = 0;
71
-
72
-out:
73
- /*
74
- * bs_new is now referenced by its new parents, we don't need the
75
- * additional reference any more.
76
- */
77
- bdrv_unref(bs_new);
78
-
79
- return ret;
80
+ return 0;
81
}
21
}
82
22
83
static void bdrv_delete(BlockDriverState *bs)
23
+/*
84
diff --git a/block/backup-top.c b/block/backup-top.c
24
+ * Must not be called while holding the lock of an AioContext other than the
85
index XXXXXXX..XXXXXXX 100644
25
+ * current one.
86
--- a/block/backup-top.c
26
+ */
87
+++ b/block/backup-top.c
27
void bdrv_img_create(const char *filename, const char *fmt,
88
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
28
const char *base_filename, const char *base_fmt,
89
29
char *options, uint64_t img_size, int flags, bool quiet,
90
bdrv_drained_begin(source);
91
92
- bdrv_ref(top);
93
ret = bdrv_append(top, source, errp);
94
if (ret < 0) {
95
error_prepend(errp, "Cannot append backup-top filter: ");
96
diff --git a/block/commit.c b/block/commit.c
97
index XXXXXXX..XXXXXXX 100644
98
--- a/block/commit.c
99
+++ b/block/commit.c
100
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
101
commit_top_bs->total_sectors = top->total_sectors;
102
103
ret = bdrv_append(commit_top_bs, top, errp);
104
+ bdrv_unref(commit_top_bs); /* referenced by new parents or failed */
105
if (ret < 0) {
106
commit_top_bs = NULL;
107
goto fail;
108
diff --git a/block/mirror.c b/block/mirror.c
109
index XXXXXXX..XXXXXXX 100644
110
--- a/block/mirror.c
111
+++ b/block/mirror.c
112
@@ -XXX,XX +XXX,XX @@ static BlockJob *mirror_start_job(
113
114
bs_opaque->is_commit = target_is_backing;
115
116
- /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep
117
- * it alive until block_job_create() succeeds even if bs has no parent. */
118
- bdrv_ref(mirror_top_bs);
119
bdrv_drained_begin(bs);
120
ret = bdrv_append(mirror_top_bs, bs, errp);
121
bdrv_drained_end(bs);
122
diff --git a/blockdev.c b/blockdev.c
30
diff --git a/blockdev.c b/blockdev.c
123
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
124
--- a/blockdev.c
32
--- a/blockdev.c
125
+++ b/blockdev.c
33
+++ b/blockdev.c
126
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
34
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
127
goto out;
35
goto out;
128
}
36
}
129
37
bdrv_refresh_filename(state->old_bs);
130
- /* This removes our old bs and adds the new bs. This is an operation that
38
+
131
- * can fail, so we need to do it in .prepare; undoing it for abort is
39
+ aio_context_release(aio_context);
132
- * always possible. */
40
bdrv_img_create(new_image_file, format,
133
- bdrv_ref(state->new_bs);
41
state->old_bs->filename,
134
ret = bdrv_append(state->new_bs, state->old_bs, errp);
42
state->old_bs->drv->format_name,
135
if (ret < 0) {
43
NULL, size, flags, false, &local_err);
136
goto out;
44
+ aio_context_acquire(aio_context);
137
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
45
+
138
index XXXXXXX..XXXXXXX 100644
46
if (local_err) {
139
--- a/tests/unit/test-bdrv-drain.c
47
error_propagate(errp, local_err);
140
+++ b/tests/unit/test-bdrv-drain.c
48
goto out;
141
@@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void)
142
g_assert_cmpint(base_s->drain_count, ==, 1);
143
g_assert_cmpint(base->in_flight, ==, 0);
144
145
- /* Takes ownership of overlay, so we don't have to unref it later */
146
bdrv_append(overlay, base, &error_abort);
147
g_assert_cmpint(base->in_flight, ==, 0);
148
g_assert_cmpint(overlay->in_flight, ==, 0);
149
@@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void)
150
g_assert_cmpint(overlay->quiesce_counter, ==, 0);
151
g_assert_cmpint(overlay_s->drain_count, ==, 0);
152
153
+ bdrv_unref(overlay);
154
bdrv_unref(base);
155
blk_unref(blk);
156
}
157
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
158
index XXXXXXX..XXXXXXX 100644
159
--- a/tests/unit/test-bdrv-graph-mod.c
160
+++ b/tests/unit/test-bdrv-graph-mod.c
161
@@ -XXX,XX +XXX,XX @@ static void test_update_perm_tree(void)
162
ret = bdrv_append(filter, bs, NULL);
163
g_assert_cmpint(ret, <, 0);
164
165
+ bdrv_unref(filter);
166
blk_unref(root);
167
}
168
169
@@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void)
170
bdrv_append(filter, bs, &error_abort);
171
g_assert(target->backing->bs == bs);
172
173
+ bdrv_unref(filter);
174
bdrv_unref(bs);
175
blk_unref(root);
176
}
177
@@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void)
178
&error_abort);
179
180
bdrv_append(fl, base, &error_abort);
181
+ bdrv_unref(fl);
182
bdrv_unref(top);
183
}
184
185
--
49
--
186
2.30.2
50
2.38.1
187
188
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
We don't have bdrv_replace_child(), so it's time for
3
Protect the main function where graph is modified.
4
bdrv_replace_child_safe() to take its place.
5
4
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20210428151804.439460-36-vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
---
11
block.c | 10 +++++-----
12
block.c | 7 +++----
12
1 file changed, 5 insertions(+), 5 deletions(-)
13
1 file changed, 3 insertions(+), 4 deletions(-)
13
14
14
diff --git a/block.c b/block.c
15
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
17
--- a/block.c
17
+++ b/block.c
18
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = {
19
@@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
19
};
20
21
/*
22
- * bdrv_replace_child_safe
23
+ * bdrv_replace_child
24
*
20
*
25
* Note: real unref of old_bs is done only on commit.
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.
26
*/
25
*/
27
-static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
26
static void bdrv_replace_child_noperm(BdrvChild *child,
28
- Transaction *tran)
27
BlockDriverState *new_bs)
29
+static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
28
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
30
+ Transaction *tran)
29
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
31
{
32
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
33
*s = (BdrvReplaceChildState) {
34
@@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
35
}
30
}
36
31
37
if (child->bs) {
32
+ /* TODO Pull this up into the callers to avoid polling here */
38
- bdrv_replace_child_safe(child, NULL, tran);
33
+ bdrv_graph_wrlock();
39
+ bdrv_replace_child(child, NULL, tran);
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
}
40
}
41
41
42
s = g_new(BdrvRemoveFilterOrCowChild, 1);
42
child->bs = new_bs;
43
@@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
43
44
c->name, from->node_name);
44
if (new_bs) {
45
return -EPERM;
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);
46
}
49
}
47
- bdrv_replace_child_safe(c, to, tran);
48
+ bdrv_replace_child(c, to, tran);
49
}
50
}
50
51
+ bdrv_graph_wrunlock();
51
return 0;
52
53
/*
54
* If the parent was drained through this BdrvChild previously, but new_bs
52
--
55
--
53
2.30.2
56
2.38.1
54
55
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
This argument is always NULL. Drop it.
3
We don't protect bdrv->aio_context with the graph rwlock,
4
so these assertions are not needed
4
5
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20221207131838.239125-13-kwolf@redhat.com>
9
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20210428151804.439460-26-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
12
---
10
block.c | 38 +++++++++++---------------------------
13
block.c | 3 ---
11
1 file changed, 11 insertions(+), 27 deletions(-)
14
1 file changed, 3 deletions(-)
12
15
13
diff --git a/block.c b/block.c
16
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
18
--- a/block.c
16
+++ b/block.c
19
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
20
@@ -XXX,XX +XXX,XX @@ static void bdrv_detach_aio_context(BlockDriverState *bs)
18
static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
21
if (bs->quiesce_counter) {
19
uint64_t new_used_perm,
22
aio_enable_external(bs->aio_context);
20
uint64_t new_shared_perm,
23
}
21
- GSList *ignore_children,
24
- assert_bdrv_graph_writable(bs);
22
Error **errp);
25
bs->aio_context = NULL;
23
24
typedef struct BlockReopenQueueEntry {
25
@@ -XXX,XX +XXX,XX @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
26
return false;
27
}
26
}
28
27
29
-static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
28
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_aio_context(BlockDriverState *bs,
30
- GSList *ignore_children,
29
aio_disable_external(new_context);
31
- Error **errp)
32
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
33
{
34
BdrvChild *a, *b;
35
36
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
37
* directions.
38
*/
39
QLIST_FOREACH(a, &bs->parents, next_parent) {
40
- if (g_slist_find(ignore_children, a)) {
41
- continue;
42
- }
43
-
44
QLIST_FOREACH(b, &bs->parents, next_parent) {
45
- if (a == b || g_slist_find(ignore_children, b)) {
46
+ if (a == b) {
47
continue;
48
}
49
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
51
static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
52
uint64_t cumulative_perms,
53
uint64_t cumulative_shared_perms,
54
- GSList *ignore_children,
55
Transaction *tran, Error **errp)
56
{
57
BlockDriver *drv = bs->drv;
58
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
59
bool use_cumulative_perms,
60
uint64_t cumulative_perms,
61
uint64_t cumulative_shared_perms,
62
- GSList *ignore_children,
63
Transaction *tran, Error **errp)
64
{
65
int ret;
66
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
67
68
ret = bdrv_node_check_perm(bs, q, cumulative_perms,
69
cumulative_shared_perms,
70
- ignore_children, tran, errp);
71
+ tran, errp);
72
if (ret < 0) {
73
return ret;
74
}
75
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
76
for ( ; list; list = list->next) {
77
bs = list->data;
78
79
- if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
80
+ if (bdrv_parent_perms_conflict(bs, errp)) {
81
return -EINVAL;
82
}
83
84
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
85
86
ret = bdrv_node_check_perm(bs, q, cumulative_perms,
87
cumulative_shared_perms,
88
- ignore_children, tran, errp);
89
+ tran, errp);
90
if (ret < 0) {
91
return ret;
92
}
93
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
94
95
static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
96
uint64_t cumulative_perms,
97
- uint64_t cumulative_shared_perms,
98
- GSList *ignore_children, Error **errp)
99
+ uint64_t cumulative_shared_perms, Error **errp)
100
{
101
g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
102
return bdrv_check_perm_common(list, q, true, cumulative_perms,
103
- cumulative_shared_perms, ignore_children,
104
- NULL, errp);
105
+ cumulative_shared_perms, NULL, errp);
106
}
107
108
static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
109
Transaction *tran, Error **errp)
110
{
111
- return bdrv_check_perm_common(list, q, false, 0, 0, NULL, tran, errp);
112
+ return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
113
}
114
115
/*
116
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
117
static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
118
uint64_t new_used_perm,
119
uint64_t new_shared_perm,
120
- GSList *ignore_children,
121
Error **errp)
122
{
123
BdrvChild *c;
124
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
125
assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
126
127
QLIST_FOREACH(c, &bs->parents, next_parent) {
128
- if (g_slist_find(ignore_children, c)) {
129
- continue;
130
- }
131
-
132
if ((new_used_perm & c->shared_perm) != new_used_perm) {
133
char *user = bdrv_child_user_desc(c);
134
char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
135
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
136
}
30
}
137
31
138
return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
32
- assert_bdrv_graph_writable(bs);
139
- ignore_children, errp);
33
bs->aio_context = new_context;
140
+ errp);
34
141
}
35
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
142
36
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_aio_context_commit(void *opaque)
143
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
37
BlockDriverState *bs = (BlockDriverState *) state->bs;
144
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
38
AioContext *new_context = state->new_ctx;
145
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
39
AioContext *old_context = bdrv_get_aio_context(bs);
146
BDRVReopenState *state = &bs_entry->state;
40
- assert_bdrv_graph_writable(bs);
147
ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
41
148
- state->shared_perm, NULL, errp);
42
/*
149
+ state->shared_perm, errp);
43
* Take the old AioContex when detaching it from bs.
150
if (ret < 0) {
151
goto cleanup_perm;
152
}
153
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
154
bs_queue, state->perm, state->shared_perm,
155
&nperm, &nshared);
156
ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
157
- nperm, nshared, NULL, errp);
158
+ nperm, nshared, errp);
159
if (ret < 0) {
160
goto cleanup_perm;
161
}
162
--
44
--
163
2.30.2
45
2.38.1
164
165
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
To be used in the following commit.
3
Remove the old assert_bdrv_graph_writable, and replace it with
4
the new version using graph-lock API.
4
5
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
See the function documentation for more information.
7
8
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20221207131838.239125-14-kwolf@redhat.com>
11
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20210428151804.439460-17-vsementsov@virtuozzo.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
14
---
10
block.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
15
include/block/block_int-global-state.h | 17 -----------------
11
1 file changed, 54 insertions(+)
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(-)
12
20
21
diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block_int-global-state.h
24
+++ b/include/block/block_int-global-state.h
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
/*
13
diff --git a/block.c b/block.c
73
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
74
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
75
--- a/block.c
16
+++ b/block.c
76
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
77
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child)
18
BdrvChildRole child_role,
78
{
19
Error **errp);
79
BlockDriverState *bs = child->opaque;
20
80
21
+static void bdrv_replace_child_noperm(BdrvChild *child,
81
- assert_bdrv_graph_writable(bs);
22
+ BlockDriverState *new_bs);
82
+ assert_bdrv_graph_writable();
83
QLIST_INSERT_HEAD(&bs->children, child, next);
84
if (bs->drv->is_filter || (child->role & BDRV_CHILD_FILTERED)) {
85
/*
86
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_detach(BdrvChild *child)
87
bdrv_backing_detach(child);
88
}
89
90
- assert_bdrv_graph_writable(bs);
91
+ assert_bdrv_graph_writable();
92
QLIST_REMOVE(child, next);
93
if (child == bs->backing) {
94
assert(child != bs->file);
95
diff --git a/block/graph-lock.c b/block/graph-lock.c
96
index XXXXXXX..XXXXXXX 100644
97
--- a/block/graph-lock.c
98
+++ b/block/graph-lock.c
99
@@ -XXX,XX +XXX,XX @@ void bdrv_graph_rdunlock_main_loop(void)
100
GLOBAL_STATE_CODE();
101
assert(!qemu_in_coroutine());
102
}
23
+
103
+
24
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue
104
+void assert_bdrv_graph_readable(void)
25
*queue, Error **errp);
26
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
27
@@ -XXX,XX +XXX,XX @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
28
return 0;
29
}
30
31
+typedef struct BdrvReplaceChildState {
32
+ BdrvChild *child;
33
+ BlockDriverState *old_bs;
34
+} BdrvReplaceChildState;
35
+
36
+static void bdrv_replace_child_commit(void *opaque)
37
+{
105
+{
38
+ BdrvReplaceChildState *s = opaque;
106
+ assert(qemu_in_main_thread() || reader_count());
39
+
40
+ bdrv_unref(s->old_bs);
41
+}
107
+}
42
+
108
+
43
+static void bdrv_replace_child_abort(void *opaque)
109
+void assert_bdrv_graph_writable(void)
44
+{
110
+{
45
+ BdrvReplaceChildState *s = opaque;
111
+ assert(qemu_in_main_thread());
46
+ BlockDriverState *new_bs = s->child->bs;
112
+ assert(qatomic_read(&has_writer));
47
+
48
+ /* old_bs reference is transparently moved from @s to @s->child */
49
+ bdrv_replace_child_noperm(s->child, s->old_bs);
50
+ bdrv_unref(new_bs);
51
+}
113
+}
52
+
53
+static TransactionActionDrv bdrv_replace_child_drv = {
54
+ .commit = bdrv_replace_child_commit,
55
+ .abort = bdrv_replace_child_abort,
56
+ .clean = g_free,
57
+};
58
+
59
+/*
60
+ * bdrv_replace_child_safe
61
+ *
62
+ * Note: real unref of old_bs is done only on commit.
63
+ */
64
+__attribute__((unused))
65
+static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
66
+ Transaction *tran)
67
+{
68
+ BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
69
+ *s = (BdrvReplaceChildState) {
70
+ .child = child,
71
+ .old_bs = child->bs,
72
+ };
73
+ tran_add(tran, &bdrv_replace_child_drv, s);
74
+
75
+ if (new_bs) {
76
+ bdrv_ref(new_bs);
77
+ }
78
+ bdrv_replace_child_noperm(child, new_bs);
79
+ /* old_bs reference is transparently moved from @child to @s */
80
+}
81
+
82
/*
83
* Check whether permissions on this node can be changed in a way that
84
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
85
--
114
--
86
2.30.2
115
2.38.1
87
88
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
2
Message-Id: <20221207131838.239125-15-kwolf@redhat.com>
3
We don't need this workaround anymore: bdrv_append is already smart
3
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
enough and we can use new bdrv_drop_filter().
5
6
This commit efficiently reverts also recent 705dde27c6c53b73, which
7
checked .active on io path. Still it said that the problem should be
8
theoretical. And the logic of filter removement is changed anyway.
9
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20210428151804.439460-25-vsementsov@virtuozzo.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
5
---
15
block/backup-top.c | 47 +-------------------------------------
6
include/block/graph-lock.h | 80 +++++++++++++++++++++++++++++++++-----
16
tests/qemu-iotests/283.out | 2 +-
7
block/graph-lock.c | 3 ++
17
2 files changed, 2 insertions(+), 47 deletions(-)
8
2 files changed, 73 insertions(+), 10 deletions(-)
18
9
19
diff --git a/block/backup-top.c b/block/backup-top.c
10
diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h
20
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
21
--- a/block/backup-top.c
12
--- a/include/block/graph-lock.h
22
+++ b/block/backup-top.c
13
+++ b/include/block/graph-lock.h
23
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@
24
typedef struct BDRVBackupTopState {
15
#define GRAPH_LOCK_H
25
BlockCopyState *bcs;
16
26
BdrvChild *target;
17
#include "qemu/osdep.h"
27
- bool active;
18
+#include "qemu/clang-tsa.h"
28
int64_t cluster_size;
19
29
} BDRVBackupTopState;
20
#include "qemu/coroutine.h"
30
21
31
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_co_preadv(
22
@@ -XXX,XX +XXX,XX @@
32
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
23
*/
33
QEMUIOVector *qiov, int flags)
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)
34
{
140
{
35
- BDRVBackupTopState *s = bs->opaque;
141
bdrv_graph_co_rdlock();
36
-
142
return x;
37
- if (!s->active) {
38
- return -EIO;
39
- }
40
-
41
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
42
}
143
}
43
144
44
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
145
-static inline void graph_lockable_auto_unlock(GraphLockable *x)
45
BDRVBackupTopState *s = bs->opaque;
146
+static inline void TSA_NO_TSA
46
uint64_t off, end;
147
+graph_lockable_auto_unlock(GraphLockable *x)
47
48
- if (!s->active) {
49
- return -EIO;
50
- }
51
-
52
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
53
return 0;
54
}
55
@@ -XXX,XX +XXX,XX @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
56
uint64_t perm, uint64_t shared,
57
uint64_t *nperm, uint64_t *nshared)
58
{
148
{
59
- BDRVBackupTopState *s = bs->opaque;
149
bdrv_graph_co_rdunlock();
60
-
150
}
61
- if (!s->active) {
151
@@ -XXX,XX +XXX,XX @@ typedef struct GraphLockableMainloop { } GraphLockableMainloop;
62
- /*
152
*/
63
- * The filter node may be in process of bdrv_append(), which firstly do
153
#define GMLML_OBJ_() (&(GraphLockableMainloop) { })
64
- * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
154
65
- * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
155
-static inline GraphLockableMainloop *
66
- * let's require nothing during bdrv_append() and refresh permissions
156
+/*
67
- * after it (see bdrv_backup_top_append()).
157
+ * This is not marked as TSA_ACQUIRE() because TSA doesn't understand the
68
- */
158
+ * cleanup attribute and would therefore complain that the graph is never
69
- *nperm = 0;
159
+ * unlocked. TSA_ASSERT() makes sure that the following calls know that we
70
- *nshared = BLK_PERM_ALL;
160
+ * hold the lock while unlocking is left unchecked.
71
- return;
161
+ */
72
- }
162
+static inline GraphLockableMainloop * TSA_ASSERT(graph_lock) TSA_NO_TSA
73
-
163
graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x)
74
if (!(role & BDRV_CHILD_FILTERED)) {
75
/*
76
* Target child
77
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
78
}
79
appended = true;
80
81
- /*
82
- * bdrv_append() finished successfully, now we can require permissions
83
- * we want.
84
- */
85
- state->active = true;
86
- ret = bdrv_child_refresh_perms(top, top->backing, errp);
87
- if (ret < 0) {
88
- error_prepend(errp, "Cannot set permissions for backup-top filter: ");
89
- goto fail;
90
- }
91
-
92
state->cluster_size = cluster_size;
93
state->bcs = block_copy_state_new(top->backing, state->target,
94
cluster_size, perf->use_copy_range,
95
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
96
97
fail:
98
if (appended) {
99
- state->active = false;
100
bdrv_backup_top_drop(top);
101
} else {
102
bdrv_unref(top);
103
@@ -XXX,XX +XXX,XX @@ void bdrv_backup_top_drop(BlockDriverState *bs)
104
{
164
{
105
BDRVBackupTopState *s = bs->opaque;
165
bdrv_graph_rdlock_main_loop();
106
166
return x;
107
- bdrv_drained_begin(bs);
108
+ bdrv_drop_filter(bs, &error_abort);
109
110
block_copy_state_free(s->bcs);
111
112
- s->active = false;
113
- bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
114
- bdrv_replace_node(bs, bs->backing->bs, &error_abort);
115
- bdrv_set_backing_hd(bs, NULL, &error_abort);
116
-
117
- bdrv_drained_end(bs);
118
-
119
bdrv_unref(bs);
120
}
167
}
121
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
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
122
index XXXXXXX..XXXXXXX 100644
175
index XXXXXXX..XXXXXXX 100644
123
--- a/tests/qemu-iotests/283.out
176
--- a/block/graph-lock.c
124
+++ b/tests/qemu-iotests/283.out
177
+++ b/block/graph-lock.c
125
@@ -XXX,XX +XXX,XX @@
178
@@ -XXX,XX +XXX,XX @@
126
{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}}
179
#include "block/block.h"
127
{"return": {}}
180
#include "block/block_int.h"
128
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
181
129
-{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}}
182
+/* Dummy lock object to use for Thread Safety Analysis (TSA) */
130
+{"error": {"class": "GenericError", "desc": "Cannot append backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}}
183
+BdrvGraphLock graph_lock;
131
184
+
132
=== backup-top should be gone after job-finalize ===
185
/* Protects the list of aiocontext and orphaned_reader_count */
186
static QemuMutex aio_context_list_lock;
133
187
134
--
188
--
135
2.30.2
189
2.38.1
136
137
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
2
Message-Id: <20221207131838.239125-16-kwolf@redhat.com>
3
Rewrite bdrv_check_perm(), bdrv_abort_perm_update() and bdrv_set_perm()
3
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
to update nodes in topological sort order instead of simple DFS. With
5
topologically sorted nodes, we update a node only when all its parents
6
already updated. With DFS it's not so.
7
8
Consider the following example:
9
10
A -+
11
| |
12
| v
13
| B
14
| |
15
v |
16
C<-+
17
18
A is parent for B and C, B is parent for C.
19
20
Obviously, to update permissions, we should go in order A B C, so, when
21
we update C, all parent permissions already updated. But with current
22
approach (simple recursion) we can update in sequence A C B C (C is
23
updated twice). On first update of C, we consider old B permissions, so
24
doing wrong thing. If it succeed, all is OK, on second C update we will
25
finish with correct graph. But if the wrong thing failed, we break the
26
whole process for no reason (it's possible that updated B permission
27
will be less strict, but we will never check it).
28
29
Also new approach gives a way to simultaneously and correctly update
30
several nodes, we just need to run bdrv_topological_dfs() several times
31
to add all nodes and their subtrees into one topologically sorted list
32
(next patch will update bdrv_replace_node() in this manner).
33
34
Test test_parallel_perm_update() is now passing, so move it out of
35
debugging "if".
36
37
We also need to support ignore_children in
38
bdrv_parent_perms_conflict()
39
40
For test 283 order of conflicting parents check is changed.
41
42
Note also that in bdrv_check_perm() we don't check for parents conflict
43
at root bs, as we may be in the middle of permission update in
44
bdrv_reopen_multiple(). bdrv_reopen_multiple() will be updated soon.
45
46
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
47
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
48
Message-Id: <20210428151804.439460-14-vsementsov@virtuozzo.com>
49
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
50
---
5
---
51
block.c | 116 +++++++++++++++++++++++++------
6
include/block/block_int-common.h | 4 ++--
52
tests/unit/test-bdrv-graph-mod.c | 4 +-
7
include/block/graph-lock.h | 4 ++--
53
tests/qemu-iotests/283.out | 2 +-
8
block.c | 4 ++--
54
3 files changed, 99 insertions(+), 23 deletions(-)
9
3 files changed, 6 insertions(+), 6 deletions(-)
55
10
11
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
12
index XXXXXXX..XXXXXXX 100644
13
--- a/include/block/block_int-common.h
14
+++ b/include/block/block_int-common.h
15
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
16
void (*activate)(BdrvChild *child, Error **errp);
17
int (*inactivate)(BdrvChild *child);
18
19
- void (*attach)(BdrvChild *child);
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
56
diff --git a/block.c b/block.c
47
diff --git a/block.c b/block.c
57
index XXXXXXX..XXXXXXX 100644
48
index XXXXXXX..XXXXXXX 100644
58
--- a/block.c
49
--- a/block.c
59
+++ b/block.c
50
+++ b/block.c
60
@@ -XXX,XX +XXX,XX @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
51
@@ -XXX,XX +XXX,XX @@ static void bdrv_inherited_options(BdrvChildRole role, bool parent_is_format,
61
return false;
52
*child_flags = flags;
62
}
53
}
63
54
64
-static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
55
-static void bdrv_child_cb_attach(BdrvChild *child)
65
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
56
+static void GRAPH_WRLOCK bdrv_child_cb_attach(BdrvChild *child)
66
+ GSList *ignore_children,
67
+ Error **errp)
68
{
57
{
69
BdrvChild *a, *b;
58
BlockDriverState *bs = child->opaque;
70
59
71
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
60
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child)
72
* directions.
73
*/
74
QLIST_FOREACH(a, &bs->parents, next_parent) {
75
+ if (g_slist_find(ignore_children, a)) {
76
+ continue;
77
+ }
78
+
79
QLIST_FOREACH(b, &bs->parents, next_parent) {
80
- if (a == b) {
81
+ if (a == b || g_slist_find(ignore_children, b)) {
82
continue;
83
}
84
85
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
86
}
61
}
87
}
62
}
88
63
89
+/*
64
-static void bdrv_child_cb_detach(BdrvChild *child)
90
+ * Adds the whole subtree of @bs (including @bs itself) to the @list (except for
65
+static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child)
91
+ * nodes that are already in the @list, of course) so that final list is
92
+ * topologically sorted. Return the result (GSList @list object is updated, so
93
+ * don't use old reference after function call).
94
+ *
95
+ * On function start @list must be already topologically sorted and for any node
96
+ * in the @list the whole subtree of the node must be in the @list as well. The
97
+ * simplest way to satisfy this criteria: use only result of
98
+ * bdrv_topological_dfs() or NULL as @list parameter.
99
+ */
100
+static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found,
101
+ BlockDriverState *bs)
102
+{
103
+ BdrvChild *child;
104
+ g_autoptr(GHashTable) local_found = NULL;
105
+
106
+ if (!found) {
107
+ assert(!list);
108
+ found = local_found = g_hash_table_new(NULL, NULL);
109
+ }
110
+
111
+ if (g_hash_table_contains(found, bs)) {
112
+ return list;
113
+ }
114
+ g_hash_table_add(found, bs);
115
+
116
+ QLIST_FOREACH(child, &bs->children, next) {
117
+ list = bdrv_topological_dfs(list, found, child->bs);
118
+ }
119
+
120
+ return g_slist_prepend(list, bs);
121
+}
122
+
123
static void bdrv_child_set_perm_commit(void *opaque)
124
{
66
{
125
BdrvChild *c = opaque;
67
BlockDriverState *bs = child->opaque;
126
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
127
* A call to this function must always be followed by a call to bdrv_set_perm()
128
* or bdrv_abort_perm_update().
129
*/
130
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
131
- uint64_t cumulative_perms,
132
- uint64_t cumulative_shared_perms,
133
- GSList *ignore_children, Error **errp)
134
+static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
135
+ uint64_t cumulative_perms,
136
+ uint64_t cumulative_shared_perms,
137
+ GSList *ignore_children, Error **errp)
138
{
139
BlockDriver *drv = bs->drv;
140
BdrvChild *c;
141
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
142
/* Check all children */
143
QLIST_FOREACH(c, &bs->children, next) {
144
uint64_t cur_perm, cur_shared;
145
- GSList *cur_ignore_children;
146
147
bdrv_child_perm(bs, c->bs, c, c->role, q,
148
cumulative_perms, cumulative_shared_perms,
149
&cur_perm, &cur_shared);
150
+ bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
151
+ }
152
+
153
+ return 0;
154
+}
155
+
156
+static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
157
+ uint64_t cumulative_perms,
158
+ uint64_t cumulative_shared_perms,
159
+ GSList *ignore_children, Error **errp)
160
+{
161
+ int ret;
162
+ BlockDriverState *root = bs;
163
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, root);
164
165
- cur_ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
166
- ret = bdrv_check_update_perm(c->bs, q, cur_perm, cur_shared,
167
- cur_ignore_children, errp);
168
- g_slist_free(cur_ignore_children);
169
+ for ( ; list; list = list->next) {
170
+ bs = list->data;
171
+
172
+ if (bs != root) {
173
+ if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
174
+ return -EINVAL;
175
+ }
176
+
177
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
178
+ &cumulative_shared_perms);
179
+ }
180
+
181
+ ret = bdrv_node_check_perm(bs, q, cumulative_perms,
182
+ cumulative_shared_perms,
183
+ ignore_children, errp);
184
if (ret < 0) {
185
return ret;
186
}
187
-
188
- bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
189
}
190
191
return 0;
192
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
193
* Notifies drivers that after a previous bdrv_check_perm() call, the
194
* permission update is not performed and any preparations made for it (e.g.
195
* taken file locks) need to be undone.
196
- *
197
- * This function recursively notifies all child nodes.
198
*/
199
-static void bdrv_abort_perm_update(BlockDriverState *bs)
200
+static void bdrv_node_abort_perm_update(BlockDriverState *bs)
201
{
202
BlockDriver *drv = bs->drv;
203
BdrvChild *c;
204
@@ -XXX,XX +XXX,XX @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
205
206
QLIST_FOREACH(c, &bs->children, next) {
207
bdrv_child_set_perm_abort(c);
208
- bdrv_abort_perm_update(c->bs);
209
}
210
}
211
212
-static void bdrv_set_perm(BlockDriverState *bs)
213
+static void bdrv_abort_perm_update(BlockDriverState *bs)
214
+{
215
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
216
+
217
+ for ( ; list; list = list->next) {
218
+ bdrv_node_abort_perm_update((BlockDriverState *)list->data);
219
+ }
220
+}
221
+
222
+static void bdrv_node_set_perm(BlockDriverState *bs)
223
{
224
uint64_t cumulative_perms, cumulative_shared_perms;
225
BlockDriver *drv = bs->drv;
226
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs)
227
/* Update all children */
228
QLIST_FOREACH(c, &bs->children, next) {
229
bdrv_child_set_perm_commit(c);
230
- bdrv_set_perm(c->bs);
231
+ }
232
+}
233
+
234
+static void bdrv_set_perm(BlockDriverState *bs)
235
+{
236
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
237
+
238
+ for ( ; list; list = list->next) {
239
+ bdrv_node_set_perm((BlockDriverState *)list->data);
240
}
241
}
242
243
@@ -XXX,XX +XXX,XX @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
244
int ret;
245
uint64_t perm, shared_perm;
246
247
- if (bdrv_parent_perms_conflict(bs, errp)) {
248
+ if (bdrv_parent_perms_conflict(bs, NULL, errp)) {
249
return -EPERM;
250
}
251
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
252
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
253
index XXXXXXX..XXXXXXX 100644
254
--- a/tests/unit/test-bdrv-graph-mod.c
255
+++ b/tests/unit/test-bdrv-graph-mod.c
256
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
257
g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree);
258
g_test_add_func("/bdrv-graph-mod/should-update-child",
259
test_should_update_child);
260
+ g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
261
+ test_parallel_perm_update);
262
263
if (debug) {
264
g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
265
test_parallel_exclusive_write);
266
- g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
267
- test_parallel_perm_update);
268
g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
269
test_append_greedy_filter);
270
}
271
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
272
index XXXXXXX..XXXXXXX 100644
273
--- a/tests/qemu-iotests/283.out
274
+++ b/tests/qemu-iotests/283.out
275
@@ -XXX,XX +XXX,XX @@
276
{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}}
277
{"return": {}}
278
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
279
-{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by other as 'image', which uses 'write' on base"}}
280
+{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}}
281
282
=== backup-top should be gone after job-finalize ===
283
68
284
--
69
--
285
2.30.2
70
2.38.1
286
287
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
1
For a successful conversion of an image, we must make sure that its
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
content doesn't change during the conversion.
3
2
4
A special case of this is using the same image file both as the source
3
Take the rdlock already, before we add the assertions.
5
and as the destination. If both input and output format are raw, the
6
operation would just be useless work, with other formats it is a sure
7
way to destroy the image. This will now fail because the image file
8
can't be opened a second time for the output when opening it for the
9
input has already acquired file locks to unshare BLK_PERM_WRITE.
10
4
11
Nevertheless, if there is some reason in a special case why it is
5
All these functions either read the graph recursively, or call
12
actually okay to allow writes to the image while it is being converted,
6
BlockDriver callbacks that will eventually need to be protected by the
13
-U can still be used to force sharing all permissions.
7
graph rdlock.
14
8
15
Note that for most image formats, BLK_PERM_WRITE would already be
9
Do it now to all functions together, because many of these recursively
16
unshared by the format driver, so this only really makes a difference
10
call each other.
17
for raw source images (but any output format).
18
11
19
Reported-by: Xueqiang Wei <xuwei@redhat.com>
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>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
Reviewed-by: Eric Blake <eblake@redhat.com>
19
Message-Id: <20221207131838.239125-18-kwolf@redhat.com>
22
Message-Id: <20210422164344.283389-3-kwolf@redhat.com>
20
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
23
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
21
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
23
---
26
qemu-img.c | 2 +-
24
block/coroutines.h | 2 +-
27
1 file changed, 1 insertion(+), 1 deletion(-)
25
include/block/block-io.h | 53 +++++++++++++++++++++++-----------------
26
2 files changed, 32 insertions(+), 23 deletions(-)
28
27
29
diff --git a/qemu-img.c b/qemu-img.c
28
diff --git a/block/coroutines.h b/block/coroutines.h
30
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
31
--- a/qemu-img.c
30
--- a/block/coroutines.h
32
+++ b/qemu-img.c
31
+++ b/block/coroutines.h
33
@@ -XXX,XX +XXX,XX @@ static void set_rate_limit(BlockBackend *blk, int64_t rate_limit)
32
@@ -XXX,XX +XXX,XX @@ nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
34
33
* the "I/O or GS" API.
35
static int img_convert(int argc, char **argv)
34
*/
36
{
35
37
- int c, bs_i, flags, src_flags = 0;
36
-int co_wrapper_mixed
38
+ int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE;
37
+int co_wrapper_mixed_bdrv_rdlock
39
const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe",
38
bdrv_common_block_status_above(BlockDriverState *bs,
40
*src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL,
39
BlockDriverState *base,
41
*out_filename, *out_baseimg_param, *snapshot_name = NULL;
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
/**
42
--
119
--
43
2.30.2
120
2.38.1
44
45
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
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.
2
4
3
To be used in further commit.
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.
4
8
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20221207131838.239125-19-kwolf@redhat.com>
7
Message-Id: <20210428151804.439460-28-vsementsov@virtuozzo.com>
11
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
13
---
10
include/block/block.h | 3 ++-
14
block/coroutines.h | 17 ++++++++++-------
11
block.c | 9 ++++-----
15
include/block/block_int-common.h | 20 +++++++++-----------
12
block/io.c | 31 +++++++++++++++++++++++++++++--
16
block.c | 2 ++
13
3 files changed, 35 insertions(+), 8 deletions(-)
17
block/io.c | 2 ++
18
4 files changed, 23 insertions(+), 18 deletions(-)
14
19
15
diff --git a/include/block/block.h b/include/block/block.h
20
diff --git a/block/coroutines.h b/block/coroutines.h
16
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/block.h
22
--- a/block/coroutines.h
18
+++ b/include/block/block.h
23
+++ b/block/coroutines.h
19
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@
20
#include "block/dirty-bitmap.h"
25
* the I/O API.
21
#include "block/blockjob.h"
26
*/
22
#include "qemu/hbitmap.h"
27
23
+#include "qemu/transactions.h"
28
-int coroutine_fn bdrv_co_check(BlockDriverState *bs,
24
29
- BdrvCheckResult *res, BdrvCheckMode fix);
25
/*
30
-int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp);
26
* generated_co_wrapper
31
+int coroutine_fn GRAPH_RDLOCK
27
@@ -XXX,XX +XXX,XX @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
32
+bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
28
BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts,
33
+
29
BlockDriverState *in_bs, Error **errp);
34
+int coroutine_fn GRAPH_RDLOCK
30
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
35
+bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp);
31
-void bdrv_refresh_limits(BlockDriverState *bs, Error **errp);
36
32
+void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp);
37
int coroutine_fn
33
int bdrv_commit(BlockDriverState *bs);
38
bdrv_co_common_block_status_above(BlockDriverState *bs,
34
int bdrv_make_empty(BdrvChild *c, Error **errp);
39
@@ -XXX,XX +XXX,XX @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
35
int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
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
36
diff --git a/block.c b/block.c
100
diff --git a/block.c b/block.c
37
index XXXXXXX..XXXXXXX 100644
101
index XXXXXXX..XXXXXXX 100644
38
--- a/block.c
102
--- a/block.c
39
+++ b/block.c
103
+++ b/block.c
40
@@ -XXX,XX +XXX,XX @@
104
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_check(BlockDriverState *bs,
41
#include "qemu/timer.h"
105
BdrvCheckResult *res, BdrvCheckMode fix)
42
#include "qemu/cutils.h"
106
{
43
#include "qemu/id.h"
107
IO_CODE();
44
-#include "qemu/transactions.h"
108
+ assert_bdrv_graph_readable();
45
#include "block/coroutines.h"
109
if (bs->drv == NULL) {
46
110
return -ENOMEDIUM;
47
#ifdef CONFIG_BSD
48
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
49
return ret;
50
}
111
}
51
112
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp)
52
- bdrv_refresh_limits(bs, &local_err);
113
IO_CODE();
53
+ bdrv_refresh_limits(bs, NULL, &local_err);
114
54
if (local_err) {
115
assert(!(bs->open_flags & BDRV_O_INACTIVE));
55
error_propagate(errp, local_err);
116
+ assert_bdrv_graph_readable();
56
return -EINVAL;
117
57
@@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
118
if (bs->drv->bdrv_co_invalidate_cache) {
58
}
119
bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
59
60
out:
61
- bdrv_refresh_limits(bs, NULL);
62
+ bdrv_refresh_limits(bs, NULL, NULL);
63
64
return ret;
65
}
66
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
67
bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
68
}
69
70
- bdrv_refresh_limits(bs, NULL);
71
+ bdrv_refresh_limits(bs, NULL, NULL);
72
}
73
74
/*
75
@@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
76
out:
77
tran_finalize(tran, ret);
78
79
- bdrv_refresh_limits(bs_top, NULL);
80
+ bdrv_refresh_limits(bs_top, NULL, NULL);
81
82
return ret;
83
}
84
diff --git a/block/io.c b/block/io.c
120
diff --git a/block/io.c b/block/io.c
85
index XXXXXXX..XXXXXXX 100644
121
index XXXXXXX..XXXXXXX 100644
86
--- a/block/io.c
122
--- a/block/io.c
87
+++ b/block/io.c
123
+++ b/block/io.c
88
@@ -XXX,XX +XXX,XX @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
124
@@ -XXX,XX +XXX,XX @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
89
dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov);
125
BlockDriverState *child_bs = bdrv_primary_bs(bs);
90
}
126
int ret;
91
127
IO_CODE();
92
-void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
128
+ assert_bdrv_graph_readable();
93
+typedef struct BdrvRefreshLimitsState {
129
94
+ BlockDriverState *bs;
130
ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL);
95
+ BlockLimits old_bl;
131
if (ret < 0) {
96
+} BdrvRefreshLimitsState;
132
@@ -XXX,XX +XXX,XX @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
97
+
133
BlockDriverState *child_bs = bdrv_primary_bs(bs);
98
+static void bdrv_refresh_limits_abort(void *opaque)
134
int ret;
99
+{
135
IO_CODE();
100
+ BdrvRefreshLimitsState *s = opaque;
136
+ assert_bdrv_graph_readable();
101
+
137
102
+ s->bs->bl = s->old_bl;
138
ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL);
103
+}
139
if (ret < 0) {
104
+
105
+static TransactionActionDrv bdrv_refresh_limits_drv = {
106
+ .abort = bdrv_refresh_limits_abort,
107
+ .clean = g_free,
108
+};
109
+
110
+/* @tran is allowed to be NULL, in this case no rollback is possible. */
111
+void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp)
112
{
113
ERRP_GUARD();
114
BlockDriver *drv = bs->drv;
115
BdrvChild *c;
116
bool have_limits;
117
118
+ if (tran) {
119
+ BdrvRefreshLimitsState *s = g_new(BdrvRefreshLimitsState, 1);
120
+ *s = (BdrvRefreshLimitsState) {
121
+ .bs = bs,
122
+ .old_bl = bs->bl,
123
+ };
124
+ tran_add(tran, &bdrv_refresh_limits_drv, s);
125
+ }
126
+
127
memset(&bs->bl, 0, sizeof(bs->bl));
128
129
if (!drv) {
130
@@ -XXX,XX +XXX,XX @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
131
QLIST_FOREACH(c, &bs->children, next) {
132
if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW))
133
{
134
- bdrv_refresh_limits(c->bs, errp);
135
+ bdrv_refresh_limits(c->bs, tran, errp);
136
if (*errp) {
137
return;
138
}
139
--
140
--
140
2.30.2
141
2.38.1
141
142
diff view generated by jsdifflib