1
The following changes since commit 0274f45bdef73283f2c213610f11d4e5dcba43b6:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-4.1-pull-request' into staging (2019-07-19 09:44:43 +0100)
3
Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.12-pull-request' into staging (2017-12-22 00:11:36 +0000)
4
4
5
are available in the Git repository at:
5
are available in the git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to 49278ec065da3fbf90f7effcde3b39ac606b2e9e:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
iotests: Test quitting with job on throttled node (2019-07-19 15:17:55 +0200)
11
block: Keep nodes drained between reopen_queue/multiple (2017-12-22 15:05:32 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
16
- block: Fix forbidden use of polling in drained_end
17
- block: Don't wait for I/O throttling while exiting QEMU
18
- iotests: Use read-zeroes for the null driver to be Valgrind-friendly
19
15
20
----------------------------------------------------------------
16
----------------------------------------------------------------
21
Andrey Shinkevich (1):
17
Doug Gale (1):
22
iotests: Set read-zeroes on in null block driver for Valgrind
18
nvme: Add tracing
23
19
24
Max Reitz (12):
20
Edgar Kaziakhmedov (1):
25
block: Introduce BdrvChild.parent_quiesce_counter
21
qcow2: get rid of qcow2_backing_read1 routine
26
tests: Add job commit by drained_end test
27
block: Add @drained_end_counter
28
block: Make bdrv_parent_drained_[^_]*() static
29
tests: Lock AioContexts in test-block-iothread
30
block: Do not poll in bdrv_do_drained_end()
31
tests: Extend commit by drained_end test
32
block: Loop unsafely in bdrv*drained_end()
33
iotests: Add @has_quit to vm.shutdown()
34
iotests: Test commit with a filter on the chain
35
vl: Drain before (block) job cancel when quitting
36
iotests: Test quitting with job on throttled node
37
22
38
include/block/block.h | 42 ++++++++----
23
Fam Zheng (2):
39
include/block/block_int.h | 15 ++++-
24
block: Open backing image in force share mode for size probe
40
block.c | 52 ++++++++++-----
25
block: Remove unused bdrv_requests_pending
41
block/block-backend.c | 6 +-
42
block/io.c | 134 +++++++++++++++++++++++++++----------
43
blockjob.c | 2 +-
44
tests/test-bdrv-drain.c | 147 ++++++++++++++++++++++++++++++++++++++++
45
tests/test-block-iothread.c | 40 +++++++----
46
vl.c | 11 +++
47
python/qemu/machine.py | 5 +-
48
tests/qemu-iotests/040 | 40 ++++++++++-
49
tests/qemu-iotests/040.out | 4 +-
50
tests/qemu-iotests/051 | 10 +--
51
tests/qemu-iotests/051.pc.out | 10 +--
52
tests/qemu-iotests/093 | 9 +--
53
tests/qemu-iotests/136 | 1 +
54
tests/qemu-iotests/186 | 20 +++---
55
tests/qemu-iotests/186.out | 152 +++++++++++++++++++++---------------------
56
tests/qemu-iotests/218 | 55 ++++++++++++++-
57
tests/qemu-iotests/218.out | 4 ++
58
tests/qemu-iotests/227 | 4 +-
59
tests/qemu-iotests/227.out | 4 +-
60
tests/qemu-iotests/238 | 2 +-
61
tests/qemu-iotests/240 | 8 +--
62
tests/qemu-iotests/255 | 2 +-
63
25 files changed, 576 insertions(+), 203 deletions(-)
64
26
27
John Snow (1):
28
iotests: fix 197 for vpc
29
30
Kevin Wolf (27):
31
block: Formats don't need CONSISTENT_READ with NO_IO
32
block: Make bdrv_drain_invoke() recursive
33
block: Call .drain_begin only once in bdrv_drain_all_begin()
34
test-bdrv-drain: Test BlockDriver callbacks for drain
35
block: bdrv_drain_recurse(): Remove unused begin parameter
36
block: Don't wait for requests in bdrv_drain*_end()
37
block: Unify order in drain functions
38
block: Don't acquire AioContext in hmp_qemu_io()
39
block: Document that x-blockdev-change breaks quorum children list
40
block: Assert drain_all is only called from main AioContext
41
block: Make bdrv_drain() driver callbacks non-recursive
42
test-bdrv-drain: Test callback for bdrv_drain
43
test-bdrv-drain: Test bs->quiesce_counter
44
blockjob: Pause job on draining any job BDS
45
test-bdrv-drain: Test drain vs. block jobs
46
block: Don't block_job_pause_all() in bdrv_drain_all()
47
block: Nested drain_end must still call callbacks
48
test-bdrv-drain: Test nested drain sections
49
block: Don't notify parents in drain call chain
50
block: Add bdrv_subtree_drained_begin/end()
51
test-bdrv-drain: Tests for bdrv_subtree_drain
52
test-bdrv-drain: Test behaviour in coroutine context
53
test-bdrv-drain: Recursive draining with multiple parents
54
block: Allow graph changes in subtree drained section
55
test-bdrv-drain: Test graph changes in drained section
56
commit: Simplify reopen of base
57
block: Keep nodes drained between reopen_queue/multiple
58
59
Thomas Huth (3):
60
block: Remove the obsolete -drive boot=on|off parameter
61
block: Remove the deprecated -hdachs option
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
63
64
qapi/block-core.json | 4 +
65
block/qcow2.h | 3 -
66
include/block/block.h | 15 +-
67
include/block/block_int.h | 6 +-
68
block.c | 75 ++++-
69
block/commit.c | 8 +-
70
block/io.c | 164 +++++++---
71
block/qcow2.c | 51 +--
72
block/replication.c | 6 +
73
blockdev.c | 11 -
74
blockjob.c | 22 +-
75
hmp.c | 6 -
76
hw/block/nvme.c | 349 +++++++++++++++++----
77
qemu-io-cmds.c | 3 +
78
tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++
79
vl.c | 86 +-----
80
hw/block/trace-events | 93 ++++++
81
qemu-doc.texi | 29 +-
82
qemu-options.hx | 19 +-
83
tests/Makefile.include | 2 +
84
tests/qemu-iotests/197 | 4 +
85
tests/qemu-iotests/common.filter | 3 +-
86
22 files changed, 1294 insertions(+), 316 deletions(-)
87
create mode 100644 tests/test-bdrv-drain.c
88
diff view generated by jsdifflib
New patch
1
Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently
2
in use as a mirror target. It is not enough for image formats, though,
3
as these still unconditionally request BLK_PERM_CONSISTENT_READ.
1
4
5
As this permission is geared towards whether the guest-visible data is
6
consistent, and has no impact on whether the metadata is sane, and
7
'qemu-img info' does not read guest-visible data (except for the raw
8
format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there
9
is not going to be any guest I/O performed, regardless of image format.
10
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block.c | 6 +++++-
14
1 file changed, 5 insertions(+), 1 deletion(-)
15
16
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
19
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
21
assert(role == &child_backing || role == &child_file);
22
23
if (!backing) {
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
25
+
26
/* Apart from the modifications below, the same permissions are
27
* forwarded and left alone as for filters */
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
30
31
/* bs->file always needs to be consistent because of the metadata. We
32
* can never allow other users to resize or write to it. */
33
- perm |= BLK_PERM_CONSISTENT_READ;
34
+ if (!(flags & BDRV_O_NO_IO)) {
35
+ perm |= BLK_PERM_CONSISTENT_READ;
36
+ }
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
38
} else {
39
/* We want consistent read from backing files if the parent needs it.
40
--
41
2.13.6
42
43
diff view generated by jsdifflib
New patch
1
From: John Snow <jsnow@redhat.com>
1
2
3
VPC has some difficulty creating geometries of particular size.
4
However, we can indeed force it to use a literal one, so let's
5
do that for the sake of test 197, which is testing some specific
6
offsets.
7
8
Signed-off-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
13
---
14
tests/qemu-iotests/197 | 4 ++++
15
tests/qemu-iotests/common.filter | 3 ++-
16
2 files changed, 6 insertions(+), 1 deletion(-)
17
18
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/197
21
+++ b/tests/qemu-iotests/197
22
@@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ==='
23
echo
24
25
# Prep the images
26
+# VPC rounds image sizes to a specific geometry, force a specific size.
27
+if [ "$IMGFMT" = "vpc" ]; then
28
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
29
+fi
30
_make_test_img 4G
31
$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io
32
IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \
33
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
34
index XXXXXXX..XXXXXXX 100644
35
--- a/tests/qemu-iotests/common.filter
36
+++ b/tests/qemu-iotests/common.filter
37
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
38
-e "s# log_size=[0-9]\\+##g" \
39
-e "s# refcount_bits=[0-9]\\+##g" \
40
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
41
- -e "s# iter-time=[0-9]\\+##g"
42
+ -e "s# iter-time=[0-9]\\+##g" \
43
+ -e "s# force_size=\\(on\\|off\\)##g"
44
}
45
46
_filter_img_info()
47
--
48
2.13.6
49
50
diff view generated by jsdifflib
New patch
1
This change separates bdrv_drain_invoke(), which calls the BlockDriver
2
drain callbacks, from bdrv_drain_recurse(). Instead, the function
3
performs its own recursion now.
1
4
5
One reason for this is that bdrv_drain_recurse() can be called multiple
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
7
once. The separation is necessary to fix this bug.
8
9
The other reason is that we intend to go to a model where we call all
10
driver callbacks first, and only then start polling. This is not fully
11
achieved yet with this patch, as bdrv_drain_invoke() contains a
12
BDRV_POLL_WHILE() loop for the block driver callbacks, which can still
13
call callbacks for any unrelated event. It's a step in this direction
14
anyway.
15
16
Cc: qemu-stable@nongnu.org
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
---
20
block/io.c | 14 +++++++++++---
21
1 file changed, 11 insertions(+), 3 deletions(-)
22
23
diff --git a/block/io.c b/block/io.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/block/io.c
26
+++ b/block/io.c
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
28
bdrv_wakeup(bs);
29
}
30
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
33
{
34
+ BdrvChild *child, *tmp;
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
36
37
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
39
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
40
bdrv_coroutine_enter(bs, data.co);
41
BDRV_POLL_WHILE(bs, !data.done);
42
+
43
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
44
+ bdrv_drain_invoke(child->bs, begin);
45
+ }
46
}
47
48
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
49
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
50
BdrvChild *child, *tmp;
51
bool waited;
52
53
- /* Ensure any pending metadata writes are submitted to bs->file. */
54
- bdrv_drain_invoke(bs, begin);
55
-
56
/* Wait for drained requests to finish */
57
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
58
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
60
bdrv_parent_drained_begin(bs);
61
}
62
63
+ bdrv_drain_invoke(bs, true);
64
bdrv_drain_recurse(bs, true);
65
}
66
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
68
}
69
70
bdrv_parent_drained_end(bs);
71
+ bdrv_drain_invoke(bs, false);
72
bdrv_drain_recurse(bs, false);
73
aio_enable_external(bdrv_get_aio_context(bs));
74
}
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
76
aio_context_acquire(aio_context);
77
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
78
if (aio_context == bdrv_get_aio_context(bs)) {
79
+ /* FIXME Calling this multiple times is wrong */
80
+ bdrv_drain_invoke(bs, true);
81
waited |= bdrv_drain_recurse(bs, true);
82
}
83
}
84
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
85
aio_context_acquire(aio_context);
86
aio_enable_external(aio_context);
87
bdrv_parent_drained_end(bs);
88
+ bdrv_drain_invoke(bs, false);
89
bdrv_drain_recurse(bs, false);
90
aio_context_release(aio_context);
91
}
92
--
93
2.13.6
94
95
diff view generated by jsdifflib
New patch
1
bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver
2
callback inside its polling loop. This means that how many times it got
3
called for each node depended on long it had to poll the event loop.
1
4
5
This is obviously not right and results in nodes that stay drained even
6
after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per
7
node.
8
9
Fix bdrv_drain_all_begin() to call the callback only once, too.
10
11
Cc: qemu-stable@nongnu.org
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
---
15
block/io.c | 3 +--
16
1 file changed, 1 insertion(+), 2 deletions(-)
17
18
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/io.c
21
+++ b/block/io.c
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
23
aio_context_acquire(aio_context);
24
bdrv_parent_drained_begin(bs);
25
aio_disable_external(aio_context);
26
+ bdrv_drain_invoke(bs, true);
27
aio_context_release(aio_context);
28
29
if (!g_slist_find(aio_ctxs, aio_context)) {
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
31
aio_context_acquire(aio_context);
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
33
if (aio_context == bdrv_get_aio_context(bs)) {
34
- /* FIXME Calling this multiple times is wrong */
35
- bdrv_drain_invoke(bs, true);
36
waited |= bdrv_drain_recurse(bs, true);
37
}
38
}
39
--
40
2.13.6
41
42
diff view generated by jsdifflib
New patch
1
This adds a test case that the BlockDriver callbacks for drain are
2
called in bdrv_drained_all_begin/end(), and that both of them are called
3
exactly once.
1
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
10
tests/Makefile.include | 2 +
11
2 files changed, 139 insertions(+)
12
create mode 100644 tests/test-bdrv-drain.c
13
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
15
new file mode 100644
16
index XXXXXXX..XXXXXXX
17
--- /dev/null
18
+++ b/tests/test-bdrv-drain.c
19
@@ -XXX,XX +XXX,XX @@
20
+/*
21
+ * Block node draining tests
22
+ *
23
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
24
+ *
25
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
26
+ * of this software and associated documentation files (the "Software"), to deal
27
+ * in the Software without restriction, including without limitation the rights
28
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29
+ * copies of the Software, and to permit persons to whom the Software is
30
+ * furnished to do so, subject to the following conditions:
31
+ *
32
+ * The above copyright notice and this permission notice shall be included in
33
+ * all copies or substantial portions of the Software.
34
+ *
35
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41
+ * THE SOFTWARE.
42
+ */
43
+
44
+#include "qemu/osdep.h"
45
+#include "block/block.h"
46
+#include "sysemu/block-backend.h"
47
+#include "qapi/error.h"
48
+
49
+typedef struct BDRVTestState {
50
+ int drain_count;
51
+} BDRVTestState;
52
+
53
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
54
+{
55
+ BDRVTestState *s = bs->opaque;
56
+ s->drain_count++;
57
+}
58
+
59
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
60
+{
61
+ BDRVTestState *s = bs->opaque;
62
+ s->drain_count--;
63
+}
64
+
65
+static void bdrv_test_close(BlockDriverState *bs)
66
+{
67
+ BDRVTestState *s = bs->opaque;
68
+ g_assert_cmpint(s->drain_count, >, 0);
69
+}
70
+
71
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
72
+ uint64_t offset, uint64_t bytes,
73
+ QEMUIOVector *qiov, int flags)
74
+{
75
+ /* We want this request to stay until the polling loop in drain waits for
76
+ * it to complete. We need to sleep a while as bdrv_drain_invoke() comes
77
+ * first and polls its result, too, but it shouldn't accidentally complete
78
+ * this request yet. */
79
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
80
+
81
+ return 0;
82
+}
83
+
84
+static BlockDriver bdrv_test = {
85
+ .format_name = "test",
86
+ .instance_size = sizeof(BDRVTestState),
87
+
88
+ .bdrv_close = bdrv_test_close,
89
+ .bdrv_co_preadv = bdrv_test_co_preadv,
90
+
91
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
92
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
93
+};
94
+
95
+static void aio_ret_cb(void *opaque, int ret)
96
+{
97
+ int *aio_ret = opaque;
98
+ *aio_ret = ret;
99
+}
100
+
101
+static void test_drv_cb_drain_all(void)
102
+{
103
+ BlockBackend *blk;
104
+ BlockDriverState *bs;
105
+ BDRVTestState *s;
106
+ BlockAIOCB *acb;
107
+ int aio_ret;
108
+
109
+ QEMUIOVector qiov;
110
+ struct iovec iov = {
111
+ .iov_base = NULL,
112
+ .iov_len = 0,
113
+ };
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
115
+
116
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
117
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
118
+ &error_abort);
119
+ s = bs->opaque;
120
+ blk_insert_bs(blk, bs, &error_abort);
121
+
122
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
123
+ g_assert_cmpint(s->drain_count, ==, 0);
124
+ bdrv_drain_all_begin();
125
+ g_assert_cmpint(s->drain_count, ==, 1);
126
+ bdrv_drain_all_end();
127
+ g_assert_cmpint(s->drain_count, ==, 0);
128
+
129
+ /* Now do the same while a request is pending */
130
+ aio_ret = -EINPROGRESS;
131
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
132
+ g_assert(acb != NULL);
133
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
134
+
135
+ g_assert_cmpint(s->drain_count, ==, 0);
136
+ bdrv_drain_all_begin();
137
+ g_assert_cmpint(aio_ret, ==, 0);
138
+ g_assert_cmpint(s->drain_count, ==, 1);
139
+ bdrv_drain_all_end();
140
+ g_assert_cmpint(s->drain_count, ==, 0);
141
+
142
+ bdrv_unref(bs);
143
+ blk_unref(blk);
144
+}
145
+
146
+int main(int argc, char **argv)
147
+{
148
+ bdrv_init();
149
+ qemu_init_main_loop(&error_abort);
150
+
151
+ g_test_init(&argc, &argv, NULL);
152
+
153
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
154
+
155
+ return g_test_run();
156
+}
157
diff --git a/tests/Makefile.include b/tests/Makefile.include
158
index XXXXXXX..XXXXXXX 100644
159
--- a/tests/Makefile.include
160
+++ b/tests/Makefile.include
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
162
gcov-files-test-hbitmap-y = util/hbitmap.c
163
check-unit-y += tests/test-hbitmap$(EXESUF)
164
gcov-files-test-hbitmap-y = blockjob.c
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
166
check-unit-y += tests/test-blockjob$(EXESUF)
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
168
check-unit-y += tests/test-x86-cpuid$(EXESUF)
169
@@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
170
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
171
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
172
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
173
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
174
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
175
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
176
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
177
--
178
2.13.6
179
180
diff view generated by jsdifflib
New patch
1
Now that the bdrv_drain_invoke() calls are pulled up to the callers of
2
bdrv_drain_recurse(), the 'begin' parameter isn't needed any more.
1
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
---
7
block/io.c | 12 ++++++------
8
1 file changed, 6 insertions(+), 6 deletions(-)
9
10
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/block/io.c
13
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
15
}
16
}
17
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
19
+static bool bdrv_drain_recurse(BlockDriverState *bs)
20
{
21
BdrvChild *child, *tmp;
22
bool waited;
23
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
24
*/
25
bdrv_ref(bs);
26
}
27
- waited |= bdrv_drain_recurse(bs, begin);
28
+ waited |= bdrv_drain_recurse(bs);
29
if (in_main_loop) {
30
bdrv_unref(bs);
31
}
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
33
}
34
35
bdrv_drain_invoke(bs, true);
36
- bdrv_drain_recurse(bs, true);
37
+ bdrv_drain_recurse(bs);
38
}
39
40
void bdrv_drained_end(BlockDriverState *bs)
41
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
42
43
bdrv_parent_drained_end(bs);
44
bdrv_drain_invoke(bs, false);
45
- bdrv_drain_recurse(bs, false);
46
+ bdrv_drain_recurse(bs);
47
aio_enable_external(bdrv_get_aio_context(bs));
48
}
49
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
51
aio_context_acquire(aio_context);
52
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
53
if (aio_context == bdrv_get_aio_context(bs)) {
54
- waited |= bdrv_drain_recurse(bs, true);
55
+ waited |= bdrv_drain_recurse(bs);
56
}
57
}
58
aio_context_release(aio_context);
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
60
aio_enable_external(aio_context);
61
bdrv_parent_drained_end(bs);
62
bdrv_drain_invoke(bs, false);
63
- bdrv_drain_recurse(bs, false);
64
+ bdrv_drain_recurse(bs);
65
aio_context_release(aio_context);
66
}
67
68
--
69
2.13.6
70
71
diff view generated by jsdifflib
New patch
1
The device is drained, so there is no point in waiting for requests at
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
there.
1
4
5
The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e
6
in order to call the .bdrv_co_drain_end() driver callback. This is now
7
done by a separate bdrv_drain_invoke() call.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
13
block/io.c | 2 --
14
1 file changed, 2 deletions(-)
15
16
diff --git a/block/io.c b/block/io.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/io.c
19
+++ b/block/io.c
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
21
22
bdrv_parent_drained_end(bs);
23
bdrv_drain_invoke(bs, false);
24
- bdrv_drain_recurse(bs);
25
aio_enable_external(bdrv_get_aio_context(bs));
26
}
27
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
29
aio_enable_external(aio_context);
30
bdrv_parent_drained_end(bs);
31
bdrv_drain_invoke(bs, false);
32
- bdrv_drain_recurse(bs);
33
aio_context_release(aio_context);
34
}
35
36
--
37
2.13.6
38
39
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Drain requests are propagated to child nodes, parent nodes and directly
2
to the AioContext. The order in which this happened was different
3
between all combinations of drain/drain_all and begin/end.
2
4
3
Callers can now pass a pointer to an integer that bdrv_drain_invoke()
5
The correct order is to keep children only drained when their parents
4
(and its recursive callees) will increment for every
6
are also drained. This means that at the start of a drained section, the
5
bdrv_drain_invoke_entry() operation they schedule.
7
AioContext needs to be drained first, the parents second and only then
6
bdrv_drain_invoke_entry() in turn will decrement it once it has invoked
8
the children. The correct order for the end of a drained section is the
7
BlockDriver.bdrv_co_drain_end().
9
opposite.
8
10
9
We use atomic operations to access the pointee, because the
11
This patch changes the three other functions to follow the example of
10
bdrv_do_drained_end() caller may wish to end drained sections for
12
bdrv_drained_begin(), which is the only one that got it right.
11
multiple nodes in different AioContexts (bdrv_drain_all_end() does, for
12
example).
13
13
14
This is the first step to moving the polling for BdrvCoDrainData.done to
15
become true out of bdrv_drain_invoke() and into the root drained_end
16
function.
17
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
20
---
16
---
21
block/io.c | 58 +++++++++++++++++++++++++++++++++++++-----------------
17
block/io.c | 12 ++++++++----
22
1 file changed, 40 insertions(+), 18 deletions(-)
18
1 file changed, 8 insertions(+), 4 deletions(-)
23
19
24
diff --git a/block/io.c b/block/io.c
20
diff --git a/block/io.c b/block/io.c
25
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
26
--- a/block/io.c
22
--- a/block/io.c
27
+++ b/block/io.c
23
+++ b/block/io.c
28
@@ -XXX,XX +XXX,XX @@ typedef struct {
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
29
bool poll;
30
BdrvChild *parent;
31
bool ignore_bds_parents;
32
+ int *drained_end_counter;
33
} BdrvCoDrainData;
34
35
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
36
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
37
atomic_mb_set(&data->done, true);
38
bdrv_dec_in_flight(bs);
39
40
- if (data->begin) {
41
+ if (data->drained_end_counter) {
42
+ atomic_dec(data->drained_end_counter);
43
+ }
44
+
45
+ if (data->begin || data->drained_end_counter) {
46
g_free(data);
47
}
48
}
49
50
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
51
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
52
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
53
+ int *drained_end_counter)
54
{
55
BdrvCoDrainData *data;
56
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
58
*data = (BdrvCoDrainData) {
59
.bs = bs,
60
.done = false,
61
- .begin = begin
62
+ .begin = begin,
63
+ .drained_end_counter = drained_end_counter,
64
};
65
66
+ if (!begin && drained_end_counter) {
67
+ atomic_inc(drained_end_counter);
68
+ }
69
+
70
/* Make sure the driver callback completes during the polling phase for
71
* drain_begin. */
72
bdrv_inc_in_flight(bs);
73
data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data);
74
aio_co_schedule(bdrv_get_aio_context(bs), data->co);
75
76
- if (!begin) {
77
+ /*
78
+ * TODO: Drop this and make callers pass @drained_end_counter and poll
79
+ * themselves
80
+ */
81
+ if (!begin && !drained_end_counter) {
82
BDRV_POLL_WHILE(bs, !data->done);
83
g_free(data);
84
}
85
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
86
BdrvChild *parent, bool ignore_bds_parents,
87
bool poll);
88
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
89
- BdrvChild *parent, bool ignore_bds_parents);
90
+ BdrvChild *parent, bool ignore_bds_parents,
91
+ int *drained_end_counter);
92
93
static void bdrv_co_drain_bh_cb(void *opaque)
94
{
95
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
96
data->ignore_bds_parents, data->poll);
97
} else {
98
bdrv_do_drained_end(bs, data->recursive, data->parent,
99
- data->ignore_bds_parents);
100
+ data->ignore_bds_parents,
101
+ data->drained_end_counter);
102
}
103
if (ctx == co_ctx) {
104
aio_context_release(ctx);
105
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
106
bool begin, bool recursive,
107
BdrvChild *parent,
108
bool ignore_bds_parents,
109
- bool poll)
110
+ bool poll,
111
+ int *drained_end_counter)
112
{
113
BdrvCoDrainData data;
114
115
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
116
.parent = parent,
117
.ignore_bds_parents = ignore_bds_parents,
118
.poll = poll,
119
+ .drained_end_counter = drained_end_counter,
120
};
121
+
122
if (bs) {
123
bdrv_inc_in_flight(bs);
124
}
125
@@ -XXX,XX +XXX,XX @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
126
}
127
128
bdrv_parent_drained_begin(bs, parent, ignore_bds_parents);
129
- bdrv_drain_invoke(bs, true);
130
+ bdrv_drain_invoke(bs, true, NULL);
131
}
132
133
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
134
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
135
136
if (qemu_in_coroutine()) {
137
bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents,
138
- poll);
139
+ poll, NULL);
140
return;
25
return;
141
}
26
}
142
27
143
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
28
+ /* Stop things in parent-to-child order */
144
}
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
145
30
aio_disable_external(bdrv_get_aio_context(bs));
146
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
31
bdrv_parent_drained_begin(bs);
147
- BdrvChild *parent, bool ignore_bds_parents)
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
148
+ BdrvChild *parent, bool ignore_bds_parents,
149
+ int *drained_end_counter)
150
{
151
BdrvChild *child, *next;
152
int old_quiesce_counter;
153
154
if (qemu_in_coroutine()) {
155
bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents,
156
- false);
157
+ false, drained_end_counter);
158
return;
33
return;
159
}
34
}
160
assert(bs->quiesce_counter > 0);
35
161
36
- bdrv_parent_drained_end(bs);
162
/* Re-enable things in child-to-parent order */
37
+ /* Re-enable things in child-to-parent order */
163
- bdrv_drain_invoke(bs, false);
38
bdrv_drain_invoke(bs, false);
164
+ bdrv_drain_invoke(bs, false, drained_end_counter);
39
+ bdrv_parent_drained_end(bs);
165
bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
40
aio_enable_external(bdrv_get_aio_context(bs));
166
167
old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
168
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
169
assert(!ignore_bds_parents);
170
bs->recursive_quiesce_counter--;
171
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
172
- bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents);
173
+ bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents,
174
+ drained_end_counter);
175
}
176
}
177
}
41
}
178
42
179
void bdrv_drained_end(BlockDriverState *bs)
180
{
181
- bdrv_do_drained_end(bs, false, NULL, false);
182
+ bdrv_do_drained_end(bs, false, NULL, false, NULL);
183
}
184
185
void bdrv_subtree_drained_end(BlockDriverState *bs)
186
{
187
- bdrv_do_drained_end(bs, true, NULL, false);
188
+ bdrv_do_drained_end(bs, true, NULL, false, NULL);
189
}
190
191
void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
192
@@ -XXX,XX +XXX,XX @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
193
int i;
194
195
for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
196
- bdrv_do_drained_end(child->bs, true, child, false);
197
+ bdrv_do_drained_end(child->bs, true, child, false, NULL);
198
}
199
}
200
201
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
43
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
202
BlockDriverState *bs = NULL;
44
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
203
45
AioContext *aio_context = bdrv_get_aio_context(bs);
204
if (qemu_in_coroutine()) {
46
205
- bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true);
47
+ /* Stop things in parent-to-child order */
206
+ bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true, NULL);
48
aio_context_acquire(aio_context);
207
return;
49
- bdrv_parent_drained_begin(bs);
208
}
50
aio_disable_external(aio_context);
51
+ bdrv_parent_drained_begin(bs);
52
bdrv_drain_invoke(bs, true);
53
aio_context_release(aio_context);
209
54
210
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
55
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
56
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
211
AioContext *aio_context = bdrv_get_aio_context(bs);
57
AioContext *aio_context = bdrv_get_aio_context(bs);
212
58
59
+ /* Re-enable things in child-to-parent order */
213
aio_context_acquire(aio_context);
60
aio_context_acquire(aio_context);
214
- bdrv_do_drained_end(bs, false, NULL, true);
61
- aio_enable_external(aio_context);
215
+ bdrv_do_drained_end(bs, false, NULL, true, NULL);
62
- bdrv_parent_drained_end(bs);
63
bdrv_drain_invoke(bs, false);
64
+ bdrv_parent_drained_end(bs);
65
+ aio_enable_external(aio_context);
216
aio_context_release(aio_context);
66
aio_context_release(aio_context);
217
}
67
}
218
68
219
--
69
--
220
2.20.1
70
2.13.6
221
71
222
72
diff view generated by jsdifflib
New patch
1
Commit 15afd94a047 added code to acquire and release the AioContext in
2
qemuio_command(). This means that the lock is taken twice now in the
3
call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for
4
any requests issued to nodes in a non-mainloop AioContext.
1
5
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
hmp.c | 6 ------
12
1 file changed, 6 deletions(-)
13
14
diff --git a/hmp.c b/hmp.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/hmp.c
17
+++ b/hmp.c
18
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
19
{
20
BlockBackend *blk;
21
BlockBackend *local_blk = NULL;
22
- AioContext *aio_context;
23
const char* device = qdict_get_str(qdict, "device");
24
const char* command = qdict_get_str(qdict, "command");
25
Error *err = NULL;
26
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
27
}
28
}
29
30
- aio_context = blk_get_aio_context(blk);
31
- aio_context_acquire(aio_context);
32
-
33
/*
34
* Notably absent: Proper permission management. This is sad, but it seems
35
* almost impossible to achieve without changing the semantics and thereby
36
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
37
*/
38
qemuio_command(blk, command);
39
40
- aio_context_release(aio_context);
41
-
42
fail:
43
blk_unref(local_blk);
44
hmp_handle_error(mon, &err);
45
--
46
2.13.6
47
48
diff view generated by jsdifflib
New patch
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
1
2
3
Since bdrv_co_preadv does all neccessary checks including
4
reading after the end of the backing file, avoid duplication
5
of verification before bdrv_co_preadv call.
6
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/qcow2.h | 3 ---
13
block/qcow2.c | 51 ++++++++-------------------------------------------
14
2 files changed, 8 insertions(+), 46 deletions(-)
15
16
diff --git a/block/qcow2.h b/block/qcow2.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2.h
19
+++ b/block/qcow2.h
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
21
}
22
23
/* qcow2.c functions */
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
25
- int64_t sector_num, int nb_sectors);
26
-
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
28
int refcount_order, bool generous_increase,
29
uint64_t *refblock_count);
30
diff --git a/block/qcow2.c b/block/qcow2.c
31
index XXXXXXX..XXXXXXX 100644
32
--- a/block/qcow2.c
33
+++ b/block/qcow2.c
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
35
return status;
36
}
37
38
-/* handle reading after the end of the backing file */
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
40
- int64_t offset, int bytes)
41
-{
42
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
43
- int n1;
44
-
45
- if ((offset + bytes) <= bs_size) {
46
- return bytes;
47
- }
48
-
49
- if (offset >= bs_size) {
50
- n1 = 0;
51
- } else {
52
- n1 = bs_size - offset;
53
- }
54
-
55
- qemu_iovec_memset(qiov, n1, 0, bytes - n1);
56
-
57
- return n1;
58
-}
59
-
60
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
61
uint64_t bytes, QEMUIOVector *qiov,
62
int flags)
63
{
64
BDRVQcow2State *s = bs->opaque;
65
- int offset_in_cluster, n1;
66
+ int offset_in_cluster;
67
int ret;
68
unsigned int cur_bytes; /* number of bytes in current iteration */
69
uint64_t cluster_offset = 0;
70
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
71
case QCOW2_CLUSTER_UNALLOCATED:
72
73
if (bs->backing) {
74
- /* read from the base image */
75
- n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
76
- offset, cur_bytes);
77
- if (n1 > 0) {
78
- QEMUIOVector local_qiov;
79
-
80
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
81
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
82
-
83
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
84
- qemu_co_mutex_unlock(&s->lock);
85
- ret = bdrv_co_preadv(bs->backing, offset, n1,
86
- &local_qiov, 0);
87
- qemu_co_mutex_lock(&s->lock);
88
-
89
- qemu_iovec_destroy(&local_qiov);
90
-
91
- if (ret < 0) {
92
- goto fail;
93
- }
94
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
95
+ qemu_co_mutex_unlock(&s->lock);
96
+ ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
97
+ &hd_qiov, 0);
98
+ qemu_co_mutex_lock(&s->lock);
99
+ if (ret < 0) {
100
+ goto fail;
101
}
102
} else {
103
/* Note: in this case, no need to wait */
104
--
105
2.13.6
106
107
diff view generated by jsdifflib
New patch
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
driver state that cannot be recreated with create options because it
3
would require a list with gaps. This causes trouble in at least
4
.bdrv_refresh_filename().
1
5
6
Document this problem so that we won't accidentally mark the command
7
stable without having addressed it.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
11
---
12
qapi/block-core.json | 4 ++++
13
1 file changed, 4 insertions(+)
14
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
index XXXXXXX..XXXXXXX 100644
17
--- a/qapi/block-core.json
18
+++ b/qapi/block-core.json
19
@@ -XXX,XX +XXX,XX @@
20
# does not support all kinds of operations, all kinds of children, nor
21
# all block drivers.
22
#
23
+# FIXME Removing children from a quorum node means introducing gaps in the
24
+# child indices. This cannot be represented in the 'children' list of
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
26
+#
27
# Warning: The data in a new quorum child MUST be consistent with that of
28
# the rest of the array.
29
#
30
--
31
2.13.6
32
33
diff view generated by jsdifflib
New patch
1
From: Doug Gale <doug16k@gmail.com>
1
2
3
Add trace output for commands, errors, and undefined behavior.
4
Add guest error log output for undefined behavior.
5
Report invalid undefined accesses to MMIO.
6
Annotate unlikely error checks with unlikely.
7
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
14
hw/block/trace-events | 93 ++++++++++++++
15
2 files changed, 390 insertions(+), 52 deletions(-)
16
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/hw/block/nvme.c
20
+++ b/hw/block/nvme.c
21
@@ -XXX,XX +XXX,XX @@
22
#include "qapi/visitor.h"
23
#include "sysemu/block-backend.h"
24
25
+#include "qemu/log.h"
26
+#include "trace.h"
27
#include "nvme.h"
28
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
30
+ do { \
31
+ (trace_##trace)(__VA_ARGS__); \
32
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
33
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
34
+ } while (0)
35
+
36
static void nvme_process_sq(void *opaque);
37
38
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
39
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
40
{
41
if (cq->irq_enabled) {
42
if (msix_enabled(&(n->parent_obj))) {
43
+ trace_nvme_irq_msix(cq->vector);
44
msix_notify(&(n->parent_obj), cq->vector);
45
} else {
46
+ trace_nvme_irq_pin();
47
pci_irq_pulse(&n->parent_obj);
48
}
49
+ } else {
50
+ trace_nvme_irq_masked();
51
}
52
}
53
54
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
55
trans_len = MIN(len, trans_len);
56
int num_prps = (len >> n->page_bits) + 1;
57
58
- if (!prp1) {
59
+ if (unlikely(!prp1)) {
60
+ trace_nvme_err_invalid_prp();
61
return NVME_INVALID_FIELD | NVME_DNR;
62
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
63
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
64
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
65
}
66
len -= trans_len;
67
if (len) {
68
- if (!prp2) {
69
+ if (unlikely(!prp2)) {
70
+ trace_nvme_err_invalid_prp2_missing();
71
goto unmap;
72
}
73
if (len > n->page_size) {
74
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
76
77
if (i == n->max_prp_ents - 1 && len > n->page_size) {
78
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
79
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
80
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
81
goto unmap;
82
}
83
84
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
85
prp_ent = le64_to_cpu(prp_list[i]);
86
}
87
88
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
89
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
90
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
91
goto unmap;
92
}
93
94
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
95
i++;
96
}
97
} else {
98
- if (prp2 & (n->page_size - 1)) {
99
+ if (unlikely(prp2 & (n->page_size - 1))) {
100
+ trace_nvme_err_invalid_prp2_align(prp2);
101
goto unmap;
102
}
103
if (qsg->nsg) {
104
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
105
QEMUIOVector iov;
106
uint16_t status = NVME_SUCCESS;
107
108
+ trace_nvme_dma_read(prp1, prp2);
109
+
110
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
111
return NVME_INVALID_FIELD | NVME_DNR;
112
}
113
if (qsg.nsg > 0) {
114
- if (dma_buf_read(ptr, len, &qsg)) {
115
+ if (unlikely(dma_buf_read(ptr, len, &qsg))) {
116
+ trace_nvme_err_invalid_dma();
117
status = NVME_INVALID_FIELD | NVME_DNR;
118
}
119
qemu_sglist_destroy(&qsg);
120
} else {
121
- if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
122
+ if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
123
+ trace_nvme_err_invalid_dma();
124
status = NVME_INVALID_FIELD | NVME_DNR;
125
}
126
qemu_iovec_destroy(&iov);
127
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
128
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
129
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
130
131
- if (slba + nlb > ns->id_ns.nsze) {
132
+ if (unlikely(slba + nlb > ns->id_ns.nsze)) {
133
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
134
return NVME_LBA_RANGE | NVME_DNR;
135
}
136
137
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
138
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
139
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
140
141
- if ((slba + nlb) > ns->id_ns.nsze) {
142
+ trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
143
+
144
+ if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
145
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
146
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
147
return NVME_LBA_RANGE | NVME_DNR;
148
}
149
150
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
151
NvmeNamespace *ns;
152
uint32_t nsid = le32_to_cpu(cmd->nsid);
153
154
- if (nsid == 0 || nsid > n->num_namespaces) {
155
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
156
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
157
return NVME_INVALID_NSID | NVME_DNR;
158
}
159
160
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
161
case NVME_CMD_READ:
162
return nvme_rw(n, ns, cmd, req);
163
default:
164
+ trace_nvme_err_invalid_opc(cmd->opcode);
165
return NVME_INVALID_OPCODE | NVME_DNR;
166
}
167
}
168
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
169
NvmeCQueue *cq;
170
uint16_t qid = le16_to_cpu(c->qid);
171
172
- if (!qid || nvme_check_sqid(n, qid)) {
173
+ if (unlikely(!qid || nvme_check_sqid(n, qid))) {
174
+ trace_nvme_err_invalid_del_sq(qid);
175
return NVME_INVALID_QID | NVME_DNR;
176
}
177
178
+ trace_nvme_del_sq(qid);
179
+
180
sq = n->sq[qid];
181
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
182
req = QTAILQ_FIRST(&sq->out_req_list);
183
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
184
uint16_t qflags = le16_to_cpu(c->sq_flags);
185
uint64_t prp1 = le64_to_cpu(c->prp1);
186
187
- if (!cqid || nvme_check_cqid(n, cqid)) {
188
+ trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags);
189
+
190
+ if (unlikely(!cqid || nvme_check_cqid(n, cqid))) {
191
+ trace_nvme_err_invalid_create_sq_cqid(cqid);
192
return NVME_INVALID_CQID | NVME_DNR;
193
}
194
- if (!sqid || !nvme_check_sqid(n, sqid)) {
195
+ if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) {
196
+ trace_nvme_err_invalid_create_sq_sqid(sqid);
197
return NVME_INVALID_QID | NVME_DNR;
198
}
199
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
200
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
201
+ trace_nvme_err_invalid_create_sq_size(qsize);
202
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
203
}
204
- if (!prp1 || prp1 & (n->page_size - 1)) {
205
+ if (unlikely(!prp1 || prp1 & (n->page_size - 1))) {
206
+ trace_nvme_err_invalid_create_sq_addr(prp1);
207
return NVME_INVALID_FIELD | NVME_DNR;
208
}
209
- if (!(NVME_SQ_FLAGS_PC(qflags))) {
210
+ if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) {
211
+ trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags));
212
return NVME_INVALID_FIELD | NVME_DNR;
213
}
214
sq = g_malloc0(sizeof(*sq));
215
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
216
NvmeCQueue *cq;
217
uint16_t qid = le16_to_cpu(c->qid);
218
219
- if (!qid || nvme_check_cqid(n, qid)) {
220
+ if (unlikely(!qid || nvme_check_cqid(n, qid))) {
221
+ trace_nvme_err_invalid_del_cq_cqid(qid);
222
return NVME_INVALID_CQID | NVME_DNR;
223
}
224
225
cq = n->cq[qid];
226
- if (!QTAILQ_EMPTY(&cq->sq_list)) {
227
+ if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) {
228
+ trace_nvme_err_invalid_del_cq_notempty(qid);
229
return NVME_INVALID_QUEUE_DEL;
230
}
231
+ trace_nvme_del_cq(qid);
232
nvme_free_cq(cq, n);
233
return NVME_SUCCESS;
234
}
235
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
236
uint16_t qflags = le16_to_cpu(c->cq_flags);
237
uint64_t prp1 = le64_to_cpu(c->prp1);
238
239
- if (!cqid || !nvme_check_cqid(n, cqid)) {
240
+ trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags,
241
+ NVME_CQ_FLAGS_IEN(qflags) != 0);
242
+
243
+ if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) {
244
+ trace_nvme_err_invalid_create_cq_cqid(cqid);
245
return NVME_INVALID_CQID | NVME_DNR;
246
}
247
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
248
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
249
+ trace_nvme_err_invalid_create_cq_size(qsize);
250
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
251
}
252
- if (!prp1) {
253
+ if (unlikely(!prp1)) {
254
+ trace_nvme_err_invalid_create_cq_addr(prp1);
255
return NVME_INVALID_FIELD | NVME_DNR;
256
}
257
- if (vector > n->num_queues) {
258
+ if (unlikely(vector > n->num_queues)) {
259
+ trace_nvme_err_invalid_create_cq_vector(vector);
260
return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
261
}
262
- if (!(NVME_CQ_FLAGS_PC(qflags))) {
263
+ if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) {
264
+ trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags));
265
return NVME_INVALID_FIELD | NVME_DNR;
266
}
267
268
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c)
269
uint64_t prp1 = le64_to_cpu(c->prp1);
270
uint64_t prp2 = le64_to_cpu(c->prp2);
271
272
+ trace_nvme_identify_ctrl();
273
+
274
return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
275
prp1, prp2);
276
}
277
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c)
278
uint64_t prp1 = le64_to_cpu(c->prp1);
279
uint64_t prp2 = le64_to_cpu(c->prp2);
280
281
- if (nsid == 0 || nsid > n->num_namespaces) {
282
+ trace_nvme_identify_ns(nsid);
283
+
284
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
285
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
286
return NVME_INVALID_NSID | NVME_DNR;
287
}
288
289
ns = &n->namespaces[nsid - 1];
290
+
291
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
292
prp1, prp2);
293
}
294
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
295
uint16_t ret;
296
int i, j = 0;
297
298
+ trace_nvme_identify_nslist(min_nsid);
299
+
300
list = g_malloc0(data_len);
301
for (i = 0; i < n->num_namespaces; i++) {
302
if (i < min_nsid) {
303
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
304
case 0x02:
305
return nvme_identify_nslist(n, c);
306
default:
307
+ trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
308
return NVME_INVALID_FIELD | NVME_DNR;
309
}
310
}
311
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
312
switch (dw10) {
313
case NVME_VOLATILE_WRITE_CACHE:
314
result = blk_enable_write_cache(n->conf.blk);
315
+ trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
316
break;
317
case NVME_NUMBER_OF_QUEUES:
318
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
319
+ trace_nvme_getfeat_numq(result);
320
break;
321
default:
322
+ trace_nvme_err_invalid_getfeat(dw10);
323
return NVME_INVALID_FIELD | NVME_DNR;
324
}
325
326
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
327
blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
328
break;
329
case NVME_NUMBER_OF_QUEUES:
330
+ trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1,
331
+ ((dw11 >> 16) & 0xFFFF) + 1,
332
+ n->num_queues - 1, n->num_queues - 1);
333
req->cqe.result =
334
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
335
break;
336
default:
337
+ trace_nvme_err_invalid_setfeat(dw10);
338
return NVME_INVALID_FIELD | NVME_DNR;
339
}
340
return NVME_SUCCESS;
341
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
342
case NVME_ADM_CMD_GET_FEATURES:
343
return nvme_get_feature(n, cmd, req);
344
default:
345
+ trace_nvme_err_invalid_admin_opc(cmd->opcode);
346
return NVME_INVALID_OPCODE | NVME_DNR;
347
}
348
}
349
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
350
uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
351
uint32_t page_size = 1 << page_bits;
352
353
- if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
354
- n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
355
- NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
356
- NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
357
- NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
358
- NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
359
- NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
360
- NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
361
- !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) {
362
+ if (unlikely(n->cq[0])) {
363
+ trace_nvme_err_startfail_cq();
364
+ return -1;
365
+ }
366
+ if (unlikely(n->sq[0])) {
367
+ trace_nvme_err_startfail_sq();
368
+ return -1;
369
+ }
370
+ if (unlikely(!n->bar.asq)) {
371
+ trace_nvme_err_startfail_nbarasq();
372
+ return -1;
373
+ }
374
+ if (unlikely(!n->bar.acq)) {
375
+ trace_nvme_err_startfail_nbaracq();
376
+ return -1;
377
+ }
378
+ if (unlikely(n->bar.asq & (page_size - 1))) {
379
+ trace_nvme_err_startfail_asq_misaligned(n->bar.asq);
380
+ return -1;
381
+ }
382
+ if (unlikely(n->bar.acq & (page_size - 1))) {
383
+ trace_nvme_err_startfail_acq_misaligned(n->bar.acq);
384
+ return -1;
385
+ }
386
+ if (unlikely(NVME_CC_MPS(n->bar.cc) <
387
+ NVME_CAP_MPSMIN(n->bar.cap))) {
388
+ trace_nvme_err_startfail_page_too_small(
389
+ NVME_CC_MPS(n->bar.cc),
390
+ NVME_CAP_MPSMIN(n->bar.cap));
391
+ return -1;
392
+ }
393
+ if (unlikely(NVME_CC_MPS(n->bar.cc) >
394
+ NVME_CAP_MPSMAX(n->bar.cap))) {
395
+ trace_nvme_err_startfail_page_too_large(
396
+ NVME_CC_MPS(n->bar.cc),
397
+ NVME_CAP_MPSMAX(n->bar.cap));
398
+ return -1;
399
+ }
400
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) <
401
+ NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) {
402
+ trace_nvme_err_startfail_cqent_too_small(
403
+ NVME_CC_IOCQES(n->bar.cc),
404
+ NVME_CTRL_CQES_MIN(n->bar.cap));
405
+ return -1;
406
+ }
407
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) >
408
+ NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) {
409
+ trace_nvme_err_startfail_cqent_too_large(
410
+ NVME_CC_IOCQES(n->bar.cc),
411
+ NVME_CTRL_CQES_MAX(n->bar.cap));
412
+ return -1;
413
+ }
414
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) <
415
+ NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) {
416
+ trace_nvme_err_startfail_sqent_too_small(
417
+ NVME_CC_IOSQES(n->bar.cc),
418
+ NVME_CTRL_SQES_MIN(n->bar.cap));
419
+ return -1;
420
+ }
421
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) >
422
+ NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) {
423
+ trace_nvme_err_startfail_sqent_too_large(
424
+ NVME_CC_IOSQES(n->bar.cc),
425
+ NVME_CTRL_SQES_MAX(n->bar.cap));
426
+ return -1;
427
+ }
428
+ if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) {
429
+ trace_nvme_err_startfail_asqent_sz_zero();
430
+ return -1;
431
+ }
432
+ if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) {
433
+ trace_nvme_err_startfail_acqent_sz_zero();
434
return -1;
435
}
436
437
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
438
static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
439
unsigned size)
440
{
441
+ if (unlikely(offset & (sizeof(uint32_t) - 1))) {
442
+ NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32,
443
+ "MMIO write not 32-bit aligned,"
444
+ " offset=0x%"PRIx64"", offset);
445
+ /* should be ignored, fall through for now */
446
+ }
447
+
448
+ if (unlikely(size < sizeof(uint32_t))) {
449
+ NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall,
450
+ "MMIO write smaller than 32-bits,"
451
+ " offset=0x%"PRIx64", size=%u",
452
+ offset, size);
453
+ /* should be ignored, fall through for now */
454
+ }
455
+
456
switch (offset) {
457
- case 0xc:
458
+ case 0xc: /* INTMS */
459
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
460
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
461
+ "undefined access to interrupt mask set"
462
+ " when MSI-X is enabled");
463
+ /* should be ignored, fall through for now */
464
+ }
465
n->bar.intms |= data & 0xffffffff;
466
n->bar.intmc = n->bar.intms;
467
+ trace_nvme_mmio_intm_set(data & 0xffffffff,
468
+ n->bar.intmc);
469
break;
470
- case 0x10:
471
+ case 0x10: /* INTMC */
472
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
473
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
474
+ "undefined access to interrupt mask clr"
475
+ " when MSI-X is enabled");
476
+ /* should be ignored, fall through for now */
477
+ }
478
n->bar.intms &= ~(data & 0xffffffff);
479
n->bar.intmc = n->bar.intms;
480
+ trace_nvme_mmio_intm_clr(data & 0xffffffff,
481
+ n->bar.intmc);
482
break;
483
- case 0x14:
484
+ case 0x14: /* CC */
485
+ trace_nvme_mmio_cfg(data & 0xffffffff);
486
/* Windows first sends data, then sends enable bit */
487
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
488
!NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
489
@@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
490
491
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
492
n->bar.cc = data;
493
- if (nvme_start_ctrl(n)) {
494
+ if (unlikely(nvme_start_ctrl(n))) {
495
+ trace_nvme_err_startfail();
496
n->bar.csts = NVME_CSTS_FAILED;
497
} else {
498
+ trace_nvme_mmio_start_success();
499
n->bar.csts = NVME_CSTS_READY;
500
}
501
} else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
502
+ trace_nvme_mmio_stopped();
503
nvme_clear_ctrl(n);
504
n->bar.csts &= ~NVME_CSTS_READY;
505
}
506
if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
507
- nvme_clear_ctrl(n);
508
- n->bar.cc = data;
509
- n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
510
+ trace_nvme_mmio_shutdown_set();
511
+ nvme_clear_ctrl(n);
512
+ n->bar.cc = data;
513
+ n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
514
} else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
515
- n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
516
- n->bar.cc = data;
517
+ trace_nvme_mmio_shutdown_cleared();
518
+ n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
519
+ n->bar.cc = data;
520
+ }
521
+ break;
522
+ case 0x1C: /* CSTS */
523
+ if (data & (1 << 4)) {
524
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported,
525
+ "attempted to W1C CSTS.NSSRO"
526
+ " but CAP.NSSRS is zero (not supported)");
527
+ } else if (data != 0) {
528
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts,
529
+ "attempted to set a read only bit"
530
+ " of controller status");
531
+ }
532
+ break;
533
+ case 0x20: /* NSSR */
534
+ if (data == 0x4E564D65) {
535
+ trace_nvme_ub_mmiowr_ssreset_unsupported();
536
+ } else {
537
+ /* The spec says that writes of other values have no effect */
538
+ return;
539
}
540
break;
541
- case 0x24:
542
+ case 0x24: /* AQA */
543
n->bar.aqa = data & 0xffffffff;
544
+ trace_nvme_mmio_aqattr(data & 0xffffffff);
545
break;
546
- case 0x28:
547
+ case 0x28: /* ASQ */
548
n->bar.asq = data;
549
+ trace_nvme_mmio_asqaddr(data);
550
break;
551
- case 0x2c:
552
+ case 0x2c: /* ASQ hi */
553
n->bar.asq |= data << 32;
554
+ trace_nvme_mmio_asqaddr_hi(data, n->bar.asq);
555
break;
556
- case 0x30:
557
+ case 0x30: /* ACQ */
558
+ trace_nvme_mmio_acqaddr(data);
559
n->bar.acq = data;
560
break;
561
- case 0x34:
562
+ case 0x34: /* ACQ hi */
563
n->bar.acq |= data << 32;
564
+ trace_nvme_mmio_acqaddr_hi(data, n->bar.acq);
565
break;
566
+ case 0x38: /* CMBLOC */
567
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved,
568
+ "invalid write to reserved CMBLOC"
569
+ " when CMBSZ is zero, ignored");
570
+ return;
571
+ case 0x3C: /* CMBSZ */
572
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly,
573
+ "invalid write to read only CMBSZ, ignored");
574
+ return;
575
default:
576
+ NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
577
+ "invalid MMIO write,"
578
+ " offset=0x%"PRIx64", data=%"PRIx64"",
579
+ offset, data);
580
break;
581
}
582
}
583
@@ -XXX,XX +XXX,XX @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
584
uint8_t *ptr = (uint8_t *)&n->bar;
585
uint64_t val = 0;
586
587
+ if (unlikely(addr & (sizeof(uint32_t) - 1))) {
588
+ NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32,
589
+ "MMIO read not 32-bit aligned,"
590
+ " offset=0x%"PRIx64"", addr);
591
+ /* should RAZ, fall through for now */
592
+ } else if (unlikely(size < sizeof(uint32_t))) {
593
+ NVME_GUEST_ERR(nvme_ub_mmiord_toosmall,
594
+ "MMIO read smaller than 32-bits,"
595
+ " offset=0x%"PRIx64"", addr);
596
+ /* should RAZ, fall through for now */
597
+ }
598
+
599
if (addr < sizeof(n->bar)) {
600
memcpy(&val, ptr + addr, size);
601
+ } else {
602
+ NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs,
603
+ "MMIO read beyond last register,"
604
+ " offset=0x%"PRIx64", returning 0", addr);
605
}
606
+
607
return val;
608
}
609
610
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
611
{
612
uint32_t qid;
613
614
- if (addr & ((1 << 2) - 1)) {
615
+ if (unlikely(addr & ((1 << 2) - 1))) {
616
+ NVME_GUEST_ERR(nvme_ub_db_wr_misaligned,
617
+ "doorbell write not 32-bit aligned,"
618
+ " offset=0x%"PRIx64", ignoring", addr);
619
return;
620
}
621
622
if (((addr - 0x1000) >> 2) & 1) {
623
+ /* Completion queue doorbell write */
624
+
625
uint16_t new_head = val & 0xffff;
626
int start_sqs;
627
NvmeCQueue *cq;
628
629
qid = (addr - (0x1000 + (1 << 2))) >> 3;
630
- if (nvme_check_cqid(n, qid)) {
631
+ if (unlikely(nvme_check_cqid(n, qid))) {
632
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq,
633
+ "completion queue doorbell write"
634
+ " for nonexistent queue,"
635
+ " sqid=%"PRIu32", ignoring", qid);
636
return;
637
}
638
639
cq = n->cq[qid];
640
- if (new_head >= cq->size) {
641
+ if (unlikely(new_head >= cq->size)) {
642
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead,
643
+ "completion queue doorbell write value"
644
+ " beyond queue size, sqid=%"PRIu32","
645
+ " new_head=%"PRIu16", ignoring",
646
+ qid, new_head);
647
return;
648
}
649
650
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
651
nvme_isr_notify(n, cq);
652
}
653
} else {
654
+ /* Submission queue doorbell write */
655
+
656
uint16_t new_tail = val & 0xffff;
657
NvmeSQueue *sq;
658
659
qid = (addr - 0x1000) >> 3;
660
- if (nvme_check_sqid(n, qid)) {
661
+ if (unlikely(nvme_check_sqid(n, qid))) {
662
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq,
663
+ "submission queue doorbell write"
664
+ " for nonexistent queue,"
665
+ " sqid=%"PRIu32", ignoring", qid);
666
return;
667
}
668
669
sq = n->sq[qid];
670
- if (new_tail >= sq->size) {
671
+ if (unlikely(new_tail >= sq->size)) {
672
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail,
673
+ "submission queue doorbell write value"
674
+ " beyond queue size, sqid=%"PRIu32","
675
+ " new_tail=%"PRIu16", ignoring",
676
+ qid, new_tail);
677
return;
678
}
679
680
diff --git a/hw/block/trace-events b/hw/block/trace-events
681
index XXXXXXX..XXXXXXX 100644
682
--- a/hw/block/trace-events
683
+++ b/hw/block/trace-events
684
@@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
685
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
686
hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
687
688
+# hw/block/nvme.c
689
+# nvme traces for successful events
690
+nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
691
+nvme_irq_pin(void) "pulsing IRQ pin"
692
+nvme_irq_masked(void) "IRQ is masked"
693
+nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
694
+nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64""
695
+nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16""
696
+nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d"
697
+nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
698
+nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
699
+nvme_identify_ctrl(void) "identify controller"
700
+nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
701
+nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
702
+nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
703
+nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
704
+nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d"
705
+nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
706
+nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
707
+nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
708
+nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
709
+nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
710
+nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
711
+nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
712
+nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
713
+nvme_mmio_start_success(void) "setting controller enable bit succeeded"
714
+nvme_mmio_stopped(void) "cleared controller enable bit"
715
+nvme_mmio_shutdown_set(void) "shutdown bit set"
716
+nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
717
+
718
+# nvme traces for error conditions
719
+nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
720
+nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
721
+nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
722
+nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
723
+nvme_err_invalid_field(void) "invalid field"
724
+nvme_err_invalid_prp(void) "invalid PRP"
725
+nvme_err_invalid_sgl(void) "invalid SGL"
726
+nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
727
+nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
728
+nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
729
+nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
730
+nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
731
+nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
732
+nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
733
+nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
734
+nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
735
+nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
736
+nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
737
+nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
738
+nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
739
+nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
740
+nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
741
+nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
742
+nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
743
+nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
744
+nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
745
+nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
746
+nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
747
+nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
748
+nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
749
+nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
750
+nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
751
+nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
752
+nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u"
753
+nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u"
754
+nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u"
755
+nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u"
756
+nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u"
757
+nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u"
758
+nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero"
759
+nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero"
760
+nvme_err_startfail(void) "setting controller enable bit failed"
761
+
762
+# Traces for undefined behavior
763
+nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64""
764
+nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u"
765
+nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled"
766
+nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status"
767
+nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)"
768
+nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
769
+nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
770
+nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
771
+nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
772
+nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
773
+nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
774
+nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0"
775
+nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring"
776
+nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring"
777
+nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring"
778
+nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring"
779
+nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
780
+
781
# hw/block/xen_disk.c
782
xen_disk_alloc(char *name) "%s"
783
xen_disk_init(char *name) "%s"
784
--
785
2.13.6
786
787
diff view generated by jsdifflib
New patch
1
From: Fam Zheng <famz@redhat.com>
1
2
3
Management tools create overlays of running guests with qemu-img:
4
5
$ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2
6
7
but this doesn't work anymore due to image locking:
8
9
qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock
10
Is another process using the image?
11
Could not open backing image to determine size.
12
Use the force share option to allow this use case again.
13
14
Cc: qemu-stable@nongnu.org
15
Signed-off-by: Fam Zheng <famz@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
19
block.c | 3 ++-
20
1 file changed, 2 insertions(+), 1 deletion(-)
21
22
diff --git a/block.c b/block.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/block.c
25
+++ b/block.c
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
27
back_flags = flags;
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
29
30
+ backing_options = qdict_new();
31
if (backing_fmt) {
32
- backing_options = qdict_new();
33
qdict_put_str(backing_options, "driver", backing_fmt);
34
}
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
36
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
38
&local_err);
39
--
40
2.13.6
41
42
diff view generated by jsdifflib
New patch
1
From: Thomas Huth <thuth@redhat.com>
1
2
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
4
5
Signed-off-by: Thomas Huth <thuth@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
blockdev.c | 11 -----------
11
qemu-doc.texi | 6 ------
12
2 files changed, 17 deletions(-)
13
14
diff --git a/blockdev.c b/blockdev.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/blockdev.c
17
+++ b/blockdev.c
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
19
.type = QEMU_OPT_STRING,
20
.help = "chs translation (auto, lba, none)",
21
},{
22
- .name = "boot",
23
- .type = QEMU_OPT_BOOL,
24
- .help = "(deprecated, ignored)",
25
- },{
26
.name = "addr",
27
.type = QEMU_OPT_STRING,
28
.help = "pci address (virtio only)",
29
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
30
goto fail;
31
}
32
33
- /* Deprecated option boot=[on|off] */
34
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
35
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
36
- "ignored. Future versions will reject this parameter. Please "
37
- "update your scripts.\n");
38
- }
39
-
40
/* Other deprecated options */
41
if (!qtest_enabled()) {
42
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
43
diff --git a/qemu-doc.texi b/qemu-doc.texi
44
index XXXXXXX..XXXXXXX 100644
45
--- a/qemu-doc.texi
46
+++ b/qemu-doc.texi
47
@@ -XXX,XX +XXX,XX @@ deprecated.
48
49
@section System emulator command line arguments
50
51
-@subsection -drive boot=on|off (since 1.3.0)
52
-
53
-The ``boot=on|off'' option to the ``-drive'' argument is
54
-ignored. Applications should use the ``bootindex=N'' parameter
55
-to set an absolute ordering between devices instead.
56
-
57
@subsection -tdf (since 1.3.0)
58
59
The ``-tdf'' argument is ignored. The behaviour implemented
60
--
61
2.13.6
62
63
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
If the main loop cancels all block jobs while the block layer is not
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
drained, this cancelling may not happen instantaneously. We can start a
4
complained that we should keep it, so let's remove this legacy option
5
drained section before vm_shutdown(), which entails another
5
now to simplify the code quite a bit.
6
bdrv_drain_all(); this nested bdrv_drain_all() will thus be a no-op,
6
7
basically.
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
We do not have to end the drained section, because we actually do not
9
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
want any requests to happen from this point on.
11
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
11
---
15
vl.c | 11 +++++++++++
12
vl.c | 86 ++-------------------------------------------------------
16
1 file changed, 11 insertions(+)
13
qemu-doc.texi | 8 ------
14
qemu-options.hx | 19 ++-----------
15
3 files changed, 4 insertions(+), 109 deletions(-)
17
16
18
diff --git a/vl.c b/vl.c
17
diff --git a/vl.c b/vl.c
19
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
20
--- a/vl.c
19
--- a/vl.c
21
+++ b/vl.c
20
+++ b/vl.c
22
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
23
*/
22
const char *boot_order = NULL;
24
migration_shutdown();
23
const char *boot_once = NULL;
25
24
DisplayState *ds;
26
+ /*
25
- int cyls, heads, secs, translation;
27
+ * We must cancel all block jobs while the block layer is drained,
26
QemuOpts *opts, *machine_opts;
28
+ * or cancelling will be affected by throttling and thus may block
27
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
29
+ * for an extended period of time.
28
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
30
+ * vm_shutdown() will bdrv_drain_all(), so we may as well include
29
QemuOptsList *olist;
31
+ * it in the drained section.
30
int optind;
32
+ * We do not need to end this section, because we do not want any
31
const char *optarg;
33
+ * requests happening from here on anyway.
32
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
34
+ */
33
35
+ bdrv_drain_all_begin();
34
cpu_model = NULL;
36
+
35
snapshot = 0;
37
/* No more vcpu or device emulation activity beyond this point */
36
- cyls = heads = secs = 0;
38
vm_shutdown();
37
- translation = BIOS_ATA_TRANSLATION_AUTO;
39
38
39
nb_nics = 0;
40
41
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
42
if (optind >= argc)
43
break;
44
if (argv[optind][0] != '-') {
45
- hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
46
+ drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
47
} else {
48
const QEMUOption *popt;
49
50
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
51
cpu_model = optarg;
52
break;
53
case QEMU_OPTION_hda:
54
- {
55
- char buf[256];
56
- if (cyls == 0)
57
- snprintf(buf, sizeof(buf), "%s", HD_OPTS);
58
- else
59
- snprintf(buf, sizeof(buf),
60
- "%s,cyls=%d,heads=%d,secs=%d%s",
61
- HD_OPTS , cyls, heads, secs,
62
- translation == BIOS_ATA_TRANSLATION_LBA ?
63
- ",trans=lba" :
64
- translation == BIOS_ATA_TRANSLATION_NONE ?
65
- ",trans=none" : "");
66
- drive_add(IF_DEFAULT, 0, optarg, buf);
67
- break;
68
- }
69
case QEMU_OPTION_hdb:
70
case QEMU_OPTION_hdc:
71
case QEMU_OPTION_hdd:
72
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
73
case QEMU_OPTION_snapshot:
74
snapshot = 1;
75
break;
76
- case QEMU_OPTION_hdachs:
77
- {
78
- const char *p;
79
- p = optarg;
80
- cyls = strtol(p, (char **)&p, 0);
81
- if (cyls < 1 || cyls > 16383)
82
- goto chs_fail;
83
- if (*p != ',')
84
- goto chs_fail;
85
- p++;
86
- heads = strtol(p, (char **)&p, 0);
87
- if (heads < 1 || heads > 16)
88
- goto chs_fail;
89
- if (*p != ',')
90
- goto chs_fail;
91
- p++;
92
- secs = strtol(p, (char **)&p, 0);
93
- if (secs < 1 || secs > 63)
94
- goto chs_fail;
95
- if (*p == ',') {
96
- p++;
97
- if (!strcmp(p, "large")) {
98
- translation = BIOS_ATA_TRANSLATION_LARGE;
99
- } else if (!strcmp(p, "rechs")) {
100
- translation = BIOS_ATA_TRANSLATION_RECHS;
101
- } else if (!strcmp(p, "none")) {
102
- translation = BIOS_ATA_TRANSLATION_NONE;
103
- } else if (!strcmp(p, "lba")) {
104
- translation = BIOS_ATA_TRANSLATION_LBA;
105
- } else if (!strcmp(p, "auto")) {
106
- translation = BIOS_ATA_TRANSLATION_AUTO;
107
- } else {
108
- goto chs_fail;
109
- }
110
- } else if (*p != '\0') {
111
- chs_fail:
112
- error_report("invalid physical CHS format");
113
- exit(1);
114
- }
115
- if (hda_opts != NULL) {
116
- qemu_opt_set_number(hda_opts, "cyls", cyls,
117
- &error_abort);
118
- qemu_opt_set_number(hda_opts, "heads", heads,
119
- &error_abort);
120
- qemu_opt_set_number(hda_opts, "secs", secs,
121
- &error_abort);
122
- if (translation == BIOS_ATA_TRANSLATION_LARGE) {
123
- qemu_opt_set(hda_opts, "trans", "large",
124
- &error_abort);
125
- } else if (translation == BIOS_ATA_TRANSLATION_RECHS) {
126
- qemu_opt_set(hda_opts, "trans", "rechs",
127
- &error_abort);
128
- } else if (translation == BIOS_ATA_TRANSLATION_LBA) {
129
- qemu_opt_set(hda_opts, "trans", "lba",
130
- &error_abort);
131
- } else if (translation == BIOS_ATA_TRANSLATION_NONE) {
132
- qemu_opt_set(hda_opts, "trans", "none",
133
- &error_abort);
134
- }
135
- }
136
- }
137
- error_report("'-hdachs' is deprecated, please use '-device"
138
- " ide-hd,cyls=c,heads=h,secs=s,...' instead");
139
- break;
140
case QEMU_OPTION_numa:
141
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
142
optarg, true);
143
diff --git a/qemu-doc.texi b/qemu-doc.texi
144
index XXXXXXX..XXXXXXX 100644
145
--- a/qemu-doc.texi
146
+++ b/qemu-doc.texi
147
@@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the
148
``-object filter-dump'' argument which works in combination
149
with the modern ``-netdev`` backends instead.
150
151
-@subsection -hdachs (since 2.10.0)
152
-
153
-The ``-hdachs'' argument is now a synonym for setting
154
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
155
-on the ``ide-hd'' device using the ``-device'' argument.
156
-The new syntax allows different settings to be provided
157
-per disk.
158
-
159
@subsection -usbdevice (since 2.10.0)
160
161
The ``-usbdevice DEV'' argument is now a synonym for setting
162
diff --git a/qemu-options.hx b/qemu-options.hx
163
index XXXXXXX..XXXXXXX 100644
164
--- a/qemu-options.hx
165
+++ b/qemu-options.hx
166
@@ -XXX,XX +XXX,XX @@ of available connectors of a given interface type.
167
@item media=@var{media}
168
This option defines the type of the media: disk or cdrom.
169
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
170
-These options have the same definition as they have in @option{-hdachs}.
171
-These parameters are deprecated, use the corresponding parameters
172
+Force disk physical geometry and the optional BIOS translation (trans=none or
173
+lba). These parameters are deprecated, use the corresponding parameters
174
of @code{-device} instead.
175
@item snapshot=@var{snapshot}
176
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
177
@@ -XXX,XX +XXX,XX @@ the raw disk image you use is not written back. You can however force
178
the write back by pressing @key{C-a s} (@pxref{disk_images}).
179
ETEXI
180
181
-DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
182
- "-hdachs c,h,s[,t]\n" \
183
- " force hard disk 0 physical geometry and the optional BIOS\n" \
184
- " translation (t=none or lba) (usually QEMU can guess them)\n",
185
- QEMU_ARCH_ALL)
186
-STEXI
187
-@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
188
-@findex -hdachs
189
-Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
190
-@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
191
-translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
192
-all those parameters. This option is deprecated, please use
193
-@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead.
194
-ETEXI
195
-
196
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
197
"-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n"
198
" [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n"
40
--
199
--
41
2.20.1
200
2.13.6
42
201
43
202
diff view generated by jsdifflib
New patch
1
From: Thomas Huth <thuth@redhat.com>
1
2
3
Looks like we forgot to announce the deprecation of these options in
4
the corresponding chapter of the qemu-doc text, so let's do that now.
5
6
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
qemu-doc.texi | 15 +++++++++++++++
12
1 file changed, 15 insertions(+)
13
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qemu-doc.texi
17
+++ b/qemu-doc.texi
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
19
The ``-drive if=scsi'' argument is replaced by the the
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
21
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
23
+
24
+The drive geometry arguments are replaced by the the geometry arguments
25
+that can be specified with the ``-device'' parameter.
26
+
27
+@subsection -drive serial=... (since 2.10.0)
28
+
29
+The drive serial argument is replaced by the the serial argument
30
+that can be specified with the ``-device'' parameter.
31
+
32
+@subsection -drive addr=... (since 2.10.0)
33
+
34
+The drive addr argument is replaced by the the addr argument
35
+that can be specified with the ``-device'' parameter.
36
+
37
@subsection -net dump (since 2.10.0)
38
39
The ``--net dump'' argument is now replaced with the
40
--
41
2.13.6
42
43
diff view generated by jsdifflib
New patch
1
From: Fam Zheng <famz@redhat.com>
1
2
3
Signed-off-by: Fam Zheng <famz@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
6
include/block/block_int.h | 1 -
7
block/io.c | 18 ------------------
8
2 files changed, 19 deletions(-)
9
10
diff --git a/include/block/block_int.h b/include/block/block_int.h
11
index XXXXXXX..XXXXXXX 100644
12
--- a/include/block/block_int.h
13
+++ b/include/block/block_int.h
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
16
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
18
-bool bdrv_requests_pending(BlockDriverState *bs);
19
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
22
diff --git a/block/io.c b/block/io.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/block/io.c
25
+++ b/block/io.c
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
27
assert(old >= 1);
28
}
29
30
-/* Check if any requests are in-flight (including throttled requests) */
31
-bool bdrv_requests_pending(BlockDriverState *bs)
32
-{
33
- BdrvChild *child;
34
-
35
- if (atomic_read(&bs->in_flight)) {
36
- return true;
37
- }
38
-
39
- QLIST_FOREACH(child, &bs->children, next) {
40
- if (bdrv_requests_pending(child->bs)) {
41
- return true;
42
- }
43
- }
44
-
45
- return false;
46
-}
47
-
48
typedef struct {
49
Coroutine *co;
50
BlockDriverState *bs;
51
--
52
2.13.6
53
54
diff view generated by jsdifflib
New patch
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Fam Zheng <famz@redhat.com>
3
---
4
block/io.c | 6 ++++++
5
1 file changed, 6 insertions(+)
1
6
7
diff --git a/block/io.c b/block/io.c
8
index XXXXXXX..XXXXXXX 100644
9
--- a/block/io.c
10
+++ b/block/io.c
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
12
BdrvNextIterator it;
13
GSList *aio_ctxs = NULL, *ctx;
14
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
17
+ * nodes in several different AioContexts, so make sure we're in the main
18
+ * context. */
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
20
+
21
block_job_pause_all();
22
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
24
--
25
2.13.6
26
27
diff view generated by jsdifflib
New patch
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
and also doesn't notify other parent nodes of children, which both means
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
is providing useful functionality only on a single node.
1
5
6
To keep things consistent, we also shouldn't call the block driver
7
callbacks recursively.
8
9
A proper recursive drain version that provides an actually working
10
drained section for child nodes will be introduced later.
11
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
14
---
15
block/io.c | 16 +++++++++-------
16
1 file changed, 9 insertions(+), 7 deletions(-)
17
18
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/io.c
21
+++ b/block/io.c
22
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
23
}
24
25
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
26
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
27
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
28
{
29
BdrvChild *child, *tmp;
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
32
bdrv_coroutine_enter(bs, data.co);
33
BDRV_POLL_WHILE(bs, !data.done);
34
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
36
- bdrv_drain_invoke(child->bs, begin);
37
+ if (recursive) {
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
39
+ bdrv_drain_invoke(child->bs, begin, true);
40
+ }
41
}
42
}
43
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
45
bdrv_parent_drained_begin(bs);
46
}
47
48
- bdrv_drain_invoke(bs, true);
49
+ bdrv_drain_invoke(bs, true, false);
50
bdrv_drain_recurse(bs);
51
}
52
53
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
54
}
55
56
/* Re-enable things in child-to-parent order */
57
- bdrv_drain_invoke(bs, false);
58
+ bdrv_drain_invoke(bs, false, false);
59
bdrv_parent_drained_end(bs);
60
aio_enable_external(bdrv_get_aio_context(bs));
61
}
62
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
63
aio_context_acquire(aio_context);
64
aio_disable_external(aio_context);
65
bdrv_parent_drained_begin(bs);
66
- bdrv_drain_invoke(bs, true);
67
+ bdrv_drain_invoke(bs, true, true);
68
aio_context_release(aio_context);
69
70
if (!g_slist_find(aio_ctxs, aio_context)) {
71
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
72
73
/* Re-enable things in child-to-parent order */
74
aio_context_acquire(aio_context);
75
- bdrv_drain_invoke(bs, false);
76
+ bdrv_drain_invoke(bs, false, true);
77
bdrv_parent_drained_end(bs);
78
aio_enable_external(aio_context);
79
aio_context_release(aio_context);
80
--
81
2.13.6
82
83
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
The existing test is for bdrv_drain_all_begin/end() only. Generalise the
2
test case so that it can be run for the other variants as well. At the
3
moment this is only bdrv_drain_begin/end(), but in a while, we'll add
4
another one.
2
5
3
When changing a node's AioContext, the caller must acquire the old
6
Also, add a backing file to the test node to test whether the operations
4
AioContext (unless it currently runs in that old context). Therefore,
7
work recursively.
5
unless the node currently is in the main context, we always have to
6
acquire the old context around calls that may change a node's
7
AioContext.
8
8
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
10
---
12
tests/test-block-iothread.c | 40 ++++++++++++++++++++++++-------------
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
13
1 file changed, 26 insertions(+), 14 deletions(-)
12
1 file changed, 62 insertions(+), 7 deletions(-)
14
13
15
diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
16
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/test-block-iothread.c
16
--- a/tests/test-bdrv-drain.c
18
+++ b/tests/test-block-iothread.c
17
+++ b/tests/test-bdrv-drain.c
19
@@ -XXX,XX +XXX,XX @@ static void test_sync_op(const void *opaque)
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
20
if (t->blkfn) {
19
21
t->blkfn(blk);
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
22
}
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
23
- aio_context_release(ctx);
22
+
24
blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort);
23
+ .bdrv_child_perm = bdrv_format_default_perms,
25
+ aio_context_release(ctx);
24
};
26
25
27
bdrv_unref(bs);
26
static void aio_ret_cb(void *opaque, int ret)
28
blk_unref(blk);
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
29
@@ -XXX,XX +XXX,XX @@ static void test_propagate_basic(void)
28
*aio_ret = ret;
29
}
30
31
-static void test_drv_cb_drain_all(void)
32
+enum drain_type {
33
+ BDRV_DRAIN_ALL,
34
+ BDRV_DRAIN,
35
+};
36
+
37
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
38
+{
39
+ switch (drain_type) {
40
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
41
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
42
+ default: g_assert_not_reached();
43
+ }
44
+}
45
+
46
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
47
+{
48
+ switch (drain_type) {
49
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
50
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
51
+ default: g_assert_not_reached();
52
+ }
53
+}
54
+
55
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
30
{
56
{
31
IOThread *iothread = iothread_new();
32
AioContext *ctx = iothread_get_aio_context(iothread);
33
+ AioContext *main_ctx;
34
BlockBackend *blk;
57
BlockBackend *blk;
35
BlockDriverState *bs_a, *bs_b, *bs_verify;
58
- BlockDriverState *bs;
36
QDict *options;
59
- BDRVTestState *s;
37
@@ -XXX,XX +XXX,XX @@ static void test_propagate_basic(void)
60
+ BlockDriverState *bs, *backing;
38
g_assert(bdrv_get_aio_context(bs_b) == ctx);
61
+ BDRVTestState *s, *backing_s;
39
62
BlockAIOCB *acb;
40
/* Switch the AioContext back */
63
int aio_ret;
41
- ctx = qemu_get_aio_context();
64
42
- blk_set_aio_context(blk, ctx, &error_abort);
65
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
43
- g_assert(blk_get_aio_context(blk) == ctx);
66
s = bs->opaque;
44
- g_assert(bdrv_get_aio_context(bs_a) == ctx);
67
blk_insert_bs(blk, bs, &error_abort);
45
- g_assert(bdrv_get_aio_context(bs_verify) == ctx);
68
46
- g_assert(bdrv_get_aio_context(bs_b) == ctx);
69
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
47
+ main_ctx = qemu_get_aio_context();
70
+ backing_s = backing->opaque;
48
+ aio_context_acquire(ctx);
71
+ bdrv_set_backing_hd(bs, backing, &error_abort);
49
+ blk_set_aio_context(blk, main_ctx, &error_abort);
72
+
50
+ aio_context_release(ctx);
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
51
+ g_assert(blk_get_aio_context(blk) == main_ctx);
74
g_assert_cmpint(s->drain_count, ==, 0);
52
+ g_assert(bdrv_get_aio_context(bs_a) == main_ctx);
75
- bdrv_drain_all_begin();
53
+ g_assert(bdrv_get_aio_context(bs_verify) == main_ctx);
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
54
+ g_assert(bdrv_get_aio_context(bs_b) == main_ctx);
77
+
55
78
+ do_drain_begin(drain_type, bs);
56
bdrv_unref(bs_verify);
79
+
57
bdrv_unref(bs_b);
80
g_assert_cmpint(s->drain_count, ==, 1);
58
@@ -XXX,XX +XXX,XX @@ static void test_propagate_diamond(void)
81
- bdrv_drain_all_end();
59
{
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
60
IOThread *iothread = iothread_new();
83
+
61
AioContext *ctx = iothread_get_aio_context(iothread);
84
+ do_drain_end(drain_type, bs);
62
+ AioContext *main_ctx;
85
+
63
BlockBackend *blk;
86
g_assert_cmpint(s->drain_count, ==, 0);
64
BlockDriverState *bs_a, *bs_b, *bs_c, *bs_verify;
87
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
65
QDict *options;
88
66
@@ -XXX,XX +XXX,XX @@ static void test_propagate_diamond(void)
89
/* Now do the same while a request is pending */
67
g_assert(bdrv_get_aio_context(bs_c) == ctx);
90
aio_ret = -EINPROGRESS;
68
91
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
69
/* Switch the AioContext back */
92
g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
70
- ctx = qemu_get_aio_context();
93
71
- blk_set_aio_context(blk, ctx, &error_abort);
94
g_assert_cmpint(s->drain_count, ==, 0);
72
- g_assert(blk_get_aio_context(blk) == ctx);
95
- bdrv_drain_all_begin();
73
- g_assert(bdrv_get_aio_context(bs_verify) == ctx);
96
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
74
- g_assert(bdrv_get_aio_context(bs_a) == ctx);
97
+
75
- g_assert(bdrv_get_aio_context(bs_b) == ctx);
98
+ do_drain_begin(drain_type, bs);
76
- g_assert(bdrv_get_aio_context(bs_c) == ctx);
99
+
77
+ main_ctx = qemu_get_aio_context();
100
g_assert_cmpint(aio_ret, ==, 0);
78
+ aio_context_acquire(ctx);
101
g_assert_cmpint(s->drain_count, ==, 1);
79
+ blk_set_aio_context(blk, main_ctx, &error_abort);
102
- bdrv_drain_all_end();
80
+ aio_context_release(ctx);
103
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
81
+ g_assert(blk_get_aio_context(blk) == main_ctx);
104
+
82
+ g_assert(bdrv_get_aio_context(bs_verify) == main_ctx);
105
+ do_drain_end(drain_type, bs);
83
+ g_assert(bdrv_get_aio_context(bs_a) == main_ctx);
106
+
84
+ g_assert(bdrv_get_aio_context(bs_b) == main_ctx);
107
g_assert_cmpint(s->drain_count, ==, 0);
85
+ g_assert(bdrv_get_aio_context(bs_c) == main_ctx);
108
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
86
109
87
blk_unref(blk);
110
+ bdrv_unref(backing);
88
bdrv_unref(bs_verify);
89
@@ -XXX,XX +XXX,XX @@ static void test_attach_second_node(void)
90
g_assert(bdrv_get_aio_context(bs) == ctx);
91
g_assert(bdrv_get_aio_context(filter) == ctx);
92
93
+ aio_context_acquire(ctx);
94
blk_set_aio_context(blk, main_ctx, &error_abort);
95
+ aio_context_release(ctx);
96
g_assert(blk_get_aio_context(blk) == main_ctx);
97
g_assert(bdrv_get_aio_context(bs) == main_ctx);
98
g_assert(bdrv_get_aio_context(filter) == main_ctx);
99
@@ -XXX,XX +XXX,XX @@ static void test_attach_preserve_blk_ctx(void)
100
g_assert(bdrv_get_aio_context(bs) == ctx);
101
102
/* Remove the node again */
103
+ aio_context_acquire(ctx);
104
blk_remove_bs(blk);
105
+ aio_context_release(ctx);
106
g_assert(blk_get_aio_context(blk) == ctx);
107
g_assert(bdrv_get_aio_context(bs) == qemu_get_aio_context());
108
109
@@ -XXX,XX +XXX,XX @@ static void test_attach_preserve_blk_ctx(void)
110
g_assert(blk_get_aio_context(blk) == ctx);
111
g_assert(bdrv_get_aio_context(bs) == ctx);
112
113
+ aio_context_acquire(ctx);
114
blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort);
115
+ aio_context_release(ctx);
116
bdrv_unref(bs);
111
bdrv_unref(bs);
117
blk_unref(blk);
112
blk_unref(blk);
118
}
113
}
114
115
+static void test_drv_cb_drain_all(void)
116
+{
117
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
118
+}
119
+
120
+static void test_drv_cb_drain(void)
121
+{
122
+ test_drv_cb_common(BDRV_DRAIN, false);
123
+}
124
+
125
int main(int argc, char **argv)
126
{
127
bdrv_init();
128
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
129
g_test_init(&argc, &argv, NULL);
130
131
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
132
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
133
134
return g_test_run();
135
}
119
--
136
--
120
2.20.1
137
2.13.6
121
138
122
139
diff view generated by jsdifflib
New patch
1
This is currently only working correctly for bdrv_drain(), not for
2
bdrv_drain_all(). Leave a comment for the drain_all case, we'll address
3
it later.
1
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
8
1 file changed, 45 insertions(+)
9
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/test-bdrv-drain.c
13
+++ b/tests/test-bdrv-drain.c
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
15
test_drv_cb_common(BDRV_DRAIN, false);
16
}
17
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
19
+{
20
+ BlockBackend *blk;
21
+ BlockDriverState *bs, *backing;
22
+
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
25
+ &error_abort);
26
+ blk_insert_bs(blk, bs, &error_abort);
27
+
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
29
+ bdrv_set_backing_hd(bs, backing, &error_abort);
30
+
31
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
32
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
33
+
34
+ do_drain_begin(drain_type, bs);
35
+
36
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
37
+ g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
38
+
39
+ do_drain_end(drain_type, bs);
40
+
41
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
43
+
44
+ bdrv_unref(backing);
45
+ bdrv_unref(bs);
46
+ blk_unref(blk);
47
+}
48
+
49
+static void test_quiesce_drain_all(void)
50
+{
51
+ // XXX drain_all doesn't quiesce
52
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
53
+}
54
+
55
+static void test_quiesce_drain(void)
56
+{
57
+ test_quiesce_common(BDRV_DRAIN, false);
58
+}
59
+
60
int main(int argc, char **argv)
61
{
62
bdrv_init();
63
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
64
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
65
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
66
67
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
68
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
69
+
70
return g_test_run();
71
}
72
--
73
2.13.6
74
75
diff view generated by jsdifflib
New patch
1
Block jobs already paused themselves when their main BlockBackend
2
entered a drained section. This is not good enough: We also want to
3
pause a block job and may not submit new requests if, for example, the
4
mirror target node should be drained.
1
5
6
This implements .drained_begin/end callbacks in child_job in order to
7
consider all block nodes related to the job, and removes the
8
BlockBackend callbacks which are unnecessary now because the root of the
9
job main BlockBackend is always referenced with a child_job, too.
10
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
blockjob.c | 22 +++++++++-------------
14
1 file changed, 9 insertions(+), 13 deletions(-)
15
16
diff --git a/blockjob.c b/blockjob.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/blockjob.c
19
+++ b/blockjob.c
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
21
job->id);
22
}
23
24
-static const BdrvChildRole child_job = {
25
- .get_parent_desc = child_job_get_parent_desc,
26
- .stay_at_node = true,
27
-};
28
-
29
-static void block_job_drained_begin(void *opaque)
30
+static void child_job_drained_begin(BdrvChild *c)
31
{
32
- BlockJob *job = opaque;
33
+ BlockJob *job = c->opaque;
34
block_job_pause(job);
35
}
36
37
-static void block_job_drained_end(void *opaque)
38
+static void child_job_drained_end(BdrvChild *c)
39
{
40
- BlockJob *job = opaque;
41
+ BlockJob *job = c->opaque;
42
block_job_resume(job);
43
}
44
45
-static const BlockDevOps block_job_dev_ops = {
46
- .drained_begin = block_job_drained_begin,
47
- .drained_end = block_job_drained_end,
48
+static const BdrvChildRole child_job = {
49
+ .get_parent_desc = child_job_get_parent_desc,
50
+ .drained_begin = child_job_drained_begin,
51
+ .drained_end = child_job_drained_end,
52
+ .stay_at_node = true,
53
};
54
55
void block_job_remove_all_bdrv(BlockJob *job)
56
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
57
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
58
bs->job = job;
59
60
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
61
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
62
63
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
64
--
65
2.13.6
66
67
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Block jobs must be paused if any of the involved nodes are drained.
2
2
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
4
---
6
tests/test-bdrv-drain.c | 119 ++++++++++++++++++++++++++++++++++++++++
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
7
1 file changed, 119 insertions(+)
6
1 file changed, 121 insertions(+)
8
7
9
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
10
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
11
--- a/tests/test-bdrv-drain.c
10
--- a/tests/test-bdrv-drain.c
12
+++ b/tests/test-bdrv-drain.c
11
+++ b/tests/test-bdrv-drain.c
13
@@ -XXX,XX +XXX,XX @@ static void test_set_aio_context(void)
12
@@ -XXX,XX +XXX,XX @@
14
iothread_join(b);
13
14
#include "qemu/osdep.h"
15
#include "block/block.h"
16
+#include "block/blockjob_int.h"
17
#include "sysemu/block-backend.h"
18
#include "qapi/error.h"
19
20
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
21
test_quiesce_common(BDRV_DRAIN, false);
15
}
22
}
16
23
17
+
24
+
18
+typedef struct TestDropBackingBlockJob {
25
+typedef struct TestBlockJob {
19
+ BlockJob common;
26
+ BlockJob common;
20
+ bool should_complete;
27
+ bool should_complete;
21
+ bool *did_complete;
28
+} TestBlockJob;
22
+} TestDropBackingBlockJob;
23
+
29
+
24
+static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp)
30
+static void test_job_completed(BlockJob *job, void *opaque)
25
+{
31
+{
26
+ TestDropBackingBlockJob *s =
32
+ block_job_completed(job, 0);
27
+ container_of(job, TestDropBackingBlockJob, common.job);
33
+}
34
+
35
+static void coroutine_fn test_job_start(void *opaque)
36
+{
37
+ TestBlockJob *s = opaque;
28
+
38
+
29
+ while (!s->should_complete) {
39
+ while (!s->should_complete) {
30
+ job_sleep_ns(job, 0);
40
+ block_job_sleep_ns(&s->common, 100000);
31
+ }
41
+ }
32
+
42
+
33
+ return 0;
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
34
+}
44
+}
35
+
45
+
36
+static void test_drop_backing_job_commit(Job *job)
46
+static void test_job_complete(BlockJob *job, Error **errp)
37
+{
47
+{
38
+ TestDropBackingBlockJob *s =
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
39
+ container_of(job, TestDropBackingBlockJob, common.job);
49
+ s->should_complete = true;
40
+
41
+ bdrv_set_backing_hd(blk_bs(s->common.blk), NULL, &error_abort);
42
+
43
+ *s->did_complete = true;
44
+}
50
+}
45
+
51
+
46
+static const BlockJobDriver test_drop_backing_job_driver = {
52
+BlockJobDriver test_job_driver = {
47
+ .job_driver = {
53
+ .instance_size = sizeof(TestBlockJob),
48
+ .instance_size = sizeof(TestDropBackingBlockJob),
54
+ .start = test_job_start,
49
+ .free = block_job_free,
55
+ .complete = test_job_complete,
50
+ .user_resume = block_job_user_resume,
51
+ .drain = block_job_drain,
52
+ .run = test_drop_backing_job_run,
53
+ .commit = test_drop_backing_job_commit,
54
+ }
55
+};
56
+};
56
+
57
+
57
+/**
58
+static void test_blockjob_common(enum drain_type drain_type)
58
+ * Creates a child node with three parent nodes on it, and then runs a
59
+ * block job on the final one, parent-node-2.
60
+ *
61
+ * (TODO: parent-node-0 currently serves no purpose, but will as of a
62
+ * follow-up patch.)
63
+ *
64
+ * The job is then asked to complete before a section where the child
65
+ * is drained.
66
+ *
67
+ * Ending this section will undrain the child's parents, first
68
+ * parent-node-2, then parent-node-1, then parent-node-0 -- the parent
69
+ * list is in reverse order of how they were added. Ending the drain
70
+ * on parent-node-2 will resume the job, thus completing it and
71
+ * scheduling job_exit().
72
+ *
73
+ * Ending the drain on parent-node-1 will poll the AioContext, which
74
+ * lets job_exit() and thus test_drop_backing_job_commit() run. That
75
+ * function removes the child as parent-node-2's backing file.
76
+ *
77
+ * In old (and buggy) implementations, there are two problems with
78
+ * that:
79
+ * (A) bdrv_drain_invoke() polls for every node that leaves the
80
+ * drained section. This means that job_exit() is scheduled
81
+ * before the child has left the drained section. Its
82
+ * quiesce_counter is therefore still 1 when it is removed from
83
+ * parent-node-2.
84
+ *
85
+ * (B) bdrv_replace_child_noperm() calls drained_end() on the old
86
+ * child's parents as many times as the child is quiesced. This
87
+ * means it will call drained_end() on parent-node-2 once.
88
+ * Because parent-node-2 is no longer quiesced at this point, this
89
+ * will fail.
90
+ *
91
+ * bdrv_replace_child_noperm() therefore must call drained_end() on
92
+ * the parent only if it really is still drained because the child is
93
+ * drained.
94
+ */
95
+static void test_blockjob_commit_by_drained_end(void)
96
+{
59
+{
97
+ BlockDriverState *bs_child, *bs_parents[3];
60
+ BlockBackend *blk_src, *blk_target;
98
+ TestDropBackingBlockJob *job;
61
+ BlockDriverState *src, *target;
99
+ bool job_has_completed = false;
62
+ BlockJob *job;
100
+ int i;
63
+ int ret;
101
+
64
+
102
+ bs_child = bdrv_new_open_driver(&bdrv_test, "child-node", BDRV_O_RDWR,
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
103
+ &error_abort);
66
+ &error_abort);
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
68
+ blk_insert_bs(blk_src, src, &error_abort);
104
+
69
+
105
+ for (i = 0; i < 3; i++) {
70
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
106
+ char name[32];
71
+ &error_abort);
107
+ snprintf(name, sizeof(name), "parent-node-%i", i);
72
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
108
+ bs_parents[i] = bdrv_new_open_driver(&bdrv_test, name, BDRV_O_RDWR,
73
+ blk_insert_bs(blk_target, target, &error_abort);
109
+ &error_abort);
74
+
110
+ bdrv_set_backing_hd(bs_parents[i], bs_child, &error_abort);
75
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
76
+ 0, NULL, NULL, &error_abort);
77
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
78
+ block_job_start(job);
79
+
80
+ g_assert_cmpint(job->pause_count, ==, 0);
81
+ g_assert_false(job->paused);
82
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
83
+
84
+ do_drain_begin(drain_type, src);
85
+
86
+ if (drain_type == BDRV_DRAIN_ALL) {
87
+ /* bdrv_drain_all() drains both src and target, and involves an
88
+ * additional block_job_pause_all() */
89
+ g_assert_cmpint(job->pause_count, ==, 3);
90
+ } else {
91
+ g_assert_cmpint(job->pause_count, ==, 1);
111
+ }
92
+ }
93
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
94
+ /* g_assert_true(job->paused); */
95
+ g_assert_false(job->busy); /* The job is paused */
112
+
96
+
113
+ job = block_job_create("job", &test_drop_backing_job_driver, NULL,
97
+ do_drain_end(drain_type, src);
114
+ bs_parents[2], 0, BLK_PERM_ALL, 0, 0, NULL, NULL,
115
+ &error_abort);
116
+
98
+
117
+ job->did_complete = &job_has_completed;
99
+ g_assert_cmpint(job->pause_count, ==, 0);
100
+ g_assert_false(job->paused);
101
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
118
+
102
+
119
+ job_start(&job->common.job);
103
+ do_drain_begin(drain_type, target);
120
+
104
+
121
+ job->should_complete = true;
105
+ if (drain_type == BDRV_DRAIN_ALL) {
122
+ bdrv_drained_begin(bs_child);
106
+ /* bdrv_drain_all() drains both src and target, and involves an
123
+ g_assert(!job_has_completed);
107
+ * additional block_job_pause_all() */
124
+ bdrv_drained_end(bs_child);
108
+ g_assert_cmpint(job->pause_count, ==, 3);
125
+ g_assert(job_has_completed);
109
+ } else {
110
+ g_assert_cmpint(job->pause_count, ==, 1);
111
+ }
112
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
113
+ /* g_assert_true(job->paused); */
114
+ g_assert_false(job->busy); /* The job is paused */
126
+
115
+
127
+ bdrv_unref(bs_parents[0]);
116
+ do_drain_end(drain_type, target);
128
+ bdrv_unref(bs_parents[1]);
117
+
129
+ bdrv_unref(bs_parents[2]);
118
+ g_assert_cmpint(job->pause_count, ==, 0);
130
+ bdrv_unref(bs_child);
119
+ g_assert_false(job->paused);
120
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
121
+
122
+ ret = block_job_complete_sync(job, &error_abort);
123
+ g_assert_cmpint(ret, ==, 0);
124
+
125
+ blk_unref(blk_src);
126
+ blk_unref(blk_target);
127
+ bdrv_unref(src);
128
+ bdrv_unref(target);
129
+}
130
+
131
+static void test_blockjob_drain_all(void)
132
+{
133
+ test_blockjob_common(BDRV_DRAIN_ALL);
134
+}
135
+
136
+static void test_blockjob_drain(void)
137
+{
138
+ test_blockjob_common(BDRV_DRAIN);
131
+}
139
+}
132
+
140
+
133
int main(int argc, char **argv)
141
int main(int argc, char **argv)
134
{
142
{
135
int ret;
143
bdrv_init();
136
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
144
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
137
145
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
138
g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context);
146
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
139
147
140
+ g_test_add_func("/bdrv-drain/blockjob/commit_by_drained_end",
148
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
141
+ test_blockjob_commit_by_drained_end);
149
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
142
+
150
+
143
ret = g_test_run();
151
return g_test_run();
144
qemu_event_destroy(&done_event);
152
}
145
return ret;
146
--
153
--
147
2.20.1
154
2.13.6
148
155
149
156
diff view generated by jsdifflib
New patch
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
so we don't need an additional block_job_pause_all() call.
1
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
6
block/io.c | 4 ----
7
tests/test-bdrv-drain.c | 10 ++++------
8
2 files changed, 4 insertions(+), 10 deletions(-)
9
10
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/block/io.c
13
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
15
* context. */
16
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
17
18
- block_job_pause_all();
19
-
20
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
21
AioContext *aio_context = bdrv_get_aio_context(bs);
22
23
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
24
aio_enable_external(aio_context);
25
aio_context_release(aio_context);
26
}
27
-
28
- block_job_resume_all();
29
}
30
31
void bdrv_drain_all(void)
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/tests/test-bdrv-drain.c
35
+++ b/tests/test-bdrv-drain.c
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
37
do_drain_begin(drain_type, src);
38
39
if (drain_type == BDRV_DRAIN_ALL) {
40
- /* bdrv_drain_all() drains both src and target, and involves an
41
- * additional block_job_pause_all() */
42
- g_assert_cmpint(job->pause_count, ==, 3);
43
+ /* bdrv_drain_all() drains both src and target */
44
+ g_assert_cmpint(job->pause_count, ==, 2);
45
} else {
46
g_assert_cmpint(job->pause_count, ==, 1);
47
}
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
49
do_drain_begin(drain_type, target);
50
51
if (drain_type == BDRV_DRAIN_ALL) {
52
- /* bdrv_drain_all() drains both src and target, and involves an
53
- * additional block_job_pause_all() */
54
- g_assert_cmpint(job->pause_count, ==, 3);
55
+ /* bdrv_drain_all() drains both src and target */
56
+ g_assert_cmpint(job->pause_count, ==, 2);
57
} else {
58
g_assert_cmpint(job->pause_count, ==, 1);
59
}
60
--
61
2.13.6
62
63
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
aio_disable_external() to the outermost drain section, but the block
3
driver callbacks are always called. bdrv_do_drained_end() must match
4
this behaviour, otherwise nodes stay drained even if begin/end calls
5
were balanced.
2
6
3
The graph must not change in these loops (or a QLIST_FOREACH_SAFE would
4
not even be enough). We now ensure this by only polling once in the
5
root bdrv_drained_end() call, so we can drop the _SAFE suffix. Doing so
6
makes it clear that the graph must not change.
7
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
8
---
11
block/io.c | 8 ++++----
9
block/io.c | 12 +++++++-----
12
1 file changed, 4 insertions(+), 4 deletions(-)
10
1 file changed, 7 insertions(+), 5 deletions(-)
13
11
14
diff --git a/block/io.c b/block/io.c
12
diff --git a/block/io.c b/block/io.c
15
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
16
--- a/block/io.c
14
--- a/block/io.c
17
+++ b/block/io.c
15
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
19
bool ignore_bds_parents,
17
20
int *drained_end_counter)
18
void bdrv_drained_end(BlockDriverState *bs)
21
{
19
{
22
- BdrvChild *c, *next;
20
+ int old_quiesce_counter;
23
+ BdrvChild *c;
21
+
24
22
if (qemu_in_coroutine()) {
25
- QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
23
bdrv_co_yield_to_drain(bs, false);
26
+ QLIST_FOREACH(c, &bs->parents, next_parent) {
24
return;
27
if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) {
25
}
28
continue;
26
assert(bs->quiesce_counter > 0);
29
}
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
30
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
28
- return;
31
BdrvChild *parent, bool ignore_bds_parents,
29
- }
32
int *drained_end_counter)
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
33
{
31
34
- BdrvChild *child, *next;
32
/* Re-enable things in child-to-parent order */
35
+ BdrvChild *child;
33
bdrv_drain_invoke(bs, false, false);
36
int old_quiesce_counter;
34
- bdrv_parent_drained_end(bs);
37
35
- aio_enable_external(bdrv_get_aio_context(bs));
38
assert(drained_end_counter != NULL);
36
+ if (old_quiesce_counter == 1) {
39
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
37
+ bdrv_parent_drained_end(bs);
40
if (recursive) {
38
+ aio_enable_external(bdrv_get_aio_context(bs));
41
assert(!ignore_bds_parents);
39
+ }
42
bs->recursive_quiesce_counter--;
40
}
43
- QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
41
44
+ QLIST_FOREACH(child, &bs->children, next) {
42
/*
45
bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents,
46
drained_end_counter);
47
}
48
--
43
--
49
2.20.1
44
2.13.6
50
45
51
46
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
2
3
When qemu quits, all throttling should be ignored. That means, if there
4
is a mirror job running from a throttled node, it should be cancelled
5
immediately and qemu close without blocking.
6
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
2
---
10
tests/qemu-iotests/218 | 55 ++++++++++++++++++++++++++++++++++++--
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
11
tests/qemu-iotests/218.out | 4 +++
4
1 file changed, 57 insertions(+)
12
2 files changed, 57 insertions(+), 2 deletions(-)
13
5
14
diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
15
index XXXXXXX..XXXXXXX 100755
7
index XXXXXXX..XXXXXXX 100644
16
--- a/tests/qemu-iotests/218
8
--- a/tests/test-bdrv-drain.c
17
+++ b/tests/qemu-iotests/218
9
+++ b/tests/test-bdrv-drain.c
18
@@ -XXX,XX +XXX,XX @@
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
19
# Creator/Owner: Max Reitz <mreitz@redhat.com>
11
enum drain_type {
20
12
BDRV_DRAIN_ALL,
21
import iotests
13
BDRV_DRAIN,
22
-from iotests import log
14
+ DRAIN_TYPE_MAX,
23
+from iotests import log, qemu_img, qemu_io_silent
15
};
24
16
25
-iotests.verify_platform(['linux'])
17
static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
26
+iotests.verify_image_format(supported_fmts=['qcow2', 'raw'])
18
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
27
19
test_quiesce_common(BDRV_DRAIN, false);
28
20
}
29
# Launches the VM, adds two null-co nodes (source and target), and
21
30
@@ -XXX,XX +XXX,XX @@ with iotests.VM() as vm:
22
+static void test_nested(void)
31
23
+{
32
log(vm.event_wait('BLOCK_JOB_CANCELLED'),
24
+ BlockBackend *blk;
33
filters=[iotests.filter_qmp_event])
25
+ BlockDriverState *bs, *backing;
26
+ BDRVTestState *s, *backing_s;
27
+ enum drain_type outer, inner;
34
+
28
+
35
+log('')
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
36
+log('=== Cancel mirror job from throttled node by quitting ===')
30
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
37
+log('')
31
+ &error_abort);
32
+ s = bs->opaque;
33
+ blk_insert_bs(blk, bs, &error_abort);
38
+
34
+
39
+with iotests.VM() as vm, \
35
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
40
+ iotests.FilePath('src.img') as src_img_path:
36
+ backing_s = backing->opaque;
37
+ bdrv_set_backing_hd(bs, backing, &error_abort);
41
+
38
+
42
+ assert qemu_img('create', '-f', iotests.imgfmt, src_img_path, '64M') == 0
39
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
43
+ assert qemu_io_silent('-f', iotests.imgfmt, src_img_path,
40
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
44
+ '-c', 'write -P 42 0M 64M') == 0
41
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
42
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
43
+ (inner != BDRV_DRAIN_ALL);
44
+ int backing_quiesce = 0;
45
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
46
+ (inner != BDRV_DRAIN);
45
+
47
+
46
+ vm.launch()
48
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
49
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
50
+ g_assert_cmpint(s->drain_count, ==, 0);
51
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
47
+
52
+
48
+ ret = vm.qmp('object-add', qom_type='throttle-group', id='tg',
53
+ do_drain_begin(outer, bs);
49
+ props={'x-bps-read': 4096})
54
+ do_drain_begin(inner, bs);
50
+ assert ret['return'] == {}
51
+
55
+
52
+ ret = vm.qmp('blockdev-add',
56
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
53
+ node_name='source',
57
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
54
+ driver=iotests.imgfmt,
58
+ g_assert_cmpint(s->drain_count, ==, 2);
55
+ file={
59
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
56
+ 'driver': 'file',
57
+ 'filename': src_img_path
58
+ })
59
+ assert ret['return'] == {}
60
+
60
+
61
+ ret = vm.qmp('blockdev-add',
61
+ do_drain_end(inner, bs);
62
+ node_name='throttled-source',
62
+ do_drain_end(outer, bs);
63
+ driver='throttle',
64
+ throttle_group='tg',
65
+ file='source')
66
+ assert ret['return'] == {}
67
+
63
+
68
+ ret = vm.qmp('blockdev-add',
64
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
69
+ node_name='target',
65
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
70
+ driver='null-co',
66
+ g_assert_cmpint(s->drain_count, ==, 0);
71
+ size=(64 * 1048576))
67
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
72
+ assert ret['return'] == {}
68
+ }
69
+ }
73
+
70
+
74
+ ret = vm.qmp('blockdev-mirror',
71
+ bdrv_unref(backing);
75
+ job_id='mirror',
72
+ bdrv_unref(bs);
76
+ device='throttled-source',
73
+ blk_unref(blk);
77
+ target='target',
74
+}
78
+ sync='full')
79
+ assert ret['return'] == {}
80
+
75
+
81
+ log(vm.qmp('quit'))
76
77
typedef struct TestBlockJob {
78
BlockJob common;
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
82
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
82
+
84
+
83
+ with iotests.Timeout(5, 'Timeout waiting for VM to quit'):
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
84
+ vm.shutdown(has_quit=True)
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
85
diff --git a/tests/qemu-iotests/218.out b/tests/qemu-iotests/218.out
87
86
index XXXXXXX..XXXXXXX 100644
87
--- a/tests/qemu-iotests/218.out
88
+++ b/tests/qemu-iotests/218.out
89
@@ -XXX,XX +XXX,XX @@ Cancelling job
90
Cancelling job
91
{"return": {}}
92
{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
93
+
94
+=== Cancel mirror job from throttled node by quitting ===
95
+
96
+{"return": {}}
97
--
88
--
98
2.20.1
89
2.13.6
99
90
100
91
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
This is in preparation for subtree drains, i.e. drained sections that
2
2
affect not only a single node, but recursively all child nodes, too.
3
These functions are not used outside of block/io.c, there is no reason
3
4
why they should be globally available.
4
Calling the parent callbacks for drain is pointless when we just came
5
5
from that parent node recursively and leads to multiple increases of
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
bs->quiesce_counter in a single drain call. Don't do it.
7
8
In order for this to work correctly, the parent callback must be called
9
for every bdrv_drain_begin/end() call, not only for the outermost one:
10
11
If we have a node N with two parents A and B, recursive draining of A
12
should cause the quiesce_counter of B to increase because its child N is
13
drained independently of B. If now B is recursively drained, too, A must
14
increase its quiesce_counter because N is drained independently of A
15
only now, even if N is going from quiesce_counter 1 to 2.
16
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
18
---
9
include/block/block.h | 18 ------------------
19
include/block/block.h | 4 ++--
10
block/io.c | 8 ++++----
20
block.c | 13 +++++++++----
11
2 files changed, 4 insertions(+), 22 deletions(-)
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
22
3 files changed, 45 insertions(+), 19 deletions(-)
12
23
13
diff --git a/include/block/block.h b/include/block/block.h
24
diff --git a/include/block/block.h b/include/block/block.h
14
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
15
--- a/include/block/block.h
26
--- a/include/block/block.h
16
+++ b/include/block/block.h
27
+++ b/include/block/block.h
17
@@ -XXX,XX +XXX,XX @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
18
void bdrv_io_plug(BlockDriverState *bs);
29
* Begin a quiesced section of all users of @bs. This is part of
19
void bdrv_io_unplug(BlockDriverState *bs);
30
* bdrv_drained_begin.
20
31
*/
21
-/**
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
22
- * bdrv_parent_drained_begin:
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
23
- *
34
24
- * Begin a quiesced section of all users of @bs. This is part of
25
- * bdrv_drained_begin.
26
- */
27
-void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
28
- bool ignore_bds_parents);
29
-
30
/**
35
/**
31
* bdrv_parent_drained_begin_single:
36
* bdrv_parent_drained_end:
32
*
37
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs);
33
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
38
* End a quiesced section of all users of @bs. This is part of
39
* bdrv_drained_end.
34
*/
40
*/
35
void bdrv_parent_drained_end_single(BdrvChild *c);
41
-void bdrv_parent_drained_end(BlockDriverState *bs);
36
42
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
37
-/**
43
38
- * bdrv_parent_drained_end:
39
- *
40
- * End a quiesced section of all users of @bs. This is part of
41
- * bdrv_drained_end.
42
- */
43
-void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
44
- bool ignore_bds_parents);
45
-
46
/**
44
/**
47
* bdrv_drain_poll:
45
* bdrv_drained_begin:
48
*
46
diff --git a/block.c b/block.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/block.c
49
+++ b/block.c
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
51
BlockDriverState *new_bs)
52
{
53
BlockDriverState *old_bs = child->bs;
54
+ int i;
55
56
if (old_bs && new_bs) {
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
58
}
59
if (old_bs) {
60
if (old_bs->quiesce_counter && child->role->drained_end) {
61
- child->role->drained_end(child);
62
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
63
+ child->role->drained_end(child);
64
+ }
65
}
66
if (child->role->detach) {
67
child->role->detach(child);
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
69
if (new_bs) {
70
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
71
if (new_bs->quiesce_counter && child->role->drained_begin) {
72
- child->role->drained_begin(child);
73
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
74
+ child->role->drained_begin(child);
75
+ }
76
}
77
78
if (child->role->attach) {
79
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
80
AioContext *ctx = bdrv_get_aio_context(bs);
81
82
aio_disable_external(ctx);
83
- bdrv_parent_drained_begin(bs);
84
+ bdrv_parent_drained_begin(bs, NULL);
85
bdrv_drain(bs); /* ensure there are no in-flight requests */
86
87
while (aio_poll(ctx, false)) {
88
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
89
*/
90
aio_context_acquire(new_context);
91
bdrv_attach_aio_context(bs, new_context);
92
- bdrv_parent_drained_end(bs);
93
+ bdrv_parent_drained_end(bs, NULL);
94
aio_enable_external(ctx);
95
aio_context_release(new_context);
96
}
49
diff --git a/block/io.c b/block/io.c
97
diff --git a/block/io.c b/block/io.c
50
index XXXXXXX..XXXXXXX 100644
98
index XXXXXXX..XXXXXXX 100644
51
--- a/block/io.c
99
--- a/block/io.c
52
+++ b/block/io.c
100
+++ b/block/io.c
53
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs);
101
@@ -XXX,XX +XXX,XX @@
54
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
102
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
55
int64_t offset, int bytes, BdrvRequestFlags flags);
103
int64_t offset, int bytes, BdrvRequestFlags flags);
56
104
57
-void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
105
-void bdrv_parent_drained_begin(BlockDriverState *bs)
58
- bool ignore_bds_parents)
106
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
59
+static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
60
+ bool ignore_bds_parents)
61
{
107
{
62
BdrvChild *c, *next;
108
BdrvChild *c, *next;
63
109
64
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end_single(BdrvChild *c)
110
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
65
}
111
+ if (c == ignore) {
66
}
112
+ continue;
67
113
+ }
68
-void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
114
if (c->role->drained_begin) {
69
- bool ignore_bds_parents)
115
c->role->drained_begin(c);
70
+static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
116
}
71
+ bool ignore_bds_parents)
117
}
118
}
119
120
-void bdrv_parent_drained_end(BlockDriverState *bs)
121
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
72
{
122
{
73
BdrvChild *c, *next;
123
BdrvChild *c, *next;
74
124
125
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
126
+ if (c == ignore) {
127
+ continue;
128
+ }
129
if (c->role->drained_end) {
130
c->role->drained_end(c);
131
}
132
@@ -XXX,XX +XXX,XX @@ typedef struct {
133
BlockDriverState *bs;
134
bool done;
135
bool begin;
136
+ BdrvChild *parent;
137
} BdrvCoDrainData;
138
139
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
140
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
141
return waited;
142
}
143
144
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
145
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
146
+
147
static void bdrv_co_drain_bh_cb(void *opaque)
148
{
149
BdrvCoDrainData *data = opaque;
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
151
152
bdrv_dec_in_flight(bs);
153
if (data->begin) {
154
- bdrv_drained_begin(bs);
155
+ bdrv_do_drained_begin(bs, data->parent);
156
} else {
157
- bdrv_drained_end(bs);
158
+ bdrv_do_drained_end(bs, data->parent);
159
}
160
161
data->done = true;
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
163
}
164
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
166
- bool begin)
167
+ bool begin, BdrvChild *parent)
168
{
169
BdrvCoDrainData data;
170
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
172
.bs = bs,
173
.done = false,
174
.begin = begin,
175
+ .parent = parent,
176
};
177
bdrv_inc_in_flight(bs);
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
180
assert(data.done);
181
}
182
183
-void bdrv_drained_begin(BlockDriverState *bs)
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
185
{
186
if (qemu_in_coroutine()) {
187
- bdrv_co_yield_to_drain(bs, true);
188
+ bdrv_co_yield_to_drain(bs, true, parent);
189
return;
190
}
191
192
/* Stop things in parent-to-child order */
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
194
aio_disable_external(bdrv_get_aio_context(bs));
195
- bdrv_parent_drained_begin(bs);
196
}
197
198
+ bdrv_parent_drained_begin(bs, parent);
199
bdrv_drain_invoke(bs, true, false);
200
bdrv_drain_recurse(bs);
201
}
202
203
-void bdrv_drained_end(BlockDriverState *bs)
204
+void bdrv_drained_begin(BlockDriverState *bs)
205
+{
206
+ bdrv_do_drained_begin(bs, NULL);
207
+}
208
+
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
210
{
211
int old_quiesce_counter;
212
213
if (qemu_in_coroutine()) {
214
- bdrv_co_yield_to_drain(bs, false);
215
+ bdrv_co_yield_to_drain(bs, false, parent);
216
return;
217
}
218
assert(bs->quiesce_counter > 0);
219
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
220
221
/* Re-enable things in child-to-parent order */
222
bdrv_drain_invoke(bs, false, false);
223
+ bdrv_parent_drained_end(bs, parent);
224
if (old_quiesce_counter == 1) {
225
- bdrv_parent_drained_end(bs);
226
aio_enable_external(bdrv_get_aio_context(bs));
227
}
228
}
229
230
+void bdrv_drained_end(BlockDriverState *bs)
231
+{
232
+ bdrv_do_drained_end(bs, NULL);
233
+}
234
+
235
/*
236
* Wait for pending requests to complete on a single BlockDriverState subtree,
237
* and suspend block driver's internal I/O until next request arrives.
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
239
/* Stop things in parent-to-child order */
240
aio_context_acquire(aio_context);
241
aio_disable_external(aio_context);
242
- bdrv_parent_drained_begin(bs);
243
+ bdrv_parent_drained_begin(bs, NULL);
244
bdrv_drain_invoke(bs, true, true);
245
aio_context_release(aio_context);
246
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
248
/* Re-enable things in child-to-parent order */
249
aio_context_acquire(aio_context);
250
bdrv_drain_invoke(bs, false, true);
251
- bdrv_parent_drained_end(bs);
252
+ bdrv_parent_drained_end(bs, NULL);
253
aio_enable_external(aio_context);
254
aio_context_release(aio_context);
255
}
75
--
256
--
76
2.20.1
257
2.13.6
77
258
78
259
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
until bdrv_drained_end().
2
4
3
We should never poll anywhere in bdrv_do_drained_end() (including its
5
Add a version that keeps the whole subtree drained. As of this commit,
4
recursive callees like bdrv_drain_invoke()), because it does not cope
6
graph changes cannot be allowed during a subtree drained section, but
5
well with graph changes. In fact, it has been written based on the
7
this will be fixed soon.
6
postulation that no graph changes will happen in it.
7
8
8
Instead, the callers that want to poll must poll, i.e. all currently
9
globally available wrappers: bdrv_drained_end(),
10
bdrv_subtree_drained_end(), bdrv_unapply_subtree_drain(), and
11
bdrv_drain_all_end(). Graph changes there do not matter.
12
13
They can poll simply by passing a pointer to a drained_end_counter and
14
wait until it reaches 0.
15
16
This patch also adds a non-polling global wrapper for
17
bdrv_do_drained_end() that takes a drained_end_counter pointer. We need
18
such a variant because now no function called anywhere from
19
bdrv_do_drained_end() must poll. This includes
20
BdrvChildRole.drained_end(), which already must not poll according to
21
its interface documentation, but bdrv_child_cb_drained_end() just
22
violates that by invoking bdrv_drained_end() (which does poll).
23
Therefore, BdrvChildRole.drained_end() must take a *drained_end_counter
24
parameter, which bdrv_child_cb_drained_end() can pass on to the new
25
bdrv_drained_end_no_poll() function.
26
27
Note that we now have a pattern of all drained_end-related functions
28
either polling or receiving a *drained_end_counter to let the caller
29
poll based on that.
30
31
A problem with a single poll loop is that when the drained section in
32
bdrv_set_aio_context_ignore() ends, some nodes in the subgraph may be in
33
the old contexts, while others are in the new context already. To let
34
the collective poll in bdrv_drained_end() work correctly, we must not
35
hold a lock to the old context, so that the old context can make
36
progress in case it is different from the current context.
37
38
(In the process, remove the comment saying that the current context is
39
always the old context, because it is wrong.)
40
41
In all other places, all nodes in a subtree must be in the same context,
42
so we can just poll that. The exception of course is
43
bdrv_drain_all_end(), but that always runs in the main context, so we
44
can just poll NULL (like bdrv_drain_all_begin() does).
45
46
Signed-off-by: Max Reitz <mreitz@redhat.com>
47
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
48
---
10
---
49
include/block/block.h | 25 ++++++++++++
11
include/block/block.h | 13 +++++++++++++
50
include/block/block_int.h | 6 ++-
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
51
block.c | 37 ++++++++++++++----
13
2 files changed, 56 insertions(+), 11 deletions(-)
52
block/block-backend.c | 6 +--
53
block/io.c | 80 ++++++++++++++++++++++++++++-----------
54
blockjob.c | 2 +-
55
6 files changed, 120 insertions(+), 36 deletions(-)
56
14
57
diff --git a/include/block/block.h b/include/block/block.h
15
diff --git a/include/block/block.h b/include/block/block.h
58
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
59
--- a/include/block/block.h
17
--- a/include/block/block.h
60
+++ b/include/block/block.h
18
+++ b/include/block/block.h
61
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
62
* bdrv_parent_drained_end_single:
20
void bdrv_drained_begin(BlockDriverState *bs);
63
*
21
64
* End a quiesced section for the parent of @c.
22
/**
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
24
+ * exclusive access to all child nodes as well.
65
+ *
25
+ *
66
+ * This polls @bs's AioContext until all scheduled sub-drained_ends
26
+ * Graph changes are not allowed during a subtree drain section.
67
+ * have settled, which may result in graph changes.
27
+ */
68
*/
28
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
69
void bdrv_parent_drained_end_single(BdrvChild *c);
29
+
70
30
+/**
71
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs);
72
* bdrv_drained_end:
31
* bdrv_drained_end:
73
*
32
*
74
* End a quiescent section started by bdrv_drained_begin().
33
* End a quiescent section started by bdrv_drained_begin().
75
+ *
76
+ * This polls @bs's AioContext until all scheduled sub-drained_ends
77
+ * have settled. On one hand, that may result in graph changes. On
78
+ * the other, this requires that all involved nodes (@bs and all of
79
+ * its parents) are in the same AioContext, and that the caller has
80
+ * acquired it.
81
+ * If there are any nodes that are in different contexts from @bs,
82
+ * these contexts must not be acquired.
83
*/
34
*/
84
void bdrv_drained_end(BlockDriverState *bs);
35
void bdrv_drained_end(BlockDriverState *bs);
85
36
86
+/**
37
+/**
87
+ * bdrv_drained_end_no_poll:
38
+ * End a quiescent section started by bdrv_subtree_drained_begin().
88
+ *
89
+ * Same as bdrv_drained_end(), but do not poll for the subgraph to
90
+ * actually become unquiesced. Therefore, no graph changes will occur
91
+ * with this function.
92
+ *
93
+ * *drained_end_counter is incremented for every background operation
94
+ * that is scheduled, and will be decremented for every operation once
95
+ * it settles. The caller must poll until it reaches 0. The counter
96
+ * should be accessed using atomic operations only.
97
+ */
39
+ */
98
+void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter);
40
+void bdrv_subtree_drained_end(BlockDriverState *bs);
99
+
41
+
100
/**
42
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
101
* End a quiescent section started by bdrv_subtree_drained_begin().
43
Error **errp);
102
*/
44
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
103
diff --git a/include/block/block_int.h b/include/block/block_int.h
104
index XXXXXXX..XXXXXXX 100644
105
--- a/include/block/block_int.h
106
+++ b/include/block/block_int.h
107
@@ -XXX,XX +XXX,XX @@ struct BdrvChildRole {
108
* These functions must not change the graph (and therefore also must not
109
* call aio_poll(), which could change the graph indirectly).
110
*
111
+ * If drained_end() schedules background operations, it must atomically
112
+ * increment *drained_end_counter for each such operation and atomically
113
+ * decrement it once the operation has settled.
114
+ *
115
* Note that this can be nested. If drained_begin() was called twice, new
116
* I/O is allowed only after drained_end() was called twice, too.
117
*/
118
void (*drained_begin)(BdrvChild *child);
119
- void (*drained_end)(BdrvChild *child);
120
+ void (*drained_end)(BdrvChild *child, int *drained_end_counter);
121
122
/*
123
* Returns whether the parent has pending requests for the child. This
124
diff --git a/block.c b/block.c
125
index XXXXXXX..XXXXXXX 100644
126
--- a/block.c
127
+++ b/block.c
128
@@ -XXX,XX +XXX,XX @@ static bool bdrv_child_cb_drained_poll(BdrvChild *child)
129
return bdrv_drain_poll(bs, false, NULL, false);
130
}
131
132
-static void bdrv_child_cb_drained_end(BdrvChild *child)
133
+static void bdrv_child_cb_drained_end(BdrvChild *child,
134
+ int *drained_end_counter)
135
{
136
BlockDriverState *bs = child->opaque;
137
- bdrv_drained_end(bs);
138
+ bdrv_drained_end_no_poll(bs, drained_end_counter);
139
}
140
141
static void bdrv_child_cb_attach(BdrvChild *child)
142
@@ -XXX,XX +XXX,XX @@ static void bdrv_attach_aio_context(BlockDriverState *bs,
143
void bdrv_set_aio_context_ignore(BlockDriverState *bs,
144
AioContext *new_context, GSList **ignore)
145
{
146
+ AioContext *old_context = bdrv_get_aio_context(bs);
147
+ AioContext *current_context = qemu_get_current_aio_context();
148
BdrvChild *child;
149
150
- if (bdrv_get_aio_context(bs) == new_context) {
151
+ if (old_context == new_context) {
152
return;
153
}
154
155
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs,
156
157
bdrv_detach_aio_context(bs);
158
159
- /* This function executes in the old AioContext so acquire the new one in
160
- * case it runs in a different thread.
161
- */
162
- aio_context_acquire(new_context);
163
+ /* Acquire the new context, if necessary */
164
+ if (current_context != new_context) {
165
+ aio_context_acquire(new_context);
166
+ }
167
+
168
bdrv_attach_aio_context(bs, new_context);
169
+
170
+ /*
171
+ * If this function was recursively called from
172
+ * bdrv_set_aio_context_ignore(), there may be nodes in the
173
+ * subtree that have not yet been moved to the new AioContext.
174
+ * Release the old one so bdrv_drained_end() can poll them.
175
+ */
176
+ if (current_context != old_context) {
177
+ aio_context_release(old_context);
178
+ }
179
+
180
bdrv_drained_end(bs);
181
- aio_context_release(new_context);
182
+
183
+ if (current_context != old_context) {
184
+ aio_context_acquire(old_context);
185
+ }
186
+ if (current_context != new_context) {
187
+ aio_context_release(new_context);
188
+ }
189
}
190
191
static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx,
192
diff --git a/block/block-backend.c b/block/block-backend.c
193
index XXXXXXX..XXXXXXX 100644
194
--- a/block/block-backend.c
195
+++ b/block/block-backend.c
196
@@ -XXX,XX +XXX,XX @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
197
}
198
static void blk_root_drained_begin(BdrvChild *child);
199
static bool blk_root_drained_poll(BdrvChild *child);
200
-static void blk_root_drained_end(BdrvChild *child);
201
+static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter);
202
203
static void blk_root_change_media(BdrvChild *child, bool load);
204
static void blk_root_resize(BdrvChild *child);
205
@@ -XXX,XX +XXX,XX @@ int blk_pread_unthrottled(BlockBackend *blk, int64_t offset, uint8_t *buf,
206
207
blk_root_drained_begin(blk->root);
208
ret = blk_pread(blk, offset, buf, count);
209
- blk_root_drained_end(blk->root);
210
+ blk_root_drained_end(blk->root, NULL);
211
return ret;
212
}
213
214
@@ -XXX,XX +XXX,XX @@ static bool blk_root_drained_poll(BdrvChild *child)
215
return !!blk->in_flight;
216
}
217
218
-static void blk_root_drained_end(BdrvChild *child)
219
+static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
220
{
221
BlockBackend *blk = child->opaque;
222
assert(blk->quiesce_counter);
223
diff --git a/block/io.c b/block/io.c
45
diff --git a/block/io.c b/block/io.c
224
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
225
--- a/block/io.c
47
--- a/block/io.c
226
+++ b/block/io.c
48
+++ b/block/io.c
227
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
49
@@ -XXX,XX +XXX,XX @@ typedef struct {
50
BlockDriverState *bs;
51
bool done;
52
bool begin;
53
+ bool recursive;
54
BdrvChild *parent;
55
} BdrvCoDrainData;
56
57
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
58
return waited;
59
}
60
61
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
62
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
63
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
64
+ BdrvChild *parent);
65
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
66
+ BdrvChild *parent);
67
68
static void bdrv_co_drain_bh_cb(void *opaque)
69
{
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
71
72
bdrv_dec_in_flight(bs);
73
if (data->begin) {
74
- bdrv_do_drained_begin(bs, data->parent);
75
+ bdrv_do_drained_begin(bs, data->recursive, data->parent);
76
} else {
77
- bdrv_do_drained_end(bs, data->parent);
78
+ bdrv_do_drained_end(bs, data->recursive, data->parent);
228
}
79
}
80
81
data->done = true;
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
229
}
83
}
230
84
231
-void bdrv_parent_drained_end_single(BdrvChild *c)
85
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
232
+static void bdrv_parent_drained_end_single_no_poll(BdrvChild *c,
86
- bool begin, BdrvChild *parent)
233
+ int *drained_end_counter)
87
+ bool begin, bool recursive,
88
+ BdrvChild *parent)
234
{
89
{
235
assert(c->parent_quiesce_counter > 0);
90
BdrvCoDrainData data;
236
c->parent_quiesce_counter--;
91
237
if (c->role->drained_end) {
92
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
238
- c->role->drained_end(c);
93
.bs = bs,
239
+ c->role->drained_end(c, drained_end_counter);
94
.done = false,
95
.begin = begin,
96
+ .recursive = recursive,
97
.parent = parent,
98
};
99
bdrv_inc_in_flight(bs);
100
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
101
assert(data.done);
102
}
103
104
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
105
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
106
+ BdrvChild *parent)
107
{
108
+ BdrvChild *child, *next;
109
+
110
if (qemu_in_coroutine()) {
111
- bdrv_co_yield_to_drain(bs, true, parent);
112
+ bdrv_co_yield_to_drain(bs, true, recursive, parent);
113
return;
240
}
114
}
115
116
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
117
bdrv_parent_drained_begin(bs, parent);
118
bdrv_drain_invoke(bs, true, false);
119
bdrv_drain_recurse(bs);
120
+
121
+ if (recursive) {
122
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
123
+ bdrv_do_drained_begin(child->bs, true, child);
124
+ }
125
+ }
241
}
126
}
242
127
243
+void bdrv_parent_drained_end_single(BdrvChild *c)
128
void bdrv_drained_begin(BlockDriverState *bs)
244
+{
129
{
245
+ int drained_end_counter = 0;
130
- bdrv_do_drained_begin(bs, NULL);
246
+ bdrv_parent_drained_end_single_no_poll(c, &drained_end_counter);
131
+ bdrv_do_drained_begin(bs, false, NULL);
247
+ BDRV_POLL_WHILE(c->bs, atomic_read(&drained_end_counter) > 0);
248
+}
132
+}
249
+
133
+
250
static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
251
- bool ignore_bds_parents)
135
+{
252
+ bool ignore_bds_parents,
136
+ bdrv_do_drained_begin(bs, true, NULL);
253
+ int *drained_end_counter)
137
}
138
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
141
+ BdrvChild *parent)
254
{
142
{
255
BdrvChild *c, *next;
143
+ BdrvChild *child, *next;
256
144
int old_quiesce_counter;
257
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
145
258
if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) {
146
if (qemu_in_coroutine()) {
259
continue;
147
- bdrv_co_yield_to_drain(bs, false, parent);
260
}
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
261
- bdrv_parent_drained_end_single(c);
149
return;
262
+ bdrv_parent_drained_end_single_no_poll(c, drained_end_counter);
263
}
150
}
151
assert(bs->quiesce_counter > 0);
152
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
153
if (old_quiesce_counter == 1) {
154
aio_enable_external(bdrv_get_aio_context(bs));
155
}
156
+
157
+ if (recursive) {
158
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
159
+ bdrv_do_drained_end(child->bs, true, child);
160
+ }
161
+ }
264
}
162
}
265
266
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
267
atomic_mb_set(&data->done, true);
268
bdrv_dec_in_flight(bs);
269
270
- if (data->drained_end_counter) {
271
+ if (!data->begin) {
272
atomic_dec(data->drained_end_counter);
273
}
274
275
- if (data->begin || data->drained_end_counter) {
276
- g_free(data);
277
- }
278
+ g_free(data);
279
}
280
281
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
282
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
283
.drained_end_counter = drained_end_counter,
284
};
285
286
- if (!begin && drained_end_counter) {
287
+ if (!begin) {
288
atomic_inc(drained_end_counter);
289
}
290
291
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin,
292
bdrv_inc_in_flight(bs);
293
data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data);
294
aio_co_schedule(bdrv_get_aio_context(bs), data->co);
295
-
296
- /*
297
- * TODO: Drop this and make callers pass @drained_end_counter and poll
298
- * themselves
299
- */
300
- if (!begin && !drained_end_counter) {
301
- BDRV_POLL_WHILE(bs, !data->done);
302
- g_free(data);
303
- }
304
}
305
306
/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */
307
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
308
}
309
bdrv_dec_in_flight(bs);
310
if (data->begin) {
311
+ assert(!data->drained_end_counter);
312
bdrv_do_drained_begin(bs, data->recursive, data->parent,
313
data->ignore_bds_parents, data->poll);
314
} else {
315
+ assert(!data->poll);
316
bdrv_do_drained_end(bs, data->recursive, data->parent,
317
data->ignore_bds_parents,
318
data->drained_end_counter);
319
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
320
bdrv_do_drained_begin(bs, true, NULL, false, true);
321
}
322
323
+/**
324
+ * This function does not poll, nor must any of its recursively called
325
+ * functions. The *drained_end_counter pointee will be incremented
326
+ * once for every background operation scheduled, and decremented once
327
+ * the operation settles. Therefore, the pointer must remain valid
328
+ * until the pointee reaches 0. That implies that whoever sets up the
329
+ * pointee has to poll until it is 0.
330
+ *
331
+ * We use atomic operations to access *drained_end_counter, because
332
+ * (1) when called from bdrv_set_aio_context_ignore(), the subgraph of
333
+ * @bs may contain nodes in different AioContexts,
334
+ * (2) bdrv_drain_all_end() uses the same counter for all nodes,
335
+ * regardless of which AioContext they are in.
336
+ */
337
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
338
BdrvChild *parent, bool ignore_bds_parents,
339
int *drained_end_counter)
340
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
341
BdrvChild *child, *next;
342
int old_quiesce_counter;
343
344
+ assert(drained_end_counter != NULL);
345
+
346
if (qemu_in_coroutine()) {
347
bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents,
348
false, drained_end_counter);
349
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
350
351
/* Re-enable things in child-to-parent order */
352
bdrv_drain_invoke(bs, false, drained_end_counter);
353
- bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
354
+ bdrv_parent_drained_end(bs, parent, ignore_bds_parents,
355
+ drained_end_counter);
356
357
old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
358
if (old_quiesce_counter == 1) {
359
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
360
163
361
void bdrv_drained_end(BlockDriverState *bs)
164
void bdrv_drained_end(BlockDriverState *bs)
362
{
165
{
363
- bdrv_do_drained_end(bs, false, NULL, false, NULL);
166
- bdrv_do_drained_end(bs, NULL);
364
+ int drained_end_counter = 0;
167
+ bdrv_do_drained_end(bs, false, NULL);
365
+ bdrv_do_drained_end(bs, false, NULL, false, &drained_end_counter);
366
+ BDRV_POLL_WHILE(bs, atomic_read(&drained_end_counter) > 0);
367
+}
168
+}
368
+
169
+
369
+void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter)
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
370
+{
171
+{
371
+ bdrv_do_drained_end(bs, false, NULL, false, drained_end_counter);
172
+ bdrv_do_drained_end(bs, true, NULL);
372
}
173
}
373
174
374
void bdrv_subtree_drained_end(BlockDriverState *bs)
375
{
376
- bdrv_do_drained_end(bs, true, NULL, false, NULL);
377
+ int drained_end_counter = 0;
378
+ bdrv_do_drained_end(bs, true, NULL, false, &drained_end_counter);
379
+ BDRV_POLL_WHILE(bs, atomic_read(&drained_end_counter) > 0);
380
}
381
382
void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
383
@@ -XXX,XX +XXX,XX @@ void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
384
385
void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
386
{
387
+ int drained_end_counter = 0;
388
int i;
389
390
for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
391
- bdrv_do_drained_end(child->bs, true, child, false, NULL);
392
+ bdrv_do_drained_end(child->bs, true, child, false,
393
+ &drained_end_counter);
394
}
395
+
396
+ BDRV_POLL_WHILE(child->bs, atomic_read(&drained_end_counter) > 0);
397
}
398
399
/*
175
/*
400
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
401
void bdrv_drain_all_end(void)
402
{
403
BlockDriverState *bs = NULL;
404
+ int drained_end_counter = 0;
405
406
while ((bs = bdrv_next_all_states(bs))) {
407
AioContext *aio_context = bdrv_get_aio_context(bs);
408
409
aio_context_acquire(aio_context);
410
- bdrv_do_drained_end(bs, false, NULL, true, NULL);
411
+ bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter);
412
aio_context_release(aio_context);
413
}
414
415
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
416
+ AIO_WAIT_WHILE(NULL, atomic_read(&drained_end_counter) > 0);
417
+
418
assert(bdrv_drain_all_count > 0);
419
bdrv_drain_all_count--;
420
}
421
diff --git a/blockjob.c b/blockjob.c
422
index XXXXXXX..XXXXXXX 100644
423
--- a/blockjob.c
424
+++ b/blockjob.c
425
@@ -XXX,XX +XXX,XX @@ static bool child_job_drained_poll(BdrvChild *c)
426
}
427
}
428
429
-static void child_job_drained_end(BdrvChild *c)
430
+static void child_job_drained_end(BdrvChild *c, int *drained_end_counter)
431
{
432
BlockJob *job = c->opaque;
433
job_resume(&job->job);
434
--
176
--
435
2.20.1
177
2.13.6
436
178
437
179
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Add a subtree drain version to the existing test cases.
2
2
3
Before the previous patches, the first case resulted in a failed
4
assertion (which is noted as qemu receiving a SIGABRT in the test
5
output), and the second usually triggered a segmentation fault.
6
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
4
---
10
tests/qemu-iotests/040 | 40 +++++++++++++++++++++++++++++++++++++-
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
11
tests/qemu-iotests/040.out | 4 ++--
6
1 file changed, 26 insertions(+), 1 deletion(-)
12
2 files changed, 41 insertions(+), 3 deletions(-)
13
7
14
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
15
index XXXXXXX..XXXXXXX 100755
9
index XXXXXXX..XXXXXXX 100644
16
--- a/tests/qemu-iotests/040
10
--- a/tests/test-bdrv-drain.c
17
+++ b/tests/qemu-iotests/040
11
+++ b/tests/test-bdrv-drain.c
18
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(ImageCommitTestCase):
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
19
13
enum drain_type {
20
self.vm.add_device("scsi-hd,id=scsi0,drive=drive0")
14
BDRV_DRAIN_ALL,
21
self.vm.launch()
15
BDRV_DRAIN,
22
+ self.has_quit = False
16
+ BDRV_SUBTREE_DRAIN,
23
17
DRAIN_TYPE_MAX,
24
def tearDown(self):
18
};
25
- self.vm.shutdown()
19
26
+ self.vm.shutdown(has_quit=self.has_quit)
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
27
os.remove(test_img)
21
switch (drain_type) {
28
os.remove(mid_img)
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
29
os.remove(backing_img)
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
30
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(ImageCommitTestCase):
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
31
self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
25
default: g_assert_not_reached();
32
self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
26
}
33
27
}
34
+ def test_commit_with_filter_and_quit(self):
28
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
35
+ result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg')
29
switch (drain_type) {
36
+ self.assert_qmp(result, 'return', {})
30
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
31
case BDRV_DRAIN: bdrv_drained_end(bs); break;
32
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
33
default: g_assert_not_reached();
34
}
35
}
36
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
37
test_drv_cb_common(BDRV_DRAIN, false);
38
}
39
40
+static void test_drv_cb_drain_subtree(void)
41
+{
42
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
43
+}
37
+
44
+
38
+ # Add a filter outside of the backing chain
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
39
+ result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid')
46
{
40
+ self.assert_qmp(result, 'return', {})
47
BlockBackend *blk;
48
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
49
test_quiesce_common(BDRV_DRAIN, false);
50
}
51
52
+static void test_quiesce_drain_subtree(void)
53
+{
54
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
55
+}
41
+
56
+
42
+ result = self.vm.qmp('block-commit', device='drive0')
57
static void test_nested(void)
43
+ self.assert_qmp(result, 'return', {})
58
{
59
BlockBackend *blk;
60
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
61
/* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
62
int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
63
(inner != BDRV_DRAIN_ALL);
64
- int backing_quiesce = 0;
65
+ int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) +
66
+ (inner == BDRV_SUBTREE_DRAIN);
67
int backing_cb_cnt = (outer != BDRV_DRAIN) +
68
(inner != BDRV_DRAIN);
69
70
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_drain(void)
71
test_blockjob_common(BDRV_DRAIN);
72
}
73
74
+static void test_blockjob_drain_subtree(void)
75
+{
76
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
77
+}
44
+
78
+
45
+ # Quit immediately, thus forcing a simultaneous cancel of the
79
int main(int argc, char **argv)
46
+ # block job and a bdrv_drain_all()
80
{
47
+ result = self.vm.qmp('quit')
81
bdrv_init();
48
+ self.assert_qmp(result, 'return', {})
82
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
49
+
83
50
+ self.has_quit = True
84
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
51
+
85
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
52
+ # Same as above, but this time we add the filter after starting the job
86
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
53
+ def test_commit_plus_filter_and_quit(self):
87
+ test_drv_cb_drain_subtree);
54
+ result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg')
88
55
+ self.assert_qmp(result, 'return', {})
89
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
56
+
90
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
57
+ result = self.vm.qmp('block-commit', device='drive0')
91
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
58
+ self.assert_qmp(result, 'return', {})
92
+ test_quiesce_drain_subtree);
59
+
93
60
+ # Add a filter outside of the backing chain
94
g_test_add_func("/bdrv-drain/nested", test_nested);
61
+ result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid')
95
62
+ self.assert_qmp(result, 'return', {})
96
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
63
+
97
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
64
+ # Quit immediately, thus forcing a simultaneous cancel of the
98
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
65
+ # block job and a bdrv_drain_all()
99
+ test_blockjob_drain_subtree);
66
+ result = self.vm.qmp('quit')
100
67
+ self.assert_qmp(result, 'return', {})
101
return g_test_run();
68
+
102
}
69
+ self.has_quit = True
70
+
71
def test_device_not_found(self):
72
result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img)
73
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
74
diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out
75
index XXXXXXX..XXXXXXX 100644
76
--- a/tests/qemu-iotests/040.out
77
+++ b/tests/qemu-iotests/040.out
78
@@ -XXX,XX +XXX,XX @@
79
-...........................................
80
+...............................................
81
----------------------------------------------------------------------
82
-Ran 43 tests
83
+Ran 47 tests
84
85
OK
86
--
103
--
87
2.20.1
104
2.13.6
88
105
89
106
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
If bdrv_do_drained_begin/end() are called in coroutine context, they
2
first use a BH to get out of the coroutine context. Call some existing
3
tests again from a coroutine to cover this code path.
2
4
3
If a test has issued a quit command already (which may be useful to do
4
explicitly because the test wants to show its effects),
5
QEMUMachine.shutdown() should not do so again. Otherwise, the VM may
6
well return an ECONNRESET which will lead QEMUMachine.shutdown() to
7
killing it, which then turns into a "qemu received signal 9" line.
8
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
6
---
12
python/qemu/machine.py | 5 +++--
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
13
tests/qemu-iotests/255 | 2 +-
8
1 file changed, 59 insertions(+)
14
2 files changed, 4 insertions(+), 3 deletions(-)
15
9
16
diff --git a/python/qemu/machine.py b/python/qemu/machine.py
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
17
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
18
--- a/python/qemu/machine.py
12
--- a/tests/test-bdrv-drain.c
19
+++ b/python/qemu/machine.py
13
+++ b/tests/test-bdrv-drain.c
20
@@ -XXX,XX +XXX,XX @@ class QEMUMachine(object):
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
21
self._load_io_log()
15
*aio_ret = ret;
22
self._post_shutdown()
16
}
23
17
24
- def shutdown(self):
18
+typedef struct CallInCoroutineData {
25
+ def shutdown(self, has_quit=False):
19
+ void (*entry)(void);
26
"""
20
+ bool done;
27
Terminate the VM and clean up
21
+} CallInCoroutineData;
28
"""
22
+
29
if self.is_running():
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
30
try:
24
+{
31
- self._qmp.cmd('quit')
25
+ CallInCoroutineData *data = opaque;
32
+ if not has_quit:
26
+
33
+ self._qmp.cmd('quit')
27
+ data->entry();
34
self._qmp.close()
28
+ data->done = true;
35
except:
29
+}
36
self._popen.kill()
30
+
37
diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255
31
+static void call_in_coroutine(void (*entry)(void))
38
index XXXXXXX..XXXXXXX 100755
32
+{
39
--- a/tests/qemu-iotests/255
33
+ Coroutine *co;
40
+++ b/tests/qemu-iotests/255
34
+ CallInCoroutineData data = {
41
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('src.qcow2') as src_path, \
35
+ .entry = entry,
42
vm.qmp_log('block-job-cancel', device='job0')
36
+ .done = false,
43
vm.qmp_log('quit')
37
+ };
44
38
+
45
- vm.shutdown()
39
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
46
+ vm.shutdown(has_quit=True)
40
+ qemu_coroutine_enter(co);
41
+ while (!data.done) {
42
+ aio_poll(qemu_get_aio_context(), true);
43
+ }
44
+}
45
+
46
enum drain_type {
47
BDRV_DRAIN_ALL,
48
BDRV_DRAIN,
49
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_subtree(void)
50
test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
51
}
52
53
+static void test_drv_cb_co_drain(void)
54
+{
55
+ call_in_coroutine(test_drv_cb_drain);
56
+}
57
+
58
+static void test_drv_cb_co_drain_subtree(void)
59
+{
60
+ call_in_coroutine(test_drv_cb_drain_subtree);
61
+}
62
+
63
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
64
{
65
BlockBackend *blk;
66
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void)
67
test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
68
}
69
70
+static void test_quiesce_co_drain(void)
71
+{
72
+ call_in_coroutine(test_quiesce_drain);
73
+}
74
+
75
+static void test_quiesce_co_drain_subtree(void)
76
+{
77
+ call_in_coroutine(test_quiesce_drain_subtree);
78
+}
79
+
80
static void test_nested(void)
81
{
82
BlockBackend *blk;
83
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
84
g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
85
test_drv_cb_drain_subtree);
86
87
+ // XXX bdrv_drain_all() doesn't work in coroutine context
88
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
89
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
90
+ test_drv_cb_co_drain_subtree);
91
+
92
+
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
94
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
95
g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
96
test_quiesce_drain_subtree);
97
98
+ // XXX bdrv_drain_all() doesn't work in coroutine context
99
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
100
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
101
+ test_quiesce_co_drain_subtree);
102
+
103
g_test_add_func("/bdrv-drain/nested", test_nested);
104
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
47
--
106
--
48
2.20.1
107
2.13.6
49
108
50
109
diff view generated by jsdifflib
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
1
Test that drain sections are correctly propagated through the graph.
2
2
3
The Valgrind tool reports about the uninitialised buffer 'buf'
4
instantiated on the stack of the function guess_disk_lchs().
5
Pass 'read-zeroes=on' to the null block driver to make it deterministic.
6
The output of the tests 051, 186 and 227 now includes the parameter
7
'read-zeroes'. So, the benchmark output files are being changed too.
8
9
Suggested-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
4
---
13
tests/qemu-iotests/051 | 10 +--
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
14
tests/qemu-iotests/051.pc.out | 10 +--
6
1 file changed, 74 insertions(+)
15
tests/qemu-iotests/093 | 9 +-
16
tests/qemu-iotests/136 | 1 +
17
tests/qemu-iotests/186 | 20 ++---
18
tests/qemu-iotests/186.out | 152 +++++++++++++++++-----------------
19
tests/qemu-iotests/227 | 4 +-
20
tests/qemu-iotests/227.out | 4 +-
21
tests/qemu-iotests/238 | 2 +-
22
tests/qemu-iotests/240 | 8 +-
23
10 files changed, 111 insertions(+), 109 deletions(-)
24
7
25
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
26
index XXXXXXX..XXXXXXX 100755
27
--- a/tests/qemu-iotests/051
28
+++ b/tests/qemu-iotests/051
29
@@ -XXX,XX +XXX,XX @@ echo
30
# Cannot use the test image because cache=none might not work on the host FS
31
# Use cdrom so that we won't get errors about missing media
32
33
-run_qemu -drive driver=null-co,cache=none
34
-run_qemu -drive driver=null-co,cache=directsync
35
-run_qemu -drive driver=null-co,cache=writeback
36
-run_qemu -drive driver=null-co,cache=writethrough
37
-run_qemu -drive driver=null-co,cache=unsafe
38
+run_qemu -drive driver=null-co,read-zeroes=on,cache=none
39
+run_qemu -drive driver=null-co,read-zeroes=on,cache=directsync
40
+run_qemu -drive driver=null-co,read-zeroes=on,cache=writeback
41
+run_qemu -drive driver=null-co,read-zeroes=on,cache=writethrough
42
+run_qemu -drive driver=null-co,read-zeroes=on,cache=unsafe
43
run_qemu -drive driver=null-co,cache=invalid_value
44
45
# Can't test direct=on here because O_DIRECT might not be supported on this FS
46
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
47
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
48
--- a/tests/qemu-iotests/051.pc.out
10
--- a/tests/test-bdrv-drain.c
49
+++ b/tests/qemu-iotests/051.pc.out
11
+++ b/tests/test-bdrv-drain.c
50
@@ -XXX,XX +XXX,XX @@ QEMU X.Y.Z monitor - type 'help' for more information
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
51
13
blk_unref(blk);
52
=== Cache modes ===
53
54
-Testing: -drive driver=null-co,cache=none
55
+Testing: -drive driver=null-co,read-zeroes=on,cache=none
56
QEMU X.Y.Z monitor - type 'help' for more information
57
(qemu) quit
58
59
-Testing: -drive driver=null-co,cache=directsync
60
+Testing: -drive driver=null-co,read-zeroes=on,cache=directsync
61
QEMU X.Y.Z monitor - type 'help' for more information
62
(qemu) quit
63
64
-Testing: -drive driver=null-co,cache=writeback
65
+Testing: -drive driver=null-co,read-zeroes=on,cache=writeback
66
QEMU X.Y.Z monitor - type 'help' for more information
67
(qemu) quit
68
69
-Testing: -drive driver=null-co,cache=writethrough
70
+Testing: -drive driver=null-co,read-zeroes=on,cache=writethrough
71
QEMU X.Y.Z monitor - type 'help' for more information
72
(qemu) quit
73
74
-Testing: -drive driver=null-co,cache=unsafe
75
+Testing: -drive driver=null-co,read-zeroes=on,cache=unsafe
76
QEMU X.Y.Z monitor - type 'help' for more information
77
(qemu) quit
78
79
diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
80
index XXXXXXX..XXXXXXX 100755
81
--- a/tests/qemu-iotests/093
82
+++ b/tests/qemu-iotests/093
83
@@ -XXX,XX +XXX,XX @@ class ThrottleTestCase(iotests.QMPTestCase):
84
def setUp(self):
85
self.vm = iotests.VM()
86
for i in range(0, self.max_drives):
87
- self.vm.add_drive(self.test_img)
88
+ self.vm.add_drive(self.test_img, "file.read-zeroes=on")
89
self.vm.launch()
90
91
def tearDown(self):
92
@@ -XXX,XX +XXX,XX @@ class ThrottleTestGroupNames(iotests.QMPTestCase):
93
def setUp(self):
94
self.vm = iotests.VM()
95
for i in range(0, self.max_drives):
96
- self.vm.add_drive(self.test_img, "throttling.iops-total=100")
97
+ self.vm.add_drive(self.test_img,
98
+ "throttling.iops-total=100,file.read-zeroes=on")
99
self.vm.launch()
100
101
def tearDown(self):
102
@@ -XXX,XX +XXX,XX @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase):
103
def test_removable_media(self):
104
# Add a couple of dummy nodes named cd0 and cd1
105
result = self.vm.qmp("blockdev-add", driver="null-aio",
106
- node_name="cd0")
107
+ read_zeroes=True, node_name="cd0")
108
self.assert_qmp(result, 'return', {})
109
result = self.vm.qmp("blockdev-add", driver="null-aio",
110
- node_name="cd1")
111
+ read_zeroes=True, node_name="cd1")
112
self.assert_qmp(result, 'return', {})
113
114
# Attach a CD drive with cd0 inserted
115
diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136
116
index XXXXXXX..XXXXXXX 100755
117
--- a/tests/qemu-iotests/136
118
+++ b/tests/qemu-iotests/136
119
@@ -XXX,XX +XXX,XX @@ sector = "%d"
120
(self.account_invalid and "on" or "off"))
121
drive_args.append("stats-account-failed=%s" %
122
(self.account_failed and "on" or "off"))
123
+ drive_args.append("file.image.read-zeroes=on")
124
self.create_blkdebug_file()
125
self.vm = iotests.VM().add_drive('blkdebug:%s:%s' %
126
(blkdebug_file, self.test_img),
127
diff --git a/tests/qemu-iotests/186 b/tests/qemu-iotests/186
128
index XXXXXXX..XXXXXXX 100755
129
--- a/tests/qemu-iotests/186
130
+++ b/tests/qemu-iotests/186
131
@@ -XXX,XX +XXX,XX @@ echo "=== -blockdev/-device=<node-name> ==="
132
echo
133
134
for dev in $fixed $removable; do
135
- check_info_block -blockdev driver=null-co,node-name=null -device $dev,drive=null
136
- check_info_block -blockdev driver=null-co,node-name=null -device $dev,drive=null,id=qdev_id
137
+ check_info_block -blockdev driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=null
138
+ check_info_block -blockdev driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=null,id=qdev_id
139
done
140
141
echo
142
@@ -XXX,XX +XXX,XX @@ echo
143
# This creates two BlockBackends that will show up in 'info block'!
144
# A monitor-owned one from -drive, and anonymous one from -device
145
for dev in $fixed $removable; do
146
- check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=null,id=qdev_id
147
+ check_info_block -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=null,id=qdev_id
148
done
149
150
echo
151
@@ -XXX,XX +XXX,XX @@ echo "=== -drive if=none/-device=<bb-name> (with medium) ==="
152
echo
153
154
for dev in $fixed $removable; do
155
- check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=none0
156
- check_info_block -drive if=none,driver=null-co,node-name=null -device $dev,drive=none0,id=qdev_id
157
+ check_info_block -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=none0
158
+ check_info_block -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device $dev,drive=none0,id=qdev_id
159
done
160
161
echo
162
@@ -XXX,XX +XXX,XX @@ echo "=== -drive if=... ==="
163
echo
164
165
check_info_block -drive if=floppy
166
-check_info_block -drive if=floppy,driver=null-co
167
+check_info_block -drive if=floppy,driver=null-co,read-zeroes=on
168
169
-check_info_block -drive if=ide,driver=null-co
170
+check_info_block -drive if=ide,driver=null-co,read-zeroes=on
171
check_info_block -drive if=ide,media=cdrom
172
-check_info_block -drive if=ide,driver=null-co,media=cdrom
173
+check_info_block -drive if=ide,driver=null-co,read-zeroes=on,media=cdrom
174
175
-check_info_block -drive if=virtio,driver=null-co
176
+check_info_block -drive if=virtio,driver=null-co,read-zeroes=on
177
178
-check_info_block -drive if=pflash,driver=null-co,size=1M
179
+check_info_block -drive if=pflash,driver=null-co,read-zeroes=on,size=1M
180
181
# success, all done
182
echo "*** done"
183
diff --git a/tests/qemu-iotests/186.out b/tests/qemu-iotests/186.out
184
index XXXXXXX..XXXXXXX 100644
185
--- a/tests/qemu-iotests/186.out
186
+++ b/tests/qemu-iotests/186.out
187
@@ -XXX,XX +XXX,XX @@ qdev_id: [not inserted]
188
189
=== -blockdev/-device=<node-name> ===
190
191
-Testing: -blockdev driver=null-co,node-name=null -device ide-hd,drive=null
192
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=null
193
QEMU X.Y.Z monitor - type 'help' for more information
194
(qemu) info block
195
-null: null-co:// (null-co)
196
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
197
Attached to: PATH
198
Cache mode: writeback
199
(qemu) quit
200
201
-Testing: -blockdev driver=null-co,node-name=null -device ide-hd,drive=null,id=qdev_id
202
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=null,id=qdev_id
203
QEMU X.Y.Z monitor - type 'help' for more information
204
(qemu) info block
205
-null: null-co:// (null-co)
206
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
207
Attached to: qdev_id
208
Cache mode: writeback
209
(qemu) quit
210
211
-Testing: -blockdev driver=null-co,node-name=null -device scsi-hd,drive=null
212
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=null
213
QEMU X.Y.Z monitor - type 'help' for more information
214
(qemu) info block
215
-null: null-co:// (null-co)
216
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
217
Attached to: PATH
218
Cache mode: writeback
219
(qemu) quit
220
221
-Testing: -blockdev driver=null-co,node-name=null -device scsi-hd,drive=null,id=qdev_id
222
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=null,id=qdev_id
223
QEMU X.Y.Z monitor - type 'help' for more information
224
(qemu) info block
225
-null: null-co:// (null-co)
226
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
227
Attached to: qdev_id
228
Cache mode: writeback
229
(qemu) quit
230
231
-Testing: -blockdev driver=null-co,node-name=null -device virtio-blk-pci,drive=null
232
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=null
233
QEMU X.Y.Z monitor - type 'help' for more information
234
(qemu) info block
235
-null: null-co:// (null-co)
236
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
237
Attached to: PATH
238
Cache mode: writeback
239
(qemu) quit
240
241
-Testing: -blockdev driver=null-co,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
242
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
243
QEMU X.Y.Z monitor - type 'help' for more information
244
(qemu) info block
245
-null: null-co:// (null-co)
246
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
247
Attached to: PATH
248
Cache mode: writeback
249
(qemu) quit
250
251
-Testing: -blockdev driver=null-co,node-name=null -device floppy,drive=null
252
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=null
253
QEMU X.Y.Z monitor - type 'help' for more information
254
(qemu) info block
255
-null: null-co:// (null-co)
256
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
257
Attached to: PATH
258
Removable device: not locked, tray closed
259
Cache mode: writeback
260
(qemu) quit
261
262
-Testing: -blockdev driver=null-co,node-name=null -device floppy,drive=null,id=qdev_id
263
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=null,id=qdev_id
264
QEMU X.Y.Z monitor - type 'help' for more information
265
(qemu) info block
266
-null: null-co:// (null-co)
267
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
268
Attached to: qdev_id
269
Removable device: not locked, tray closed
270
Cache mode: writeback
271
(qemu) quit
272
273
-Testing: -blockdev driver=null-co,node-name=null -device ide-cd,drive=null
274
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=null
275
QEMU X.Y.Z monitor - type 'help' for more information
276
(qemu) info block
277
-null: null-co:// (null-co)
278
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
279
Attached to: PATH
280
Removable device: not locked, tray closed
281
Cache mode: writeback
282
(qemu) quit
283
284
-Testing: -blockdev driver=null-co,node-name=null -device ide-cd,drive=null,id=qdev_id
285
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=null,id=qdev_id
286
QEMU X.Y.Z monitor - type 'help' for more information
287
(qemu) info block
288
-null: null-co:// (null-co)
289
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
290
Attached to: qdev_id
291
Removable device: not locked, tray closed
292
Cache mode: writeback
293
(qemu) quit
294
295
-Testing: -blockdev driver=null-co,node-name=null -device scsi-cd,drive=null
296
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=null
297
QEMU X.Y.Z monitor - type 'help' for more information
298
(qemu) info block
299
-null: null-co:// (null-co)
300
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
301
Attached to: PATH
302
Removable device: not locked, tray closed
303
Cache mode: writeback
304
(qemu) quit
305
306
-Testing: -blockdev driver=null-co,node-name=null -device scsi-cd,drive=null,id=qdev_id
307
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=null,id=qdev_id
308
QEMU X.Y.Z monitor - type 'help' for more information
309
(qemu) info block
310
-null: null-co:// (null-co)
311
+null: json:{"read-zeroes": true, "driver": "null-co"} (null-co)
312
Attached to: qdev_id
313
Removable device: not locked, tray closed
314
Cache mode: writeback
315
@@ -XXX,XX +XXX,XX @@ null: null-co:// (null-co)
316
317
=== -drive if=none/-device=<node-name> ===
318
319
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=null,id=qdev_id
320
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=null,id=qdev_id
321
QEMU X.Y.Z monitor - type 'help' for more information
322
(qemu) info block
323
-none0 (null): null-co:// (null-co)
324
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
325
Removable device: not locked, tray closed
326
Cache mode: writeback
327
328
-null: null-co:// (null-co)
329
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
330
Attached to: qdev_id
331
Cache mode: writeback
332
(qemu) quit
333
334
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=null,id=qdev_id
335
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=null,id=qdev_id
336
QEMU X.Y.Z monitor - type 'help' for more information
337
(qemu) info block
338
-none0 (null): null-co:// (null-co)
339
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
340
Removable device: not locked, tray closed
341
Cache mode: writeback
342
343
-null: null-co:// (null-co)
344
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
345
Attached to: qdev_id
346
Cache mode: writeback
347
(qemu) quit
348
349
-Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
350
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=null,id=qdev_id
351
QEMU X.Y.Z monitor - type 'help' for more information
352
(qemu) info block
353
-none0 (null): null-co:// (null-co)
354
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
355
Removable device: not locked, tray closed
356
Cache mode: writeback
357
358
-null: null-co:// (null-co)
359
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
360
Attached to: PATH
361
Cache mode: writeback
362
(qemu) quit
363
364
-Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=null,id=qdev_id
365
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=null,id=qdev_id
366
QEMU X.Y.Z monitor - type 'help' for more information
367
(qemu) info block
368
-none0 (null): null-co:// (null-co)
369
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
370
Removable device: not locked, tray closed
371
Cache mode: writeback
372
373
-null: null-co:// (null-co)
374
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
375
Attached to: qdev_id
376
Removable device: not locked, tray closed
377
Cache mode: writeback
378
(qemu) quit
379
380
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=null,id=qdev_id
381
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=null,id=qdev_id
382
QEMU X.Y.Z monitor - type 'help' for more information
383
(qemu) info block
384
-none0 (null): null-co:// (null-co)
385
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
386
Removable device: not locked, tray closed
387
Cache mode: writeback
388
389
-null: null-co:// (null-co)
390
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
391
Attached to: qdev_id
392
Removable device: not locked, tray closed
393
Cache mode: writeback
394
(qemu) quit
395
396
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=null,id=qdev_id
397
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=null,id=qdev_id
398
QEMU X.Y.Z monitor - type 'help' for more information
399
(qemu) info block
400
-none0 (null): null-co:// (null-co)
401
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
402
Removable device: not locked, tray closed
403
Cache mode: writeback
404
405
-null: null-co:// (null-co)
406
+null: json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
407
Attached to: qdev_id
408
Removable device: not locked, tray closed
409
Cache mode: writeback
410
@@ -XXX,XX +XXX,XX @@ null: null-co:// (null-co)
411
412
=== -drive if=none/-device=<bb-name> (with medium) ===
413
414
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=none0
415
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=none0
416
QEMU X.Y.Z monitor - type 'help' for more information
417
(qemu) info block
418
-none0 (null): null-co:// (null-co)
419
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
420
Attached to: PATH
421
Cache mode: writeback
422
(qemu) quit
423
424
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-hd,drive=none0,id=qdev_id
425
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-hd,drive=none0,id=qdev_id
426
QEMU X.Y.Z monitor - type 'help' for more information
427
(qemu) info block
428
-none0 (null): null-co:// (null-co)
429
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
430
Attached to: qdev_id
431
Cache mode: writeback
432
(qemu) quit
433
434
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=none0
435
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=none0
436
QEMU X.Y.Z monitor - type 'help' for more information
437
(qemu) info block
438
-none0 (null): null-co:// (null-co)
439
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
440
Attached to: PATH
441
Cache mode: writeback
442
(qemu) quit
443
444
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-hd,drive=none0,id=qdev_id
445
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-hd,drive=none0,id=qdev_id
446
QEMU X.Y.Z monitor - type 'help' for more information
447
(qemu) info block
448
-none0 (null): null-co:// (null-co)
449
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
450
Attached to: qdev_id
451
Cache mode: writeback
452
(qemu) quit
453
454
-Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=none0
455
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=none0
456
QEMU X.Y.Z monitor - type 'help' for more information
457
(qemu) info block
458
-none0 (null): null-co:// (null-co)
459
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
460
Attached to: PATH
461
Cache mode: writeback
462
(qemu) quit
463
464
-Testing: -drive if=none,driver=null-co,node-name=null -device virtio-blk-pci,drive=none0,id=qdev_id
465
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device virtio-blk-pci,drive=none0,id=qdev_id
466
QEMU X.Y.Z monitor - type 'help' for more information
467
(qemu) info block
468
-none0 (null): null-co:// (null-co)
469
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
470
Attached to: PATH
471
Cache mode: writeback
472
(qemu) quit
473
474
-Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=none0
475
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=none0
476
QEMU X.Y.Z monitor - type 'help' for more information
477
(qemu) info block
478
-none0 (null): null-co:// (null-co)
479
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
480
Attached to: PATH
481
Removable device: not locked, tray closed
482
Cache mode: writeback
483
(qemu) quit
484
485
-Testing: -drive if=none,driver=null-co,node-name=null -device floppy,drive=none0,id=qdev_id
486
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device floppy,drive=none0,id=qdev_id
487
QEMU X.Y.Z monitor - type 'help' for more information
488
(qemu) info block
489
-none0 (null): null-co:// (null-co)
490
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
491
Attached to: qdev_id
492
Removable device: not locked, tray closed
493
Cache mode: writeback
494
(qemu) quit
495
496
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=none0
497
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=none0
498
QEMU X.Y.Z monitor - type 'help' for more information
499
(qemu) info block
500
-none0 (null): null-co:// (null-co)
501
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
502
Attached to: PATH
503
Removable device: not locked, tray closed
504
Cache mode: writeback
505
(qemu) quit
506
507
-Testing: -drive if=none,driver=null-co,node-name=null -device ide-cd,drive=none0,id=qdev_id
508
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device ide-cd,drive=none0,id=qdev_id
509
QEMU X.Y.Z monitor - type 'help' for more information
510
(qemu) info block
511
-none0 (null): null-co:// (null-co)
512
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
513
Attached to: qdev_id
514
Removable device: not locked, tray closed
515
Cache mode: writeback
516
(qemu) quit
517
518
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=none0
519
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=none0
520
QEMU X.Y.Z monitor - type 'help' for more information
521
(qemu) info block
522
-none0 (null): null-co:// (null-co)
523
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
524
Attached to: PATH
525
Removable device: not locked, tray closed
526
Cache mode: writeback
527
(qemu) quit
528
529
-Testing: -drive if=none,driver=null-co,node-name=null -device scsi-cd,drive=none0,id=qdev_id
530
+Testing: -drive if=none,driver=null-co,read-zeroes=on,node-name=null -device scsi-cd,drive=none0,id=qdev_id
531
QEMU X.Y.Z monitor - type 'help' for more information
532
(qemu) info block
533
-none0 (null): null-co:// (null-co)
534
+none0 (null): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
535
Attached to: qdev_id
536
Removable device: not locked, tray closed
537
Cache mode: writeback
538
@@ -XXX,XX +XXX,XX @@ floppy0: [not inserted]
539
Removable device: not locked, tray closed
540
(qemu) quit
541
542
-Testing: -drive if=floppy,driver=null-co
543
+Testing: -drive if=floppy,driver=null-co,read-zeroes=on
544
QEMU X.Y.Z monitor - type 'help' for more information
545
(qemu) info block
546
-floppy0 (NODE_NAME): null-co:// (null-co)
547
+floppy0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
548
Attached to: PATH
549
Removable device: not locked, tray closed
550
Cache mode: writeback
551
(qemu) quit
552
553
-Testing: -drive if=ide,driver=null-co
554
+Testing: -drive if=ide,driver=null-co,read-zeroes=on
555
QEMU X.Y.Z monitor - type 'help' for more information
556
(qemu) info block
557
-ide0-hd0 (NODE_NAME): null-co:// (null-co)
558
+ide0-hd0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
559
Attached to: PATH
560
Cache mode: writeback
561
(qemu) quit
562
@@ -XXX,XX +XXX,XX @@ ide0-cd0: [not inserted]
563
Removable device: not locked, tray closed
564
(qemu) quit
565
566
-Testing: -drive if=ide,driver=null-co,media=cdrom
567
+Testing: -drive if=ide,driver=null-co,read-zeroes=on,media=cdrom
568
QEMU X.Y.Z monitor - type 'help' for more information
569
(qemu) info block
570
-ide0-cd0 (NODE_NAME): null-co:// (null-co, read-only)
571
+ide0-cd0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co"} (null-co, read-only)
572
Attached to: PATH
573
Removable device: not locked, tray closed
574
Cache mode: writeback
575
(qemu) quit
576
577
-Testing: -drive if=virtio,driver=null-co
578
+Testing: -drive if=virtio,driver=null-co,read-zeroes=on
579
QEMU X.Y.Z monitor - type 'help' for more information
580
(qemu) info block
581
-virtio0 (NODE_NAME): null-co:// (null-co)
582
+virtio0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co"} (null-co)
583
Attached to: PATH
584
Cache mode: writeback
585
(qemu) quit
586
587
-Testing: -drive if=pflash,driver=null-co,size=1M
588
+Testing: -drive if=pflash,driver=null-co,read-zeroes=on,size=1M
589
QEMU X.Y.Z monitor - type 'help' for more information
590
(qemu) info block
591
-pflash0 (NODE_NAME): json:{"driver": "null-co", "size": "1M"} (null-co)
592
+pflash0 (NODE_NAME): json:{"read-zeroes": "on", "driver": "null-co", "size": "1M"} (null-co)
593
Attached to: PATH
594
Cache mode: writeback
595
(qemu) quit
596
diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227
597
index XXXXXXX..XXXXXXX 100755
598
--- a/tests/qemu-iotests/227
599
+++ b/tests/qemu-iotests/227
600
@@ -XXX,XX +XXX,XX @@ echo
601
echo '=== blockstats with -drive if=virtio ==='
602
echo
603
604
-run_qemu -drive driver=null-co,if=virtio <<EOF
605
+run_qemu -drive driver=null-co,read-zeroes=on,if=virtio <<EOF
606
{ "execute": "qmp_capabilities" }
607
{ "execute": "query-blockstats"}
608
{ "execute": "quit" }
609
@@ -XXX,XX +XXX,XX @@ echo
610
echo '=== blockstats with -blockdev and -device ==='
611
echo
612
613
-run_qemu -blockdev driver=null-co,node-name=null -device virtio-blk,drive=null,id=virtio0 <<EOF
614
+run_qemu -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-blk,drive=null,id=virtio0 <<EOF
615
{ "execute": "qmp_capabilities" }
616
{ "execute": "query-blockstats"}
617
{ "execute": "quit" }
618
diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out
619
index XXXXXXX..XXXXXXX 100644
620
--- a/tests/qemu-iotests/227.out
621
+++ b/tests/qemu-iotests/227.out
622
@@ -XXX,XX +XXX,XX @@ QA output created by 227
623
624
=== blockstats with -drive if=virtio ===
625
626
-Testing: -drive driver=null-co,if=virtio
627
+Testing: -drive driver=null-co,read-zeroes=on,if=virtio
628
{
629
QMP_VERSION
630
}
14
}
631
@@ -XXX,XX +XXX,XX @@ Testing: -blockdev driver=null-co,node-name=null
15
632
16
+static void test_multiparent(void)
633
=== blockstats with -blockdev and -device ===
17
+{
634
18
+ BlockBackend *blk_a, *blk_b;
635
-Testing: -blockdev driver=null-co,node-name=null -device virtio-blk,drive=null,id=virtio0
19
+ BlockDriverState *bs_a, *bs_b, *backing;
636
+Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-blk,drive=null,id=virtio0
20
+ BDRVTestState *a_s, *b_s, *backing_s;
637
{
21
+
638
QMP_VERSION
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
639
}
23
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
640
diff --git a/tests/qemu-iotests/238 b/tests/qemu-iotests/238
24
+ &error_abort);
641
index XXXXXXX..XXXXXXX 100755
25
+ a_s = bs_a->opaque;
642
--- a/tests/qemu-iotests/238
26
+ blk_insert_bs(blk_a, bs_a, &error_abort);
643
+++ b/tests/qemu-iotests/238
27
+
644
@@ -XXX,XX +XXX,XX @@ else:
28
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
645
vm = iotests.VM()
29
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
646
vm.launch()
30
+ &error_abort);
647
31
+ b_s = bs_b->opaque;
648
-log(vm.qmp('blockdev-add', node_name='hd0', driver='null-co'))
32
+ blk_insert_bs(blk_b, bs_b, &error_abort);
649
+log(vm.qmp('blockdev-add', node_name='hd0', driver='null-co', read_zeroes=True))
33
+
650
log(vm.qmp('object-add', qom_type='iothread', id='iothread0'))
34
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
651
log(vm.qmp('device_add', id='scsi0', driver=virtio_scsi_device, iothread='iothread0'))
35
+ backing_s = backing->opaque;
652
log(vm.qmp('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0'))
36
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
653
diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240
37
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
654
index XXXXXXX..XXXXXXX 100755
38
+
655
--- a/tests/qemu-iotests/240
39
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
656
+++ b/tests/qemu-iotests/240
40
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
657
@@ -XXX,XX +XXX,XX @@ echo
41
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
658
42
+ g_assert_cmpint(a_s->drain_count, ==, 0);
659
run_qemu <<EOF
43
+ g_assert_cmpint(b_s->drain_count, ==, 0);
660
{ "execute": "qmp_capabilities" }
44
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
661
-{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0"}}
45
+
662
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0"}}
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
663
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
47
+
664
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
48
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
665
{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
49
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
666
@@ -XXX,XX +XXX,XX @@ echo
50
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
667
51
+ g_assert_cmpint(a_s->drain_count, ==, 1);
668
run_qemu <<EOF
52
+ g_assert_cmpint(b_s->drain_count, ==, 1);
669
{ "execute": "qmp_capabilities" }
53
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
670
-{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
54
+
671
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}}
55
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
672
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
56
+
673
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
57
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
674
{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
58
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
675
@@ -XXX,XX +XXX,XX @@ echo
59
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
676
60
+ g_assert_cmpint(a_s->drain_count, ==, 2);
677
run_qemu <<EOF
61
+ g_assert_cmpint(b_s->drain_count, ==, 2);
678
{ "execute": "qmp_capabilities" }
62
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
679
-{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
63
+
680
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}}
64
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
681
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
65
+
682
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread1"}}
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
683
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
684
@@ -XXX,XX +XXX,XX @@ echo
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
685
69
+ g_assert_cmpint(a_s->drain_count, ==, 1);
686
run_qemu <<EOF
70
+ g_assert_cmpint(b_s->drain_count, ==, 1);
687
{ "execute": "qmp_capabilities" }
71
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
688
-{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
72
+
689
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "read-zeroes": true, "node-name": "hd0", "read-only": true}}
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
690
{ "execute": "nbd-server-start", "arguments": {"addr":{"type":"unix","data":{"path":"$TEST_DIR/nbd"}}}}
74
+
691
{ "execute": "nbd-server-add", "arguments": {"device":"hd0"}}
75
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
692
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
76
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
77
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
78
+ g_assert_cmpint(a_s->drain_count, ==, 0);
79
+ g_assert_cmpint(b_s->drain_count, ==, 0);
80
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
81
+
82
+ bdrv_unref(backing);
83
+ bdrv_unref(bs_a);
84
+ bdrv_unref(bs_b);
85
+ blk_unref(blk_a);
86
+ blk_unref(blk_b);
87
+}
88
+
89
90
typedef struct TestBlockJob {
91
BlockJob common;
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
93
test_quiesce_co_drain_subtree);
94
95
g_test_add_func("/bdrv-drain/nested", test_nested);
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
97
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
693
--
100
--
694
2.20.1
101
2.13.6
695
102
696
103
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
We need to remember how many of the drain sections in which a node is
2
2
were recursive (i.e. subtree drain rather than node drain), so that they
3
Commit 5cb2737e925042e6c7cd3fb0b01313950b03cddf laid out why
3
can be correctly applied when children are added or removed during the
4
bdrv_do_drained_end() must decrement the quiesce_counter after
4
drained section.
5
bdrv_drain_invoke(). It did not give a very good reason why it has to
5
6
happen after bdrv_parent_drained_end(), instead only claiming symmetry
6
With this change, it is safe to modify the graph even inside a
7
to bdrv_do_drained_begin().
7
bdrv_subtree_drained_begin/end() section.
8
8
9
It turns out that delaying it for so long is wrong.
10
11
Situation: We have an active commit job (i.e. a mirror job) from top to
12
base for the following graph:
13
14
filter
15
|
16
[file]
17
|
18
v
19
top --[backing]--> base
20
21
Now the VM is closed, which results in the job being cancelled and a
22
bdrv_drain_all() happening pretty much simultaneously.
23
24
Beginning the drain means the job is paused once whenever one of its
25
nodes is quiesced. This is reversed when the drain ends.
26
27
With how the code currently is, after base's drain ends (which means
28
that it will have unpaused the job once), its quiesce_counter remains at
29
1 while it goes to undrain its parents (bdrv_parent_drained_end()). For
30
some reason or another, undraining filter causes the job to be kicked
31
and enter mirror_exit_common(), where it proceeds to invoke
32
block_job_remove_all_bdrv().
33
34
Now base will be detached from the job. Because its quiesce_counter is
35
still 1, it will unpause the job once more. So in total, undraining
36
base will unpause the job twice. Eventually, this will lead to the
37
job's pause_count going negative -- well, it would, were there not an
38
assertion against this, which crashes qemu.
39
40
The general problem is that if in bdrv_parent_drained_end() we undrain
41
parent A, and then undrain parent B, which then leads to A detaching the
42
child, bdrv_replace_child_noperm() will undrain A as if we had not done
43
so yet; that is, one time too many.
44
45
It follows that we cannot decrement the quiesce_counter after invoking
46
bdrv_parent_drained_end().
47
48
Unfortunately, decrementing it before bdrv_parent_drained_end() would be
49
wrong, too. Imagine the above situation in reverse: Undraining A leads
50
to B detaching the child. If we had already decremented the
51
quiesce_counter by that point, bdrv_replace_child_noperm() would undrain
52
B one time too little; because it expects bdrv_parent_drained_end() to
53
issue this undrain. But bdrv_parent_drained_end() won't do that,
54
because B is no longer a parent.
55
56
Therefore, we have to do something else. This patch opts for
57
introducing a second quiesce_counter that counts how many times a
58
child's parent has been quiesced (though c->role->drained_*). With
59
that, bdrv_replace_child_noperm() just has to undrain the parent exactly
60
that many times when removing a child, and it will always be right.
61
62
Signed-off-by: Max Reitz <mreitz@redhat.com>
63
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
64
---
10
---
65
include/block/block.h | 7 +++++++
11
include/block/block.h | 2 --
66
include/block/block_int.h | 9 +++++++++
12
include/block/block_int.h | 5 +++++
67
block.c | 15 +++++----------
13
block.c | 32 +++++++++++++++++++++++++++++---
68
block/io.c | 14 +++++++++++---
14
block/io.c | 28 ++++++++++++++++++++++++----
69
4 files changed, 32 insertions(+), 13 deletions(-)
15
4 files changed, 58 insertions(+), 9 deletions(-)
70
16
71
diff --git a/include/block/block.h b/include/block/block.h
17
diff --git a/include/block/block.h b/include/block/block.h
72
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
73
--- a/include/block/block.h
19
--- a/include/block/block.h
74
+++ b/include/block/block.h
20
+++ b/include/block/block.h
75
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
22
/**
23
* Like bdrv_drained_begin, but recursively begins a quiesced section for
24
* exclusive access to all child nodes as well.
25
- *
26
- * Graph changes are not allowed during a subtree drain section.
76
*/
27
*/
77
void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
28
void bdrv_subtree_drained_begin(BlockDriverState *bs);
78
29
79
+/**
80
+ * bdrv_parent_drained_end_single:
81
+ *
82
+ * End a quiesced section for the parent of @c.
83
+ */
84
+void bdrv_parent_drained_end_single(BdrvChild *c);
85
+
86
/**
87
* bdrv_parent_drained_end:
88
*
89
diff --git a/include/block/block_int.h b/include/block/block_int.h
30
diff --git a/include/block/block_int.h b/include/block/block_int.h
90
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
91
--- a/include/block/block_int.h
32
--- a/include/block/block_int.h
92
+++ b/include/block/block_int.h
33
+++ b/include/block/block_int.h
93
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
94
*/
35
95
bool frozen;
36
/* Accessed with atomic ops. */
96
37
int quiesce_counter;
97
+ /*
38
+ int recursive_quiesce_counter;
98
+ * How many times the parent of this child has been drained
39
+
99
+ * (through role->drained_*).
40
unsigned int write_gen; /* Current data generation */
100
+ * Usually, this is equal to bs->quiesce_counter (potentially
41
101
+ * reduced by bdrv_drain_all_count). It may differ while the
42
/* Protected by reqs_lock. */
102
+ * child is entering or leaving a drained section.
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
103
+ */
44
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
104
+ int parent_quiesce_counter;
45
BdrvRequestFlags flags);
105
+
46
106
QLIST_ENTRY(BdrvChild) next;
47
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
107
QLIST_ENTRY(BdrvChild) next_parent;
48
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
108
};
49
+
50
int get_tmp_filename(char *filename, int size);
51
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
52
const char *filename);
109
diff --git a/block.c b/block.c
53
diff --git a/block.c b/block.c
110
index XXXXXXX..XXXXXXX 100644
54
index XXXXXXX..XXXXXXX 100644
111
--- a/block.c
55
--- a/block.c
112
+++ b/block.c
56
+++ b/block.c
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
58
bdrv_drained_end(bs);
59
}
60
61
+static void bdrv_child_cb_attach(BdrvChild *child)
62
+{
63
+ BlockDriverState *bs = child->opaque;
64
+ bdrv_apply_subtree_drain(child, bs);
65
+}
66
+
67
+static void bdrv_child_cb_detach(BdrvChild *child)
68
+{
69
+ BlockDriverState *bs = child->opaque;
70
+ bdrv_unapply_subtree_drain(child, bs);
71
+}
72
+
73
static int bdrv_child_cb_inactivate(BdrvChild *child)
74
{
75
BlockDriverState *bs = child->opaque;
76
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_file = {
77
.inherit_options = bdrv_inherited_options,
78
.drained_begin = bdrv_child_cb_drained_begin,
79
.drained_end = bdrv_child_cb_drained_end,
80
+ .attach = bdrv_child_cb_attach,
81
+ .detach = bdrv_child_cb_detach,
82
.inactivate = bdrv_child_cb_inactivate,
83
};
84
85
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = {
86
.inherit_options = bdrv_inherited_fmt_options,
87
.drained_begin = bdrv_child_cb_drained_begin,
88
.drained_end = bdrv_child_cb_drained_end,
89
+ .attach = bdrv_child_cb_attach,
90
+ .detach = bdrv_child_cb_detach,
91
.inactivate = bdrv_child_cb_inactivate,
92
};
93
94
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_attach(BdrvChild *c)
95
parent->backing_blocker);
96
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
97
parent->backing_blocker);
98
+
99
+ bdrv_child_cb_attach(c);
100
}
101
102
static void bdrv_backing_detach(BdrvChild *c)
103
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_detach(BdrvChild *c)
104
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
105
error_free(parent->backing_blocker);
106
parent->backing_blocker = NULL;
107
+
108
+ bdrv_child_cb_detach(c);
109
}
110
111
/*
113
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
114
if (child->role->detach) {
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
115
child->role->detach(child);
114
}
116
}
115
if (old_bs) {
117
- if (old_bs->quiesce_counter && child->role->drained_end) {
116
+ /* Detach first so that the recursive drain sections coming from @child
118
- int num = old_bs->quiesce_counter;
117
+ * are already gone and we only end the drain sections that came from
119
- if (child->role->parent_is_bds) {
118
+ * elsewhere. */
120
- num -= bdrv_drain_all_count;
119
+ if (child->role->detach) {
121
- }
120
+ child->role->detach(child);
122
- assert(num >= 0);
121
+ }
123
- for (i = 0; i < num; i++) {
122
if (old_bs->quiesce_counter && child->role->drained_end) {
124
- child->role->drained_end(child);
123
for (i = 0; i < old_bs->quiesce_counter; i++) {
125
- }
124
child->role->drained_end(child);
126
+ while (child->parent_quiesce_counter) {
125
}
127
+ bdrv_parent_drained_end_single(child);
126
}
128
}
127
- if (child->role->detach) {
128
- child->role->detach(child);
129
- }
129
QLIST_REMOVE(child, next_parent);
130
QLIST_REMOVE(child, next_parent);
130
+ } else {
131
+ assert(child->parent_quiesce_counter == 0);
132
}
131
}
133
132
134
child->bs = new_bs;
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
135
134
}
136
if (new_bs) {
135
}
137
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
136
138
- if (new_bs->quiesce_counter && child->role->drained_begin) {
137
+ /* Attach only after starting new drained sections, so that recursive
139
+ if (new_bs->quiesce_counter) {
138
+ * drain sections coming from @child don't get an extra .drained_begin
140
int num = new_bs->quiesce_counter;
139
+ * callback. */
141
if (child->role->parent_is_bds) {
140
if (child->role->attach) {
142
num -= bdrv_drain_all_count;
141
child->role->attach(child);
142
}
143
diff --git a/block/io.c b/block/io.c
143
diff --git a/block/io.c b/block/io.c
144
index XXXXXXX..XXXXXXX 100644
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/io.c
145
--- a/block/io.c
146
+++ b/block/io.c
146
+++ b/block/io.c
147
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore,
147
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
148
assert(data.done);
149
}
150
151
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
152
- BdrvChild *parent)
153
+void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
154
+ BdrvChild *parent)
155
{
156
BdrvChild *child, *next;
157
158
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
159
bdrv_drain_recurse(bs);
160
161
if (recursive) {
162
+ bs->recursive_quiesce_counter++;
163
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
164
bdrv_do_drained_begin(child->bs, true, child);
165
}
166
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
167
bdrv_do_drained_begin(bs, true, NULL);
168
}
169
170
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
171
- BdrvChild *parent)
172
+void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
173
+ BdrvChild *parent)
174
{
175
BdrvChild *child, *next;
176
int old_quiesce_counter;
177
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
148
}
178
}
149
}
179
150
180
if (recursive) {
151
+void bdrv_parent_drained_end_single(BdrvChild *c)
181
+ bs->recursive_quiesce_counter--;
152
+{
182
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
153
+ assert(c->parent_quiesce_counter > 0);
183
bdrv_do_drained_end(child->bs, true, child);
154
+ c->parent_quiesce_counter--;
184
}
155
+ if (c->role->drained_end) {
185
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs)
156
+ c->role->drained_end(c);
186
bdrv_do_drained_end(bs, true, NULL);
187
}
188
189
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
190
+{
191
+ int i;
192
+
193
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
194
+ bdrv_do_drained_begin(child->bs, true, child);
157
+ }
195
+ }
158
+}
196
+}
159
+
197
+
160
void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
161
bool ignore_bds_parents)
199
+{
162
{
200
+ int i;
163
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
201
+
164
if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) {
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
165
continue;
203
+ bdrv_do_drained_end(child->bs, true, child);
166
}
204
+ }
167
- if (c->role->drained_end) {
205
+}
168
- c->role->drained_end(c);
206
+
169
- }
207
/*
170
+ bdrv_parent_drained_end_single(c);
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
171
}
209
* and suspend block driver's internal I/O until next request arrives.
172
}
173
174
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore,
175
176
void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
177
{
178
+ c->parent_quiesce_counter++;
179
if (c->role->drained_begin) {
180
c->role->drained_begin(c);
181
}
182
--
210
--
183
2.20.1
211
2.13.6
184
212
185
213
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
2
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
2
---
6
tests/test-bdrv-drain.c | 36 ++++++++++++++++++++++++++++++++----
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
7
1 file changed, 32 insertions(+), 4 deletions(-)
4
1 file changed, 80 insertions(+)
8
5
9
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
10
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
11
--- a/tests/test-bdrv-drain.c
8
--- a/tests/test-bdrv-drain.c
12
+++ b/tests/test-bdrv-drain.c
9
+++ b/tests/test-bdrv-drain.c
13
@@ -XXX,XX +XXX,XX @@ typedef struct TestDropBackingBlockJob {
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
11
blk_unref(blk_b);
12
}
13
14
+static void test_graph_change(void)
15
+{
16
+ BlockBackend *blk_a, *blk_b;
17
+ BlockDriverState *bs_a, *bs_b, *backing;
18
+ BDRVTestState *a_s, *b_s, *backing_s;
19
+
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
22
+ &error_abort);
23
+ a_s = bs_a->opaque;
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
25
+
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
28
+ &error_abort);
29
+ b_s = bs_b->opaque;
30
+ blk_insert_bs(blk_b, bs_b, &error_abort);
31
+
32
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
33
+ backing_s = backing->opaque;
34
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
35
+
36
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
37
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
38
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
39
+ g_assert_cmpint(a_s->drain_count, ==, 0);
40
+ g_assert_cmpint(b_s->drain_count, ==, 0);
41
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
42
+
43
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
44
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
45
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
47
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
48
+
49
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
50
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
51
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
52
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
53
+ g_assert_cmpint(a_s->drain_count, ==, 5);
54
+ g_assert_cmpint(b_s->drain_count, ==, 5);
55
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
56
+
57
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
58
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
59
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
61
+ g_assert_cmpint(a_s->drain_count, ==, 3);
62
+ g_assert_cmpint(b_s->drain_count, ==, 2);
63
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
64
+
65
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
69
+ g_assert_cmpint(a_s->drain_count, ==, 5);
70
+ g_assert_cmpint(b_s->drain_count, ==, 5);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
74
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
75
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
76
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
77
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
78
+
79
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
80
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
81
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
82
+ g_assert_cmpint(a_s->drain_count, ==, 0);
83
+ g_assert_cmpint(b_s->drain_count, ==, 0);
84
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
85
+
86
+ bdrv_unref(backing);
87
+ bdrv_unref(bs_a);
88
+ bdrv_unref(bs_b);
89
+ blk_unref(blk_a);
90
+ blk_unref(blk_b);
91
+}
92
+
93
94
typedef struct TestBlockJob {
14
BlockJob common;
95
BlockJob common;
15
bool should_complete;
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
16
bool *did_complete;
97
17
+ BlockDriverState *detach_also;
98
g_test_add_func("/bdrv-drain/nested", test_nested);
18
} TestDropBackingBlockJob;
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
19
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
20
static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp)
101
21
@@ -XXX,XX +XXX,XX @@ static void test_drop_backing_job_commit(Job *job)
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
22
container_of(job, TestDropBackingBlockJob, common.job);
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
23
24
bdrv_set_backing_hd(blk_bs(s->common.blk), NULL, &error_abort);
25
+ bdrv_set_backing_hd(s->detach_also, NULL, &error_abort);
26
27
*s->did_complete = true;
28
}
29
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_drop_backing_job_driver = {
30
* Creates a child node with three parent nodes on it, and then runs a
31
* block job on the final one, parent-node-2.
32
*
33
- * (TODO: parent-node-0 currently serves no purpose, but will as of a
34
- * follow-up patch.)
35
- *
36
* The job is then asked to complete before a section where the child
37
* is drained.
38
*
39
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_drop_backing_job_driver = {
40
*
41
* Ending the drain on parent-node-1 will poll the AioContext, which
42
* lets job_exit() and thus test_drop_backing_job_commit() run. That
43
- * function removes the child as parent-node-2's backing file.
44
+ * function first removes the child as parent-node-2's backing file.
45
*
46
* In old (and buggy) implementations, there are two problems with
47
* that:
48
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_drop_backing_job_driver = {
49
* bdrv_replace_child_noperm() therefore must call drained_end() on
50
* the parent only if it really is still drained because the child is
51
* drained.
52
+ *
53
+ * If removing child from parent-node-2 was successful (as it should
54
+ * be), test_drop_backing_job_commit() will then also remove the child
55
+ * from parent-node-0.
56
+ *
57
+ * With an old version of our drain infrastructure ((A) above), that
58
+ * resulted in the following flow:
59
+ *
60
+ * 1. child attempts to leave its drained section. The call recurses
61
+ * to its parents.
62
+ *
63
+ * 2. parent-node-2 leaves the drained section. Polling in
64
+ * bdrv_drain_invoke() will schedule job_exit().
65
+ *
66
+ * 3. parent-node-1 leaves the drained section. Polling in
67
+ * bdrv_drain_invoke() will run job_exit(), thus disconnecting
68
+ * parent-node-0 from the child node.
69
+ *
70
+ * 4. bdrv_parent_drained_end() uses a QLIST_FOREACH_SAFE() loop to
71
+ * iterate over the parents. Thus, it now accesses the BdrvChild
72
+ * object that used to connect parent-node-0 and the child node.
73
+ * However, that object no longer exists, so it accesses a dangling
74
+ * pointer.
75
+ *
76
+ * The solution is to only poll once when running a bdrv_drained_end()
77
+ * operation, specifically at the end when all drained_end()
78
+ * operations for all involved nodes have been scheduled.
79
+ * Note that this also solves (A) above, thus hiding (B).
80
*/
81
static void test_blockjob_commit_by_drained_end(void)
82
{
83
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_commit_by_drained_end(void)
84
bs_parents[2], 0, BLK_PERM_ALL, 0, 0, NULL, NULL,
85
&error_abort);
86
87
+ job->detach_also = bs_parents[0];
88
job->did_complete = &job_has_completed;
89
90
job_start(&job->common.job);
91
--
104
--
92
2.20.1
105
2.13.6
93
106
94
107
diff view generated by jsdifflib
New patch
1
Since commit bde70715, base is the only node that is reopened in
2
commit_start(). This means that the code, which still involves an
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
1
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
7
---
8
block/commit.c | 8 +-------
9
1 file changed, 1 insertion(+), 7 deletions(-)
10
11
diff --git a/block/commit.c b/block/commit.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block/commit.c
14
+++ b/block/commit.c
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
16
const char *filter_node_name, Error **errp)
17
{
18
CommitBlockJob *s;
19
- BlockReopenQueue *reopen_queue = NULL;
20
int orig_base_flags;
21
BlockDriverState *iter;
22
BlockDriverState *commit_top_bs = NULL;
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
24
/* convert base to r/w, if necessary */
25
orig_base_flags = bdrv_get_flags(base);
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
28
- orig_base_flags | BDRV_O_RDWR);
29
- }
30
-
31
- if (reopen_queue) {
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
34
if (local_err != NULL) {
35
error_propagate(errp, local_err);
36
goto fail;
37
--
38
2.13.6
39
40
diff view generated by jsdifflib
New patch
1
The bdrv_reopen*() implementation doesn't like it if the graph is
2
changed between queuing nodes for reopen and actually reopening them
3
(one of the reasons is that queuing can be recursive).
1
4
5
So instead of draining the device only in bdrv_reopen_multiple(),
6
require that callers already drained all affected nodes, and assert this
7
in bdrv_reopen_queue().
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
11
---
12
block.c | 23 ++++++++++++++++-------
13
block/replication.c | 6 ++++++
14
qemu-io-cmds.c | 3 +++
15
3 files changed, 25 insertions(+), 7 deletions(-)
16
17
diff --git a/block.c b/block.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block.c
20
+++ b/block.c
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
22
* returns a pointer to bs_queue, which is either the newly allocated
23
* bs_queue, or the existing bs_queue being used.
24
*
25
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
26
*/
27
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
28
BlockDriverState *bs,
29
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
30
BdrvChild *child;
31
QDict *old_options, *explicit_options;
32
33
+ /* Make sure that the caller remembered to use a drained section. This is
34
+ * important to avoid graph changes between the recursive queuing here and
35
+ * bdrv_reopen_multiple(). */
36
+ assert(bs->quiesce_counter > 0);
37
+
38
if (bs_queue == NULL) {
39
bs_queue = g_new0(BlockReopenQueue, 1);
40
QSIMPLEQ_INIT(bs_queue);
41
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
42
* If all devices prepare successfully, then the changes are committed
43
* to all devices.
44
*
45
+ * All affected nodes must be drained between bdrv_reopen_queue() and
46
+ * bdrv_reopen_multiple().
47
*/
48
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
49
{
50
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
51
52
assert(bs_queue != NULL);
53
54
- aio_context_release(ctx);
55
- bdrv_drain_all_begin();
56
- aio_context_acquire(ctx);
57
-
58
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
59
+ assert(bs_entry->state.bs->quiesce_counter > 0);
60
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
61
error_propagate(errp, local_err);
62
goto cleanup;
63
@@ -XXX,XX +XXX,XX @@ cleanup:
64
}
65
g_free(bs_queue);
66
67
- bdrv_drain_all_end();
68
-
69
return ret;
70
}
71
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
73
{
74
int ret = -1;
75
Error *local_err = NULL;
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
77
+ BlockReopenQueue *queue;
78
79
+ bdrv_subtree_drained_begin(bs);
80
+
81
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
82
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
83
if (local_err != NULL) {
84
error_propagate(errp, local_err);
85
}
86
+
87
+ bdrv_subtree_drained_end(bs);
88
+
89
return ret;
90
}
91
92
diff --git a/block/replication.c b/block/replication.c
93
index XXXXXXX..XXXXXXX 100644
94
--- a/block/replication.c
95
+++ b/block/replication.c
96
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
97
new_secondary_flags = s->orig_secondary_flags;
98
}
99
100
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
101
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
102
+
103
if (orig_hidden_flags != new_hidden_flags) {
104
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
105
new_hidden_flags);
106
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
107
reopen_queue, &local_err);
108
error_propagate(errp, local_err);
109
}
110
+
111
+ bdrv_subtree_drained_end(s->hidden_disk->bs);
112
+ bdrv_subtree_drained_end(s->secondary_disk->bs);
113
}
114
115
static void backup_job_cleanup(BlockDriverState *bs)
116
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
117
index XXXXXXX..XXXXXXX 100644
118
--- a/qemu-io-cmds.c
119
+++ b/qemu-io-cmds.c
120
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
121
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
122
qemu_opts_reset(&reopen_opts);
123
124
+ bdrv_subtree_drained_begin(bs);
125
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
126
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
127
+ bdrv_subtree_drained_end(bs);
128
+
129
if (local_err) {
130
error_report_err(local_err);
131
} else {
132
--
133
2.13.6
134
135
diff view generated by jsdifflib