1
The following changes since commit 6c599282f8ab382fe59f03a6cae755b89561a7b3:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2020-02-15-v2' into staging (2020-02-17 13:32:25 +0000)
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 c45a88f4429d7a8f384b75f3fd3fed5138a6edca:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
iotests: Check that @replaces can replace filters (2020-02-18 14:52:16 +0100)
11
block: Keep nodes drained between reopen_queue/multiple (2017-12-22 15:05:32 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
16
- Fix check_to_replace_node()
17
- commit: Expose on-error option in QMP
18
- qcow2: Fix qcow2_alloc_cluster_abort() for external data file
19
- mirror: Fix deadlock
20
- vvfat: Fix segfault while closing read-write node
21
- Code cleanups
22
15
23
----------------------------------------------------------------
16
----------------------------------------------------------------
24
Alberto Garcia (1):
17
Doug Gale (1):
25
qcow2: Fix alignment checks in encrypted images
18
nvme: Add tracing
26
19
27
Hikaru Nishida (1):
20
Edgar Kaziakhmedov (1):
28
block/vvfat: Do not unref qcow on closing backing bdrv
21
qcow2: get rid of qcow2_backing_read1 routine
29
22
30
Kevin Wolf (12):
23
Fam Zheng (2):
31
mirror: Store MirrorOp.co for debuggability
24
block: Open backing image in force share mode for size probe
32
mirror: Don't let an operation wait for itself
25
block: Remove unused bdrv_requests_pending
33
qcow2: update_refcount(): Reset old_table_index after qcow2_cache_put()
34
qcow2: Fix qcow2_alloc_cluster_abort() for external data file
35
iotests: Test copy offloading with external data file
36
qapi: Document meaning of 'ignore' BlockdevOnError for jobs
37
commit: Remove unused bytes_written
38
commit: Fix argument order for block_job_error_action()
39
commit: Inline commit_populate()
40
commit: Fix is_read for block_job_error_action()
41
commit: Expose on-error option in QMP
42
iotests: Test error handling policies with block-commit
43
26
44
Max Reitz (19):
27
John Snow (1):
45
blockdev: Allow external snapshots everywhere
28
iotests: fix 197 for vpc
46
blockdev: Allow resizing everywhere
47
block: Drop bdrv_is_first_non_filter()
48
iotests: Let 041 use -blockdev for quorum children
49
quorum: Fix child permissions
50
block: Add bdrv_recurse_can_replace()
51
blkverify: Implement .bdrv_recurse_can_replace()
52
quorum: Implement .bdrv_recurse_can_replace()
53
block: Use bdrv_recurse_can_replace()
54
block: Remove bdrv_recurse_is_first_non_filter()
55
mirror: Double-check immediately before replacing
56
quorum: Stop marking it as a filter
57
iotests: Use complete_and_wait() in 155
58
iotests: Add VM.assert_block_path()
59
iotests/041: Drop superfluous shutdowns
60
iotests: Resolve TODOs in 041
61
iotests: Use self.image_len in TestRepairQuorum
62
iotests: Add tests for invalid Quorum @replaces
63
iotests: Check that @replaces can replace filters
64
29
65
Philippe Mathieu-Daudé (3):
30
Kevin Wolf (27):
66
block/qcow2-bitmap: Remove unneeded variable assignment
31
block: Formats don't need CONSISTENT_READ with NO_IO
67
block: Remove superfluous semicolons
32
block: Make bdrv_drain_invoke() recursive
68
block/io_uring: Remove superfluous semicolon
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
69
58
70
qapi/block-core.json | 9 +-
59
Thomas Huth (3):
71
include/block/block.h | 5 -
60
block: Remove the obsolete -drive boot=on|off parameter
72
include/block/block_int.h | 16 +--
61
block: Remove the deprecated -hdachs option
73
block.c | 89 ++++++-------
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
74
block/blkverify.c | 20 +--
75
block/commit.c | 37 ++----
76
block/copy-on-read.c | 9 --
77
block/filter-compress.c | 9 --
78
block/io_uring.c | 2 +-
79
block/mirror.c | 37 ++++--
80
block/qcow2-bitmap.c | 1 -
81
block/qcow2-cluster.c | 7 +-
82
block/qcow2-refcount.c | 1 +
83
block/qcow2-threads.c | 12 +-
84
block/qcow2.c | 2 -
85
block/quorum.c | 70 +++++++++--
86
block/replication.c | 7 --
87
block/throttle.c | 8 --
88
block/vvfat.c | 7 --
89
blockdev.c | 18 +--
90
tests/qemu-iotests/iotests.py | 59 +++++++++
91
tests/qemu-iotests/040 | 283 ++++++++++++++++++++++++++++++++++++++++++
92
tests/qemu-iotests/040.out | 4 +-
93
tests/qemu-iotests/041 | 138 +++++++++++++++++---
94
tests/qemu-iotests/041.out | 4 +-
95
tests/qemu-iotests/155 | 7 +-
96
tests/qemu-iotests/244 | 14 +++
97
tests/qemu-iotests/244.out | 6 +
98
28 files changed, 675 insertions(+), 206 deletions(-)
99
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
100
88
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently
2
in use as a mirror target. It is not enough for image formats, though,
3
as these still unconditionally request BLK_PERM_CONSISTENT_READ.
2
4
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
As this permission is geared towards whether the guest-visible data is
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
consistent, and has no impact on whether the metadata is sane, and
5
Message-Id: <20200218103454.296704-20-mreitz@redhat.com>
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
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
12
---
8
tests/qemu-iotests/041 | 46 ++++++++++++++++++++++++++++++++++++++
13
block.c | 6 +++++-
9
tests/qemu-iotests/041.out | 4 ++--
14
1 file changed, 5 insertions(+), 1 deletion(-)
10
2 files changed, 48 insertions(+), 2 deletions(-)
11
15
12
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
16
diff --git a/block.c b/block.c
13
index XXXXXXX..XXXXXXX 100755
17
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/qemu-iotests/041
18
--- a/block.c
15
+++ b/tests/qemu-iotests/041
19
+++ b/block.c
16
@@ -XXX,XX +XXX,XX @@ class TestOrphanedSource(iotests.QMPTestCase):
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
17
self.assertFalse('mirror-filter' in nodes,
21
assert(role == &child_backing || role == &child_file);
18
'Mirror filter node did not disappear')
22
19
23
if (!backing) {
20
+# Test cases for @replaces that do not necessarily involve Quorum
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
21
+class TestReplaces(iotests.QMPTestCase):
22
+ # Each of these test cases needs their own block graph, so do not
23
+ # create any nodes here
24
+ def setUp(self):
25
+ self.vm = iotests.VM()
26
+ self.vm.launch()
27
+
25
+
28
+ def tearDown(self):
26
/* Apart from the modifications below, the same permissions are
29
+ self.vm.shutdown()
27
* forwarded and left alone as for filters */
30
+ for img in (test_img, target_img):
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
31
+ try:
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
32
+ os.remove(img)
30
33
+ except OSError:
31
/* bs->file always needs to be consistent because of the metadata. We
34
+ pass
32
* can never allow other users to resize or write to it. */
35
+
33
- perm |= BLK_PERM_CONSISTENT_READ;
36
+ @iotests.skip_if_unsupported(['copy-on-read'])
34
+ if (!(flags & BDRV_O_NO_IO)) {
37
+ def test_replace_filter(self):
35
+ perm |= BLK_PERM_CONSISTENT_READ;
38
+ """
36
+ }
39
+ Check that we can replace filter nodes.
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
40
+ """
38
} else {
41
+ result = self.vm.qmp('blockdev-add', **{
39
/* We want consistent read from backing files if the parent needs it.
42
+ 'driver': 'copy-on-read',
43
+ 'node-name': 'filter0',
44
+ 'file': {
45
+ 'driver': 'copy-on-read',
46
+ 'node-name': 'filter1',
47
+ 'file': {
48
+ 'driver': 'null-co'
49
+ }
50
+ }
51
+ })
52
+ self.assert_qmp(result, 'return', {})
53
+
54
+ result = self.vm.qmp('blockdev-add',
55
+ node_name='target', driver='null-co')
56
+ self.assert_qmp(result, 'return', {})
57
+
58
+ result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0',
59
+ target='target', sync='full', replaces='filter1')
60
+ self.assert_qmp(result, 'return', {})
61
+
62
+ self.complete_and_wait('mirror')
63
+
64
+ self.vm.assert_block_path('filter0', '/file', 'target')
65
+
66
if __name__ == '__main__':
67
iotests.main(supported_fmts=['qcow2', 'qed'],
68
supported_protocols=['file'],
69
diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out
70
index XXXXXXX..XXXXXXX 100644
71
--- a/tests/qemu-iotests/041.out
72
+++ b/tests/qemu-iotests/041.out
73
@@ -XXX,XX +XXX,XX @@
74
-.............................................................................................
75
+..............................................................................................
76
----------------------------------------------------------------------
77
-Ran 93 tests
78
+Ran 94 tests
79
80
OK
81
--
40
--
82
2.20.1
41
2.13.6
83
42
84
43
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: John Snow <jsnow@redhat.com>
2
2
3
041's TestRepairQuorum has its own image_len, no need to refer to
3
VPC has some difficulty creating geometries of particular size.
4
TestSingleDrive. (This patch allows commenting out TestSingleDrive to
4
However, we can indeed force it to use a literal one, so let's
5
speed up 041 during test testing.)
5
do that for the sake of test 197, which is testing some specific
6
offsets.
6
7
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Message-Id: <20200218103454.296704-18-mreitz@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
11
---
13
---
12
tests/qemu-iotests/041 | 2 +-
14
tests/qemu-iotests/197 | 4 ++++
13
1 file changed, 1 insertion(+), 1 deletion(-)
15
tests/qemu-iotests/common.filter | 3 ++-
16
2 files changed, 6 insertions(+), 1 deletion(-)
14
17
15
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
18
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
16
index XXXXXXX..XXXXXXX 100755
19
index XXXXXXX..XXXXXXX 100755
17
--- a/tests/qemu-iotests/041
20
--- a/tests/qemu-iotests/197
18
+++ b/tests/qemu-iotests/041
21
+++ b/tests/qemu-iotests/197
19
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
22
@@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ==='
20
# Add each individual quorum images
23
echo
21
for i in self.IMAGES:
24
22
qemu_img('create', '-f', iotests.imgfmt, i,
25
# Prep the images
23
- str(TestSingleDrive.image_len))
26
+# VPC rounds image sizes to a specific geometry, force a specific size.
24
+ str(self.image_len))
27
+if [ "$IMGFMT" = "vpc" ]; then
25
# Assign a node name to each quorum image in order to manipulate
28
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
26
# them
29
+fi
27
opts = "node-name=img%i" % self.IMAGES.index(i)
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()
28
--
47
--
29
2.20.1
48
2.13.6
30
49
31
50
diff view generated by jsdifflib
1
mirror_wait_for_free_in_flight_slot() just picks a random operation to
1
This change separates bdrv_drain_invoke(), which calls the BlockDriver
2
wait for. However, when mirror_co_read() waits for free slots, its
2
drain callbacks, from bdrv_drain_recurse(). Instead, the function
3
MirrorOp is already in s->ops_in_flight, so if not enough slots are
3
performs its own recursion now.
4
immediately available, an operation can end up waiting for itself to
5
complete, which results in a hang.
6
4
7
Fix this by passing the current MirrorOp and skipping this operation
5
One reason for this is that bdrv_drain_recurse() can be called multiple
8
when picking an operation to wait for.
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
7
once. The separation is necessary to fix this bug.
9
8
10
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1794692
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
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
19
---
14
block/mirror.c | 21 ++++++++++++---------
20
block/io.c | 14 +++++++++++---
15
1 file changed, 12 insertions(+), 9 deletions(-)
21
1 file changed, 11 insertions(+), 3 deletions(-)
16
22
17
diff --git a/block/mirror.c b/block/mirror.c
23
diff --git a/block/io.c b/block/io.c
18
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
19
--- a/block/mirror.c
25
--- a/block/io.c
20
+++ b/block/mirror.c
26
+++ b/block/io.c
21
@@ -XXX,XX +XXX,XX @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset,
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
28
bdrv_wakeup(bs);
22
}
29
}
23
30
24
static inline void coroutine_fn
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
25
-mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
26
+mirror_wait_for_any_operation(MirrorBlockJob *s, MirrorOp *self, bool active)
27
{
33
{
28
MirrorOp *op;
34
+ BdrvChild *child, *tmp;
29
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
30
QTAILQ_FOREACH(op, &s->ops_in_flight, next) {
36
31
+ if (self == op) {
37
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
32
+ continue;
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
33
+ }
39
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
34
/* Do not wait on pseudo ops, because it may in turn wait on
40
bdrv_coroutine_enter(bs, data.co);
35
* some other operation to start, which may in fact be the
41
BDRV_POLL_WHILE(bs, !data.done);
36
* caller of this function. Since there is only one pseudo op
42
+
37
@@ -XXX,XX +XXX,XX @@ mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
43
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
44
+ bdrv_drain_invoke(child->bs, begin);
45
+ }
38
}
46
}
39
47
40
static inline void coroutine_fn
48
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
41
-mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)
49
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
42
+mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s, MirrorOp *self)
50
BdrvChild *child, *tmp;
43
{
51
bool waited;
44
/* Only non-active operations use up in-flight slots */
52
45
- mirror_wait_for_any_operation(s, false);
53
- /* Ensure any pending metadata writes are submitted to bs->file. */
46
+ mirror_wait_for_any_operation(s, self, false);
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);
47
}
65
}
48
66
49
/* Perform a mirror copy operation.
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
50
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_co_read(void *opaque)
51
52
while (s->buf_free_count < nb_chunks) {
53
trace_mirror_yield_in_flight(s, op->offset, s->in_flight);
54
- mirror_wait_for_free_in_flight_slot(s);
55
+ mirror_wait_for_free_in_flight_slot(s, op);
56
}
68
}
57
69
58
/* Now make a QEMUIOVector taking enough granularity-sized chunks
70
bdrv_parent_drained_end(bs);
59
@@ -XXX,XX +XXX,XX @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
71
+ bdrv_drain_invoke(bs, false);
60
72
bdrv_drain_recurse(bs, false);
61
while (s->in_flight >= MAX_IN_FLIGHT) {
73
aio_enable_external(bdrv_get_aio_context(bs));
62
trace_mirror_yield_in_flight(s, offset, s->in_flight);
74
}
63
- mirror_wait_for_free_in_flight_slot(s);
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
64
+ mirror_wait_for_free_in_flight_slot(s, pseudo_op);
76
aio_context_acquire(aio_context);
65
}
77
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
66
78
if (aio_context == bdrv_get_aio_context(bs)) {
67
if (s->ret < 0) {
79
+ /* FIXME Calling this multiple times is wrong */
68
@@ -XXX,XX +XXX,XX @@ static void mirror_free_init(MirrorBlockJob *s)
80
+ bdrv_drain_invoke(bs, true);
69
static void coroutine_fn mirror_wait_for_all_io(MirrorBlockJob *s)
81
waited |= bdrv_drain_recurse(bs, true);
70
{
82
}
71
while (s->in_flight > 0) {
83
}
72
- mirror_wait_for_free_in_flight_slot(s);
84
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
73
+ mirror_wait_for_free_in_flight_slot(s, NULL);
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);
74
}
91
}
75
}
76
77
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
78
if (s->in_flight >= MAX_IN_FLIGHT) {
79
trace_mirror_yield(s, UINT64_MAX, s->buf_free_count,
80
s->in_flight);
81
- mirror_wait_for_free_in_flight_slot(s);
82
+ mirror_wait_for_free_in_flight_slot(s, NULL);
83
continue;
84
}
85
86
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
87
/* Do not start passive operations while there are active
88
* writes in progress */
89
while (s->in_active_write_counter) {
90
- mirror_wait_for_any_operation(s, true);
91
+ mirror_wait_for_any_operation(s, NULL, true);
92
}
93
94
if (s->ret < 0) {
95
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
96
if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 ||
97
(cnt == 0 && s->in_flight > 0)) {
98
trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight);
99
- mirror_wait_for_free_in_flight_slot(s);
100
+ mirror_wait_for_free_in_flight_slot(s, NULL);
101
continue;
102
} else if (cnt != 0) {
103
delay_ns = mirror_iteration(s);
104
--
92
--
105
2.20.1
93
2.13.6
106
94
107
95
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
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.
2
4
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
This is obviously not right and results in nodes that stay drained even
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per
5
Message-Id: <20200218103454.296704-17-mreitz@redhat.com>
7
node.
8
9
Fix bdrv_drain_all_begin() to call the callback only once, too.
10
11
Cc: qemu-stable@nongnu.org
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
---
14
---
8
tests/qemu-iotests/041 | 6 ++----
15
block/io.c | 3 +--
9
1 file changed, 2 insertions(+), 4 deletions(-)
16
1 file changed, 1 insertion(+), 2 deletions(-)
10
17
11
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
18
diff --git a/block/io.c b/block/io.c
12
index XXXXXXX..XXXXXXX 100755
19
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/qemu-iotests/041
20
--- a/block/io.c
14
+++ b/tests/qemu-iotests/041
21
+++ b/block/io.c
15
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
16
23
aio_context_acquire(aio_context);
17
self.complete_and_wait(drive="job0")
24
bdrv_parent_drained_begin(bs);
18
self.assert_has_block_node("repair0", quorum_repair_img)
25
aio_disable_external(aio_context);
19
- # TODO: a better test requiring some QEMU infrastructure will be added
26
+ bdrv_drain_invoke(bs, true);
20
- # to check that this file is really driven by quorum
27
aio_context_release(aio_context);
21
+ self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
28
22
self.vm.shutdown()
29
if (!g_slist_find(aio_ctxs, aio_context)) {
23
self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
24
'target image does not match source after mirroring')
31
aio_context_acquire(aio_context);
25
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
26
33
if (aio_context == bdrv_get_aio_context(bs)) {
27
self.complete_and_wait('job0')
34
- /* FIXME Calling this multiple times is wrong */
28
self.assert_has_block_node("repair0", quorum_repair_img)
35
- bdrv_drain_invoke(bs, true);
29
- # TODO: a better test requiring some QEMU infrastructure will be added
36
waited |= bdrv_drain_recurse(bs, true);
30
- # to check that this file is really driven by quorum
37
}
31
+ self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
38
}
32
33
# Test mirroring with a source that does not have any parents (not even a
34
# BlockBackend)
35
--
39
--
36
2.20.1
40
2.13.6
37
41
38
42
diff view generated by jsdifflib
1
If a coroutine is launched, but the coroutine pointer isn't stored
1
This adds a test case that the BlockDriver callbacks for drain are
2
anywhere, debugging any problems inside the coroutine is quite hard.
2
called in bdrv_drained_all_begin/end(), and that both of them are called
3
Let's store the coroutine pointer of a mirror operation in MirrorOp to
3
exactly once.
4
have it available in the debugger.
5
4
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
8
---
9
block/mirror.c | 2 ++
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
10
1 file changed, 2 insertions(+)
10
tests/Makefile.include | 2 +
11
2 files changed, 139 insertions(+)
12
create mode 100644 tests/test-bdrv-drain.c
11
13
12
diff --git a/block/mirror.c b/block/mirror.c
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
13
index XXXXXXX..XXXXXXX 100644
158
index XXXXXXX..XXXXXXX 100644
14
--- a/block/mirror.c
159
--- a/tests/Makefile.include
15
+++ b/block/mirror.c
160
+++ b/tests/Makefile.include
16
@@ -XXX,XX +XXX,XX @@ struct MirrorOp {
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
17
bool is_pseudo_op;
162
gcov-files-test-hbitmap-y = util/hbitmap.c
18
bool is_active_write;
163
check-unit-y += tests/test-hbitmap$(EXESUF)
19
CoQueue waiting_requests;
164
gcov-files-test-hbitmap-y = blockjob.c
20
+ Coroutine *co;
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
21
166
check-unit-y += tests/test-blockjob$(EXESUF)
22
QTAILQ_ENTRY(MirrorOp) next;
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
23
};
168
check-unit-y += tests/test-x86-cpuid$(EXESUF)
24
@@ -XXX,XX +XXX,XX @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
169
@@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
25
default:
170
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
26
abort();
171
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
27
}
172
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
28
+ op->co = co;
173
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
29
174
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
30
QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);
175
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
31
qemu_coroutine_enter(co);
176
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
32
--
177
--
33
2.20.1
178
2.13.6
34
179
35
180
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
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.
2
3
3
Using -drive with default options means that a virtio-blk drive will be
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
created that has write access to the to-be quorum children. Quorum
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
should have exclusive write access to them, so we should use -blockdev
6
---
6
instead.
7
block/io.c | 12 ++++++------
8
1 file changed, 6 insertions(+), 6 deletions(-)
7
9
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
diff --git a/block/io.c b/block/io.c
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
index XXXXXXX..XXXXXXX 100644
10
Message-Id: <20200218103454.296704-5-mreitz@redhat.com>
12
--- a/block/io.c
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
+++ b/block/io.c
12
---
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
13
tests/qemu-iotests/041 | 5 ++++-
15
}
14
1 file changed, 4 insertions(+), 1 deletion(-)
16
}
15
17
16
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
17
index XXXXXXX..XXXXXXX 100755
19
+static bool bdrv_drain_recurse(BlockDriverState *bs)
18
--- a/tests/qemu-iotests/041
20
{
19
+++ b/tests/qemu-iotests/041
21
BdrvChild *child, *tmp;
20
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
22
bool waited;
21
# Assign a node name to each quorum image in order to manipulate
23
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
22
# them
24
*/
23
opts = "node-name=img%i" % self.IMAGES.index(i)
25
bdrv_ref(bs);
24
- self.vm = self.vm.add_drive(i, opts)
26
}
25
+ opts += ',driver=%s' % iotests.imgfmt
27
- waited |= bdrv_drain_recurse(bs, begin);
26
+ opts += ',file.driver=file'
28
+ waited |= bdrv_drain_recurse(bs);
27
+ opts += ',file.filename=%s' % i
29
if (in_main_loop) {
28
+ self.vm = self.vm.add_blockdev(opts)
30
bdrv_unref(bs);
29
31
}
30
self.vm.launch()
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
}
31
67
32
--
68
--
33
2.20.1
69
2.13.6
34
70
35
71
diff view generated by jsdifflib
1
The bytes_written variable is only ever written to, it serves no
1
The device is drained, so there is no point in waiting for requests at
2
purpose. This has actually been the case since the commit job was first
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
introduced in commit 747ff602636.
3
there.
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.
4
8
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20200214200812.28180-3-kwolf@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
7
Reviewed-by: Ján Tomko <jtomko@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
12
---
10
block/commit.c | 2 --
13
block/io.c | 2 --
11
1 file changed, 2 deletions(-)
14
1 file changed, 2 deletions(-)
12
15
13
diff --git a/block/commit.c b/block/commit.c
16
diff --git a/block/io.c b/block/io.c
14
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
15
--- a/block/commit.c
18
--- a/block/io.c
16
+++ b/block/commit.c
19
+++ b/block/io.c
17
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
18
int ret = 0;
21
19
int64_t n = 0; /* bytes */
22
bdrv_parent_drained_end(bs);
20
void *buf = NULL;
23
bdrv_drain_invoke(bs, false);
21
- int bytes_written = 0;
24
- bdrv_drain_recurse(bs);
22
int64_t len, base_len;
25
aio_enable_external(bdrv_get_aio_context(bs));
23
26
}
24
ret = len = blk_getlength(s->top);
27
25
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
26
trace_commit_one_iteration(s, offset, n, ret);
29
aio_enable_external(aio_context);
27
if (copy) {
30
bdrv_parent_drained_end(bs);
28
ret = commit_populate(s->top, s->base, offset, n, buf);
31
bdrv_drain_invoke(bs, false);
29
- bytes_written += n;
32
- bdrv_drain_recurse(bs);
30
}
33
aio_context_release(aio_context);
31
if (ret < 0) {
34
}
32
BlockErrorAction action =
35
33
--
36
--
34
2.20.1
37
2.13.6
35
38
36
39
diff view generated by jsdifflib
1
This tests both read failure (from the top node) and write failure (to
1
Drain requests are propagated to child nodes, parent nodes and directly
2
the base node) for on-error=report/stop/ignore.
2
to the AioContext. The order in which this happened was different
3
between all combinations of drain/drain_all and begin/end.
3
4
4
As block-commit actually starts two different types of block jobs
5
The correct order is to keep children only drained when their parents
5
(mirror.c for committing the active later, commit.c for intermediate
6
are also drained. This means that at the start of a drained section, the
6
layers), all tests are run for both cases.
7
AioContext needs to be drained first, the parents second and only then
8
the children. The correct order for the end of a drained section is the
9
opposite.
10
11
This patch changes the three other functions to follow the example of
12
bdrv_drained_begin(), which is the only one that got it right.
7
13
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20200214200812.28180-8-kwolf@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
16
---
12
tests/qemu-iotests/040 | 283 +++++++++++++++++++++++++++++++++++++
17
block/io.c | 12 ++++++++----
13
tests/qemu-iotests/040.out | 4 +-
18
1 file changed, 8 insertions(+), 4 deletions(-)
14
2 files changed, 285 insertions(+), 2 deletions(-)
15
19
16
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
20
diff --git a/block/io.c b/block/io.c
17
index XXXXXXX..XXXXXXX 100755
18
--- a/tests/qemu-iotests/040
19
+++ b/tests/qemu-iotests/040
20
@@ -XXX,XX +XXX,XX @@ class TestReopenOverlay(ImageCommitTestCase):
21
def test_reopen_overlay(self):
22
self.run_commit_test(self.img1, self.img0)
23
24
+class TestErrorHandling(iotests.QMPTestCase):
25
+ image_len = 2 * 1024 * 1024
26
+
27
+ def setUp(self):
28
+ iotests.create_image(backing_img, self.image_len)
29
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
30
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
31
+
32
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x11 0 512k', mid_img)
33
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x22 0 512k', test_img)
34
+
35
+ self.vm = iotests.VM()
36
+ self.vm.launch()
37
+
38
+ self.blkdebug_file = iotests.file_path("blkdebug.conf")
39
+
40
+ def tearDown(self):
41
+ self.vm.shutdown()
42
+ os.remove(test_img)
43
+ os.remove(mid_img)
44
+ os.remove(backing_img)
45
+
46
+ def blockdev_add(self, **kwargs):
47
+ result = self.vm.qmp('blockdev-add', **kwargs)
48
+ self.assert_qmp(result, 'return', {})
49
+
50
+ def add_block_nodes(self, base_debug=None, mid_debug=None, top_debug=None):
51
+ self.blockdev_add(node_name='base-file', driver='file',
52
+ filename=backing_img)
53
+ self.blockdev_add(node_name='mid-file', driver='file',
54
+ filename=mid_img)
55
+ self.blockdev_add(node_name='top-file', driver='file',
56
+ filename=test_img)
57
+
58
+ if base_debug:
59
+ self.blockdev_add(node_name='base-dbg', driver='blkdebug',
60
+ image='base-file', inject_error=base_debug)
61
+ if mid_debug:
62
+ self.blockdev_add(node_name='mid-dbg', driver='blkdebug',
63
+ image='mid-file', inject_error=mid_debug)
64
+ if top_debug:
65
+ self.blockdev_add(node_name='top-dbg', driver='blkdebug',
66
+ image='top-file', inject_error=top_debug)
67
+
68
+ self.blockdev_add(node_name='base-fmt', driver='raw',
69
+ file=('base-dbg' if base_debug else 'base-file'))
70
+ self.blockdev_add(node_name='mid-fmt', driver=iotests.imgfmt,
71
+ file=('mid-dbg' if mid_debug else 'mid-file'),
72
+ backing='base-fmt')
73
+ self.blockdev_add(node_name='top-fmt', driver=iotests.imgfmt,
74
+ file=('top-dbg' if top_debug else 'top-file'),
75
+ backing='mid-fmt')
76
+
77
+ def run_job(self, expected_events, error_pauses_job=False):
78
+ match_device = {'data': {'device': 'job0'}}
79
+ events = [
80
+ ('BLOCK_JOB_COMPLETED', match_device),
81
+ ('BLOCK_JOB_CANCELLED', match_device),
82
+ ('BLOCK_JOB_ERROR', match_device),
83
+ ('BLOCK_JOB_READY', match_device),
84
+ ]
85
+
86
+ completed = False
87
+ log = []
88
+ while not completed:
89
+ ev = self.vm.events_wait(events, timeout=5.0)
90
+ if ev['event'] == 'BLOCK_JOB_COMPLETED':
91
+ completed = True
92
+ elif ev['event'] == 'BLOCK_JOB_ERROR':
93
+ if error_pauses_job:
94
+ result = self.vm.qmp('block-job-resume', device='job0')
95
+ self.assert_qmp(result, 'return', {})
96
+ elif ev['event'] == 'BLOCK_JOB_READY':
97
+ result = self.vm.qmp('block-job-complete', device='job0')
98
+ self.assert_qmp(result, 'return', {})
99
+ else:
100
+ self.fail("Unexpected event: %s" % ev)
101
+ log.append(iotests.filter_qmp_event(ev))
102
+
103
+ self.maxDiff = None
104
+ self.assertEqual(expected_events, log)
105
+
106
+ def event_error(self, op, action):
107
+ return {
108
+ 'event': 'BLOCK_JOB_ERROR',
109
+ 'data': {'action': action, 'device': 'job0', 'operation': op},
110
+ 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}
111
+ }
112
+
113
+ def event_ready(self):
114
+ return {
115
+ 'event': 'BLOCK_JOB_READY',
116
+ 'data': {'device': 'job0',
117
+ 'len': 524288,
118
+ 'offset': 524288,
119
+ 'speed': 0,
120
+ 'type': 'commit'},
121
+ 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'},
122
+ }
123
+
124
+ def event_completed(self, errmsg=None, active=True):
125
+ max_len = 524288 if active else self.image_len
126
+ data = {
127
+ 'device': 'job0',
128
+ 'len': max_len,
129
+ 'offset': 0 if errmsg else max_len,
130
+ 'speed': 0,
131
+ 'type': 'commit'
132
+ }
133
+ if errmsg:
134
+ data['error'] = errmsg
135
+
136
+ return {
137
+ 'event': 'BLOCK_JOB_COMPLETED',
138
+ 'data': data,
139
+ 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'},
140
+ }
141
+
142
+ def blkdebug_event(self, event, is_raw=False):
143
+ if event:
144
+ return [{
145
+ 'event': event,
146
+ 'sector': 512 if is_raw else 1024,
147
+ 'once': True,
148
+ }]
149
+ return None
150
+
151
+ def prepare_and_start_job(self, on_error, active=True,
152
+ top_event=None, mid_event=None, base_event=None):
153
+
154
+ top_debug = self.blkdebug_event(top_event)
155
+ mid_debug = self.blkdebug_event(mid_event)
156
+ base_debug = self.blkdebug_event(base_event, True)
157
+
158
+ self.add_block_nodes(top_debug=top_debug, mid_debug=mid_debug,
159
+ base_debug=base_debug)
160
+
161
+ result = self.vm.qmp('block-commit', job_id='job0', device='top-fmt',
162
+ top_node='top-fmt' if active else 'mid-fmt',
163
+ base_node='mid-fmt' if active else 'base-fmt',
164
+ on_error=on_error)
165
+ self.assert_qmp(result, 'return', {})
166
+
167
+ def testActiveReadErrorReport(self):
168
+ self.prepare_and_start_job('report', top_event='read_aio')
169
+ self.run_job([
170
+ self.event_error('read', 'report'),
171
+ self.event_completed('Input/output error')
172
+ ])
173
+
174
+ self.vm.shutdown()
175
+ self.assertFalse(iotests.compare_images(test_img, mid_img),
176
+ 'target image matches source after error')
177
+
178
+ def testActiveReadErrorStop(self):
179
+ self.prepare_and_start_job('stop', top_event='read_aio')
180
+ self.run_job([
181
+ self.event_error('read', 'stop'),
182
+ self.event_ready(),
183
+ self.event_completed()
184
+ ], error_pauses_job=True)
185
+
186
+ self.vm.shutdown()
187
+ self.assertTrue(iotests.compare_images(test_img, mid_img),
188
+ 'target image does not match source after commit')
189
+
190
+ def testActiveReadErrorIgnore(self):
191
+ self.prepare_and_start_job('ignore', top_event='read_aio')
192
+ self.run_job([
193
+ self.event_error('read', 'ignore'),
194
+ self.event_ready(),
195
+ self.event_completed()
196
+ ])
197
+
198
+ # For commit, 'ignore' actually means retry, so this will succeed
199
+ self.vm.shutdown()
200
+ self.assertTrue(iotests.compare_images(test_img, mid_img),
201
+ 'target image does not match source after commit')
202
+
203
+ def testActiveWriteErrorReport(self):
204
+ self.prepare_and_start_job('report', mid_event='write_aio')
205
+ self.run_job([
206
+ self.event_error('write', 'report'),
207
+ self.event_completed('Input/output error')
208
+ ])
209
+
210
+ self.vm.shutdown()
211
+ self.assertFalse(iotests.compare_images(test_img, mid_img),
212
+ 'target image matches source after error')
213
+
214
+ def testActiveWriteErrorStop(self):
215
+ self.prepare_and_start_job('stop', mid_event='write_aio')
216
+ self.run_job([
217
+ self.event_error('write', 'stop'),
218
+ self.event_ready(),
219
+ self.event_completed()
220
+ ], error_pauses_job=True)
221
+
222
+ self.vm.shutdown()
223
+ self.assertTrue(iotests.compare_images(test_img, mid_img),
224
+ 'target image does not match source after commit')
225
+
226
+ def testActiveWriteErrorIgnore(self):
227
+ self.prepare_and_start_job('ignore', mid_event='write_aio')
228
+ self.run_job([
229
+ self.event_error('write', 'ignore'),
230
+ self.event_ready(),
231
+ self.event_completed()
232
+ ])
233
+
234
+ # For commit, 'ignore' actually means retry, so this will succeed
235
+ self.vm.shutdown()
236
+ self.assertTrue(iotests.compare_images(test_img, mid_img),
237
+ 'target image does not match source after commit')
238
+
239
+ def testIntermediateReadErrorReport(self):
240
+ self.prepare_and_start_job('report', active=False, mid_event='read_aio')
241
+ self.run_job([
242
+ self.event_error('read', 'report'),
243
+ self.event_completed('Input/output error', active=False)
244
+ ])
245
+
246
+ self.vm.shutdown()
247
+ self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
248
+ 'target image matches source after error')
249
+
250
+ def testIntermediateReadErrorStop(self):
251
+ self.prepare_and_start_job('stop', active=False, mid_event='read_aio')
252
+ self.run_job([
253
+ self.event_error('read', 'stop'),
254
+ self.event_completed(active=False)
255
+ ], error_pauses_job=True)
256
+
257
+ self.vm.shutdown()
258
+ self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
259
+ 'target image does not match source after commit')
260
+
261
+ def testIntermediateReadErrorIgnore(self):
262
+ self.prepare_and_start_job('ignore', active=False, mid_event='read_aio')
263
+ self.run_job([
264
+ self.event_error('read', 'ignore'),
265
+ self.event_completed(active=False)
266
+ ])
267
+
268
+ # For commit, 'ignore' actually means retry, so this will succeed
269
+ self.vm.shutdown()
270
+ self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
271
+ 'target image does not match source after commit')
272
+
273
+ def testIntermediateWriteErrorReport(self):
274
+ self.prepare_and_start_job('report', active=False, base_event='write_aio')
275
+ self.run_job([
276
+ self.event_error('write', 'report'),
277
+ self.event_completed('Input/output error', active=False)
278
+ ])
279
+
280
+ self.vm.shutdown()
281
+ self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
282
+ 'target image matches source after error')
283
+
284
+ def testIntermediateWriteErrorStop(self):
285
+ self.prepare_and_start_job('stop', active=False, base_event='write_aio')
286
+ self.run_job([
287
+ self.event_error('write', 'stop'),
288
+ self.event_completed(active=False)
289
+ ], error_pauses_job=True)
290
+
291
+ self.vm.shutdown()
292
+ self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
293
+ 'target image does not match source after commit')
294
+
295
+ def testIntermediateWriteErrorIgnore(self):
296
+ self.prepare_and_start_job('ignore', active=False, base_event='write_aio')
297
+ self.run_job([
298
+ self.event_error('write', 'ignore'),
299
+ self.event_completed(active=False)
300
+ ])
301
+
302
+ # For commit, 'ignore' actually means retry, so this will succeed
303
+ self.vm.shutdown()
304
+ self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'),
305
+ 'target image does not match source after commit')
306
+
307
if __name__ == '__main__':
308
iotests.main(supported_fmts=['qcow2', 'qed'],
309
supported_protocols=['file'])
310
diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out
311
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
312
--- a/tests/qemu-iotests/040.out
22
--- a/block/io.c
313
+++ b/tests/qemu-iotests/040.out
23
+++ b/block/io.c
314
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
315
-...............................................
25
return;
316
+...........................................................
26
}
317
----------------------------------------------------------------------
27
318
-Ran 47 tests
28
+ /* Stop things in parent-to-child order */
319
+Ran 59 tests
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
320
30
aio_disable_external(bdrv_get_aio_context(bs));
321
OK
31
bdrv_parent_drained_begin(bs);
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
33
return;
34
}
35
36
- bdrv_parent_drained_end(bs);
37
+ /* Re-enable things in child-to-parent order */
38
bdrv_drain_invoke(bs, false);
39
+ bdrv_parent_drained_end(bs);
40
aio_enable_external(bdrv_get_aio_context(bs));
41
}
42
43
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
44
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
45
AioContext *aio_context = bdrv_get_aio_context(bs);
46
47
+ /* Stop things in parent-to-child order */
48
aio_context_acquire(aio_context);
49
- bdrv_parent_drained_begin(bs);
50
aio_disable_external(aio_context);
51
+ bdrv_parent_drained_begin(bs);
52
bdrv_drain_invoke(bs, true);
53
aio_context_release(aio_context);
54
55
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
56
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
57
AioContext *aio_context = bdrv_get_aio_context(bs);
58
59
+ /* Re-enable things in child-to-parent order */
60
aio_context_acquire(aio_context);
61
- aio_enable_external(aio_context);
62
- bdrv_parent_drained_end(bs);
63
bdrv_drain_invoke(bs, false);
64
+ bdrv_parent_drained_end(bs);
65
+ aio_enable_external(aio_context);
66
aio_context_release(aio_context);
67
}
68
322
--
69
--
323
2.20.1
70
2.13.6
324
71
325
72
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Commit 15afd94a047 added code to acquire and release the AioContext in
2
qemuio_command(). This means that the lock is taken twice now in the
3
call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for
4
any requests issued to nodes in a non-mainloop AioContext.
2
5
3
There is no good reason why we would allow external snapshots only on
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
4
the first non-filter node in a chain. Parent BDSs should not care
5
whether their child is replaced by a snapshot. (If they do care, they
6
should announce that via freezing the chain, which is checked in
7
bdrv_append() through bdrv_set_backing_hd().)
8
7
9
Before we had bdrv_is_first_non_filter() here (since 212a5a8f095), there
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
was a special function bdrv_check_ext_snapshot() that allowed snapshots
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
by default, but block drivers could override this. Only blkverify did
10
---
12
so, however.
11
hmp.c | 6 ------
12
1 file changed, 6 deletions(-)
13
13
14
It is not clear to me why blkverify would do so; maybe just so that the
14
diff --git a/hmp.c b/hmp.c
15
testee block driver would not be replaced. The introducing commit
16
f6186f49e2c does not explain why. Maybe because 08b24cfe376 would have
17
been the correct solution? (Which adds a .supports_backing check.)
18
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
20
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
21
Message-Id: <20200218103454.296704-2-mreitz@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
24
blockdev.c | 5 -----
25
1 file changed, 5 deletions(-)
26
27
diff --git a/blockdev.c b/blockdev.c
28
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
29
--- a/blockdev.c
16
--- a/hmp.c
30
+++ b/blockdev.c
17
+++ b/hmp.c
31
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
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)
32
}
27
}
33
}
28
}
34
29
35
- if (!bdrv_is_first_non_filter(state->old_bs)) {
30
- aio_context = blk_get_aio_context(blk);
36
- error_setg(errp, QERR_FEATURE_DISABLED, "snapshot");
31
- aio_context_acquire(aio_context);
37
- goto out;
38
- }
39
-
32
-
40
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
33
/*
41
BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
34
* Notably absent: Proper permission management. This is sad, but it seems
42
const char *format = s->has_format ? s->format : "qcow2";
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);
43
--
45
--
44
2.20.1
46
2.13.6
45
47
46
48
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
2
2
3
I/O requests to encrypted media should be aligned to the sector size
3
Since bdrv_co_preadv does all neccessary checks including
4
used by the underlying encryption method, not to BDRV_SECTOR_SIZE.
4
reading after the end of the backing file, avoid duplication
5
Fortunately this doesn't break anything at the moment because
5
of verification before bdrv_co_preadv call.
6
both existing QCRYPTO_BLOCK_*_SECTOR_SIZE have the same value as
7
BDRV_SECTOR_SIZE.
8
6
9
The checks in qcow2_co_preadv_encrypted() are also unnecessary because
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
10
they are repeated immediately afterwards in qcow2_co_encdec().
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
11
9
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Signed-off-by: Alberto Garcia <berto@igalia.com>
13
Message-Id: <20200213171646.15876-1-berto@igalia.com>
14
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
11
---
17
block/qcow2-threads.c | 12 ++++++++----
12
block/qcow2.h | 3 ---
18
block/qcow2.c | 2 --
13
block/qcow2.c | 51 ++++++++-------------------------------------------
19
2 files changed, 8 insertions(+), 6 deletions(-)
14
2 files changed, 8 insertions(+), 46 deletions(-)
20
15
21
diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c
16
diff --git a/block/qcow2.h b/block/qcow2.h
22
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
23
--- a/block/qcow2-threads.c
18
--- a/block/qcow2.h
24
+++ b/block/qcow2-threads.c
19
+++ b/block/qcow2.h
25
@@ -XXX,XX +XXX,XX @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset,
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
26
.len = len,
27
.func = func,
28
};
29
+ uint64_t sector_size;
30
31
- assert(QEMU_IS_ALIGNED(guest_offset, BDRV_SECTOR_SIZE));
32
- assert(QEMU_IS_ALIGNED(host_offset, BDRV_SECTOR_SIZE));
33
- assert(QEMU_IS_ALIGNED(len, BDRV_SECTOR_SIZE));
34
assert(s->crypto);
35
36
+ sector_size = qcrypto_block_get_sector_size(s->crypto);
37
+ assert(QEMU_IS_ALIGNED(guest_offset, sector_size));
38
+ assert(QEMU_IS_ALIGNED(host_offset, sector_size));
39
+ assert(QEMU_IS_ALIGNED(len, sector_size));
40
+
41
return len == 0 ? 0 : qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
42
}
21
}
43
22
44
@@ -XXX,XX +XXX,XX @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset,
23
/* qcow2.c functions */
45
* will be written to the underlying storage device at
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
46
* @host_offset
25
- int64_t sector_num, int nb_sectors);
47
*
26
-
48
- * @len - length of the buffer (must be a BDRV_SECTOR_SIZE multiple)
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
49
+ * @len - length of the buffer (must be a multiple of the encryption
28
int refcount_order, bool generous_increase,
50
+ * sector size)
29
uint64_t *refblock_count);
51
*
52
* Depending on the encryption method, @host_offset and/or @guest_offset
53
* may be used for generating the initialization vector for
54
diff --git a/block/qcow2.c b/block/qcow2.c
30
diff --git a/block/qcow2.c b/block/qcow2.c
55
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
56
--- a/block/qcow2.c
32
--- a/block/qcow2.c
57
+++ b/block/qcow2.c
33
+++ b/block/qcow2.c
58
@@ -XXX,XX +XXX,XX @@ qcow2_co_preadv_encrypted(BlockDriverState *bs,
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
59
goto fail;
35
return status;
60
}
36
}
61
37
62
- assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
38
-/* handle reading after the end of the backing file */
63
- assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
64
if (qcow2_co_decrypt(bs,
40
- int64_t offset, int bytes)
65
file_cluster_offset + offset_into_cluster(s, offset),
41
-{
66
offset, buf, bytes) < 0)
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 */
67
--
104
--
68
2.20.1
105
2.13.6
69
106
70
107
diff view generated by jsdifflib
1
It is not obvious what 'ignore' actually means for block jobs: It could
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
be continuing the job and returning success in the end despite the error
2
driver state that cannot be recreated with create options because it
3
(no block job does this). It could also mean continuing and returning
3
would require a list with gaps. This causes trouble in at least
4
failure in the end (this is what stream does). And it can mean retrying
4
.bdrv_refresh_filename().
5
the failed request later (this is what backup, commit and mirror do).
6
5
7
This (somewhat inconsistent) behaviour was introduced and described for
6
Document this problem so that we won't accidentally mark the command
8
stream and mirror in commit 32c81a4a6ec. backup and commit were
7
stable without having addressed it.
9
introduced later and use the same model as mirror.
10
8
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20200214200812.28180-2-kwolf@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
13
Reviewed-by: Ján Tomko <jtomko@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
11
---
16
qapi/block-core.json | 5 ++++-
12
qapi/block-core.json | 4 ++++
17
1 file changed, 4 insertions(+), 1 deletion(-)
13
1 file changed, 4 insertions(+)
18
14
19
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
20
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
21
--- a/qapi/block-core.json
17
--- a/qapi/block-core.json
22
+++ b/qapi/block-core.json
18
+++ b/qapi/block-core.json
23
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
24
# for jobs, cancel the job
20
# does not support all kinds of operations, all kinds of children, nor
21
# all block drivers.
25
#
22
#
26
# @ignore: ignore the error, only report a QMP event (BLOCK_IO_ERROR
23
+# FIXME Removing children from a quorum node means introducing gaps in the
27
-# or BLOCK_JOB_ERROR)
24
+# child indices. This cannot be represented in the 'children' list of
28
+# or BLOCK_JOB_ERROR). The backup, mirror and commit block jobs retry
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
29
+# the failing request later and may still complete successfully. The
26
+#
30
+# stream block job continues to stream and will complete with an
27
# Warning: The data in a new quorum child MUST be consistent with that of
31
+# error.
28
# the rest of the array.
32
#
33
# @enospc: same as @stop on ENOSPC, same as @report otherwise.
34
#
29
#
35
--
30
--
36
2.20.1
31
2.13.6
37
32
38
33
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Doug Gale <doug16k@gmail.com>
2
2
3
All tearDowns in 041 shutdown the VM. Thus, test cases do not need to
3
Add trace output for commands, errors, and undefined behavior.
4
do it themselves (unless they need the VM to be down for some
4
Add guest error log output for undefined behavior.
5
post-operation check).
5
Report invalid undefined accesses to MMIO.
6
Annotate unlikely error checks with unlikely.
6
7
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
9
Message-Id: <20200218103454.296704-16-mreitz@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
---
12
tests/qemu-iotests/041 | 11 -----------
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
13
1 file changed, 11 deletions(-)
14
hw/block/trace-events | 93 ++++++++++++++
15
2 files changed, 390 insertions(+), 52 deletions(-)
14
16
15
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
16
index XXXXXXX..XXXXXXX 100755
18
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/041
19
--- a/hw/block/nvme.c
18
+++ b/tests/qemu-iotests/041
20
+++ b/hw/block/nvme.c
19
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase):
21
@@ -XXX,XX +XXX,XX @@
20
self.cancel_and_wait(force=True)
22
#include "qapi/visitor.h"
21
result = self.vm.qmp('query-block')
23
#include "sysemu/block-backend.h"
22
self.assert_qmp(result, 'return[0]/inserted/file', test_img)
24
23
- self.vm.shutdown()
25
+#include "qemu/log.h"
24
26
+#include "trace.h"
25
def test_cancel_after_ready(self):
27
#include "nvme.h"
26
self.assert_no_active_block_jobs()
28
27
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase):
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
28
self.assert_qmp(result, 'return[0]/node-name', 'top')
30
+ do { \
29
self.assert_qmp(result, 'return[0]/backing/node-name', 'base')
31
+ (trace_##trace)(__VA_ARGS__); \
30
32
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
31
- self.vm.shutdown()
33
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
32
-
34
+ } while (0)
33
def test_medium_not_found(self):
35
+
34
if iotests.qemu_default_machine != 'pc':
36
static void nvme_process_sq(void *opaque);
35
return
37
36
@@ -XXX,XX +XXX,XX @@ new_state = "1"
38
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
37
self.assert_qmp(event, 'data/id', 'drive0')
39
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
38
40
{
39
self.assert_no_active_block_jobs()
41
if (cq->irq_enabled) {
40
- self.vm.shutdown()
42
if (msix_enabled(&(n->parent_obj))) {
41
43
+ trace_nvme_irq_msix(cq->vector);
42
def test_ignore_read(self):
44
msix_notify(&(n->parent_obj), cq->vector);
43
self.assert_no_active_block_jobs()
45
} else {
44
@@ -XXX,XX +XXX,XX @@ new_state = "1"
46
+ trace_nvme_irq_pin();
45
result = self.vm.qmp('query-block-jobs')
47
pci_irq_pulse(&n->parent_obj);
46
self.assert_qmp(result, 'return[0]/paused', False)
48
}
47
self.complete_and_wait()
49
+ } else {
48
- self.vm.shutdown()
50
+ trace_nvme_irq_masked();
49
51
}
50
def test_large_cluster(self):
52
}
51
self.assert_no_active_block_jobs()
53
52
@@ -XXX,XX +XXX,XX @@ new_state = "1"
54
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
53
55
trans_len = MIN(len, trans_len);
54
self.complete_and_wait(wait_ready=False)
56
int num_prps = (len >> n->page_bits) + 1;
55
self.assert_no_active_block_jobs()
57
56
- self.vm.shutdown()
58
- if (!prp1) {
57
59
+ if (unlikely(!prp1)) {
58
class TestWriteErrors(iotests.QMPTestCase):
60
+ trace_nvme_err_invalid_prp();
59
image_len = 2 * 1024 * 1024 # MB
61
return NVME_INVALID_FIELD | NVME_DNR;
60
@@ -XXX,XX +XXX,XX @@ new_state = "1"
62
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
61
completed = True
63
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
62
64
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
63
self.assert_no_active_block_jobs()
65
}
64
- self.vm.shutdown()
66
len -= trans_len;
65
67
if (len) {
66
def test_ignore_write(self):
68
- if (!prp2) {
67
self.assert_no_active_block_jobs()
69
+ if (unlikely(!prp2)) {
68
@@ -XXX,XX +XXX,XX @@ new_state = "1"
70
+ trace_nvme_err_invalid_prp2_missing();
69
result = self.vm.qmp('query-block-jobs')
71
goto unmap;
70
self.assert_qmp(result, 'return[0]/paused', False)
72
}
71
self.complete_and_wait()
73
if (len > n->page_size) {
72
- self.vm.shutdown()
74
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
73
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
74
def test_stop_write(self):
76
75
self.assert_no_active_block_jobs()
77
if (i == n->max_prp_ents - 1 && len > n->page_size) {
76
@@ -XXX,XX +XXX,XX @@ new_state = "1"
78
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
77
79
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
78
self.complete_and_wait(wait_ready=False)
80
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
79
self.assert_no_active_block_jobs()
81
goto unmap;
80
- self.vm.shutdown()
82
}
81
83
82
class TestSetSpeed(iotests.QMPTestCase):
84
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
83
image_len = 80 * 1024 * 1024 # MB
85
prp_ent = le64_to_cpu(prp_list[i]);
84
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
86
}
85
# here we check that the last registered quorum file has not been
87
86
# swapped out and unref
88
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
87
self.assert_has_block_node(None, quorum_img3)
89
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
88
- self.vm.shutdown()
90
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
89
91
goto unmap;
90
def test_cancel_after_ready(self):
92
}
91
self.assert_no_active_block_jobs()
93
92
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
94
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
93
self.assert_has_block_node("repair0", quorum_repair_img)
95
i++;
94
# TODO: a better test requiring some QEMU infrastructure will be added
96
}
95
# to check that this file is really driven by quorum
97
} else {
96
- self.vm.shutdown()
98
- if (prp2 & (n->page_size - 1)) {
97
99
+ if (unlikely(prp2 & (n->page_size - 1))) {
98
# Test mirroring with a source that does not have any parents (not even a
100
+ trace_nvme_err_invalid_prp2_align(prp2);
99
# BlockBackend)
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"
100
--
784
--
101
2.20.1
785
2.13.6
102
786
103
787
diff view generated by jsdifflib
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
Fixes: 132ada80c4a
3
Management tools create overlays of running guests with qemu-img:
4
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
4
5
Message-Id: <20200218094402.26625-4-philmd@redhat.com>
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>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
18
---
8
block.c | 4 ++--
19
block.c | 3 ++-
9
1 file changed, 2 insertions(+), 2 deletions(-)
20
1 file changed, 2 insertions(+), 1 deletion(-)
10
21
11
diff --git a/block.c b/block.c
22
diff --git a/block.c b/block.c
12
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
13
--- a/block.c
24
--- a/block.c
14
+++ b/block.c
25
+++ b/block.c
15
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
16
if (bdrv_get_aio_context(child_bs) != ctx) {
27
back_flags = flags;
17
ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err);
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
18
if (ret < 0 && child_role->can_set_aio_ctx) {
29
19
- GSList *ignore = g_slist_prepend(NULL, child);;
30
+ backing_options = qdict_new();
20
+ GSList *ignore = g_slist_prepend(NULL, child);
31
if (backing_fmt) {
21
ctx = bdrv_get_aio_context(child_bs);
32
- backing_options = qdict_new();
22
if (child_role->can_set_aio_ctx(child, ctx, &ignore, NULL)) {
33
qdict_put_str(backing_options, "driver", backing_fmt);
23
error_free(local_err);
34
}
24
ret = 0;
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
25
g_slist_free(ignore);
36
26
- ignore = g_slist_prepend(NULL, child);;
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
27
+ ignore = g_slist_prepend(NULL, child);
38
&local_err);
28
child_role->set_aio_ctx(child, ctx, &ignore);
29
}
30
g_slist_free(ignore);
31
--
39
--
32
2.20.1
40
2.13.6
33
41
34
42
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
Block nodes that do not allow resizing should not share BLK_PERM_RESIZE.
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
4
It does not matter whether they are the first non-filter in their chain
5
or not.
6
4
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
9
Message-Id: <20200218103454.296704-3-mreitz@redhat.com>
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
9
---
12
blockdev.c | 5 -----
10
blockdev.c | 11 -----------
13
1 file changed, 5 deletions(-)
11
qemu-doc.texi | 6 ------
12
2 files changed, 17 deletions(-)
14
13
15
diff --git a/blockdev.c b/blockdev.c
14
diff --git a/blockdev.c b/blockdev.c
16
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
17
--- a/blockdev.c
16
--- a/blockdev.c
18
+++ b/blockdev.c
17
+++ b/blockdev.c
19
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
20
aio_context = bdrv_get_aio_context(bs);
19
.type = QEMU_OPT_STRING,
21
aio_context_acquire(aio_context);
20
.help = "chs translation (auto, lba, none)",
22
21
},{
23
- if (!bdrv_is_first_non_filter(bs)) {
22
- .name = "boot",
24
- error_setg(errp, QERR_FEATURE_DISABLED, "resize");
23
- .type = QEMU_OPT_BOOL,
25
- goto out;
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");
26
- }
38
- }
27
-
39
-
28
if (size < 0) {
40
/* Other deprecated options */
29
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
41
if (!qtest_enabled()) {
30
goto out;
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
31
--
60
--
32
2.20.1
61
2.13.6
33
62
34
63
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
This way, we get to see errors during the completion phase.
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
4
complained that we should keep it, so let's remove this legacy option
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
now to simplify the code quite a bit.
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
7
Message-Id: <20200218103454.296704-14-mreitz@redhat.com>
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Markus Armbruster <armbru@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
11
---
10
tests/qemu-iotests/155 | 7 +------
12
vl.c | 86 ++-------------------------------------------------------
11
1 file changed, 1 insertion(+), 6 deletions(-)
13
qemu-doc.texi | 8 ------
12
14
qemu-options.hx | 19 ++-----------
13
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
15
3 files changed, 4 insertions(+), 109 deletions(-)
14
index XXXXXXX..XXXXXXX 100755
16
15
--- a/tests/qemu-iotests/155
17
diff --git a/vl.c b/vl.c
16
+++ b/tests/qemu-iotests/155
18
index XXXXXXX..XXXXXXX 100644
17
@@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass):
19
--- a/vl.c
18
20
+++ b/vl.c
19
self.assert_qmp(result, 'return', {})
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
20
22
const char *boot_order = NULL;
21
- self.vm.event_wait('BLOCK_JOB_READY')
23
const char *boot_once = NULL;
24
DisplayState *ds;
25
- int cyls, heads, secs, translation;
26
QemuOpts *opts, *machine_opts;
27
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
28
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
29
QemuOptsList *olist;
30
int optind;
31
const char *optarg;
32
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
33
34
cpu_model = NULL;
35
snapshot = 0;
36
- cyls = heads = secs = 0;
37
- translation = BIOS_ATA_TRANSLATION_AUTO;
38
39
nb_nics = 0;
40
41
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
42
if (optind >= argc)
43
break;
44
if (argv[optind][0] != '-') {
45
- hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
46
+ drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
47
} else {
48
const QEMUOption *popt;
49
50
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
51
cpu_model = optarg;
52
break;
53
case QEMU_OPTION_hda:
54
- {
55
- char buf[256];
56
- if (cyls == 0)
57
- snprintf(buf, sizeof(buf), "%s", HD_OPTS);
58
- else
59
- snprintf(buf, sizeof(buf),
60
- "%s,cyls=%d,heads=%d,secs=%d%s",
61
- HD_OPTS , cyls, heads, secs,
62
- translation == BIOS_ATA_TRANSLATION_LBA ?
63
- ",trans=lba" :
64
- translation == BIOS_ATA_TRANSLATION_NONE ?
65
- ",trans=none" : "");
66
- drive_add(IF_DEFAULT, 0, optarg, buf);
67
- break;
68
- }
69
case QEMU_OPTION_hdb:
70
case QEMU_OPTION_hdc:
71
case QEMU_OPTION_hdd:
72
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
73
case QEMU_OPTION_snapshot:
74
snapshot = 1;
75
break;
76
- case QEMU_OPTION_hdachs:
77
- {
78
- const char *p;
79
- p = optarg;
80
- cyls = strtol(p, (char **)&p, 0);
81
- if (cyls < 1 || cyls > 16383)
82
- goto chs_fail;
83
- if (*p != ',')
84
- goto chs_fail;
85
- p++;
86
- heads = strtol(p, (char **)&p, 0);
87
- if (heads < 1 || heads > 16)
88
- goto chs_fail;
89
- if (*p != ',')
90
- goto chs_fail;
91
- p++;
92
- secs = strtol(p, (char **)&p, 0);
93
- if (secs < 1 || secs > 63)
94
- goto chs_fail;
95
- if (*p == ',') {
96
- p++;
97
- if (!strcmp(p, "large")) {
98
- translation = BIOS_ATA_TRANSLATION_LARGE;
99
- } else if (!strcmp(p, "rechs")) {
100
- translation = BIOS_ATA_TRANSLATION_RECHS;
101
- } else if (!strcmp(p, "none")) {
102
- translation = BIOS_ATA_TRANSLATION_NONE;
103
- } else if (!strcmp(p, "lba")) {
104
- translation = BIOS_ATA_TRANSLATION_LBA;
105
- } else if (!strcmp(p, "auto")) {
106
- translation = BIOS_ATA_TRANSLATION_AUTO;
107
- } else {
108
- goto chs_fail;
109
- }
110
- } else if (*p != '\0') {
111
- chs_fail:
112
- error_report("invalid physical CHS format");
113
- exit(1);
114
- }
115
- if (hda_opts != NULL) {
116
- qemu_opt_set_number(hda_opts, "cyls", cyls,
117
- &error_abort);
118
- qemu_opt_set_number(hda_opts, "heads", heads,
119
- &error_abort);
120
- qemu_opt_set_number(hda_opts, "secs", secs,
121
- &error_abort);
122
- if (translation == BIOS_ATA_TRANSLATION_LARGE) {
123
- qemu_opt_set(hda_opts, "trans", "large",
124
- &error_abort);
125
- } else if (translation == BIOS_ATA_TRANSLATION_RECHS) {
126
- qemu_opt_set(hda_opts, "trans", "rechs",
127
- &error_abort);
128
- } else if (translation == BIOS_ATA_TRANSLATION_LBA) {
129
- qemu_opt_set(hda_opts, "trans", "lba",
130
- &error_abort);
131
- } else if (translation == BIOS_ATA_TRANSLATION_NONE) {
132
- qemu_opt_set(hda_opts, "trans", "none",
133
- &error_abort);
134
- }
135
- }
136
- }
137
- error_report("'-hdachs' is deprecated, please use '-device"
138
- " ide-hd,cyls=c,heads=h,secs=s,...' instead");
139
- break;
140
case QEMU_OPTION_numa:
141
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
142
optarg, true);
143
diff --git a/qemu-doc.texi b/qemu-doc.texi
144
index XXXXXXX..XXXXXXX 100644
145
--- a/qemu-doc.texi
146
+++ b/qemu-doc.texi
147
@@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the
148
``-object filter-dump'' argument which works in combination
149
with the modern ``-netdev`` backends instead.
150
151
-@subsection -hdachs (since 2.10.0)
22
-
152
-
23
- result = self.vm.qmp('block-job-complete', device='mirror-job')
153
-The ``-hdachs'' argument is now a synonym for setting
24
- self.assert_qmp(result, 'return', {})
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.
25
-
158
-
26
- self.vm.event_wait('BLOCK_JOB_COMPLETED')
159
@subsection -usbdevice (since 2.10.0)
27
+ self.complete_and_wait('mirror-job')
160
28
161
The ``-usbdevice DEV'' argument is now a synonym for setting
29
def testFull(self):
162
diff --git a/qemu-options.hx b/qemu-options.hx
30
self.runMirror('full')
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"
31
--
199
--
32
2.20.1
200
2.13.6
33
201
34
202
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
Quorum is not a filter, for example because it cannot guarantee which of
3
Looks like we forgot to announce the deprecation of these options in
4
its children will serve the next request. Thus, any of its children may
4
the corresponding chapter of the qemu-doc text, so let's do that now.
5
differ from the data visible to quorum's parents.
6
5
7
We have other filters with multiple children, but they differ in this
6
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
aspect:
7
Reviewed-by: John Snow <jsnow@redhat.com>
9
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
- blkverify quits the whole qemu process if its children differ. As
11
such, we can always skip it when we want to skip it (as a filter node)
12
by going to any of its children. Both have the same data.
13
14
- replication generally serves requests from bs->file, so this is its
15
only actually filtered child.
16
17
- Block job filters currently only have one child, but they will
18
probably get more children in the future. Still, they will always
19
have only one actually filtered child.
20
21
Having "filters" as a dedicated node category only makes sense if you
22
can skip them by going to a one fixed child that always shows the same
23
data as the filter node. Quorum cannot fulfill this, so it is not a
24
filter.
25
26
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
28
Message-Id: <20200218103454.296704-13-mreitz@redhat.com>
29
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
30
---
10
---
31
block/quorum.c | 1 -
11
qemu-doc.texi | 15 +++++++++++++++
32
1 file changed, 1 deletion(-)
12
1 file changed, 15 insertions(+)
33
13
34
diff --git a/block/quorum.c b/block/quorum.c
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
35
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
36
--- a/block/quorum.c
16
--- a/qemu-doc.texi
37
+++ b/block/quorum.c
17
+++ b/qemu-doc.texi
38
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
39
19
The ``-drive if=scsi'' argument is replaced by the the
40
.bdrv_child_perm = quorum_child_perm,
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
41
21
42
- .is_filter = true,
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
43
.bdrv_recurse_can_replace = quorum_recurse_can_replace,
23
+
44
24
+The drive geometry arguments are replaced by the the geometry arguments
45
.strong_runtime_opts = quorum_strong_runtime_opts,
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
46
--
40
--
47
2.20.1
41
2.13.6
48
42
49
43
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
It no longer has any users.
3
Signed-off-by: Fam Zheng <famz@redhat.com>
4
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Message-Id: <20200218103454.296704-11-mreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
5
---
10
include/block/block.h | 4 ----
6
include/block/block_int.h | 1 -
11
include/block/block_int.h | 8 --------
7
block/io.c | 18 ------------------
12
block.c | 33 ---------------------------------
8
2 files changed, 19 deletions(-)
13
block/blkverify.c | 15 ---------------
14
block/copy-on-read.c | 9 ---------
15
block/filter-compress.c | 9 ---------
16
block/quorum.c | 18 ------------------
17
block/replication.c | 7 -------
18
block/throttle.c | 8 --------
19
9 files changed, 111 deletions(-)
20
9
21
diff --git a/include/block/block.h b/include/block/block.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block.h
24
+++ b/include/block/block.h
25
@@ -XXX,XX +XXX,XX @@ int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
26
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
27
Error **errp);
28
29
-/* external snapshots */
30
-bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
31
- BlockDriverState *candidate);
32
-
33
/* check if a named node can be replaced when doing drive-mirror */
34
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
35
const char *node_name, Error **errp);
36
diff --git a/include/block/block_int.h b/include/block/block_int.h
10
diff --git a/include/block/block_int.h b/include/block/block_int.h
37
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
38
--- a/include/block/block_int.h
12
--- a/include/block/block_int.h
39
+++ b/include/block/block_int.h
13
+++ b/include/block/block_int.h
40
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
41
* must implement them and return -ENOTSUP.
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
42
*/
16
43
bool is_filter;
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
44
- /* for snapshots block filter like Quorum can implement the
18
-bool bdrv_requests_pending(BlockDriverState *bs);
45
- * following recursive callback.
19
46
- * It's purpose is to recurse on the filter children while calling
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
47
- * bdrv_recurse_is_first_non_filter on them.
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
48
- * For a sample implementation look in the future Quorum block filter.
22
diff --git a/block/io.c b/block/io.c
49
- */
50
- bool (*bdrv_recurse_is_first_non_filter)(BlockDriverState *bs,
51
- BlockDriverState *candidate);
52
/*
53
* Return true if @to_replace can be replaced by a BDS with the
54
* same data as @bs without it affecting @bs's behavior (that is,
55
diff --git a/block.c b/block.c
56
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
57
--- a/block.c
24
--- a/block/io.c
58
+++ b/block.c
25
+++ b/block/io.c
59
@@ -XXX,XX +XXX,XX @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
60
return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
27
assert(old >= 1);
61
}
28
}
62
29
63
-/* This function will be called by the bdrv_recurse_is_first_non_filter method
30
-/* Check if any requests are in-flight (including throttled requests) */
64
- * of block filter and by bdrv_is_first_non_filter.
31
-bool bdrv_requests_pending(BlockDriverState *bs)
65
- * It is used to test if the given bs is the candidate or recurse more in the
66
- * node graph.
67
- */
68
-bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
69
- BlockDriverState *candidate)
70
-{
32
-{
71
- /* return false if basic checks fails */
33
- BdrvChild *child;
72
- if (!bs || !bs->drv) {
73
- return false;
74
- }
75
-
34
-
76
- /* the code reached a non block filter driver -> check if the bs is
35
- if (atomic_read(&bs->in_flight)) {
77
- * the same as the candidate. It's the recursion termination condition.
78
- */
79
- if (!bs->drv->is_filter) {
80
- return bs == candidate;
81
- }
82
- /* Down this path the driver is a block filter driver */
83
-
84
- /* If the block filter recursion method is defined use it to recurse down
85
- * the node graph.
86
- */
87
- if (bs->drv->bdrv_recurse_is_first_non_filter) {
88
- return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate);
89
- }
90
-
91
- /* the driver is a block filter but don't allow to recurse -> return false
92
- */
93
- return false;
94
-}
95
-
96
/*
97
* This function checks whether the given @to_replace is allowed to be
98
* replaced by a node that always shows the same data as @bs. This is
99
diff --git a/block/blkverify.c b/block/blkverify.c
100
index XXXXXXX..XXXXXXX 100644
101
--- a/block/blkverify.c
102
+++ b/block/blkverify.c
103
@@ -XXX,XX +XXX,XX @@ static int blkverify_co_flush(BlockDriverState *bs)
104
return bdrv_co_flush(s->test_file->bs);
105
}
106
107
-static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
108
- BlockDriverState *candidate)
109
-{
110
- BDRVBlkverifyState *s = bs->opaque;
111
-
112
- bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
113
-
114
- if (perm) {
115
- return true;
36
- return true;
116
- }
37
- }
117
-
38
-
118
- return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
39
- QLIST_FOREACH(child, &bs->children, next) {
119
-}
40
- if (bdrv_requests_pending(child->bs)) {
120
-
121
static bool blkverify_recurse_can_replace(BlockDriverState *bs,
122
BlockDriverState *to_replace)
123
{
124
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkverify = {
125
.bdrv_co_flush = blkverify_co_flush,
126
127
.is_filter = true,
128
- .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
129
.bdrv_recurse_can_replace = blkverify_recurse_can_replace,
130
};
131
132
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
133
index XXXXXXX..XXXXXXX 100644
134
--- a/block/copy-on-read.c
135
+++ b/block/copy-on-read.c
136
@@ -XXX,XX +XXX,XX @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
137
}
138
139
140
-static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
141
- BlockDriverState *candidate)
142
-{
143
- return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
144
-}
145
-
146
-
147
static BlockDriver bdrv_copy_on_read = {
148
.format_name = "copy-on-read",
149
150
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_copy_on_read = {
151
152
.bdrv_co_block_status = bdrv_co_block_status_from_file,
153
154
- .bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter,
155
-
156
.has_variable_length = true,
157
.is_filter = true,
158
};
159
diff --git a/block/filter-compress.c b/block/filter-compress.c
160
index XXXXXXX..XXXXXXX 100644
161
--- a/block/filter-compress.c
162
+++ b/block/filter-compress.c
163
@@ -XXX,XX +XXX,XX @@ static void compress_lock_medium(BlockDriverState *bs, bool locked)
164
}
165
166
167
-static bool compress_recurse_is_first_non_filter(BlockDriverState *bs,
168
- BlockDriverState *candidate)
169
-{
170
- return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
171
-}
172
-
173
-
174
static BlockDriver bdrv_compress = {
175
.format_name = "compress",
176
177
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_compress = {
178
179
.bdrv_co_block_status = bdrv_co_block_status_from_file,
180
181
- .bdrv_recurse_is_first_non_filter = compress_recurse_is_first_non_filter,
182
-
183
.has_variable_length = true,
184
.is_filter = true,
185
};
186
diff --git a/block/quorum.c b/block/quorum.c
187
index XXXXXXX..XXXXXXX 100644
188
--- a/block/quorum.c
189
+++ b/block/quorum.c
190
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
191
return result;
192
}
193
194
-static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
195
- BlockDriverState *candidate)
196
-{
197
- BDRVQuorumState *s = bs->opaque;
198
- int i;
199
-
200
- for (i = 0; i < s->num_children; i++) {
201
- bool perm = bdrv_recurse_is_first_non_filter(s->children[i]->bs,
202
- candidate);
203
- if (perm) {
204
- return true;
41
- return true;
205
- }
42
- }
206
- }
43
- }
207
-
44
-
208
- return false;
45
- return false;
209
-}
46
-}
210
-
47
-
211
static bool quorum_recurse_can_replace(BlockDriverState *bs,
48
typedef struct {
212
BlockDriverState *to_replace)
49
Coroutine *co;
213
{
50
BlockDriverState *bs;
214
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
215
.bdrv_child_perm = quorum_child_perm,
216
217
.is_filter = true,
218
- .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
219
.bdrv_recurse_can_replace = quorum_recurse_can_replace,
220
221
.strong_runtime_opts = quorum_strong_runtime_opts,
222
diff --git a/block/replication.c b/block/replication.c
223
index XXXXXXX..XXXXXXX 100644
224
--- a/block/replication.c
225
+++ b/block/replication.c
226
@@ -XXX,XX +XXX,XX @@ out:
227
return ret;
228
}
229
230
-static bool replication_recurse_is_first_non_filter(BlockDriverState *bs,
231
- BlockDriverState *candidate)
232
-{
233
- return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
234
-}
235
-
236
static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
237
{
238
Error *local_err = NULL;
239
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_replication = {
240
.bdrv_co_writev = replication_co_writev,
241
242
.is_filter = true,
243
- .bdrv_recurse_is_first_non_filter = replication_recurse_is_first_non_filter,
244
245
.has_variable_length = true,
246
.strong_runtime_opts = replication_strong_runtime_opts,
247
diff --git a/block/throttle.c b/block/throttle.c
248
index XXXXXXX..XXXXXXX 100644
249
--- a/block/throttle.c
250
+++ b/block/throttle.c
251
@@ -XXX,XX +XXX,XX @@ static void throttle_reopen_abort(BDRVReopenState *reopen_state)
252
reopen_state->opaque = NULL;
253
}
254
255
-static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs,
256
- BlockDriverState *candidate)
257
-{
258
- return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
259
-}
260
-
261
static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs)
262
{
263
ThrottleGroupMember *tgm = bs->opaque;
264
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_throttle = {
265
.bdrv_co_pwrite_zeroes = throttle_co_pwrite_zeroes,
266
.bdrv_co_pdiscard = throttle_co_pdiscard,
267
268
- .bdrv_recurse_is_first_non_filter = throttle_recurse_is_first_non_filter,
269
-
270
.bdrv_attach_aio_context = throttle_attach_aio_context,
271
.bdrv_detach_aio_context = throttle_detach_aio_context,
272
273
--
51
--
274
2.20.1
52
2.13.6
275
53
276
54
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
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(+)
2
6
3
There is no guarantee that we can still replace the node we want to
7
diff --git a/block/io.c b/block/io.c
4
replace at the end of the mirror job. Double-check by calling
5
bdrv_recurse_can_replace().
6
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Message-Id: <20200218103454.296704-12-mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/mirror.c | 14 +++++++++++++-
13
1 file changed, 13 insertions(+), 1 deletion(-)
14
15
diff --git a/block/mirror.c b/block/mirror.c
16
index XXXXXXX..XXXXXXX 100644
8
index XXXXXXX..XXXXXXX 100644
17
--- a/block/mirror.c
9
--- a/block/io.c
18
+++ b/block/mirror.c
10
+++ b/block/io.c
19
@@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job)
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
20
* drain potential other users of the BDS before changing the graph. */
12
BdrvNextIterator it;
21
assert(s->in_drain);
13
GSList *aio_ctxs = NULL, *ctx;
22
bdrv_drained_begin(target_bs);
14
23
- bdrv_replace_node(to_replace, target_bs, &local_err);
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
24
+ /*
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
25
+ * Cannot use check_to_replace_node() here, because that would
17
+ * nodes in several different AioContexts, so make sure we're in the main
26
+ * check for an op blocker on @to_replace, and we have our own
18
+ * context. */
27
+ * there.
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
28
+ */
20
+
29
+ if (bdrv_recurse_can_replace(src, to_replace)) {
21
block_job_pause_all();
30
+ bdrv_replace_node(to_replace, target_bs, &local_err);
22
31
+ } else {
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
32
+ error_setg(&local_err, "Can no longer replace '%s' by '%s', "
33
+ "because it can no longer be guaranteed that doing so "
34
+ "would not lead to an abrupt change of visible data",
35
+ to_replace->node_name, target_bs->node_name);
36
+ }
37
bdrv_drained_end(target_bs);
38
if (local_err) {
39
error_report_err(local_err);
40
--
24
--
41
2.20.1
25
2.13.6
42
26
43
27
diff view generated by jsdifflib
1
block_job_error_action() needs to know if reading from the top node or
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
writing to the base node failed so that it can set the right 'operation'
2
and also doesn't notify other parent nodes of children, which both means
3
in the BLOCK_JOB_ERROR QMP event.
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
is providing useful functionality only on a single node.
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.
4
11
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20200214200812.28180-6-kwolf@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
7
Reviewed-by: Ján Tomko <jtomko@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
14
---
10
block/commit.c | 7 ++++++-
15
block/io.c | 16 +++++++++-------
11
1 file changed, 6 insertions(+), 1 deletion(-)
16
1 file changed, 9 insertions(+), 7 deletions(-)
12
17
13
diff --git a/block/commit.c b/block/commit.c
18
diff --git a/block/io.c b/block/io.c
14
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
15
--- a/block/commit.c
20
--- a/block/io.c
16
+++ b/block/commit.c
21
+++ b/block/io.c
17
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
22
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
18
23
}
19
for (offset = 0; offset < len; offset += n) {
24
20
bool copy;
25
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
21
+ bool error_in_source = true;
26
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
22
27
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
23
/* Note that even when no rate limit is applied we need to yield
28
{
24
* with no pending I/O here so that bdrv_drain_all() returns.
29
BdrvChild *child, *tmp;
25
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
26
ret = blk_co_pread(s->top, offset, n, buf, 0);
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
27
if (ret >= 0) {
32
bdrv_coroutine_enter(bs, data.co);
28
ret = blk_co_pwrite(s->base, offset, n, buf, 0);
33
BDRV_POLL_WHILE(bs, !data.done);
29
+ if (ret < 0) {
34
30
+ error_in_source = false;
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
31
+ }
36
- bdrv_drain_invoke(child->bs, begin);
32
}
37
+ if (recursive) {
33
}
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
34
if (ret < 0) {
39
+ bdrv_drain_invoke(child->bs, begin, true);
35
BlockErrorAction action =
40
+ }
36
- block_job_error_action(&s->common, s->on_error, false, -ret);
41
}
37
+ block_job_error_action(&s->common, s->on_error,
42
}
38
+ error_in_source, -ret);
43
39
if (action == BLOCK_ERROR_ACTION_REPORT) {
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
40
goto out;
45
bdrv_parent_drained_begin(bs);
41
} else {
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);
42
--
80
--
43
2.20.1
81
2.13.6
44
82
45
83
diff view generated by jsdifflib
1
From: Philippe Mathieu-Daudé <philmd@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
Fixes: 6663a0a3376
6
Also, add a backing file to the test node to test whether the operations
4
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
7
work recursively.
5
Message-Id: <20200218094402.26625-5-philmd@redhat.com>
8
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
10
---
8
block/io_uring.c | 2 +-
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
9
1 file changed, 1 insertion(+), 1 deletion(-)
12
1 file changed, 62 insertions(+), 7 deletions(-)
10
13
11
diff --git a/block/io_uring.c b/block/io_uring.c
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
12
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
13
--- a/block/io_uring.c
16
--- a/tests/test-bdrv-drain.c
14
+++ b/block/io_uring.c
17
+++ b/tests/test-bdrv-drain.c
15
@@ -XXX,XX +XXX,XX @@ static void luring_process_completions(LuringState *s)
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
16
ret = 0;
19
17
}
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
18
} else {
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
19
- ret = -ENOSPC;;
22
+
20
+ ret = -ENOSPC;
23
+ .bdrv_child_perm = bdrv_format_default_perms,
21
}
24
};
22
}
25
23
end:
26
static void aio_ret_cb(void *opaque, int ret)
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
28
*aio_ret = ret;
29
}
30
31
-static void test_drv_cb_drain_all(void)
32
+enum drain_type {
33
+ BDRV_DRAIN_ALL,
34
+ BDRV_DRAIN,
35
+};
36
+
37
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
38
+{
39
+ switch (drain_type) {
40
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
41
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
42
+ default: g_assert_not_reached();
43
+ }
44
+}
45
+
46
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
47
+{
48
+ switch (drain_type) {
49
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
50
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
51
+ default: g_assert_not_reached();
52
+ }
53
+}
54
+
55
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
56
{
57
BlockBackend *blk;
58
- BlockDriverState *bs;
59
- BDRVTestState *s;
60
+ BlockDriverState *bs, *backing;
61
+ BDRVTestState *s, *backing_s;
62
BlockAIOCB *acb;
63
int aio_ret;
64
65
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
66
s = bs->opaque;
67
blk_insert_bs(blk, bs, &error_abort);
68
69
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
70
+ backing_s = backing->opaque;
71
+ bdrv_set_backing_hd(bs, backing, &error_abort);
72
+
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
74
g_assert_cmpint(s->drain_count, ==, 0);
75
- bdrv_drain_all_begin();
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
77
+
78
+ do_drain_begin(drain_type, bs);
79
+
80
g_assert_cmpint(s->drain_count, ==, 1);
81
- bdrv_drain_all_end();
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
83
+
84
+ do_drain_end(drain_type, bs);
85
+
86
g_assert_cmpint(s->drain_count, ==, 0);
87
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
88
89
/* Now do the same while a request is pending */
90
aio_ret = -EINPROGRESS;
91
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
92
g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
93
94
g_assert_cmpint(s->drain_count, ==, 0);
95
- bdrv_drain_all_begin();
96
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
97
+
98
+ do_drain_begin(drain_type, bs);
99
+
100
g_assert_cmpint(aio_ret, ==, 0);
101
g_assert_cmpint(s->drain_count, ==, 1);
102
- bdrv_drain_all_end();
103
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
104
+
105
+ do_drain_end(drain_type, bs);
106
+
107
g_assert_cmpint(s->drain_count, ==, 0);
108
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
109
110
+ bdrv_unref(backing);
111
bdrv_unref(bs);
112
blk_unref(blk);
113
}
114
115
+static void test_drv_cb_drain_all(void)
116
+{
117
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
118
+}
119
+
120
+static void test_drv_cb_drain(void)
121
+{
122
+ test_drv_cb_common(BDRV_DRAIN, false);
123
+}
124
+
125
int main(int argc, char **argv)
126
{
127
bdrv_init();
128
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
129
g_test_init(&argc, &argv, NULL);
130
131
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
132
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
133
134
return g_test_run();
135
}
24
--
136
--
25
2.20.1
137
2.13.6
26
138
27
139
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
This is currently only working correctly for bdrv_drain(), not for
2
bdrv_drain_all(). Leave a comment for the drain_all case, we'll address
3
it later.
2
4
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Message-Id: <20200218103454.296704-8-mreitz@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
6
---
8
block/blkverify.c | 15 +++++++++++++++
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
9
1 file changed, 15 insertions(+)
8
1 file changed, 45 insertions(+)
10
9
11
diff --git a/block/blkverify.c b/block/blkverify.c
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
12
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
13
--- a/block/blkverify.c
12
--- a/tests/test-bdrv-drain.c
14
+++ b/block/blkverify.c
13
+++ b/tests/test-bdrv-drain.c
15
@@ -XXX,XX +XXX,XX @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
16
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
15
test_drv_cb_common(BDRV_DRAIN, false);
17
}
16
}
18
17
19
+static bool blkverify_recurse_can_replace(BlockDriverState *bs,
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
20
+ BlockDriverState *to_replace)
21
+{
19
+{
22
+ BDRVBlkverifyState *s = bs->opaque;
20
+ BlockBackend *blk;
21
+ BlockDriverState *bs, *backing;
23
+
22
+
24
+ /*
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
25
+ * blkverify quits the whole qemu process if there is a mismatch
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
26
+ * between bs->file->bs and s->test_file->bs. Therefore, we know
25
+ &error_abort);
27
+ * know that both must match bs and we can recurse down to either.
26
+ blk_insert_bs(blk, bs, &error_abort);
28
+ */
27
+
29
+ return bdrv_recurse_can_replace(bs->file->bs, to_replace) ||
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
30
+ bdrv_recurse_can_replace(s->test_file->bs, to_replace);
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);
31
+}
47
+}
32
+
48
+
33
static void blkverify_refresh_filename(BlockDriverState *bs)
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)
34
{
61
{
35
BDRVBlkverifyState *s = bs->opaque;
62
bdrv_init();
36
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkverify = {
63
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
37
64
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
38
.is_filter = true,
65
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
39
.bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
66
40
+ .bdrv_recurse_can_replace = blkverify_recurse_can_replace,
67
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
41
};
68
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
42
69
+
43
static void bdrv_blkverify_init(void)
70
return g_test_run();
71
}
44
--
72
--
45
2.20.1
73
2.13.6
46
74
47
75
diff view generated by jsdifflib
1
From: Hikaru Nishida <hikarupsp@gmail.com>
1
Block jobs already paused themselves when their main BlockBackend
2
entered a drained section. This is not good enough: We also want to
3
pause a block job and may not submit new requests if, for example, the
4
mirror target node should be drained.
2
5
3
Before this commit, BDRVVVFATState.qcow is unrefed in write_target_close
6
This implements .drained_begin/end callbacks in child_job in order to
4
on closing backing bdrv of vvfat. However, qcow bdrv is opend as a child
7
consider all block nodes related to the job, and removes the
5
of vvfat in enable_write_target() so it will be also unrefed on closing
8
BlockBackend callbacks which are unnecessary now because the root of the
6
vvfat itself. This causes use-after-free of qcow on freeing vvfat which
9
job main BlockBackend is always referenced with a child_job, too.
7
has backing bdrv and qcow bdrv as children in this order because
8
bdrv_close(vvfat) tries to free qcow bdrv after freeing backing bdrv
9
as QLIST_FOREACH_SAFE() loop keeps next pointer, but BdrvChild of qcow
10
is already freed in bdrv_close(backing bdrv).
11
10
12
Signed-off-by: Hikaru Nishida <hikarupsp@gmail.com>
13
Message-Id: <20200209175156.85748-1-hikarupsp@gmail.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
12
---
16
block/vvfat.c | 7 -------
13
blockjob.c | 22 +++++++++-------------
17
1 file changed, 7 deletions(-)
14
1 file changed, 9 insertions(+), 13 deletions(-)
18
15
19
diff --git a/block/vvfat.c b/block/vvfat.c
16
diff --git a/blockjob.c b/blockjob.c
20
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
21
--- a/block/vvfat.c
18
--- a/blockjob.c
22
+++ b/block/vvfat.c
19
+++ b/blockjob.c
23
@@ -XXX,XX +XXX,XX @@ write_target_commit(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
24
return ret;
21
job->id);
25
}
22
}
26
23
27
-static void write_target_close(BlockDriverState *bs) {
24
-static const BdrvChildRole child_job = {
28
- BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
25
- .get_parent_desc = child_job_get_parent_desc,
29
- bdrv_unref_child(s->bs, s->qcow);
26
- .stay_at_node = true,
30
- g_free(s->qcow_filename);
27
-};
31
-}
32
-
28
-
33
static BlockDriver vvfat_write_target = {
29
-static void block_job_drained_begin(void *opaque)
34
.format_name = "vvfat_write_target",
30
+static void child_job_drained_begin(BdrvChild *c)
35
.instance_size = sizeof(void*),
31
{
36
.bdrv_co_pwritev = write_target_commit,
32
- BlockJob *job = opaque;
37
- .bdrv_close = write_target_close,
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,
38
};
53
};
39
54
40
static void vvfat_qcow_options(int *child_flags, QDict *child_options,
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);
41
--
64
--
42
2.20.1
65
2.13.6
43
66
44
67
diff view generated by jsdifflib
Deleted patch
1
In the case that update_refcount() frees a refcount block, it evicts it
2
from the metadata cache. Before doing so, however, it returns the
3
currently used refcount block to the cache because it might be the same.
4
Returning the refcount block early means that we need to reset
5
old_table_index so that we reload the refcount block in the next
6
iteration if it is actually still in use.
7
1
8
Fixes: f71c08ea8e60f035485a512fd2af8908567592f0
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20200211094900.17315-2-kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
block/qcow2-refcount.c | 1 +
14
1 file changed, 1 insertion(+)
15
16
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2-refcount.c
19
+++ b/block/qcow2-refcount.c
20
@@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
21
offset);
22
if (table != NULL) {
23
qcow2_cache_put(s->refcount_block_cache, &refcount_block);
24
+ old_table_index = -1;
25
qcow2_cache_discard(s->refcount_block_cache, table);
26
}
27
28
--
29
2.20.1
30
31
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
Add two tests to see that you cannot replace a Quorum child with the
4
mirror job while the child is in use by a different parent.
5
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-Id: <20200218103454.296704-19-mreitz@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
4
---
11
tests/qemu-iotests/041 | 70 +++++++++++++++++++++++++++++++++++++-
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
12
tests/qemu-iotests/041.out | 4 +--
6
1 file changed, 121 insertions(+)
13
2 files changed, 71 insertions(+), 3 deletions(-)
14
7
15
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
16
index XXXXXXX..XXXXXXX 100755
9
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/041
10
--- a/tests/test-bdrv-drain.c
18
+++ b/tests/qemu-iotests/041
11
+++ b/tests/test-bdrv-drain.c
19
@@ -XXX,XX +XXX,XX @@
12
@@ -XXX,XX +XXX,XX @@
20
13
21
import time
14
#include "qemu/osdep.h"
22
import os
15
#include "block/block.h"
23
+import re
16
+#include "block/blockjob_int.h"
24
import iotests
17
#include "sysemu/block-backend.h"
25
from iotests import qemu_img, qemu_io
18
#include "qapi/error.h"
26
19
27
@@ -XXX,XX +XXX,XX @@ quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img')
20
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
28
quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img')
21
test_quiesce_common(BDRV_DRAIN, false);
29
quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img')
22
}
30
23
31
+nbd_sock_path = os.path.join(iotests.test_dir, 'nbd.sock')
32
+
24
+
33
class TestSingleDrive(iotests.QMPTestCase):
25
+typedef struct TestBlockJob {
34
image_len = 1 * 1024 * 1024 # MB
26
+ BlockJob common;
35
qmp_cmd = 'drive-mirror'
27
+ bool should_complete;
36
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
28
+} TestBlockJob;
37
38
def tearDown(self):
39
self.vm.shutdown()
40
- for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file ]:
41
+ for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file,
42
+ nbd_sock_path ]:
43
# Do a try/except because the test may have deleted some images
44
try:
45
os.remove(i)
46
@@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase):
47
self.assert_has_block_node("repair0", quorum_repair_img)
48
self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
49
50
+ def test_with_other_parent(self):
51
+ """
52
+ Check that we cannot replace a Quorum child when it has other
53
+ parents.
54
+ """
55
+ result = self.vm.qmp('nbd-server-start',
56
+ addr={
57
+ 'type': 'unix',
58
+ 'data': {'path': nbd_sock_path}
59
+ })
60
+ self.assert_qmp(result, 'return', {})
61
+
29
+
62
+ result = self.vm.qmp('nbd-server-add', device='img1')
30
+static void test_job_completed(BlockJob *job, void *opaque)
63
+ self.assert_qmp(result, 'return', {})
31
+{
32
+ block_job_completed(job, 0);
33
+}
64
+
34
+
65
+ result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
35
+static void coroutine_fn test_job_start(void *opaque)
66
+ sync='full', node_name='repair0', replaces='img1',
36
+{
67
+ target=quorum_repair_img, format=iotests.imgfmt)
37
+ TestBlockJob *s = opaque;
68
+ self.assert_qmp(result, 'error/desc',
69
+ "Cannot replace 'img1' by a node mirrored from "
70
+ "'quorum0', because it cannot be guaranteed that doing "
71
+ "so would not lead to an abrupt change of visible data")
72
+
38
+
73
+ def test_with_other_parents_after_mirror_start(self):
39
+ while (!s->should_complete) {
74
+ """
40
+ block_job_sleep_ns(&s->common, 100000);
75
+ The same as test_with_other_parent(), but add the NBD server
41
+ }
76
+ only when the mirror job is already running.
77
+ """
78
+ result = self.vm.qmp('nbd-server-start',
79
+ addr={
80
+ 'type': 'unix',
81
+ 'data': {'path': nbd_sock_path}
82
+ })
83
+ self.assert_qmp(result, 'return', {})
84
+
42
+
85
+ result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
86
+ sync='full', node_name='repair0', replaces='img1',
44
+}
87
+ target=quorum_repair_img, format=iotests.imgfmt)
88
+ self.assert_qmp(result, 'return', {})
89
+
45
+
90
+ result = self.vm.qmp('nbd-server-add', device='img1')
46
+static void test_job_complete(BlockJob *job, Error **errp)
91
+ self.assert_qmp(result, 'return', {})
47
+{
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
49
+ s->should_complete = true;
50
+}
92
+
51
+
93
+ # The full error message goes to stderr, we will check it later
52
+BlockJobDriver test_job_driver = {
94
+ self.complete_and_wait('mirror',
53
+ .instance_size = sizeof(TestBlockJob),
95
+ completion_error='Operation not permitted')
54
+ .start = test_job_start,
55
+ .complete = test_job_complete,
56
+};
96
+
57
+
97
+ # Should not have been replaced
58
+static void test_blockjob_common(enum drain_type drain_type)
98
+ self.vm.assert_block_path('quorum0', '/children.1', 'img1')
59
+{
60
+ BlockBackend *blk_src, *blk_target;
61
+ BlockDriverState *src, *target;
62
+ BlockJob *job;
63
+ int ret;
99
+
64
+
100
+ # Check the full error message now
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
101
+ self.vm.shutdown()
66
+ &error_abort);
102
+ log = self.vm.get_log()
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
103
+ log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
68
+ blk_insert_bs(blk_src, src, &error_abort);
104
+ log = re.sub(r'^Formatting.*\n', '', log)
105
+ log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
106
+ log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log)
107
+
69
+
108
+ self.assertEqual(log,
70
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
109
+ "Can no longer replace 'img1' by 'repair0', because " +
71
+ &error_abort);
110
+ "it can no longer be guaranteed that doing so would " +
72
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
111
+ "not lead to an abrupt change of visible data")
73
+ blk_insert_bs(blk_target, target, &error_abort);
112
+
74
+
75
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
76
+ 0, NULL, NULL, &error_abort);
77
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
78
+ block_job_start(job);
113
+
79
+
114
# Test mirroring with a source that does not have any parents (not even a
80
+ g_assert_cmpint(job->pause_count, ==, 0);
115
# BlockBackend)
81
+ g_assert_false(job->paused);
116
class TestOrphanedSource(iotests.QMPTestCase):
82
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
117
diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out
83
+
118
index XXXXXXX..XXXXXXX 100644
84
+ do_drain_begin(drain_type, src);
119
--- a/tests/qemu-iotests/041.out
85
+
120
+++ b/tests/qemu-iotests/041.out
86
+ if (drain_type == BDRV_DRAIN_ALL) {
121
@@ -XXX,XX +XXX,XX @@
87
+ /* bdrv_drain_all() drains both src and target, and involves an
122
-...........................................................................................
88
+ * additional block_job_pause_all() */
123
+.............................................................................................
89
+ g_assert_cmpint(job->pause_count, ==, 3);
124
----------------------------------------------------------------------
90
+ } else {
125
-Ran 91 tests
91
+ g_assert_cmpint(job->pause_count, ==, 1);
126
+Ran 93 tests
92
+ }
127
93
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
128
OK
94
+ /* g_assert_true(job->paused); */
95
+ g_assert_false(job->busy); /* The job is paused */
96
+
97
+ do_drain_end(drain_type, src);
98
+
99
+ g_assert_cmpint(job->pause_count, ==, 0);
100
+ g_assert_false(job->paused);
101
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
102
+
103
+ do_drain_begin(drain_type, target);
104
+
105
+ if (drain_type == BDRV_DRAIN_ALL) {
106
+ /* bdrv_drain_all() drains both src and target, and involves an
107
+ * additional block_job_pause_all() */
108
+ g_assert_cmpint(job->pause_count, ==, 3);
109
+ } else {
110
+ g_assert_cmpint(job->pause_count, ==, 1);
111
+ }
112
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
113
+ /* g_assert_true(job->paused); */
114
+ g_assert_false(job->busy); /* The job is paused */
115
+
116
+ do_drain_end(drain_type, target);
117
+
118
+ g_assert_cmpint(job->pause_count, ==, 0);
119
+ g_assert_false(job->paused);
120
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
121
+
122
+ ret = block_job_complete_sync(job, &error_abort);
123
+ g_assert_cmpint(ret, ==, 0);
124
+
125
+ blk_unref(blk_src);
126
+ blk_unref(blk_target);
127
+ bdrv_unref(src);
128
+ bdrv_unref(target);
129
+}
130
+
131
+static void test_blockjob_drain_all(void)
132
+{
133
+ test_blockjob_common(BDRV_DRAIN_ALL);
134
+}
135
+
136
+static void test_blockjob_drain(void)
137
+{
138
+ test_blockjob_common(BDRV_DRAIN);
139
+}
140
+
141
int main(int argc, char **argv)
142
{
143
bdrv_init();
144
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
145
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
146
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
147
148
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
149
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
150
+
151
return g_test_run();
152
}
129
--
153
--
130
2.20.1
154
2.13.6
131
155
132
156
diff view generated by jsdifflib
1
Now that the error handling in the common block job is fixed, we can
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
expose the on-error option in QMP instead of hard-coding it as 'report'
2
so we don't need an additional block_job_pause_all() call.
3
in qmp_block_commit().
4
5
This fulfills the promise that the old comment in that function made,
6
even if a bit later than expected: "This will be part of the QMP
7
command, if/when the BlockdevOnError change for blkmirror makes it in".
8
3
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20200214200812.28180-7-kwolf@redhat.com>
11
Reviewed-by: Ján Tomko <jtomko@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
5
---
14
qapi/block-core.json | 4 ++++
6
block/io.c | 4 ----
15
blockdev.c | 8 ++++----
7
tests/test-bdrv-drain.c | 10 ++++------
16
2 files changed, 8 insertions(+), 4 deletions(-)
8
2 files changed, 4 insertions(+), 10 deletions(-)
17
9
18
diff --git a/qapi/block-core.json b/qapi/block-core.json
10
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
20
--- a/qapi/block-core.json
12
--- a/block/io.c
21
+++ b/qapi/block-core.json
13
+++ b/block/io.c
22
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
23
#
15
* context. */
24
# @speed: the maximum speed, in bytes per second
16
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
25
#
17
26
+# @on-error: the action to take on an error. 'ignore' means that the request
18
- block_job_pause_all();
27
+# should be retried. (default: report; Since: 5.0)
19
-
28
+#
20
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
29
# @filter-node-name: the node name that should be assigned to the
21
AioContext *aio_context = bdrv_get_aio_context(bs);
30
# filter driver that the commit job inserts into the graph
22
31
# above @top. If this option is not given, a node name is
23
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
32
@@ -XXX,XX +XXX,XX @@
24
aio_enable_external(aio_context);
33
'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str',
25
aio_context_release(aio_context);
34
'*base': 'str', '*top-node': 'str', '*top': 'str',
26
}
35
'*backing-file': 'str', '*speed': 'int',
27
-
36
+ '*on-error': 'BlockdevOnError',
28
- block_job_resume_all();
37
'*filter-node-name': 'str',
29
}
38
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
30
39
31
void bdrv_drain_all(void)
40
diff --git a/blockdev.c b/blockdev.c
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
41
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
42
--- a/blockdev.c
34
--- a/tests/test-bdrv-drain.c
43
+++ b/blockdev.c
35
+++ b/tests/test-bdrv-drain.c
44
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
45
bool has_top, const char *top,
37
do_drain_begin(drain_type, src);
46
bool has_backing_file, const char *backing_file,
38
47
bool has_speed, int64_t speed,
39
if (drain_type == BDRV_DRAIN_ALL) {
48
+ bool has_on_error, BlockdevOnError on_error,
40
- /* bdrv_drain_all() drains both src and target, and involves an
49
bool has_filter_node_name, const char *filter_node_name,
41
- * additional block_job_pause_all() */
50
bool has_auto_finalize, bool auto_finalize,
42
- g_assert_cmpint(job->pause_count, ==, 3);
51
bool has_auto_dismiss, bool auto_dismiss,
43
+ /* bdrv_drain_all() drains both src and target */
52
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
44
+ g_assert_cmpint(job->pause_count, ==, 2);
53
BlockDriverState *base_bs, *top_bs;
45
} else {
54
AioContext *aio_context;
46
g_assert_cmpint(job->pause_count, ==, 1);
55
Error *local_err = NULL;
56
- /* This will be part of the QMP command, if/when the
57
- * BlockdevOnError change for blkmirror makes it in
58
- */
59
- BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT;
60
int job_flags = JOB_DEFAULT;
61
62
if (!has_speed) {
63
speed = 0;
64
}
47
}
65
+ if (!has_on_error) {
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
66
+ on_error = BLOCKDEV_ON_ERROR_REPORT;
49
do_drain_begin(drain_type, target);
67
+ }
50
68
if (!has_filter_node_name) {
51
if (drain_type == BDRV_DRAIN_ALL) {
69
filter_node_name = NULL;
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);
70
}
59
}
71
--
60
--
72
2.20.1
61
2.13.6
73
62
74
63
diff view generated by jsdifflib
1
For external data file, cluster allocations return an offset in the data
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
file and are not refcounted. In this case, there is nothing to do for
2
aio_disable_external() to the outermost drain section, but the block
3
qcow2_alloc_cluster_abort(). Freeing the same offset in the qcow2 file
3
driver callbacks are always called. bdrv_do_drained_end() must match
4
is wrong and causes crashes in the better case or image corruption in
4
this behaviour, otherwise nodes stay drained even if begin/end calls
5
the worse case.
5
were balanced.
6
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20200211094900.17315-3-kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
8
---
11
block/qcow2-cluster.c | 7 +++++--
9
block/io.c | 12 +++++++-----
12
1 file changed, 5 insertions(+), 2 deletions(-)
10
1 file changed, 7 insertions(+), 5 deletions(-)
13
11
14
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.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/qcow2-cluster.c
14
--- a/block/io.c
17
+++ b/block/qcow2-cluster.c
15
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@ err:
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
19
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
17
18
void bdrv_drained_end(BlockDriverState *bs)
20
{
19
{
21
BDRVQcow2State *s = bs->opaque;
20
+ int old_quiesce_counter;
22
- qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits,
21
+
23
- QCOW2_DISCARD_NEVER);
22
if (qemu_in_coroutine()) {
24
+ if (!has_data_file(bs)) {
23
bdrv_co_yield_to_drain(bs, false);
25
+ qcow2_free_clusters(bs, m->alloc_offset,
24
return;
26
+ m->nb_clusters << s->cluster_bits,
25
}
27
+ QCOW2_DISCARD_NEVER);
26
assert(bs->quiesce_counter > 0);
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
28
- return;
29
- }
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
31
32
/* Re-enable things in child-to-parent order */
33
bdrv_drain_invoke(bs, false, false);
34
- bdrv_parent_drained_end(bs);
35
- aio_enable_external(bdrv_get_aio_context(bs));
36
+ if (old_quiesce_counter == 1) {
37
+ bdrv_parent_drained_end(bs);
38
+ aio_enable_external(bdrv_get_aio_context(bs));
28
+ }
39
+ }
29
}
40
}
30
41
31
/*
42
/*
32
--
43
--
33
2.20.1
44
2.13.6
34
45
35
46
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
2
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Message-Id: <20200218103454.296704-9-mreitz@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
2
---
7
block/quorum.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
8
1 file changed, 54 insertions(+)
4
1 file changed, 57 insertions(+)
9
5
10
diff --git a/block/quorum.c b/block/quorum.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
11
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
12
--- a/block/quorum.c
8
--- a/tests/test-bdrv-drain.c
13
+++ b/block/quorum.c
9
+++ b/tests/test-bdrv-drain.c
14
@@ -XXX,XX +XXX,XX @@ static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
15
return false;
11
enum drain_type {
12
BDRV_DRAIN_ALL,
13
BDRV_DRAIN,
14
+ DRAIN_TYPE_MAX,
15
};
16
17
static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
18
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
19
test_quiesce_common(BDRV_DRAIN, false);
16
}
20
}
17
21
18
+static bool quorum_recurse_can_replace(BlockDriverState *bs,
22
+static void test_nested(void)
19
+ BlockDriverState *to_replace)
20
+{
23
+{
21
+ BDRVQuorumState *s = bs->opaque;
24
+ BlockBackend *blk;
22
+ int i;
25
+ BlockDriverState *bs, *backing;
26
+ BDRVTestState *s, *backing_s;
27
+ enum drain_type outer, inner;
23
+
28
+
24
+ for (i = 0; i < s->num_children; i++) {
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
25
+ /*
30
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
26
+ * We have no idea whether our children show the same data as
31
+ &error_abort);
27
+ * this node (@bs). It is actually highly likely that
32
+ s = bs->opaque;
28
+ * @to_replace does not, because replacing a broken child is
33
+ blk_insert_bs(blk, bs, &error_abort);
29
+ * one of the main use cases here.
34
+
30
+ *
35
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
31
+ * We do know that the new BDS will match @bs, so replacing
36
+ backing_s = backing->opaque;
32
+ * any of our children by it will be safe. It cannot change
37
+ bdrv_set_backing_hd(bs, backing, &error_abort);
33
+ * the data this quorum node presents to its parents.
38
+
34
+ *
39
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
35
+ * However, replacing @to_replace by @bs in any of our
40
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
36
+ * children's chains may change visible data somewhere in
41
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
37
+ * there. We therefore cannot recurse down those chains with
42
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
38
+ * bdrv_recurse_can_replace().
43
+ (inner != BDRV_DRAIN_ALL);
39
+ * (More formally, bdrv_recurse_can_replace() requires that
44
+ int backing_quiesce = 0;
40
+ * @to_replace will be replaced by something matching the @bs
45
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
41
+ * passed to it. We cannot guarantee that.)
46
+ (inner != BDRV_DRAIN);
42
+ *
47
+
43
+ * Thus, we can only check whether any of our immediate
48
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
44
+ * children matches @to_replace.
49
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
45
+ *
50
+ g_assert_cmpint(s->drain_count, ==, 0);
46
+ * (In the future, we might add a function to recurse down a
51
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
47
+ * chain that checks that nothing there cares about a change
52
+
48
+ * in data from the respective child in question. For
53
+ do_drain_begin(outer, bs);
49
+ * example, most filters do not care when their child's data
54
+ do_drain_begin(inner, bs);
50
+ * suddenly changes, as long as their parents do not care.)
55
+
51
+ */
56
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
52
+ if (s->children[i]->bs == to_replace) {
57
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
53
+ /*
58
+ g_assert_cmpint(s->drain_count, ==, 2);
54
+ * We now have to ensure that there is no other parent
59
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
55
+ * that cares about replacing this child by a node with
60
+
56
+ * potentially different data.
61
+ do_drain_end(inner, bs);
57
+ * We do so by checking whether there are any other parents
62
+ do_drain_end(outer, bs);
58
+ * at all, which is stricter than necessary, but also very
63
+
59
+ * simple. (We may decide to implement something more
64
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
60
+ * complex and permissive when there is an actual need for
65
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
61
+ * it.)
66
+ g_assert_cmpint(s->drain_count, ==, 0);
62
+ */
67
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
63
+ return QLIST_FIRST(&to_replace->parents) == s->children[i] &&
64
+ QLIST_NEXT(s->children[i], next_parent) == NULL;
65
+ }
68
+ }
66
+ }
69
+ }
67
+
70
+
68
+ return false;
71
+ bdrv_unref(backing);
72
+ bdrv_unref(bs);
73
+ blk_unref(blk);
69
+}
74
+}
70
+
75
+
71
static int quorum_valid_threshold(int threshold, int num_children, Error **errp)
76
72
{
77
typedef struct TestBlockJob {
73
78
BlockJob common;
74
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
75
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
76
.is_filter = true,
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
77
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
82
78
+ .bdrv_recurse_can_replace = quorum_recurse_can_replace,
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
79
84
+
80
.strong_runtime_opts = quorum_strong_runtime_opts,
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
81
};
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
87
82
--
88
--
83
2.20.1
89
2.13.6
84
90
85
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
It is unused now. (And it was ugly because it needed to explore all BDS
3
4
chains from the top.)
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
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
8
Message-Id: <20200218103454.296704-4-mreitz@redhat.com>
8
In order for this to work correctly, the parent callback must be called
9
for every bdrv_drain_begin/end() call, not only for the outermost one:
10
11
If we have a node N with two parents A and B, recursive draining of A
12
should cause the quiesce_counter of B to increase because its child N is
13
drained independently of B. If now B is recursively drained, too, A must
14
increase its quiesce_counter because N is drained independently of A
15
only now, even if N is going from quiesce_counter 1 to 2.
16
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
18
---
11
include/block/block.h | 1 -
19
include/block/block.h | 4 ++--
12
block.c | 26 --------------------------
20
block.c | 13 +++++++++----
13
2 files changed, 27 deletions(-)
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
22
3 files changed, 45 insertions(+), 19 deletions(-)
14
23
15
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
16
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/block.h
26
--- a/include/block/block.h
18
+++ b/include/block/block.h
27
+++ b/include/block/block.h
19
@@ -XXX,XX +XXX,XX @@ int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
20
/* external snapshots */
29
* Begin a quiesced section of all users of @bs. This is part of
21
bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
30
* bdrv_drained_begin.
22
BlockDriverState *candidate);
31
*/
23
-bool bdrv_is_first_non_filter(BlockDriverState *candidate);
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
24
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
25
/* check if a named node can be replaced when doing drive-mirror */
34
26
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
35
/**
36
* bdrv_parent_drained_end:
37
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs);
38
* End a quiesced section of all users of @bs. This is part of
39
* bdrv_drained_end.
40
*/
41
-void bdrv_parent_drained_end(BlockDriverState *bs);
42
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
43
44
/**
45
* bdrv_drained_begin:
27
diff --git a/block.c b/block.c
46
diff --git a/block.c b/block.c
28
index XXXXXXX..XXXXXXX 100644
47
index XXXXXXX..XXXXXXX 100644
29
--- a/block.c
48
--- a/block.c
30
+++ b/block.c
49
+++ b/block.c
31
@@ -XXX,XX +XXX,XX @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
32
return false;
51
BlockDriverState *new_bs)
33
}
52
{
34
53
BlockDriverState *old_bs = child->bs;
35
-/* This function checks if the candidate is the first non filter bs down it's
54
+ int i;
36
- * bs chain. Since we don't have pointers to parents it explore all bs chains
55
37
- * from the top. Some filters can choose not to pass down the recursion.
56
if (old_bs && new_bs) {
38
- */
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
39
-bool bdrv_is_first_non_filter(BlockDriverState *candidate)
58
}
40
-{
59
if (old_bs) {
41
- BlockDriverState *bs;
60
if (old_bs->quiesce_counter && child->role->drained_end) {
42
- BdrvNextIterator it;
61
- child->role->drained_end(child);
43
-
62
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
44
- /* walk down the bs forest recursively */
63
+ child->role->drained_end(child);
45
- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
64
+ }
46
- bool perm;
65
}
47
-
66
if (child->role->detach) {
48
- /* try to recurse in this top level bs */
67
child->role->detach(child);
49
- perm = bdrv_recurse_is_first_non_filter(bs, candidate);
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
50
-
69
if (new_bs) {
51
- /* candidate is the first non filter */
70
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
52
- if (perm) {
71
if (new_bs->quiesce_counter && child->role->drained_begin) {
53
- bdrv_next_cleanup(&it);
72
- child->role->drained_begin(child);
54
- return true;
73
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
55
- }
74
+ child->role->drained_begin(child);
56
- }
75
+ }
57
-
76
}
58
- return false;
77
59
-}
78
if (child->role->attach) {
60
-
79
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
61
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
80
AioContext *ctx = bdrv_get_aio_context(bs);
62
const char *node_name, Error **errp)
81
63
{
82
aio_disable_external(ctx);
83
- bdrv_parent_drained_begin(bs);
84
+ bdrv_parent_drained_begin(bs, NULL);
85
bdrv_drain(bs); /* ensure there are no in-flight requests */
86
87
while (aio_poll(ctx, false)) {
88
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
89
*/
90
aio_context_acquire(new_context);
91
bdrv_attach_aio_context(bs, new_context);
92
- bdrv_parent_drained_end(bs);
93
+ bdrv_parent_drained_end(bs, NULL);
94
aio_enable_external(ctx);
95
aio_context_release(new_context);
96
}
97
diff --git a/block/io.c b/block/io.c
98
index XXXXXXX..XXXXXXX 100644
99
--- a/block/io.c
100
+++ b/block/io.c
101
@@ -XXX,XX +XXX,XX @@
102
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
103
int64_t offset, int bytes, BdrvRequestFlags flags);
104
105
-void bdrv_parent_drained_begin(BlockDriverState *bs)
106
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
107
{
108
BdrvChild *c, *next;
109
110
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
111
+ if (c == ignore) {
112
+ continue;
113
+ }
114
if (c->role->drained_begin) {
115
c->role->drained_begin(c);
116
}
117
}
118
}
119
120
-void bdrv_parent_drained_end(BlockDriverState *bs)
121
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
122
{
123
BdrvChild *c, *next;
124
125
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
126
+ if (c == ignore) {
127
+ continue;
128
+ }
129
if (c->role->drained_end) {
130
c->role->drained_end(c);
131
}
132
@@ -XXX,XX +XXX,XX @@ typedef struct {
133
BlockDriverState *bs;
134
bool done;
135
bool begin;
136
+ BdrvChild *parent;
137
} BdrvCoDrainData;
138
139
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
140
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
141
return waited;
142
}
143
144
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
145
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
146
+
147
static void bdrv_co_drain_bh_cb(void *opaque)
148
{
149
BdrvCoDrainData *data = opaque;
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
151
152
bdrv_dec_in_flight(bs);
153
if (data->begin) {
154
- bdrv_drained_begin(bs);
155
+ bdrv_do_drained_begin(bs, data->parent);
156
} else {
157
- bdrv_drained_end(bs);
158
+ bdrv_do_drained_end(bs, data->parent);
159
}
160
161
data->done = true;
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
163
}
164
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
166
- bool begin)
167
+ bool begin, BdrvChild *parent)
168
{
169
BdrvCoDrainData data;
170
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
172
.bs = bs,
173
.done = false,
174
.begin = begin,
175
+ .parent = parent,
176
};
177
bdrv_inc_in_flight(bs);
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
180
assert(data.done);
181
}
182
183
-void bdrv_drained_begin(BlockDriverState *bs)
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
185
{
186
if (qemu_in_coroutine()) {
187
- bdrv_co_yield_to_drain(bs, true);
188
+ bdrv_co_yield_to_drain(bs, true, parent);
189
return;
190
}
191
192
/* Stop things in parent-to-child order */
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
194
aio_disable_external(bdrv_get_aio_context(bs));
195
- bdrv_parent_drained_begin(bs);
196
}
197
198
+ bdrv_parent_drained_begin(bs, parent);
199
bdrv_drain_invoke(bs, true, false);
200
bdrv_drain_recurse(bs);
201
}
202
203
-void bdrv_drained_end(BlockDriverState *bs)
204
+void bdrv_drained_begin(BlockDriverState *bs)
205
+{
206
+ bdrv_do_drained_begin(bs, NULL);
207
+}
208
+
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
210
{
211
int old_quiesce_counter;
212
213
if (qemu_in_coroutine()) {
214
- bdrv_co_yield_to_drain(bs, false);
215
+ bdrv_co_yield_to_drain(bs, false, parent);
216
return;
217
}
218
assert(bs->quiesce_counter > 0);
219
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
220
221
/* Re-enable things in child-to-parent order */
222
bdrv_drain_invoke(bs, false, false);
223
+ bdrv_parent_drained_end(bs, parent);
224
if (old_quiesce_counter == 1) {
225
- bdrv_parent_drained_end(bs);
226
aio_enable_external(bdrv_get_aio_context(bs));
227
}
228
}
229
230
+void bdrv_drained_end(BlockDriverState *bs)
231
+{
232
+ bdrv_do_drained_end(bs, NULL);
233
+}
234
+
235
/*
236
* Wait for pending requests to complete on a single BlockDriverState subtree,
237
* and suspend block driver's internal I/O until next request arrives.
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
239
/* Stop things in parent-to-child order */
240
aio_context_acquire(aio_context);
241
aio_disable_external(aio_context);
242
- bdrv_parent_drained_begin(bs);
243
+ bdrv_parent_drained_begin(bs, NULL);
244
bdrv_drain_invoke(bs, true, true);
245
aio_context_release(aio_context);
246
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
248
/* Re-enable things in child-to-parent order */
249
aio_context_acquire(aio_context);
250
bdrv_drain_invoke(bs, false, true);
251
- bdrv_parent_drained_end(bs);
252
+ bdrv_parent_drained_end(bs, NULL);
253
aio_enable_external(aio_context);
254
aio_context_release(aio_context);
255
}
64
--
256
--
65
2.20.1
257
2.13.6
66
258
67
259
diff view generated by jsdifflib
1
The block_job_error_action() error call in the commit job gives the
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
on_err and is_read arguments in the wrong order. Fix this.
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
until bdrv_drained_end().
3
4
4
(Of course, hard-coded is_read = false is wrong, too, but that's a
5
Add a version that keeps the whole subtree drained. As of this commit,
5
separate problem for a separate patch.)
6
graph changes cannot be allowed during a subtree drained section, but
7
this will be fixed soon.
6
8
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Message-Id: <20200214200812.28180-4-kwolf@redhat.com>
9
Reviewed-by: Ján Tomko <jtomko@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
10
---
12
block/commit.c | 2 +-
11
include/block/block.h | 13 +++++++++++++
13
1 file changed, 1 insertion(+), 1 deletion(-)
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
13
2 files changed, 56 insertions(+), 11 deletions(-)
14
14
15
diff --git a/block/commit.c b/block/commit.c
15
diff --git a/include/block/block.h b/include/block/block.h
16
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/commit.c
17
--- a/include/block/block.h
18
+++ b/block/commit.c
18
+++ b/include/block/block.h
19
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
20
}
20
void bdrv_drained_begin(BlockDriverState *bs);
21
if (ret < 0) {
21
22
BlockErrorAction action =
22
/**
23
- block_job_error_action(&s->common, false, s->on_error, -ret);
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
24
+ block_job_error_action(&s->common, s->on_error, false, -ret);
24
+ * exclusive access to all child nodes as well.
25
if (action == BLOCK_ERROR_ACTION_REPORT) {
25
+ *
26
goto out;
26
+ * Graph changes are not allowed during a subtree drain section.
27
} else {
27
+ */
28
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
29
+
30
+/**
31
* bdrv_drained_end:
32
*
33
* End a quiescent section started by bdrv_drained_begin().
34
*/
35
void bdrv_drained_end(BlockDriverState *bs);
36
37
+/**
38
+ * End a quiescent section started by bdrv_subtree_drained_begin().
39
+ */
40
+void bdrv_subtree_drained_end(BlockDriverState *bs);
41
+
42
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
43
Error **errp);
44
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
45
diff --git a/block/io.c b/block/io.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/block/io.c
48
+++ b/block/io.c
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);
79
}
80
81
data->done = true;
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
83
}
84
85
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
86
- bool begin, BdrvChild *parent)
87
+ bool begin, bool recursive,
88
+ BdrvChild *parent)
89
{
90
BdrvCoDrainData data;
91
92
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
93
.bs = bs,
94
.done = false,
95
.begin = begin,
96
+ .recursive = recursive,
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;
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
+ }
126
}
127
128
void bdrv_drained_begin(BlockDriverState *bs)
129
{
130
- bdrv_do_drained_begin(bs, NULL);
131
+ bdrv_do_drained_begin(bs, false, NULL);
132
+}
133
+
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
135
+{
136
+ bdrv_do_drained_begin(bs, true, NULL);
137
}
138
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
141
+ BdrvChild *parent)
142
{
143
+ BdrvChild *child, *next;
144
int old_quiesce_counter;
145
146
if (qemu_in_coroutine()) {
147
- bdrv_co_yield_to_drain(bs, false, parent);
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
149
return;
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
+ }
162
}
163
164
void bdrv_drained_end(BlockDriverState *bs)
165
{
166
- bdrv_do_drained_end(bs, NULL);
167
+ bdrv_do_drained_end(bs, false, NULL);
168
+}
169
+
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
171
+{
172
+ bdrv_do_drained_end(bs, true, NULL);
173
}
174
175
/*
28
--
176
--
29
2.20.1
177
2.13.6
30
178
31
179
diff view generated by jsdifflib
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
1
Add a subtree drain version to the existing test cases.
2
2
3
Fix warning reported by Clang static code analyzer:
4
5
CC block/qcow2-bitmap.o
6
block/qcow2-bitmap.c:650:5: warning: Value stored to 'ret' is never read
7
ret = -EINVAL;
8
^ ~~~~~~~
9
10
Fixes: 88ddffae8
11
Reported-by: Clang Static Analyzer
12
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
13
Message-Id: <20200215161557.4077-2-philmd@redhat.com>
14
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
15
Reviewed-by: Ján Tomko <jtomko@redhat.com>
16
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
4
---
19
block/qcow2-bitmap.c | 1 -
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
20
1 file changed, 1 deletion(-)
6
1 file changed, 26 insertions(+), 1 deletion(-)
21
7
22
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
23
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
24
--- a/block/qcow2-bitmap.c
10
--- a/tests/test-bdrv-drain.c
25
+++ b/block/qcow2-bitmap.c
11
+++ b/tests/test-bdrv-drain.c
26
@@ -XXX,XX +XXX,XX @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
27
return bm_list;
13
enum drain_type {
28
14
BDRV_DRAIN_ALL,
29
broken_dir:
15
BDRV_DRAIN,
30
- ret = -EINVAL;
16
+ BDRV_SUBTREE_DRAIN,
31
error_setg(errp, "Broken bitmap directory");
17
DRAIN_TYPE_MAX,
32
18
};
33
fail:
19
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
21
switch (drain_type) {
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
25
default: g_assert_not_reached();
26
}
27
}
28
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
29
switch (drain_type) {
30
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
31
case BDRV_DRAIN: bdrv_drained_end(bs); break;
32
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
33
default: g_assert_not_reached();
34
}
35
}
36
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
37
test_drv_cb_common(BDRV_DRAIN, false);
38
}
39
40
+static void test_drv_cb_drain_subtree(void)
41
+{
42
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
43
+}
44
+
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
46
{
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
+}
56
+
57
static void test_nested(void)
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
+}
78
+
79
int main(int argc, char **argv)
80
{
81
bdrv_init();
82
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
83
84
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
85
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
86
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
87
+ test_drv_cb_drain_subtree);
88
89
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
90
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
91
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
92
+ test_quiesce_drain_subtree);
93
94
g_test_add_func("/bdrv-drain/nested", test_nested);
95
96
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
97
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
98
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
99
+ test_blockjob_drain_subtree);
100
101
return g_test_run();
102
}
34
--
103
--
35
2.20.1
104
2.13.6
36
105
37
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
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Message-Id: <20200218103454.296704-15-mreitz@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
6
---
7
tests/qemu-iotests/iotests.py | 59 +++++++++++++++++++++++++++++++++++
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
8
1 file changed, 59 insertions(+)
8
1 file changed, 59 insertions(+)
9
9
10
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
11
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/qemu-iotests/iotests.py
12
--- a/tests/test-bdrv-drain.c
13
+++ b/tests/qemu-iotests/iotests.py
13
+++ b/tests/test-bdrv-drain.c
14
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
15
15
*aio_ret = ret;
16
return fields.items() <= ret.items()
16
}
17
17
18
+ def assert_block_path(self, root, path, expected_node, graph=None):
18
+typedef struct CallInCoroutineData {
19
+ """
19
+ void (*entry)(void);
20
+ Check whether the node under the given path in the block graph
20
+ bool done;
21
+ is @expected_node.
21
+} CallInCoroutineData;
22
+
22
+
23
+ @root is the node name of the node where the @path is rooted.
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
24
+{
25
+ CallInCoroutineData *data = opaque;
24
+
26
+
25
+ @path is a string that consists of child names separated by
27
+ data->entry();
26
+ slashes. It must begin with a slash.
28
+ data->done = true;
29
+}
27
+
30
+
28
+ Examples for @root + @path:
31
+static void call_in_coroutine(void (*entry)(void))
29
+ - root="qcow2-node", path="/backing/file"
32
+{
30
+ - root="quorum-node", path="/children.2/file"
33
+ Coroutine *co;
34
+ CallInCoroutineData data = {
35
+ .entry = entry,
36
+ .done = false,
37
+ };
31
+
38
+
32
+ Hypothetically, @path could be empty, in which case it would
39
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
33
+ point to @root. However, in practice this case is not useful
40
+ qemu_coroutine_enter(co);
34
+ and hence not allowed.
41
+ while (!data.done) {
42
+ aio_poll(qemu_get_aio_context(), true);
43
+ }
44
+}
35
+
45
+
36
+ @expected_node may be None. (All elements of the path but the
46
enum drain_type {
37
+ leaf must still exist.)
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
+}
38
+
57
+
39
+ @graph may be None or the result of an x-debug-query-block-graph
58
+static void test_drv_cb_co_drain_subtree(void)
40
+ call that has already been performed.
59
+{
41
+ """
60
+ call_in_coroutine(test_drv_cb_drain_subtree);
42
+ if graph is None:
61
+}
43
+ graph = self.qmp('x-debug-query-block-graph')['return']
44
+
62
+
45
+ iter_path = iter(path.split('/'))
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
+}
46
+
74
+
47
+ # Must start with a /
75
+static void test_quiesce_co_drain_subtree(void)
48
+ assert next(iter_path) == ''
76
+{
77
+ call_in_coroutine(test_quiesce_drain_subtree);
78
+}
49
+
79
+
50
+ node = next((node for node in graph['nodes'] if node['name'] == root),
80
static void test_nested(void)
51
+ None)
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);
52
+
91
+
53
+ # An empty @path is not allowed, so the root node must be present
54
+ assert node is not None, 'Root node %s not found' % root
55
+
92
+
56
+ for child_name in iter_path:
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
57
+ assert node is not None, 'Cannot follow path %s%s' % (root, path)
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);
58
+
102
+
59
+ try:
103
g_test_add_func("/bdrv-drain/nested", test_nested);
60
+ node_id = next(edge['child'] for edge in graph['edges'] \
104
61
+ if edge['parent'] == node['id'] and
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
62
+ edge['name'] == child_name)
63
+
64
+ node = next(node for node in graph['nodes'] \
65
+ if node['id'] == node_id)
66
+ except StopIteration:
67
+ node = None
68
+
69
+ if node is None:
70
+ assert expected_node is None, \
71
+ 'No node found under %s (but expected %s)' % \
72
+ (path, expected_node)
73
+ else:
74
+ assert node['name'] == expected_node, \
75
+ 'Found node %s under %s (but expected %s)' % \
76
+ (node['name'], path, expected_node)
77
78
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
79
80
--
106
--
81
2.20.1
107
2.13.6
82
108
83
109
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Test that drain sections are correctly propagated through the graph.
2
2
3
Quorum cannot share WRITE or RESIZE on its children. Presumably, it
4
only does so because as a filter, it seemed intuitively correct to point
5
its .bdrv_child_perm to bdrv_filter_default_perm().
6
7
However, it is not really a filter, and bdrv_filter_default_perm() does
8
not work for it, so we have to provide a custom .bdrv_child_perm
9
implementation.
10
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Message-Id: <20200218103454.296704-6-mreitz@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
4
---
16
block/quorum.c | 19 ++++++++++++++++++-
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
17
1 file changed, 18 insertions(+), 1 deletion(-)
6
1 file changed, 74 insertions(+)
18
7
19
diff --git a/block/quorum.c b/block/quorum.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
20
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
21
--- a/block/quorum.c
10
--- a/tests/test-bdrv-drain.c
22
+++ b/block/quorum.c
11
+++ b/tests/test-bdrv-drain.c
23
@@ -XXX,XX +XXX,XX @@ static char *quorum_dirname(BlockDriverState *bs, Error **errp)
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
24
return NULL;
13
blk_unref(blk);
25
}
14
}
26
15
27
+static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c,
16
+static void test_multiparent(void)
28
+ const BdrvChildRole *role,
29
+ BlockReopenQueue *reopen_queue,
30
+ uint64_t perm, uint64_t shared,
31
+ uint64_t *nperm, uint64_t *nshared)
32
+{
17
+{
33
+ *nperm = perm & DEFAULT_PERM_PASSTHROUGH;
18
+ BlockBackend *blk_a, *blk_b;
19
+ BlockDriverState *bs_a, *bs_b, *backing;
20
+ BDRVTestState *a_s, *b_s, *backing_s;
34
+
21
+
35
+ /*
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
36
+ * We cannot share RESIZE or WRITE, as this would make the
23
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
37
+ * children differ from each other.
24
+ &error_abort);
38
+ */
25
+ a_s = bs_a->opaque;
39
+ *nshared = (shared & (BLK_PERM_CONSISTENT_READ |
26
+ blk_insert_bs(blk_a, bs_a, &error_abort);
40
+ BLK_PERM_WRITE_UNCHANGED))
27
+
41
+ | DEFAULT_PERM_UNCHANGED;
28
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
29
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
30
+ &error_abort);
31
+ b_s = bs_b->opaque;
32
+ blk_insert_bs(blk_b, bs_b, &error_abort);
33
+
34
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
35
+ backing_s = backing->opaque;
36
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
37
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
38
+
39
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
40
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
41
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(a_s->drain_count, ==, 0);
43
+ g_assert_cmpint(b_s->drain_count, ==, 0);
44
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
45
+
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
47
+
48
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
49
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
50
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
51
+ g_assert_cmpint(a_s->drain_count, ==, 1);
52
+ g_assert_cmpint(b_s->drain_count, ==, 1);
53
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
54
+
55
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
56
+
57
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
58
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
59
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(a_s->drain_count, ==, 2);
61
+ g_assert_cmpint(b_s->drain_count, ==, 2);
62
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
63
+
64
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
65
+
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
69
+ g_assert_cmpint(a_s->drain_count, ==, 1);
70
+ g_assert_cmpint(b_s->drain_count, ==, 1);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
74
+
75
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
76
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
77
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
78
+ g_assert_cmpint(a_s->drain_count, ==, 0);
79
+ g_assert_cmpint(b_s->drain_count, ==, 0);
80
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
81
+
82
+ bdrv_unref(backing);
83
+ bdrv_unref(bs_a);
84
+ bdrv_unref(bs_b);
85
+ blk_unref(blk_a);
86
+ blk_unref(blk_b);
42
+}
87
+}
43
+
88
+
44
static const char *const quorum_strong_runtime_opts[] = {
89
45
QUORUM_OPT_VOTE_THRESHOLD,
90
typedef struct TestBlockJob {
46
QUORUM_OPT_BLKVERIFY,
91
BlockJob common;
47
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
48
.bdrv_add_child = quorum_add_child,
93
test_quiesce_co_drain_subtree);
49
.bdrv_del_child = quorum_del_child,
94
50
95
g_test_add_func("/bdrv-drain/nested", test_nested);
51
- .bdrv_child_perm = bdrv_filter_default_perms,
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
52
+ .bdrv_child_perm = quorum_child_perm,
97
53
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
54
.is_filter = true,
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
55
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
56
--
100
--
57
2.20.1
101
2.13.6
58
102
59
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
After a couple of follow-up patches, this function will replace
3
can be correctly applied when children are added or removed during the
4
bdrv_recurse_is_first_non_filter() in check_to_replace_node().
4
drained section.
5
5
6
bdrv_recurse_is_first_non_filter() is both not sufficiently specific for
6
With this change, it is safe to modify the graph even inside a
7
check_to_replace_node() (it allows cases that should not be allowed,
7
bdrv_subtree_drained_begin/end() section.
8
like replacing child nodes of quorum with dissenting data that have more
8
9
parents than just quorum), and it is too restrictive (it is perfectly
10
fine to replace filters).
11
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
Message-Id: <20200218103454.296704-7-mreitz@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
10
---
17
include/block/block_int.h | 10 ++++++++++
11
include/block/block.h | 2 --
18
block.c | 38 ++++++++++++++++++++++++++++++++++++++
12
include/block/block_int.h | 5 +++++
19
2 files changed, 48 insertions(+)
13
block.c | 32 +++++++++++++++++++++++++++++---
20
14
block/io.c | 28 ++++++++++++++++++++++++----
15
4 files changed, 58 insertions(+), 9 deletions(-)
16
17
diff --git a/include/block/block.h b/include/block/block.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/block.h
20
+++ b/include/block/block.h
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
22
/**
23
* Like bdrv_drained_begin, but recursively begins a quiesced section for
24
* exclusive access to all child nodes as well.
25
- *
26
- * Graph changes are not allowed during a subtree drain section.
27
*/
28
void bdrv_subtree_drained_begin(BlockDriverState *bs);
29
21
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
22
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block_int.h
32
--- a/include/block/block_int.h
24
+++ b/include/block/block_int.h
33
+++ b/include/block/block_int.h
25
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
26
*/
35
27
bool (*bdrv_recurse_is_first_non_filter)(BlockDriverState *bs,
36
/* Accessed with atomic ops. */
28
BlockDriverState *candidate);
37
int quiesce_counter;
29
+ /*
38
+ int recursive_quiesce_counter;
30
+ * Return true if @to_replace can be replaced by a BDS with the
39
+
31
+ * same data as @bs without it affecting @bs's behavior (that is,
40
unsigned int write_gen; /* Current data generation */
32
+ * without it being visible to @bs's parents).
41
33
+ */
42
/* Protected by reqs_lock. */
34
+ bool (*bdrv_recurse_can_replace)(BlockDriverState *bs,
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
35
+ BlockDriverState *to_replace);
44
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
36
45
BdrvRequestFlags flags);
37
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
46
38
int (*bdrv_probe_device)(const char *filename);
47
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
39
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
48
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
40
uint64_t perm, uint64_t shared,
49
+
41
uint64_t *nperm, uint64_t *nshared);
50
int get_tmp_filename(char *filename, int size);
42
51
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
43
+bool bdrv_recurse_can_replace(BlockDriverState *bs,
52
const char *filename);
44
+ BlockDriverState *to_replace);
45
+
46
/*
47
* Default implementation for drivers to pass bdrv_co_block_status() to
48
* their file.
49
diff --git a/block.c b/block.c
53
diff --git a/block.c b/block.c
50
index XXXXXXX..XXXXXXX 100644
54
index XXXXXXX..XXXXXXX 100644
51
--- a/block.c
55
--- a/block.c
52
+++ b/block.c
56
+++ b/block.c
53
@@ -XXX,XX +XXX,XX @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
54
return false;
58
bdrv_drained_end(bs);
55
}
59
}
56
60
57
+/*
61
+static void bdrv_child_cb_attach(BdrvChild *child)
58
+ * This function checks whether the given @to_replace is allowed to be
62
+{
59
+ * replaced by a node that always shows the same data as @bs. This is
63
+ BlockDriverState *bs = child->opaque;
60
+ * used for example to verify whether the mirror job can replace
64
+ bdrv_apply_subtree_drain(child, bs);
61
+ * @to_replace by the target mirrored from @bs.
65
+}
62
+ * To be replaceable, @bs and @to_replace may either be guaranteed to
66
+
63
+ * always show the same data (because they are only connected through
67
+static void bdrv_child_cb_detach(BdrvChild *child)
64
+ * filters), or some driver may allow replacing one of its children
68
+{
65
+ * because it can guarantee that this child's data is not visible at
69
+ BlockDriverState *bs = child->opaque;
66
+ * all (for example, for dissenting quorum children that have no other
70
+ bdrv_unapply_subtree_drain(child, bs);
67
+ * parents).
71
+}
68
+ */
72
+
69
+bool bdrv_recurse_can_replace(BlockDriverState *bs,
73
static int bdrv_child_cb_inactivate(BdrvChild *child)
70
+ BlockDriverState *to_replace)
74
{
71
+{
75
BlockDriverState *bs = child->opaque;
72
+ if (!bs || !bs->drv) {
76
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_file = {
73
+ return false;
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
/*
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
114
}
115
if (old_bs) {
116
+ /* Detach first so that the recursive drain sections coming from @child
117
+ * are already gone and we only end the drain sections that came from
118
+ * elsewhere. */
119
+ if (child->role->detach) {
120
+ child->role->detach(child);
121
+ }
122
if (old_bs->quiesce_counter && child->role->drained_end) {
123
for (i = 0; i < old_bs->quiesce_counter; i++) {
124
child->role->drained_end(child);
125
}
126
}
127
- if (child->role->detach) {
128
- child->role->detach(child);
129
- }
130
QLIST_REMOVE(child, next_parent);
131
}
132
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
134
}
135
}
136
137
+ /* Attach only after starting new drained sections, so that recursive
138
+ * drain sections coming from @child don't get an extra .drained_begin
139
+ * callback. */
140
if (child->role->attach) {
141
child->role->attach(child);
142
}
143
diff --git a/block/io.c b/block/io.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/io.c
146
+++ b/block/io.c
147
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
148
assert(data.done);
149
}
150
151
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
152
- BdrvChild *parent)
153
+void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
154
+ BdrvChild *parent)
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,
178
}
179
180
if (recursive) {
181
+ bs->recursive_quiesce_counter--;
182
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
183
bdrv_do_drained_end(child->bs, true, child);
184
}
185
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs)
186
bdrv_do_drained_end(bs, true, NULL);
187
}
188
189
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
190
+{
191
+ int i;
192
+
193
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
194
+ bdrv_do_drained_begin(child->bs, true, child);
74
+ }
195
+ }
75
+
196
+}
76
+ if (bs == to_replace) {
197
+
77
+ return true;
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
199
+{
200
+ int i;
201
+
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
203
+ bdrv_do_drained_end(child->bs, true, child);
78
+ }
204
+ }
79
+
205
+}
80
+ /* See what the driver can do */
206
+
81
+ if (bs->drv->bdrv_recurse_can_replace) {
207
/*
82
+ return bs->drv->bdrv_recurse_can_replace(bs, to_replace);
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
83
+ }
209
* and suspend block driver's internal I/O until next request arrives.
84
+
85
+ /* For filters without an own implementation, we can recurse on our own */
86
+ if (bs->drv->is_filter) {
87
+ BdrvChild *child = bs->file ?: bs->backing;
88
+ return bdrv_recurse_can_replace(child->bs, to_replace);
89
+ }
90
+
91
+ /* Safe default */
92
+ return false;
93
+}
94
+
95
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
96
const char *node_name, Error **errp)
97
{
98
--
210
--
99
2.20.1
211
2.13.6
100
212
101
213
diff view generated by jsdifflib
1
This adds a test for 'qemu-img convert' with copy offloading where the
2
target image has an external data file. If the test hosts supports it,
3
it tests both the case where copy offloading is supported and the case
4
where it isn't (otherwise we just test unsupported twice).
5
6
More specifically, the case with unsupported copy offloading tests
7
qcow2_alloc_cluster_abort() with external data files.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20200211094900.17315-4-kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
2
---
13
tests/qemu-iotests/244 | 14 ++++++++++++++
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
14
tests/qemu-iotests/244.out | 6 ++++++
4
1 file changed, 80 insertions(+)
15
2 files changed, 20 insertions(+)
16
5
17
diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
18
index XXXXXXX..XXXXXXX 100755
7
index XXXXXXX..XXXXXXX 100644
19
--- a/tests/qemu-iotests/244
8
--- a/tests/test-bdrv-drain.c
20
+++ b/tests/qemu-iotests/244
9
+++ b/tests/test-bdrv-drain.c
21
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c 'read -P 0x11 0 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
22
$QEMU_IMG map --output=human "$TEST_IMG" | _filter_testdir
11
blk_unref(blk_b);
23
$QEMU_IMG map --output=json "$TEST_IMG"
12
}
24
13
25
+echo
14
+static void test_graph_change(void)
26
+echo "=== Copy offloading ==="
15
+{
27
+echo
16
+ BlockBackend *blk_a, *blk_b;
17
+ BlockDriverState *bs_a, *bs_b, *backing;
18
+ BDRVTestState *a_s, *b_s, *backing_s;
28
+
19
+
29
+# Make use of copy offloading if the test host can provide it
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
30
+_make_test_img -o "data_file=$TEST_IMG.data" 64M
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
31
+$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG"
22
+ &error_abort);
32
+$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG"
23
+ a_s = bs_a->opaque;
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
33
+
25
+
34
+# blkdebug doesn't support copy offloading, so this tests the error path
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
35
+$QEMU_IMG amend -f $IMGFMT -o "data_file=blkdebug::$TEST_IMG.data" "$TEST_IMG"
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
36
+$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG"
28
+ &error_abort);
37
+$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG"
29
+ b_s = bs_b->opaque;
30
+ blk_insert_bs(blk_b, bs_b, &error_abort);
38
+
31
+
39
# success, all done
32
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
40
echo "*** done"
33
+ backing_s = backing->opaque;
41
rm -f $seq.full
34
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
42
diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out
43
index XXXXXXX..XXXXXXX 100644
44
--- a/tests/qemu-iotests/244.out
45
+++ b/tests/qemu-iotests/244.out
46
@@ -XXX,XX +XXX,XX @@ Offset Length Mapped to File
47
0 0x100000 0 TEST_DIR/t.qcow2.data
48
[{ "start": 0, "length": 1048576, "depth": 0, "zero": false, "data": true, "offset": 0},
49
{ "start": 1048576, "length": 66060288, "depth": 0, "zero": true, "data": false}]
50
+
35
+
51
+=== Copy offloading ===
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);
52
+
42
+
53
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data
43
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
54
+Images are identical.
44
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
55
+Images are identical.
45
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
56
*** done
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 {
95
BlockJob common;
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
97
98
g_test_add_func("/bdrv-drain/nested", test_nested);
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
101
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
57
--
104
--
58
2.20.1
105
2.13.6
59
106
60
107
diff view generated by jsdifflib
1
commit_populate() is a very short function and only called in a single
1
Since commit bde70715, base is the only node that is reopened in
2
place. Its return value doesn't tell us whether an error happened while
2
commit_start(). This means that the code, which still involves an
3
reading or writing, which would be necessary for sending the right data
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
4
in the BLOCK_JOB_ERROR QMP event.
5
4
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20200214200812.28180-5-kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
8
Reviewed-by: Ján Tomko <jtomko@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
7
---
11
block/commit.c | 28 ++++++----------------------
8
block/commit.c | 8 +-------
12
1 file changed, 6 insertions(+), 22 deletions(-)
9
1 file changed, 1 insertion(+), 7 deletions(-)
13
10
14
diff --git a/block/commit.c b/block/commit.c
11
diff --git a/block/commit.c b/block/commit.c
15
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
16
--- a/block/commit.c
13
--- a/block/commit.c
17
+++ b/block/commit.c
14
+++ b/block/commit.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct CommitBlockJob {
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
19
char *backing_file_str;
16
const char *filter_node_name, Error **errp)
20
} CommitBlockJob;
17
{
21
18
CommitBlockJob *s;
22
-static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
19
- BlockReopenQueue *reopen_queue = NULL;
23
- int64_t offset, uint64_t bytes,
20
int orig_base_flags;
24
- void *buf)
21
BlockDriverState *iter;
25
-{
22
BlockDriverState *commit_top_bs = NULL;
26
- int ret = 0;
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
27
-
24
/* convert base to r/w, if necessary */
28
- assert(bytes < SIZE_MAX);
25
orig_base_flags = bdrv_get_flags(base);
29
-
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
30
- ret = blk_co_pread(bs, offset, bytes, buf, 0);
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
31
- if (ret < 0) {
28
- orig_base_flags | BDRV_O_RDWR);
32
- return ret;
33
- }
29
- }
34
-
30
-
35
- ret = blk_co_pwrite(base, offset, bytes, buf, 0);
31
- if (reopen_queue) {
36
- if (ret < 0) {
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
37
- return ret;
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
38
- }
34
if (local_err != NULL) {
39
-
35
error_propagate(errp, local_err);
40
- return 0;
36
goto fail;
41
-}
42
-
43
static int commit_prepare(Job *job)
44
{
45
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
46
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
47
copy = (ret == 1);
48
trace_commit_one_iteration(s, offset, n, ret);
49
if (copy) {
50
- ret = commit_populate(s->top, s->base, offset, n, buf);
51
+ assert(n < SIZE_MAX);
52
+
53
+ ret = blk_co_pread(s->top, offset, n, buf, 0);
54
+ if (ret >= 0) {
55
+ ret = blk_co_pwrite(s->base, offset, n, buf, 0);
56
+ }
57
}
58
if (ret < 0) {
59
BlockErrorAction action =
60
--
37
--
61
2.20.1
38
2.13.6
62
39
63
40
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
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).
2
4
3
Let check_to_replace_node() use the more specialized
5
So instead of draining the device only in bdrv_reopen_multiple(),
4
bdrv_recurse_can_replace() instead of
6
require that callers already drained all affected nodes, and assert this
5
bdrv_recurse_is_first_non_filter(), which is too restrictive (or, in the
7
in bdrv_reopen_queue().
6
case of quorum, sometimes not restrictive enough).
7
8
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Message-Id: <20200218103454.296704-10-mreitz@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
12
---
11
---
13
block.c | 18 ++++++++++++++++--
12
block.c | 23 ++++++++++++++++-------
14
1 file changed, 16 insertions(+), 2 deletions(-)
13
block/replication.c | 6 ++++++
14
qemu-io-cmds.c | 3 +++
15
3 files changed, 25 insertions(+), 7 deletions(-)
15
16
16
diff --git a/block.c b/block.c
17
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
19
--- a/block.c
19
+++ b/block.c
20
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ bool bdrv_recurse_can_replace(BlockDriverState *bs,
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
21
return false;
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;
22
}
70
}
23
71
24
+/*
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
25
+ * Check whether the given @node_name can be replaced by a node that
26
+ * has the same data as @parent_bs. If so, return @node_name's BDS;
27
+ * NULL otherwise.
28
+ *
29
+ * @node_name must be a (recursive) *child of @parent_bs (or this
30
+ * function will return NULL).
31
+ *
32
+ * The result (whether the node can be replaced or not) is only valid
33
+ * for as long as no graph or permission changes occur.
34
+ */
35
BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
36
const char *node_name, Error **errp)
37
{
73
{
38
@@ -XXX,XX +XXX,XX @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
74
int ret = -1;
39
* Another benefit is that this tests exclude backing files which are
75
Error *local_err = NULL;
40
* blocked by the backing blockers.
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
41
*/
77
+ BlockReopenQueue *queue;
42
- if (!bdrv_recurse_is_first_non_filter(parent_bs, to_replace_bs)) {
78
43
- error_setg(errp, "Only top most non filter can be replaced");
79
+ bdrv_subtree_drained_begin(bs);
44
+ if (!bdrv_recurse_can_replace(parent_bs, to_replace_bs)) {
80
+
45
+ error_setg(errp, "Cannot replace '%s' by a node mirrored from '%s', "
81
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
46
+ "because it cannot be guaranteed that doing so would not "
82
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
47
+ "lead to an abrupt change of visible data",
83
if (local_err != NULL) {
48
+ node_name, parent_bs->node_name);
84
error_propagate(errp, local_err);
49
to_replace_bs = NULL;
50
goto out;
51
}
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 {
52
--
132
--
53
2.20.1
133
2.13.6
54
134
55
135
diff view generated by jsdifflib