1
The following changes since commit eda1df0345f5a1e337e30367124dcb0e802bdfde:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
Merge remote-tracking branch 'remotes/armbru/tags/pull-pflash-2019-03-11' into staging (2019-03-12 11:12:36 +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 c31dfeb02a1d155bdb961edeb61a137a589c174b:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
qemu-iotests: Test the x-blockdev-reopen QMP command (2019-03-12 17:58:37 +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
- file-posix: Make auto-read-only dynamic
17
- Add x-blockdev-reopen QMP command
18
- Finalize block-latency-histogram QMP command
19
- gluster: Build fixes for newer lib version
20
15
21
----------------------------------------------------------------
16
----------------------------------------------------------------
22
Alberto Garcia (13):
17
Doug Gale (1):
23
block: Allow freezing BdrvChild links
18
nvme: Add tracing
24
block: Freeze the backing chain for the duration of the commit job
25
block: Freeze the backing chain for the duration of the mirror job
26
block: Freeze the backing chain for the duration of the stream job
27
block: Add 'keep_old_opts' parameter to bdrv_reopen_queue()
28
block: Handle child references in bdrv_reopen_queue()
29
block: Allow omitting the 'backing' option in certain cases
30
block: Allow changing the backing file on reopen
31
block: Add a 'mutable_opts' field to BlockDriver
32
block: Add bdrv_reset_options_allowed()
33
block: Remove the AioContext parameter from bdrv_reopen_multiple()
34
block: Add an 'x-blockdev-reopen' QMP command
35
qemu-iotests: Test the x-blockdev-reopen QMP command
36
19
37
Keith Busch (1):
20
Edgar Kaziakhmedov (1):
38
nvme: fix write zeroes offset and count
21
qcow2: get rid of qcow2_backing_read1 routine
39
22
40
Kevin Wolf (10):
23
Fam Zheng (2):
41
tests/virtio-blk-test: Disable auto-read-only
24
block: Open backing image in force share mode for size probe
42
qemu-iotests: commit to backing file with auto-read-only
25
block: Remove unused bdrv_requests_pending
43
block: Avoid useless local_err
44
block: Make permission changes in reopen less wrong
45
file-posix: Fix bdrv_open_flags() for snapshot=on
46
file-posix: Factor out raw_reconfigure_getfd()
47
file-posix: Store BDRVRawState.reopen_state during reopen
48
file-posix: Lock new fd in raw_reopen_prepare()
49
file-posix: Prepare permission code for fd switching
50
file-posix: Make auto-read-only dynamic
51
26
52
Niels de Vos (1):
27
John Snow (1):
53
gluster: the glfs_io_cbk callback function pointer adds pre/post stat args
28
iotests: fix 197 for vpc
54
29
55
Prasanna Kumar Kalever (1):
30
Kevin Wolf (27):
56
gluster: Handle changed glfs_ftruncate signature
31
block: Formats don't need CONSISTENT_READ with NO_IO
32
block: Make bdrv_drain_invoke() recursive
33
block: Call .drain_begin only once in bdrv_drain_all_begin()
34
test-bdrv-drain: Test BlockDriver callbacks for drain
35
block: bdrv_drain_recurse(): Remove unused begin parameter
36
block: Don't wait for requests in bdrv_drain*_end()
37
block: Unify order in drain functions
38
block: Don't acquire AioContext in hmp_qemu_io()
39
block: Document that x-blockdev-change breaks quorum children list
40
block: Assert drain_all is only called from main AioContext
41
block: Make bdrv_drain() driver callbacks non-recursive
42
test-bdrv-drain: Test callback for bdrv_drain
43
test-bdrv-drain: Test bs->quiesce_counter
44
blockjob: Pause job on draining any job BDS
45
test-bdrv-drain: Test drain vs. block jobs
46
block: Don't block_job_pause_all() in bdrv_drain_all()
47
block: Nested drain_end must still call callbacks
48
test-bdrv-drain: Test nested drain sections
49
block: Don't notify parents in drain call chain
50
block: Add bdrv_subtree_drained_begin/end()
51
test-bdrv-drain: Tests for bdrv_subtree_drain
52
test-bdrv-drain: Test behaviour in coroutine context
53
test-bdrv-drain: Recursive draining with multiple parents
54
block: Allow graph changes in subtree drained section
55
test-bdrv-drain: Test graph changes in drained section
56
commit: Simplify reopen of base
57
block: Keep nodes drained between reopen_queue/multiple
57
58
58
Vladimir Sementsov-Ogievskiy (2):
59
Thomas Huth (3):
59
qapi: move to QOM path for x-block-latency-histogram-set
60
block: Remove the obsolete -drive boot=on|off parameter
60
qapi: drop x- from x-block-latency-histogram-set
61
block: Remove the deprecated -hdachs option
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
61
63
62
qapi/block-core.json | 66 ++-
64
qapi/block-core.json | 4 +
63
configure | 42 ++
65
block/qcow2.h | 3 -
64
include/block/block.h | 13 +-
66
include/block/block.h | 15 +-
65
include/block/block_int.h | 14 +
67
include/block/block_int.h | 6 +-
66
block.c | 440 +++++++++++++++++--
68
block.c | 75 ++++-
67
block/commit.c | 16 +
69
block/commit.c | 8 +-
68
block/file-posix.c | 254 ++++++++---
70
block/io.c | 164 +++++++---
69
block/gluster.c | 10 +-
71
block/qcow2.c | 51 +--
70
block/mirror.c | 8 +
72
block/replication.c | 6 +
71
block/qapi.c | 12 +-
73
blockdev.c | 11 -
72
block/qcow2.c | 25 ++
74
blockjob.c | 22 +-
73
block/raw-format.c | 3 +
75
hmp.c | 6 -
74
block/replication.c | 7 +-
76
hw/block/nvme.c | 349 +++++++++++++++++----
75
block/stream.c | 21 +
77
qemu-io-cmds.c | 3 +
76
blockdev.c | 61 ++-
78
tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++
77
hw/block/nvme.c | 6 +-
79
vl.c | 86 +-----
78
qemu-io-cmds.c | 4 +-
80
hw/block/trace-events | 93 ++++++
79
tests/virtio-blk-test.c | 2 +-
81
qemu-doc.texi | 29 +-
80
tests/qemu-iotests/051 | 7 +
82
qemu-options.hx | 19 +-
81
tests/qemu-iotests/051.out | 9 +
83
tests/Makefile.include | 2 +
82
tests/qemu-iotests/051.pc.out | 9 +
84
tests/qemu-iotests/197 | 4 +
83
tests/qemu-iotests/232 | 31 ++
85
tests/qemu-iotests/common.filter | 3 +-
84
tests/qemu-iotests/232.out | 32 +-
86
22 files changed, 1294 insertions(+), 316 deletions(-)
85
tests/qemu-iotests/245 | 991 ++++++++++++++++++++++++++++++++++++++++++
87
create mode 100644 tests/test-bdrv-drain.c
86
tests/qemu-iotests/245.out | 5 +
87
tests/qemu-iotests/group | 1 +
88
26 files changed, 1929 insertions(+), 160 deletions(-)
89
create mode 100644 tests/qemu-iotests/245
90
create mode 100644 tests/qemu-iotests/245.out
91
88
diff view generated by jsdifflib
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.
4
5
As this permission is geared towards whether the guest-visible data is
6
consistent, and has no impact on whether the metadata is sane, and
7
'qemu-img info' does not read guest-visible data (except for the raw
8
format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there
9
is not going to be any guest I/O performed, regardless of image format.
10
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Alberto Garcia <berto@igalia.com>
3
Reviewed-by: Eric Blake <eblake@redhat.com>
4
---
12
---
5
block.c | 4 +---
13
block.c | 6 +++++-
6
1 file changed, 1 insertion(+), 3 deletions(-)
14
1 file changed, 5 insertions(+), 1 deletion(-)
7
15
8
diff --git a/block.c b/block.c
16
diff --git a/block.c b/block.c
9
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
10
--- a/block.c
18
--- a/block.c
11
+++ b/block.c
19
+++ b/block.c
12
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
13
{
21
assert(role == &child_backing || role == &child_file);
14
int ret = -1;
22
15
BlockReopenQueueEntry *bs_entry, *next;
23
if (!backing) {
16
- Error *local_err = NULL;
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
17
25
+
18
assert(bs_queue != NULL);
26
/* Apart from the modifications below, the same permissions are
19
27
* forwarded and left alone as for filters */
20
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
21
assert(bs_entry->state.bs->quiesce_counter > 0);
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
22
- if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
30
23
- error_propagate(errp, local_err);
31
/* bs->file always needs to be consistent because of the metadata. We
24
+ if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
32
* can never allow other users to resize or write to it. */
25
goto cleanup;
33
- perm |= BLK_PERM_CONSISTENT_READ;
26
}
34
+ if (!(flags & BDRV_O_NO_IO)) {
27
bs_entry->prepared = true;
35
+ perm |= BLK_PERM_CONSISTENT_READ;
36
+ }
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
38
} else {
39
/* We want consistent read from backing files if the parent needs it.
28
--
40
--
29
2.20.1
41
2.13.6
30
42
31
43
diff view generated by jsdifflib
New patch
1
From: John Snow <jsnow@redhat.com>
1
2
3
VPC has some difficulty creating geometries of particular size.
4
However, we can indeed force it to use a literal one, so let's
5
do that for the sake of test 197, which is testing some specific
6
offsets.
7
8
Signed-off-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
13
---
14
tests/qemu-iotests/197 | 4 ++++
15
tests/qemu-iotests/common.filter | 3 ++-
16
2 files changed, 6 insertions(+), 1 deletion(-)
17
18
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/197
21
+++ b/tests/qemu-iotests/197
22
@@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ==='
23
echo
24
25
# Prep the images
26
+# VPC rounds image sizes to a specific geometry, force a specific size.
27
+if [ "$IMGFMT" = "vpc" ]; then
28
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
29
+fi
30
_make_test_img 4G
31
$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io
32
IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \
33
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
34
index XXXXXXX..XXXXXXX 100644
35
--- a/tests/qemu-iotests/common.filter
36
+++ b/tests/qemu-iotests/common.filter
37
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
38
-e "s# log_size=[0-9]\\+##g" \
39
-e "s# refcount_bits=[0-9]\\+##g" \
40
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
41
- -e "s# iter-time=[0-9]\\+##g"
42
+ -e "s# iter-time=[0-9]\\+##g" \
43
+ -e "s# force_size=\\(on\\|off\\)##g"
44
}
45
46
_filter_img_info()
47
--
48
2.13.6
49
50
diff view generated by jsdifflib
New patch
1
This change separates bdrv_drain_invoke(), which calls the BlockDriver
2
drain callbacks, from bdrv_drain_recurse(). Instead, the function
3
performs its own recursion now.
1
4
5
One reason for this is that bdrv_drain_recurse() can be called multiple
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
7
once. The separation is necessary to fix this bug.
8
9
The other reason is that we intend to go to a model where we call all
10
driver callbacks first, and only then start polling. This is not fully
11
achieved yet with this patch, as bdrv_drain_invoke() contains a
12
BDRV_POLL_WHILE() loop for the block driver callbacks, which can still
13
call callbacks for any unrelated event. It's a step in this direction
14
anyway.
15
16
Cc: qemu-stable@nongnu.org
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
---
20
block/io.c | 14 +++++++++++---
21
1 file changed, 11 insertions(+), 3 deletions(-)
22
23
diff --git a/block/io.c b/block/io.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/block/io.c
26
+++ b/block/io.c
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
28
bdrv_wakeup(bs);
29
}
30
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
33
{
34
+ BdrvChild *child, *tmp;
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
36
37
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
39
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
40
bdrv_coroutine_enter(bs, data.co);
41
BDRV_POLL_WHILE(bs, !data.done);
42
+
43
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
44
+ bdrv_drain_invoke(child->bs, begin);
45
+ }
46
}
47
48
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
49
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
50
BdrvChild *child, *tmp;
51
bool waited;
52
53
- /* Ensure any pending metadata writes are submitted to bs->file. */
54
- bdrv_drain_invoke(bs, begin);
55
-
56
/* Wait for drained requests to finish */
57
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
58
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
60
bdrv_parent_drained_begin(bs);
61
}
62
63
+ bdrv_drain_invoke(bs, true);
64
bdrv_drain_recurse(bs, true);
65
}
66
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
68
}
69
70
bdrv_parent_drained_end(bs);
71
+ bdrv_drain_invoke(bs, false);
72
bdrv_drain_recurse(bs, false);
73
aio_enable_external(bdrv_get_aio_context(bs));
74
}
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
76
aio_context_acquire(aio_context);
77
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
78
if (aio_context == bdrv_get_aio_context(bs)) {
79
+ /* FIXME Calling this multiple times is wrong */
80
+ bdrv_drain_invoke(bs, true);
81
waited |= bdrv_drain_recurse(bs, true);
82
}
83
}
84
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
85
aio_context_acquire(aio_context);
86
aio_enable_external(aio_context);
87
bdrv_parent_drained_end(bs);
88
+ bdrv_drain_invoke(bs, false);
89
bdrv_drain_recurse(bs, false);
90
aio_context_release(aio_context);
91
}
92
--
93
2.13.6
94
95
diff view generated by jsdifflib
New patch
1
bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver
2
callback inside its polling loop. This means that how many times it got
3
called for each node depended on long it had to poll the event loop.
1
4
5
This is obviously not right and results in nodes that stay drained even
6
after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per
7
node.
8
9
Fix bdrv_drain_all_begin() to call the callback only once, too.
10
11
Cc: qemu-stable@nongnu.org
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
---
15
block/io.c | 3 +--
16
1 file changed, 1 insertion(+), 2 deletions(-)
17
18
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/io.c
21
+++ b/block/io.c
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
23
aio_context_acquire(aio_context);
24
bdrv_parent_drained_begin(bs);
25
aio_disable_external(aio_context);
26
+ bdrv_drain_invoke(bs, true);
27
aio_context_release(aio_context);
28
29
if (!g_slist_find(aio_ctxs, aio_context)) {
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
31
aio_context_acquire(aio_context);
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
33
if (aio_context == bdrv_get_aio_context(bs)) {
34
- /* FIXME Calling this multiple times is wrong */
35
- bdrv_drain_invoke(bs, true);
36
waited |= bdrv_drain_recurse(bs, true);
37
}
38
}
39
--
40
2.13.6
41
42
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
This adds a test case that the BlockDriver callbacks for drain are
2
called in bdrv_drained_all_begin/end(), and that both of them are called
3
exactly once.
2
4
3
This patch adds several tests for the x-blockdev-reopen QMP command.
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
10
tests/Makefile.include | 2 +
11
2 files changed, 139 insertions(+)
12
create mode 100644 tests/test-bdrv-drain.c
4
13
5
Signed-off-by: Alberto Garcia <berto@igalia.com>
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
tests/qemu-iotests/245 | 991 +++++++++++++++++++++++++++++++++++++
9
tests/qemu-iotests/245.out | 5 +
10
tests/qemu-iotests/group | 1 +
11
3 files changed, 997 insertions(+)
12
create mode 100644 tests/qemu-iotests/245
13
create mode 100644 tests/qemu-iotests/245.out
14
15
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
16
new file mode 100644
15
new file mode 100644
17
index XXXXXXX..XXXXXXX
16
index XXXXXXX..XXXXXXX
18
--- /dev/null
17
--- /dev/null
19
+++ b/tests/qemu-iotests/245
18
+++ b/tests/test-bdrv-drain.c
20
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
21
+#!/usr/bin/env python
20
+/*
22
+#
21
+ * Block node draining tests
23
+# Test cases for the QMP 'x-blockdev-reopen' command
22
+ *
24
+#
23
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
25
+# Copyright (C) 2018-2019 Igalia, S.L.
24
+ *
26
+# Author: Alberto Garcia <berto@igalia.com>
25
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
27
+#
26
+ * of this software and associated documentation files (the "Software"), to deal
28
+# This program is free software; you can redistribute it and/or modify
27
+ * in the Software without restriction, including without limitation the rights
29
+# it under the terms of the GNU General Public License as published by
28
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30
+# the Free Software Foundation; either version 2 of the License, or
29
+ * copies of the Software, and to permit persons to whom the Software is
31
+# (at your option) any later version.
30
+ * furnished to do so, subject to the following conditions:
32
+#
31
+ *
33
+# This program is distributed in the hope that it will be useful,
32
+ * The above copyright notice and this permission notice shall be included in
34
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
33
+ * all copies or substantial portions of the Software.
35
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34
+ *
36
+# GNU General Public License for more details.
35
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37
+#
36
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38
+# You should have received a copy of the GNU General Public License
37
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
39
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
38
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40
+#
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
+ */
41
+
43
+
42
+import os
44
+#include "qemu/osdep.h"
43
+import re
45
+#include "block/block.h"
44
+import iotests
46
+#include "sysemu/block-backend.h"
45
+import copy
47
+#include "qapi/error.h"
46
+import json
47
+from iotests import qemu_img, qemu_io
48
+
48
+
49
+hd_path = [
49
+typedef struct BDRVTestState {
50
+ os.path.join(iotests.test_dir, 'hd0.img'),
50
+ int drain_count;
51
+ os.path.join(iotests.test_dir, 'hd1.img'),
51
+} BDRVTestState;
52
+ os.path.join(iotests.test_dir, 'hd2.img')
53
+]
54
+
52
+
55
+def hd_opts(idx):
53
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
56
+ return {'driver': iotests.imgfmt,
54
+{
57
+ 'node-name': 'hd%d' % idx,
55
+ BDRVTestState *s = bs->opaque;
58
+ 'file': {'driver': 'file',
56
+ s->drain_count++;
59
+ 'node-name': 'hd%d-file' % idx,
57
+}
60
+ 'filename': hd_path[idx] } }
61
+
58
+
62
+class TestBlockdevReopen(iotests.QMPTestCase):
59
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
63
+ total_io_cmds = 0
60
+{
61
+ BDRVTestState *s = bs->opaque;
62
+ s->drain_count--;
63
+}
64
+
64
+
65
+ def setUp(self):
65
+static void bdrv_test_close(BlockDriverState *bs)
66
+ qemu_img('create', '-f', iotests.imgfmt, hd_path[0], '3M')
66
+{
67
+ qemu_img('create', '-f', iotests.imgfmt, '-b', hd_path[0], hd_path[1])
67
+ BDRVTestState *s = bs->opaque;
68
+ qemu_img('create', '-f', iotests.imgfmt, hd_path[2], '3M')
68
+ g_assert_cmpint(s->drain_count, >, 0);
69
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa0 0 1M', hd_path[0])
69
+}
70
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa1 1M 1M', hd_path[1])
71
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa2 2M 1M', hd_path[2])
72
+ self.vm = iotests.VM()
73
+ self.vm.launch()
74
+
70
+
75
+ def tearDown(self):
71
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
76
+ self.vm.shutdown()
72
+ uint64_t offset, uint64_t bytes,
77
+ self.check_qemu_io_errors()
73
+ QEMUIOVector *qiov, int flags)
78
+ os.remove(hd_path[0])
74
+{
79
+ os.remove(hd_path[1])
75
+ /* We want this request to stay until the polling loop in drain waits for
80
+ os.remove(hd_path[2])
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);
81
+
80
+
82
+ # The output of qemu-io is not returned by vm.hmp_qemu_io() but
81
+ return 0;
83
+ # it's stored in the log and can only be read when the VM has been
82
+}
84
+ # shut down. This function runs qemu-io and keeps track of the
85
+ # number of times it's been called.
86
+ def run_qemu_io(self, img, cmd):
87
+ result = self.vm.hmp_qemu_io(img, cmd)
88
+ self.assert_qmp(result, 'return', '')
89
+ self.total_io_cmds += 1
90
+
83
+
91
+ # Once the VM is shut down we can parse the log and see if qemu-io
84
+static BlockDriver bdrv_test = {
92
+ # ran without errors.
85
+ .format_name = "test",
93
+ def check_qemu_io_errors(self):
86
+ .instance_size = sizeof(BDRVTestState),
94
+ self.assertFalse(self.vm.is_running())
95
+ found = 0
96
+ log = self.vm.get_log()
97
+ for line in log.split("\n"):
98
+ if line.startswith("Pattern verification failed"):
99
+ raise Exception("%s (command #%d)" % (line, found))
100
+ if re.match("read .*/.* bytes at offset", line):
101
+ found += 1
102
+ self.assertEqual(found, self.total_io_cmds,
103
+ "Expected output of %d qemu-io commands, found %d" %
104
+ (found, self.total_io_cmds))
105
+
87
+
106
+ # Run x-blockdev-reopen with 'opts' but applying 'newopts'
88
+ .bdrv_close = bdrv_test_close,
107
+ # on top of it. The original 'opts' dict is unmodified
89
+ .bdrv_co_preadv = bdrv_test_co_preadv,
108
+ def reopen(self, opts, newopts = {}, errmsg = None):
109
+ opts = copy.deepcopy(opts)
110
+
90
+
111
+ # Apply the changes from 'newopts' on top of 'opts'
91
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
112
+ for key in newopts:
92
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
113
+ value = newopts[key]
93
+};
114
+ # If key has the form "foo.bar" then we need to do
115
+ # opts["foo"]["bar"] = value, not opts["foo.bar"] = value
116
+ subdict = opts
117
+ while key.find('.') != -1:
118
+ [prefix, key] = key.split('.', 1)
119
+ subdict = opts[prefix]
120
+ subdict[key] = value
121
+
94
+
122
+ result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts)
95
+static void aio_ret_cb(void *opaque, int ret)
123
+ if errmsg:
96
+{
124
+ self.assert_qmp(result, 'error/class', 'GenericError')
97
+ int *aio_ret = opaque;
125
+ self.assert_qmp(result, 'error/desc', errmsg)
98
+ *aio_ret = ret;
126
+ else:
99
+}
127
+ self.assert_qmp(result, 'return', {})
128
+
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;
129
+
108
+
130
+ # Run query-named-block-nodes and return the specified entry
109
+ QEMUIOVector qiov;
131
+ def get_node(self, node_name):
110
+ struct iovec iov = {
132
+ result = self.vm.qmp('query-named-block-nodes')
111
+ .iov_base = NULL,
133
+ for node in result['return']:
112
+ .iov_len = 0,
134
+ if node['node-name'] == node_name:
113
+ };
135
+ return node
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
136
+ return None
137
+
115
+
138
+ # Run 'query-named-block-nodes' and compare its output with the
116
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
139
+ # value passed by the user in 'graph'
117
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
140
+ def check_node_graph(self, graph):
118
+ &error_abort);
141
+ result = self.vm.qmp('query-named-block-nodes')
119
+ s = bs->opaque;
142
+ self.assertEqual(json.dumps(graph, sort_keys=True),
120
+ blk_insert_bs(blk, bs, &error_abort);
143
+ json.dumps(result, sort_keys=True))
144
+
121
+
145
+ # This test opens one single disk image (without backing files)
122
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
146
+ # and tries to reopen it with illegal / incorrect parameters.
123
+ g_assert_cmpint(s->drain_count, ==, 0);
147
+ def test_incorrect_parameters_single_file(self):
124
+ bdrv_drain_all_begin();
148
+ # Open 'hd0' only (no backing files)
125
+ g_assert_cmpint(s->drain_count, ==, 1);
149
+ opts = hd_opts(0)
126
+ bdrv_drain_all_end();
150
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
127
+ g_assert_cmpint(s->drain_count, ==, 0);
151
+ self.assert_qmp(result, 'return', {})
152
+ original_graph = self.vm.qmp('query-named-block-nodes')
153
+
128
+
154
+ # We can reopen the image passing the same options
129
+ /* Now do the same while a request is pending */
155
+ self.reopen(opts)
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);
156
+
134
+
157
+ # We can also reopen passing a child reference in 'file'
135
+ g_assert_cmpint(s->drain_count, ==, 0);
158
+ self.reopen(opts, {'file': 'hd0-file'})
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);
159
+
141
+
160
+ # We cannot change any of these
142
+ bdrv_unref(bs);
161
+ self.reopen(opts, {'node-name': 'not-found'}, "Cannot find node named 'not-found'")
143
+ blk_unref(blk);
162
+ self.reopen(opts, {'node-name': ''}, "Cannot find node named ''")
144
+}
163
+ self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string")
164
+ self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'")
165
+ self.reopen(opts, {'driver': ''}, "Invalid parameter ''")
166
+ self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string")
167
+ self.reopen(opts, {'file': 'not-found'}, "Cannot change the option 'file'")
168
+ self.reopen(opts, {'file': ''}, "Cannot change the option 'file'")
169
+ self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef")
170
+ self.reopen(opts, {'file.node-name': 'newname'}, "Cannot change the option 'node-name'")
171
+ self.reopen(opts, {'file.driver': 'host_device'}, "Cannot change the option 'driver'")
172
+ self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'")
173
+ self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'")
174
+ self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
175
+ self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string")
176
+
145
+
177
+ # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it
146
+int main(int argc, char **argv)
178
+ del opts['node-name']
147
+{
179
+ self.reopen(opts, {}, "Node name not specified")
148
+ bdrv_init();
149
+ qemu_init_main_loop(&error_abort);
180
+
150
+
181
+ # Check that nothing has changed
151
+ g_test_init(&argc, &argv, NULL);
182
+ self.check_node_graph(original_graph)
183
+
152
+
184
+ # Remove the node
153
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
185
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
186
+ self.assert_qmp(result, 'return', {})
187
+
154
+
188
+ # This test opens an image with a backing file and tries to reopen
155
+ return g_test_run();
189
+ # it with illegal / incorrect parameters.
156
+}
190
+ def test_incorrect_parameters_backing_file(self):
157
diff --git a/tests/Makefile.include b/tests/Makefile.include
191
+ # Open hd1 omitting the backing options (hd0 will be opened
192
+ # with the default options)
193
+ opts = hd_opts(1)
194
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
195
+ self.assert_qmp(result, 'return', {})
196
+ original_graph = self.vm.qmp('query-named-block-nodes')
197
+
198
+ # We can't reopen the image passing the same options, 'backing' is mandatory
199
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
200
+
201
+ # Everything works if we pass 'backing' using the existing node name
202
+ for node in original_graph['return']:
203
+ if node['drv'] == iotests.imgfmt and node['file'] == hd_path[0]:
204
+ backing_node_name = node['node-name']
205
+ self.reopen(opts, {'backing': backing_node_name})
206
+
207
+ # We can't use a non-existing or empty (non-NULL) node as the backing image
208
+ self.reopen(opts, {'backing': 'not-found'}, "Cannot find device= nor node_name=not-found")
209
+ self.reopen(opts, {'backing': ''}, "Cannot find device= nor node_name=")
210
+
211
+ # We can reopen the image just fine if we specify the backing options
212
+ opts['backing'] = {'driver': iotests.imgfmt,
213
+ 'file': {'driver': 'file',
214
+ 'filename': hd_path[0]}}
215
+ self.reopen(opts)
216
+
217
+ # We cannot change any of these options
218
+ self.reopen(opts, {'backing.node-name': 'newname'}, "Cannot change the option 'node-name'")
219
+ self.reopen(opts, {'backing.driver': 'raw'}, "Cannot change the option 'driver'")
220
+ self.reopen(opts, {'backing.file.node-name': 'newname'}, "Cannot change the option 'node-name'")
221
+ self.reopen(opts, {'backing.file.driver': 'host_device'}, "Cannot change the option 'driver'")
222
+
223
+ # Check that nothing has changed since the beginning
224
+ self.check_node_graph(original_graph)
225
+
226
+ # Remove the node
227
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
228
+ self.assert_qmp(result, 'return', {})
229
+
230
+ # Reopen an image several times changing some of its options
231
+ def test_reopen(self):
232
+ # Open the hd1 image passing all backing options
233
+ opts = hd_opts(1)
234
+ opts['backing'] = hd_opts(0)
235
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
236
+ self.assert_qmp(result, 'return', {})
237
+ original_graph = self.vm.qmp('query-named-block-nodes')
238
+
239
+ # We can reopen the image passing the same options
240
+ self.reopen(opts)
241
+
242
+ # Reopen in read-only mode
243
+ self.assert_qmp(self.get_node('hd1'), 'ro', False)
244
+
245
+ self.reopen(opts, {'read-only': True})
246
+ self.assert_qmp(self.get_node('hd1'), 'ro', True)
247
+ self.reopen(opts)
248
+ self.assert_qmp(self.get_node('hd1'), 'ro', False)
249
+
250
+ # Change the cache options
251
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
252
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', False)
253
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False)
254
+ self.reopen(opts, {'cache': { 'direct': True, 'no-flush': True }})
255
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
256
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', True)
257
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', True)
258
+
259
+ # Reopen again with the original options
260
+ self.reopen(opts)
261
+ self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True)
262
+ self.assert_qmp(self.get_node('hd1'), 'cache/direct', False)
263
+ self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False)
264
+
265
+ # Change 'detect-zeroes' and 'discard'
266
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off')
267
+ self.reopen(opts, {'detect-zeroes': 'on'})
268
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
269
+ self.reopen(opts, {'detect-zeroes': 'unmap'},
270
+ "setting detect-zeroes to unmap is not allowed " +
271
+ "without setting discard operation to unmap")
272
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
273
+ self.reopen(opts, {'detect-zeroes': 'unmap', 'discard': 'unmap'})
274
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'unmap')
275
+ self.reopen(opts)
276
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off')
277
+
278
+ # Changing 'force-share' is currently not supported
279
+ self.reopen(opts, {'force-share': True}, "Cannot change the option 'force-share'")
280
+
281
+ # Change some qcow2-specific options
282
+ # No way to test for success other than checking the return message
283
+ if iotests.imgfmt == 'qcow2':
284
+ self.reopen(opts, {'l2-cache-entry-size': 128 * 1024},
285
+ "L2 cache entry size must be a power of two "+
286
+ "between 512 and the cluster size (65536)")
287
+ self.reopen(opts, {'l2-cache-size': 1024 * 1024,
288
+ 'cache-size': 512 * 1024},
289
+ "l2-cache-size may not exceed cache-size")
290
+ self.reopen(opts, {'l2-cache-size': 4 * 1024 * 1024,
291
+ 'refcount-cache-size': 4 * 1024 * 1024,
292
+ 'l2-cache-entry-size': 32 * 1024})
293
+ self.reopen(opts, {'pass-discard-request': True})
294
+
295
+ # Check that nothing has changed since the beginning
296
+ # (from the parameters that we can check)
297
+ self.check_node_graph(original_graph)
298
+
299
+ # Check that the node names (other than the top-level one) are optional
300
+ del opts['file']['node-name']
301
+ del opts['backing']['node-name']
302
+ del opts['backing']['file']['node-name']
303
+ self.reopen(opts)
304
+ self.check_node_graph(original_graph)
305
+
306
+ # Reopen setting backing = null, this removes the backing image from the chain
307
+ self.reopen(opts, {'backing': None})
308
+ self.assert_qmp_absent(self.get_node('hd1'), 'image/backing-image')
309
+
310
+ # Open the 'hd0' image
311
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **hd_opts(0))
312
+ self.assert_qmp(result, 'return', {})
313
+
314
+ # Reopen the hd1 image setting 'hd0' as its backing image
315
+ self.reopen(opts, {'backing': 'hd0'})
316
+ self.assert_qmp(self.get_node('hd1'), 'image/backing-image/filename', hd_path[0])
317
+
318
+ # Check that nothing has changed since the beginning
319
+ self.reopen(hd_opts(0), {'read-only': True})
320
+ self.check_node_graph(original_graph)
321
+
322
+ # The backing file (hd0) is now a reference, we cannot change backing.* anymore
323
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
324
+
325
+ # We can't remove 'hd0' while it's a backing image of 'hd1'
326
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
327
+ self.assert_qmp(result, 'error/class', 'GenericError')
328
+ self.assert_qmp(result, 'error/desc', "Node 'hd0' is busy: node is used as backing hd of 'hd1'")
329
+
330
+ # But we can remove both nodes if done in the proper order
331
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
332
+ self.assert_qmp(result, 'return', {})
333
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
334
+ self.assert_qmp(result, 'return', {})
335
+
336
+ # Reopen a raw image and see the effect of changing the 'offset' option
337
+ def test_reopen_raw(self):
338
+ opts = {'driver': 'raw', 'node-name': 'hd0',
339
+ 'file': { 'driver': 'file',
340
+ 'filename': hd_path[0],
341
+ 'node-name': 'hd0-file' } }
342
+
343
+ # First we create a 2MB raw file, and fill each half with a
344
+ # different value
345
+ qemu_img('create', '-f', 'raw', hd_path[0], '2M')
346
+ qemu_io('-f', 'raw', '-c', 'write -P 0xa0 0 1M', hd_path[0])
347
+ qemu_io('-f', 'raw', '-c', 'write -P 0xa1 1M 1M', hd_path[0])
348
+
349
+ # Open the raw file with QEMU
350
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
351
+ self.assert_qmp(result, 'return', {})
352
+
353
+ # Read 1MB from offset 0
354
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
355
+
356
+ # Reopen the image with a 1MB offset.
357
+ # Now the results are different
358
+ self.reopen(opts, {'offset': 1024*1024})
359
+ self.run_qemu_io("hd0", "read -P 0xa1 0 1M")
360
+
361
+ # Reopen again with the original options.
362
+ # We get the original results again
363
+ self.reopen(opts)
364
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
365
+
366
+ # Remove the block device
367
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
368
+ self.assert_qmp(result, 'return', {})
369
+
370
+ # Omitting an option should reset it to the default value, but if
371
+ # an option cannot be changed it shouldn't be possible to reset it
372
+ # to its default value either
373
+ def test_reset_default_values(self):
374
+ opts = {'driver': 'qcow2', 'node-name': 'hd0',
375
+ 'file': { 'driver': 'file',
376
+ 'filename': hd_path[0],
377
+ 'x-check-cache-dropped': True, # This one can be changed
378
+ 'locking': 'off', # This one can NOT be changed
379
+ 'node-name': 'hd0-file' } }
380
+
381
+ # Open the file with QEMU
382
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
383
+ self.assert_qmp(result, 'return', {})
384
+
385
+ # file.x-check-cache-dropped can be changed...
386
+ self.reopen(opts, { 'file.x-check-cache-dropped': False })
387
+ # ...and dropped completely (resetting to the default value)
388
+ del opts['file']['x-check-cache-dropped']
389
+ self.reopen(opts)
390
+
391
+ # file.locking cannot be changed nor reset to the default value
392
+ self.reopen(opts, { 'file.locking': 'on' }, "Cannot change the option 'locking'")
393
+ del opts['file']['locking']
394
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
395
+ # But we can reopen it if we maintain its previous value
396
+ self.reopen(opts, { 'file.locking': 'off' })
397
+
398
+ # Remove the block device
399
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
400
+ self.assert_qmp(result, 'return', {})
401
+
402
+ # This test modifies the node graph a few times by changing the
403
+ # 'backing' option on reopen and verifies that the guest data that
404
+ # is read afterwards is consistent with the graph changes.
405
+ def test_io_with_graph_changes(self):
406
+ opts = []
407
+
408
+ # Open hd0, hd1 and hd2 without any backing image
409
+ for i in range(3):
410
+ opts.append(hd_opts(i))
411
+ opts[i]['backing'] = None
412
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i])
413
+ self.assert_qmp(result, 'return', {})
414
+
415
+ # hd0
416
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
417
+ self.run_qemu_io("hd0", "read -P 0 1M 1M")
418
+ self.run_qemu_io("hd0", "read -P 0 2M 1M")
419
+
420
+ # hd1 <- hd0
421
+ self.reopen(opts[0], {'backing': 'hd1'})
422
+
423
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
424
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
425
+ self.run_qemu_io("hd0", "read -P 0 2M 1M")
426
+
427
+ # hd1 <- hd0 , hd1 <- hd2
428
+ self.reopen(opts[2], {'backing': 'hd1'})
429
+
430
+ self.run_qemu_io("hd2", "read -P 0 0 1M")
431
+ self.run_qemu_io("hd2", "read -P 0xa1 1M 1M")
432
+ self.run_qemu_io("hd2", "read -P 0xa2 2M 1M")
433
+
434
+ # hd1 <- hd2 <- hd0
435
+ self.reopen(opts[0], {'backing': 'hd2'})
436
+
437
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
438
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
439
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
440
+
441
+ # hd2 <- hd0
442
+ self.reopen(opts[2], {'backing': None})
443
+
444
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
445
+ self.run_qemu_io("hd0", "read -P 0 1M 1M")
446
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
447
+
448
+ # hd2 <- hd1 <- hd0
449
+ self.reopen(opts[1], {'backing': 'hd2'})
450
+ self.reopen(opts[0], {'backing': 'hd1'})
451
+
452
+ self.run_qemu_io("hd0", "read -P 0xa0 0 1M")
453
+ self.run_qemu_io("hd0", "read -P 0xa1 1M 1M")
454
+ self.run_qemu_io("hd0", "read -P 0xa2 2M 1M")
455
+
456
+ # Illegal operation: hd2 is a child of hd1
457
+ self.reopen(opts[2], {'backing': 'hd1'},
458
+ "Making 'hd1' a backing file of 'hd2' would create a cycle")
459
+
460
+ # hd2 <- hd0, hd2 <- hd1
461
+ self.reopen(opts[0], {'backing': 'hd2'})
462
+
463
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
464
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
465
+ self.run_qemu_io("hd1", "read -P 0xa2 2M 1M")
466
+
467
+ # More illegal operations
468
+ self.reopen(opts[2], {'backing': 'hd1'},
469
+ "Making 'hd1' a backing file of 'hd2' would create a cycle")
470
+ self.reopen(opts[2], {'file': 'hd0-file'}, "Cannot change the option 'file'")
471
+
472
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
473
+ self.assert_qmp(result, 'error/class', 'GenericError')
474
+ self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'")
475
+
476
+ # hd1 doesn't have a backing file now
477
+ self.reopen(opts[1], {'backing': None})
478
+
479
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
480
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
481
+ self.run_qemu_io("hd1", "read -P 0 2M 1M")
482
+
483
+ # We can't remove the 'backing' option if the image has a
484
+ # default backing file
485
+ del opts[1]['backing']
486
+ self.reopen(opts[1], {}, "backing is missing for 'hd1'")
487
+
488
+ self.run_qemu_io("hd1", "read -P 0 0 1M")
489
+ self.run_qemu_io("hd1", "read -P 0xa1 1M 1M")
490
+ self.run_qemu_io("hd1", "read -P 0 2M 1M")
491
+
492
+ # This test verifies that we can't change the children of a block
493
+ # device during a reopen operation in a way that would create
494
+ # cycles in the node graph
495
+ def test_graph_cycles(self):
496
+ opts = []
497
+
498
+ # Open all three images without backing file
499
+ for i in range(3):
500
+ opts.append(hd_opts(i))
501
+ opts[i]['backing'] = None
502
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i])
503
+ self.assert_qmp(result, 'return', {})
504
+
505
+ # hd1 <- hd0, hd1 <- hd2
506
+ self.reopen(opts[0], {'backing': 'hd1'})
507
+ self.reopen(opts[2], {'backing': 'hd1'})
508
+
509
+ # Illegal: hd2 is backed by hd1
510
+ self.reopen(opts[1], {'backing': 'hd2'},
511
+ "Making 'hd2' a backing file of 'hd1' would create a cycle")
512
+
513
+ # hd1 <- hd0 <- hd2
514
+ self.reopen(opts[2], {'backing': 'hd0'})
515
+
516
+ # Illegal: hd2 is backed by hd0, which is backed by hd1
517
+ self.reopen(opts[1], {'backing': 'hd2'},
518
+ "Making 'hd2' a backing file of 'hd1' would create a cycle")
519
+
520
+ # Illegal: hd1 cannot point to itself
521
+ self.reopen(opts[1], {'backing': 'hd1'},
522
+ "Making 'hd1' a backing file of 'hd1' would create a cycle")
523
+
524
+ # Remove all backing files
525
+ self.reopen(opts[0])
526
+ self.reopen(opts[2])
527
+
528
+ ##########################################
529
+ # Add a blkverify node using hd0 and hd1 #
530
+ ##########################################
531
+ bvopts = {'driver': 'blkverify',
532
+ 'node-name': 'bv',
533
+ 'test': 'hd0',
534
+ 'raw': 'hd1'}
535
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **bvopts)
536
+ self.assert_qmp(result, 'return', {})
537
+
538
+ # blkverify doesn't currently allow reopening. TODO: implement this
539
+ self.reopen(bvopts, {}, "Block format 'blkverify' used by node 'bv'" +
540
+ " does not support reopening files")
541
+
542
+ # Illegal: hd0 is a child of the blkverify node
543
+ self.reopen(opts[0], {'backing': 'bv'},
544
+ "Making 'bv' a backing file of 'hd0' would create a cycle")
545
+
546
+ # Delete the blkverify node
547
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv')
548
+ self.assert_qmp(result, 'return', {})
549
+
550
+ # Misc reopen tests with different block drivers
551
+ def test_misc_drivers(self):
552
+ ####################
553
+ ###### quorum ######
554
+ ####################
555
+ for i in range(3):
556
+ opts = hd_opts(i)
557
+ # Open all three images without backing file
558
+ opts['backing'] = None
559
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
560
+ self.assert_qmp(result, 'return', {})
561
+
562
+ opts = {'driver': 'quorum',
563
+ 'node-name': 'quorum0',
564
+ 'children': ['hd0', 'hd1', 'hd2'],
565
+ 'vote-threshold': 2}
566
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
567
+ self.assert_qmp(result, 'return', {})
568
+
569
+ # Quorum doesn't currently allow reopening. TODO: implement this
570
+ self.reopen(opts, {}, "Block format 'quorum' used by node 'quorum0'" +
571
+ " does not support reopening files")
572
+
573
+ # You can't make quorum0 a backing file of hd0:
574
+ # hd0 is already a child of quorum0.
575
+ self.reopen(hd_opts(0), {'backing': 'quorum0'},
576
+ "Making 'quorum0' a backing file of 'hd0' would create a cycle")
577
+
578
+ # Delete quorum0
579
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'quorum0')
580
+ self.assert_qmp(result, 'return', {})
581
+
582
+ # Delete hd0, hd1 and hd2
583
+ for i in range(3):
584
+ result = self.vm.qmp('blockdev-del', conv_keys = True,
585
+ node_name = 'hd%d' % i)
586
+ self.assert_qmp(result, 'return', {})
587
+
588
+ ######################
589
+ ###### blkdebug ######
590
+ ######################
591
+ opts = {'driver': 'blkdebug',
592
+ 'node-name': 'bd',
593
+ 'config': '/dev/null',
594
+ 'image': hd_opts(0)}
595
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
596
+ self.assert_qmp(result, 'return', {})
597
+
598
+ # blkdebug allows reopening if we keep the same options
599
+ self.reopen(opts)
600
+
601
+ # but it currently does not allow changes
602
+ self.reopen(opts, {'image': 'hd1'}, "Cannot change the option 'image'")
603
+ self.reopen(opts, {'align': 33554432}, "Cannot change the option 'align'")
604
+ self.reopen(opts, {'config': '/non/existent'}, "Cannot change the option 'config'")
605
+ del opts['config']
606
+ self.reopen(opts, {}, "Option 'config' cannot be reset to its default value")
607
+
608
+ # Delete the blkdebug node
609
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bd')
610
+ self.assert_qmp(result, 'return', {})
611
+
612
+ ##################
613
+ ###### null ######
614
+ ##################
615
+ opts = {'driver': 'null-aio', 'node-name': 'root', 'size': 1024}
616
+
617
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
618
+ self.assert_qmp(result, 'return', {})
619
+
620
+ # 1 << 30 is the default value, but we cannot change it explicitly
621
+ self.reopen(opts, {'size': (1 << 30)}, "Cannot change the option 'size'")
622
+
623
+ # We cannot change 'size' back to its default value either
624
+ del opts['size']
625
+ self.reopen(opts, {}, "Option 'size' cannot be reset to its default value")
626
+
627
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'root')
628
+ self.assert_qmp(result, 'return', {})
629
+
630
+ ##################
631
+ ###### file ######
632
+ ##################
633
+ opts = hd_opts(0)
634
+ opts['file']['locking'] = 'on'
635
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
636
+ self.assert_qmp(result, 'return', {})
637
+
638
+ # 'locking' cannot be changed
639
+ del opts['file']['locking']
640
+ self.reopen(opts, {'file.locking': 'on'})
641
+ self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'")
642
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
643
+
644
+ # Trying to reopen the 'file' node directly does not make a difference
645
+ opts = opts['file']
646
+ self.reopen(opts, {'locking': 'on'})
647
+ self.reopen(opts, {'locking': 'off'}, "Cannot change the option 'locking'")
648
+ self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value")
649
+
650
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
651
+ self.assert_qmp(result, 'return', {})
652
+
653
+ ######################
654
+ ###### throttle ######
655
+ ######################
656
+ opts = { 'qom-type': 'throttle-group', 'id': 'group0',
657
+ 'props': { 'limits': { 'iops-total': 1000 } } }
658
+ result = self.vm.qmp('object-add', conv_keys = False, **opts)
659
+ self.assert_qmp(result, 'return', {})
660
+
661
+ opts = { 'qom-type': 'throttle-group', 'id': 'group1',
662
+ 'props': { 'limits': { 'iops-total': 2000 } } }
663
+ result = self.vm.qmp('object-add', conv_keys = False, **opts)
664
+ self.assert_qmp(result, 'return', {})
665
+
666
+ # Add a throttle filter with group = group0
667
+ opts = { 'driver': 'throttle', 'node-name': 'throttle0',
668
+ 'throttle-group': 'group0', 'file': hd_opts(0) }
669
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
670
+ self.assert_qmp(result, 'return', {})
671
+
672
+ # We can reopen it if we keep the same options
673
+ self.reopen(opts)
674
+
675
+ # We can also reopen if 'file' is a reference to the child
676
+ self.reopen(opts, {'file': 'hd0'})
677
+
678
+ # This is illegal
679
+ self.reopen(opts, {'throttle-group': 'notfound'}, "Throttle group 'notfound' does not exist")
680
+
681
+ # But it's possible to change the group to group1
682
+ self.reopen(opts, {'throttle-group': 'group1'})
683
+
684
+ # Now group1 is in use, it cannot be deleted
685
+ result = self.vm.qmp('object-del', id = 'group1')
686
+ self.assert_qmp(result, 'error/class', 'GenericError')
687
+ self.assert_qmp(result, 'error/desc', "object 'group1' is in use, can not be deleted")
688
+
689
+ # Default options, this switches the group back to group0
690
+ self.reopen(opts)
691
+
692
+ # So now we cannot delete group0
693
+ result = self.vm.qmp('object-del', id = 'group0')
694
+ self.assert_qmp(result, 'error/class', 'GenericError')
695
+ self.assert_qmp(result, 'error/desc', "object 'group0' is in use, can not be deleted")
696
+
697
+ # But group1 is free this time, and it can be deleted
698
+ result = self.vm.qmp('object-del', id = 'group1')
699
+ self.assert_qmp(result, 'return', {})
700
+
701
+ # Let's delete the filter node
702
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'throttle0')
703
+ self.assert_qmp(result, 'return', {})
704
+
705
+ # And we can finally get rid of group0
706
+ result = self.vm.qmp('object-del', id = 'group0')
707
+ self.assert_qmp(result, 'return', {})
708
+
709
+ # If an image has a backing file then the 'backing' option must be
710
+ # passed on reopen. We don't allow leaving the option out in this
711
+ # case because it's unclear what the correct semantics would be.
712
+ def test_missing_backing_options_1(self):
713
+ # hd2
714
+ opts = hd_opts(2)
715
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
716
+ self.assert_qmp(result, 'return', {})
717
+
718
+ # hd0
719
+ opts = hd_opts(0)
720
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
721
+ self.assert_qmp(result, 'return', {})
722
+
723
+ # hd0 has no backing file: we can omit the 'backing' option
724
+ self.reopen(opts)
725
+
726
+ # hd2 <- hd0
727
+ self.reopen(opts, {'backing': 'hd2'})
728
+
729
+ # hd0 has a backing file: we must set the 'backing' option
730
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
731
+
732
+ # hd2 can't be removed because it's the backing file of hd0
733
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
734
+ self.assert_qmp(result, 'error/class', 'GenericError')
735
+ self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'")
736
+
737
+ # Detach hd2 from hd0.
738
+ self.reopen(opts, {'backing': None})
739
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
740
+
741
+ # Remove both hd0 and hd2
742
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
743
+ self.assert_qmp(result, 'return', {})
744
+
745
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2')
746
+ self.assert_qmp(result, 'return', {})
747
+
748
+ # If an image has default backing file (as part of its metadata)
749
+ # then the 'backing' option must be passed on reopen. We don't
750
+ # allow leaving the option out in this case because it's unclear
751
+ # what the correct semantics would be.
752
+ def test_missing_backing_options_2(self):
753
+ # hd0 <- hd1
754
+ # (hd0 is hd1's default backing file)
755
+ opts = hd_opts(1)
756
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
757
+ self.assert_qmp(result, 'return', {})
758
+
759
+ # hd1 has a backing file: we can't omit the 'backing' option
760
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
761
+
762
+ # Let's detach the backing file
763
+ self.reopen(opts, {'backing': None})
764
+
765
+ # No backing file attached to hd1 now, but we still can't omit the 'backing' option
766
+ self.reopen(opts, {}, "backing is missing for 'hd1'")
767
+
768
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1')
769
+ self.assert_qmp(result, 'return', {})
770
+
771
+ # Test that making 'backing' a reference to an existing child
772
+ # keeps its current options
773
+ def test_backing_reference(self):
774
+ # hd2 <- hd1 <- hd0
775
+ opts = hd_opts(0)
776
+ opts['backing'] = hd_opts(1)
777
+ opts['backing']['backing'] = hd_opts(2)
778
+ # Enable 'detect-zeroes' on all three nodes
779
+ opts['detect-zeroes'] = 'on'
780
+ opts['backing']['detect-zeroes'] = 'on'
781
+ opts['backing']['backing']['detect-zeroes'] = 'on'
782
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
783
+ self.assert_qmp(result, 'return', {})
784
+
785
+ # Reopen the chain passing the minimum amount of required options.
786
+ # By making 'backing' a reference to hd1 (instead of a sub-dict)
787
+ # we tell QEMU to keep its current set of options.
788
+ opts = {'driver': iotests.imgfmt,
789
+ 'node-name': 'hd0',
790
+ 'file': 'hd0-file',
791
+ 'backing': 'hd1' }
792
+ self.reopen(opts)
793
+
794
+ # This has reset 'detect-zeroes' on hd0, but not on hd1 and hd2.
795
+ self.assert_qmp(self.get_node('hd0'), 'detect_zeroes', 'off')
796
+ self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on')
797
+ self.assert_qmp(self.get_node('hd2'), 'detect_zeroes', 'on')
798
+
799
+ # Test what happens if the graph changes due to other operations
800
+ # such as block-stream
801
+ def test_block_stream_1(self):
802
+ # hd1 <- hd0
803
+ opts = hd_opts(0)
804
+ opts['backing'] = hd_opts(1)
805
+ opts['backing']['backing'] = None
806
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
807
+ self.assert_qmp(result, 'return', {})
808
+
809
+ # Stream hd1 into hd0 and wait until it's done
810
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', device = 'hd0')
811
+ self.assert_qmp(result, 'return', {})
812
+ self.wait_until_completed(drive = 'stream0')
813
+
814
+ # Now we have only hd0
815
+ self.assertEqual(self.get_node('hd1'), None)
816
+
817
+ # We have backing.* options but there's no backing file anymore
818
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
819
+
820
+ # If we remove the 'backing' option then we can reopen hd0 just fine
821
+ del opts['backing']
822
+ self.reopen(opts)
823
+
824
+ # We can also reopen hd0 if we set 'backing' to null
825
+ self.reopen(opts, {'backing': None})
826
+
827
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
828
+ self.assert_qmp(result, 'return', {})
829
+
830
+ # Another block_stream test
831
+ def test_block_stream_2(self):
832
+ # hd2 <- hd1 <- hd0
833
+ opts = hd_opts(0)
834
+ opts['backing'] = hd_opts(1)
835
+ opts['backing']['backing'] = hd_opts(2)
836
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
837
+ self.assert_qmp(result, 'return', {})
838
+
839
+ # Stream hd1 into hd0 and wait until it's done
840
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
841
+ device = 'hd0', base_node = 'hd2')
842
+ self.assert_qmp(result, 'return', {})
843
+ self.wait_until_completed(drive = 'stream0')
844
+
845
+ # The chain is hd2 <- hd0 now. hd1 is missing
846
+ self.assertEqual(self.get_node('hd1'), None)
847
+
848
+ # The backing options in the dict were meant for hd1, but we cannot
849
+ # use them with hd2 because hd1 had a backing file while hd2 does not.
850
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
851
+
852
+ # If we remove hd1's options from the dict then things work fine
853
+ opts['backing'] = opts['backing']['backing']
854
+ self.reopen(opts)
855
+
856
+ # We can also reopen hd0 if we use a reference to the backing file
857
+ self.reopen(opts, {'backing': 'hd2'})
858
+
859
+ # But we cannot leave the option out
860
+ del opts['backing']
861
+ self.reopen(opts, {}, "backing is missing for 'hd0'")
862
+
863
+ # Now we can delete hd0 (and hd2)
864
+ result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0')
865
+ self.assert_qmp(result, 'return', {})
866
+ self.assertEqual(self.get_node('hd2'), None)
867
+
868
+ # Reopen the chain during a block-stream job (from hd1 to hd0)
869
+ def test_block_stream_3(self):
870
+ # hd2 <- hd1 <- hd0
871
+ opts = hd_opts(0)
872
+ opts['backing'] = hd_opts(1)
873
+ opts['backing']['backing'] = hd_opts(2)
874
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
875
+ self.assert_qmp(result, 'return', {})
876
+
877
+ # hd2 <- hd0
878
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
879
+ device = 'hd0', base_node = 'hd2', speed = 512 * 1024)
880
+ self.assert_qmp(result, 'return', {})
881
+
882
+ # We can't remove hd2 while the stream job is ongoing
883
+ opts['backing']['backing'] = None
884
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
885
+
886
+ # We can't remove hd1 while the stream job is ongoing
887
+ opts['backing'] = None
888
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'")
889
+
890
+ self.wait_until_completed(drive = 'stream0')
891
+
892
+ # Reopen the chain during a block-stream job (from hd2 to hd1)
893
+ def test_block_stream_4(self):
894
+ # hd2 <- hd1 <- hd0
895
+ opts = hd_opts(0)
896
+ opts['backing'] = hd_opts(1)
897
+ opts['backing']['backing'] = hd_opts(2)
898
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
899
+ self.assert_qmp(result, 'return', {})
900
+
901
+ # hd1 <- hd0
902
+ result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0',
903
+ device = 'hd1', speed = 512 * 1024)
904
+ self.assert_qmp(result, 'return', {})
905
+
906
+ # We can't reopen with the original options because that would
907
+ # make hd1 read-only and block-stream requires it to be read-write
908
+ self.reopen(opts, {}, "Can't set node 'hd1' to r/o with copy-on-read enabled")
909
+
910
+ # We can't remove hd2 while the stream job is ongoing
911
+ opts['backing']['backing'] = None
912
+ self.reopen(opts, {'backing.read-only': False}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
913
+
914
+ # We can detach hd1 from hd0 because it doesn't affect the stream job
915
+ opts['backing'] = None
916
+ self.reopen(opts)
917
+
918
+ self.wait_until_completed(drive = 'stream0')
919
+
920
+ # Reopen the chain during a block-commit job (from hd0 to hd2)
921
+ def test_block_commit_1(self):
922
+ # hd2 <- hd1 <- hd0
923
+ opts = hd_opts(0)
924
+ opts['backing'] = hd_opts(1)
925
+ opts['backing']['backing'] = hd_opts(2)
926
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
927
+ self.assert_qmp(result, 'return', {})
928
+
929
+ result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0',
930
+ device = 'hd0', speed = 1024 * 1024)
931
+ self.assert_qmp(result, 'return', {})
932
+
933
+ # We can't remove hd2 while the commit job is ongoing
934
+ opts['backing']['backing'] = None
935
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'")
936
+
937
+ # We can't remove hd1 while the commit job is ongoing
938
+ opts['backing'] = None
939
+ self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'")
940
+
941
+ event = self.vm.event_wait(name='BLOCK_JOB_READY')
942
+ self.assert_qmp(event, 'data/device', 'commit0')
943
+ self.assert_qmp(event, 'data/type', 'commit')
944
+ self.assert_qmp_absent(event, 'data/error')
945
+
946
+ result = self.vm.qmp('block-job-complete', device='commit0')
947
+ self.assert_qmp(result, 'return', {})
948
+
949
+ self.wait_until_completed(drive = 'commit0')
950
+
951
+ # Reopen the chain during a block-commit job (from hd1 to hd2)
952
+ def test_block_commit_2(self):
953
+ # hd2 <- hd1 <- hd0
954
+ opts = hd_opts(0)
955
+ opts['backing'] = hd_opts(1)
956
+ opts['backing']['backing'] = hd_opts(2)
957
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
958
+ self.assert_qmp(result, 'return', {})
959
+
960
+ result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0',
961
+ device = 'hd0', top_node = 'hd1', speed = 1024 * 1024)
962
+ self.assert_qmp(result, 'return', {})
963
+
964
+ # We can't remove hd2 while the commit job is ongoing
965
+ opts['backing']['backing'] = None
966
+ self.reopen(opts, {}, "Cannot change the option 'backing.driver'")
967
+
968
+ # We can't remove hd1 while the commit job is ongoing
969
+ opts['backing'] = None
970
+ self.reopen(opts, {}, "Cannot change backing link if 'hd0' has an implicit backing file")
971
+
972
+ # hd2 <- hd0
973
+ self.wait_until_completed(drive = 'commit0')
974
+
975
+ self.assert_qmp(self.get_node('hd0'), 'ro', False)
976
+ self.assertEqual(self.get_node('hd1'), None)
977
+ self.assert_qmp(self.get_node('hd2'), 'ro', True)
978
+
979
+ # We don't allow setting a backing file that uses a different AioContext
980
+ def test_iothreads(self):
981
+ opts = hd_opts(0)
982
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
983
+ self.assert_qmp(result, 'return', {})
984
+
985
+ opts2 = hd_opts(2)
986
+ result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2)
987
+ self.assert_qmp(result, 'return', {})
988
+
989
+ result = self.vm.qmp('object-add', qom_type='iothread', id='iothread0')
990
+ self.assert_qmp(result, 'return', {})
991
+
992
+ result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1')
993
+ self.assert_qmp(result, 'return', {})
994
+
995
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd0', iothread='iothread0')
996
+ self.assert_qmp(result, 'return', {})
997
+
998
+ self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
999
+
1000
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread1')
1001
+ self.assert_qmp(result, 'return', {})
1002
+
1003
+ self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
1004
+
1005
+ result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread0')
1006
+ self.assert_qmp(result, 'return', {})
1007
+
1008
+ self.reopen(opts, {'backing': 'hd2'})
1009
+
1010
+if __name__ == '__main__':
1011
+ iotests.main(supported_fmts=["qcow2"])
1012
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
1013
new file mode 100644
1014
index XXXXXXX..XXXXXXX
1015
--- /dev/null
1016
+++ b/tests/qemu-iotests/245.out
1017
@@ -XXX,XX +XXX,XX @@
1018
+..................
1019
+----------------------------------------------------------------------
1020
+Ran 18 tests
1021
+
1022
+OK
1023
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
1024
index XXXXXXX..XXXXXXX 100644
158
index XXXXXXX..XXXXXXX 100644
1025
--- a/tests/qemu-iotests/group
159
--- a/tests/Makefile.include
1026
+++ b/tests/qemu-iotests/group
160
+++ b/tests/Makefile.include
1027
@@ -XXX,XX +XXX,XX @@
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
1028
242 rw auto quick
162
gcov-files-test-hbitmap-y = util/hbitmap.c
1029
243 rw auto quick
163
check-unit-y += tests/test-hbitmap$(EXESUF)
1030
244 rw auto quick
164
gcov-files-test-hbitmap-y = blockjob.c
1031
+245 rw auto
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
166
check-unit-y += tests/test-blockjob$(EXESUF)
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
168
check-unit-y += tests/test-x86-cpuid$(EXESUF)
169
@@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
170
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
171
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
172
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
173
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
174
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
175
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
176
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
1032
--
177
--
1033
2.20.1
178
2.13.6
1034
179
1035
180
diff view generated by jsdifflib
1
In order to be able to dynamically reopen the file read-only or
1
Now that the bdrv_drain_invoke() calls are pulled up to the callers of
2
read-write, depending on the users that are attached, we need to be able
2
bdrv_drain_recurse(), the 'begin' parameter isn't needed any more.
3
to switch to a different file descriptor during the permission change.
4
5
This interacts with reopen, which also creates a new file descriptor and
6
performs permission changes internally. In this case, the permission
7
change code must reuse the reopen file descriptor instead of creating a
8
third one.
9
10
In turn, reopen can drop its code to copy file locks to the new file
11
descriptor because that is now done when applying the new permissions.
12
3
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
---
6
---
15
block/file-posix.c | 96 ++++++++++++++++++++++++++++++++++++++++------
7
block/io.c | 12 ++++++------
16
1 file changed, 85 insertions(+), 11 deletions(-)
8
1 file changed, 6 insertions(+), 6 deletions(-)
17
9
18
diff --git a/block/file-posix.c b/block/file-posix.c
10
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
20
--- a/block/file-posix.c
12
--- a/block/io.c
21
+++ b/block/file-posix.c
13
+++ b/block/io.c
22
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
23
uint64_t locked_perm;
15
}
24
uint64_t locked_shared_perm;
25
26
+ int perm_change_fd;
27
BDRVReopenState *reopen_state;
28
29
#ifdef CONFIG_XFS
30
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
31
}
16
}
32
17
33
static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
34
- int *open_flags, Error **errp)
19
+static bool bdrv_drain_recurse(BlockDriverState *bs)
35
+ int *open_flags, bool force_dup,
36
+ Error **errp)
37
{
20
{
38
BDRVRawState *s = bs->opaque;
21
BdrvChild *child, *tmp;
39
int fd = -1;
22
bool waited;
40
@@ -XXX,XX +XXX,XX @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
23
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
41
assert((s->open_flags & O_ASYNC) == 0);
24
*/
42
#endif
25
bdrv_ref(bs);
43
44
+ if (!force_dup && *open_flags == s->open_flags) {
45
+ /* We're lucky, the existing fd is fine */
46
+ return s->fd;
47
+ }
48
+
49
if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
50
/* dup the original fd */
51
fd = qemu_dup(s->fd);
52
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
53
qemu_opts_to_qdict(opts, state->options);
54
55
rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
56
- &local_err);
57
+ true, &local_err);
58
if (local_err) {
59
error_propagate(errp, local_err);
60
ret = -1;
61
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
62
ret = -EINVAL;
63
goto out_fd;
64
}
26
}
65
-
27
- waited |= bdrv_drain_recurse(bs, begin);
66
- /* Copy locks to the new fd */
28
+ waited |= bdrv_drain_recurse(bs);
67
- ret = raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
29
if (in_main_loop) {
68
- s->locked_shared_perm, false, errp);
30
bdrv_unref(bs);
69
- if (ret < 0) {
31
}
70
- ret = -EINVAL;
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
71
- goto out_fd;
72
- }
73
}
33
}
74
34
75
s->reopen_state = state;
35
bdrv_drain_invoke(bs, true);
76
@@ -XXX,XX +XXX,XX @@ static QemuOptsList raw_create_opts = {
36
- bdrv_drain_recurse(bs, true);
77
static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
37
+ bdrv_drain_recurse(bs);
78
Error **errp)
79
{
80
- return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
81
+ BDRVRawState *s = bs->opaque;
82
+ BDRVRawReopenState *rs = NULL;
83
+ int open_flags;
84
+ int ret;
85
+
86
+ if (s->perm_change_fd) {
87
+ /*
88
+ * In the context of reopen, this function may be called several times
89
+ * (directly and recursively while change permissions of the parent).
90
+ * This is even true for children that don't inherit from the original
91
+ * reopen node, so s->reopen_state is not set.
92
+ *
93
+ * Ignore all but the first call.
94
+ */
95
+ return 0;
96
+ }
97
+
98
+ if (s->reopen_state) {
99
+ /* We already have a new file descriptor to set permissions for */
100
+ assert(s->reopen_state->perm == perm);
101
+ assert(s->reopen_state->shared_perm == shared);
102
+ rs = s->reopen_state->opaque;
103
+ s->perm_change_fd = rs->fd;
104
+ } else {
105
+ /* We may need a new fd if auto-read-only switches the mode */
106
+ ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags,
107
+ false, errp);
108
+ if (ret < 0) {
109
+ return ret;
110
+ } else if (ret != s->fd) {
111
+ s->perm_change_fd = ret;
112
+ }
113
+ }
114
+
115
+ /* Prepare permissions on old fd to avoid conflicts between old and new,
116
+ * but keep everything locked that new will need. */
117
+ ret = raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp);
118
+ if (ret < 0) {
119
+ goto fail;
120
+ }
121
+
122
+ /* Copy locks to the new fd */
123
+ if (s->perm_change_fd) {
124
+ ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared,
125
+ false, errp);
126
+ if (ret < 0) {
127
+ raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
128
+ goto fail;
129
+ }
130
+ }
131
+ return 0;
132
+
133
+fail:
134
+ if (s->perm_change_fd && !s->reopen_state) {
135
+ qemu_close(s->perm_change_fd);
136
+ }
137
+ s->perm_change_fd = 0;
138
+ return ret;
139
}
38
}
140
39
141
static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
40
void bdrv_drained_end(BlockDriverState *bs)
142
{
41
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
143
BDRVRawState *s = bs->opaque;
42
144
+
43
bdrv_parent_drained_end(bs);
145
+ /* For reopen, we have already switched to the new fd (.bdrv_set_perm is
44
bdrv_drain_invoke(bs, false);
146
+ * called after .bdrv_reopen_commit) */
45
- bdrv_drain_recurse(bs, false);
147
+ if (s->perm_change_fd && s->fd != s->perm_change_fd) {
46
+ bdrv_drain_recurse(bs);
148
+ qemu_close(s->fd);
47
aio_enable_external(bdrv_get_aio_context(bs));
149
+ s->fd = s->perm_change_fd;
150
+ }
151
+ s->perm_change_fd = 0;
152
+
153
raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL);
154
s->perm = perm;
155
s->shared_perm = shared;
156
@@ -XXX,XX +XXX,XX @@ static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared)
157
158
static void raw_abort_perm_update(BlockDriverState *bs)
159
{
160
+ BDRVRawState *s = bs->opaque;
161
+
162
+ /* For reopen, .bdrv_reopen_abort is called afterwards and will close
163
+ * the file descriptor. */
164
+ if (s->perm_change_fd && !s->reopen_state) {
165
+ qemu_close(s->perm_change_fd);
166
+ }
167
+ s->perm_change_fd = 0;
168
+
169
raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
170
}
48
}
171
49
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
51
aio_context_acquire(aio_context);
52
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
53
if (aio_context == bdrv_get_aio_context(bs)) {
54
- waited |= bdrv_drain_recurse(bs, true);
55
+ waited |= bdrv_drain_recurse(bs);
56
}
57
}
58
aio_context_release(aio_context);
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
60
aio_enable_external(aio_context);
61
bdrv_parent_drained_end(bs);
62
bdrv_drain_invoke(bs, false);
63
- bdrv_drain_recurse(bs, false);
64
+ bdrv_drain_recurse(bs);
65
aio_context_release(aio_context);
66
}
67
172
--
68
--
173
2.20.1
69
2.13.6
174
70
175
71
diff view generated by jsdifflib
New patch
1
The device is drained, so there is no point in waiting for requests at
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
there.
1
4
5
The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e
6
in order to call the .bdrv_co_drain_end() driver callback. This is now
7
done by a separate bdrv_drain_invoke() call.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
13
block/io.c | 2 --
14
1 file changed, 2 deletions(-)
15
16
diff --git a/block/io.c b/block/io.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/io.c
19
+++ b/block/io.c
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
21
22
bdrv_parent_drained_end(bs);
23
bdrv_drain_invoke(bs, false);
24
- bdrv_drain_recurse(bs);
25
aio_enable_external(bdrv_get_aio_context(bs));
26
}
27
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
29
aio_enable_external(aio_context);
30
bdrv_parent_drained_end(bs);
31
bdrv_drain_invoke(bs, false);
32
- bdrv_drain_recurse(bs);
33
aio_context_release(aio_context);
34
}
35
36
--
37
2.13.6
38
39
diff view generated by jsdifflib
1
tests/virtio-blk-test uses a temporary image file that it deletes while
1
Drain requests are propagated to child nodes, parent nodes and directly
2
QEMU is still running, so it can't be reopened when writers are
2
to the AioContext. The order in which this happened was different
3
attached or detached. Disable auto-read-only to keep it always writable.
3
between all combinations of drain/drain_all and begin/end.
4
5
The correct order is to keep children only drained when their parents
6
are also drained. This means that at the start of a drained section, the
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.
4
13
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
---
16
---
8
tests/virtio-blk-test.c | 2 +-
17
block/io.c | 12 ++++++++----
9
1 file changed, 1 insertion(+), 1 deletion(-)
18
1 file changed, 8 insertions(+), 4 deletions(-)
10
19
11
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
20
diff --git a/block/io.c b/block/io.c
12
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/virtio-blk-test.c
22
--- a/block/io.c
14
+++ b/tests/virtio-blk-test.c
23
+++ b/block/io.c
15
@@ -XXX,XX +XXX,XX @@ static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
16
char *tmp_path = drive_create();
25
return;
17
26
}
18
g_string_append_printf(cmd_line,
27
19
- " -drive if=none,id=drive0,file=%s,format=raw "
28
+ /* Stop things in parent-to-child order */
20
+ " -drive if=none,id=drive0,file=%s,format=raw,auto-read-only=off "
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
21
"-drive if=none,id=drive1,file=null-co://,format=raw ",
30
aio_disable_external(bdrv_get_aio_context(bs));
22
tmp_path);
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
}
23
68
24
--
69
--
25
2.20.1
70
2.13.6
26
71
27
72
diff view generated by jsdifflib
1
There is no reason why we can take locks on the new file descriptor only
1
Commit 15afd94a047 added code to acquire and release the AioContext in
2
in raw_reopen_commit() where error handling isn't possible any more.
2
qemuio_command(). This means that the lock is taken twice now in the
3
Instead, we can already do this in raw_reopen_prepare().
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.
5
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
4
7
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
---
10
---
7
block/file-posix.c | 27 ++++++++++++++++-----------
11
hmp.c | 6 ------
8
1 file changed, 16 insertions(+), 11 deletions(-)
12
1 file changed, 6 deletions(-)
9
13
10
diff --git a/block/file-posix.c b/block/file-posix.c
14
diff --git a/hmp.c b/hmp.c
11
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
12
--- a/block/file-posix.c
16
--- a/hmp.c
13
+++ b/block/file-posix.c
17
+++ b/hmp.c
14
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
18
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
15
BDRVRawState *s;
19
{
16
BDRVRawReopenState *rs;
20
BlockBackend *blk;
17
QemuOpts *opts;
21
BlockBackend *local_blk = NULL;
18
- int ret = 0;
22
- AioContext *aio_context;
19
+ int ret;
23
const char* device = qdict_get_str(qdict, "device");
20
Error *local_err = NULL;
24
const char* command = qdict_get_str(qdict, "command");
21
25
Error *err = NULL;
22
assert(state != NULL);
26
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
23
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
24
if (rs->fd != -1) {
25
raw_probe_alignment(state->bs, rs->fd, &local_err);
26
if (local_err) {
27
- qemu_close(rs->fd);
28
- rs->fd = -1;
29
error_propagate(errp, local_err);
30
ret = -EINVAL;
31
+ goto out_fd;
32
+ }
33
+
34
+ /* Copy locks to the new fd */
35
+ ret = raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
36
+ s->locked_shared_perm, false, errp);
37
+ if (ret < 0) {
38
+ ret = -EINVAL;
39
+ goto out_fd;
40
}
27
}
41
}
28
}
42
29
43
s->reopen_state = state;
30
- aio_context = blk_get_aio_context(blk);
44
+ ret = 0;
31
- aio_context_acquire(aio_context);
45
+out_fd:
32
-
46
+ if (ret < 0) {
33
/*
47
+ qemu_close(rs->fd);
34
* Notably absent: Proper permission management. This is sad, but it seems
48
+ rs->fd = -1;
35
* almost impossible to achieve without changing the semantics and thereby
49
+ }
36
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
50
out:
37
*/
51
qemu_opts_del(opts);
38
qemuio_command(blk, command);
52
return ret;
39
53
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
40
- aio_context_release(aio_context);
54
{
41
-
55
BDRVRawReopenState *rs = state->opaque;
42
fail:
56
BDRVRawState *s = state->bs->opaque;
43
blk_unref(local_blk);
57
- Error *local_err = NULL;
44
hmp_handle_error(mon, &err);
58
59
s->check_cache_dropped = rs->check_cache_dropped;
60
s->open_flags = rs->open_flags;
61
62
- /* Copy locks to the new fd before closing the old one. */
63
- raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
64
- s->locked_shared_perm, false, &local_err);
65
- if (local_err) {
66
- /* shouldn't fail in a sane host, but report it just in case. */
67
- error_report_err(local_err);
68
- }
69
qemu_close(s->fd);
70
s->fd = rs->fd;
71
72
--
45
--
73
2.20.1
46
2.13.6
74
47
75
48
diff view generated by jsdifflib
New patch
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
1
2
3
Since bdrv_co_preadv does all neccessary checks including
4
reading after the end of the backing file, avoid duplication
5
of verification before bdrv_co_preadv call.
6
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/qcow2.h | 3 ---
13
block/qcow2.c | 51 ++++++++-------------------------------------------
14
2 files changed, 8 insertions(+), 46 deletions(-)
15
16
diff --git a/block/qcow2.h b/block/qcow2.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2.h
19
+++ b/block/qcow2.h
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
21
}
22
23
/* qcow2.c functions */
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
25
- int64_t sector_num, int nb_sectors);
26
-
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
28
int refcount_order, bool generous_increase,
29
uint64_t *refblock_count);
30
diff --git a/block/qcow2.c b/block/qcow2.c
31
index XXXXXXX..XXXXXXX 100644
32
--- a/block/qcow2.c
33
+++ b/block/qcow2.c
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
35
return status;
36
}
37
38
-/* handle reading after the end of the backing file */
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
40
- int64_t offset, int bytes)
41
-{
42
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
43
- int n1;
44
-
45
- if ((offset + bytes) <= bs_size) {
46
- return bytes;
47
- }
48
-
49
- if (offset >= bs_size) {
50
- n1 = 0;
51
- } else {
52
- n1 = bs_size - offset;
53
- }
54
-
55
- qemu_iovec_memset(qiov, n1, 0, bytes - n1);
56
-
57
- return n1;
58
-}
59
-
60
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
61
uint64_t bytes, QEMUIOVector *qiov,
62
int flags)
63
{
64
BDRVQcow2State *s = bs->opaque;
65
- int offset_in_cluster, n1;
66
+ int offset_in_cluster;
67
int ret;
68
unsigned int cur_bytes; /* number of bytes in current iteration */
69
uint64_t cluster_offset = 0;
70
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
71
case QCOW2_CLUSTER_UNALLOCATED:
72
73
if (bs->backing) {
74
- /* read from the base image */
75
- n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
76
- offset, cur_bytes);
77
- if (n1 > 0) {
78
- QEMUIOVector local_qiov;
79
-
80
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
81
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
82
-
83
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
84
- qemu_co_mutex_unlock(&s->lock);
85
- ret = bdrv_co_preadv(bs->backing, offset, n1,
86
- &local_qiov, 0);
87
- qemu_co_mutex_lock(&s->lock);
88
-
89
- qemu_iovec_destroy(&local_qiov);
90
-
91
- if (ret < 0) {
92
- goto fail;
93
- }
94
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
95
+ qemu_co_mutex_unlock(&s->lock);
96
+ ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
97
+ &hd_qiov, 0);
98
+ qemu_co_mutex_lock(&s->lock);
99
+ if (ret < 0) {
100
+ goto fail;
101
}
102
} else {
103
/* Note: in this case, no need to wait */
104
--
105
2.13.6
106
107
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
driver state that cannot be recreated with create options because it
3
would require a list with gaps. This causes trouble in at least
4
.bdrv_refresh_filename().
2
5
3
This command allows reopening an arbitrary BlockDriverState with a
6
Document this problem so that we won't accidentally mark the command
4
new set of options. Some options (e.g node-name) cannot be changed
7
stable without having addressed it.
5
and some block drivers don't allow reopening, but otherwise this
6
command is modelled after 'blockdev-add' and the state of the reopened
7
BlockDriverState should generally be the same as if it had just been
8
added by 'blockdev-add' with the same set of options.
9
8
10
One notable exception is the 'backing' option: 'x-blockdev-reopen'
11
requires that it is always present unless the BlockDriverState in
12
question doesn't have a current or default backing file.
13
14
This command allows reconfiguring the graph by using the appropriate
15
options to change the children of a node. At the moment it's possible
16
to change a backing file by setting the 'backing' option to the name
17
of the new node, but it should also be possible to add a similar
18
functionality to other block drivers (e.g. Quorum, blkverify).
19
20
Although the API is unlikely to change, this command is marked
21
experimental for the time being so there's room to see if the
22
semantics need changes.
23
24
Signed-off-by: Alberto Garcia <berto@igalia.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
26
---
11
---
27
qapi/block-core.json | 42 +++++++++++++++++++++++++++++++++++++++
12
qapi/block-core.json | 4 ++++
28
blockdev.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
13
1 file changed, 4 insertions(+)
29
2 files changed, 89 insertions(+)
30
14
31
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
32
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
33
--- a/qapi/block-core.json
17
--- a/qapi/block-core.json
34
+++ b/qapi/block-core.json
18
+++ b/qapi/block-core.json
35
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
36
##
20
# does not support all kinds of operations, all kinds of children, nor
37
{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true }
21
# all block drivers.
38
22
#
39
+##
23
+# FIXME Removing children from a quorum node means introducing gaps in the
40
+# @x-blockdev-reopen:
24
+# child indices. This cannot be represented in the 'children' list of
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
41
+#
26
+#
42
+# Reopens a block device using the given set of options. Any option
27
# Warning: The data in a new quorum child MUST be consistent with that of
43
+# not specified will be reset to its default value regardless of its
28
# the rest of the array.
44
+# previous status. If an option cannot be changed or a particular
45
+# driver does not support reopening then the command will return an
46
+# error.
47
+#
48
+# The top-level @node-name option (from BlockdevOptions) must be
49
+# specified and is used to select the block device to be reopened.
50
+# Other @node-name options must be either omitted or set to the
51
+# current name of the appropriate node. This command won't change any
52
+# node name and any attempt to do it will result in an error.
53
+#
54
+# In the case of options that refer to child nodes, the behavior of
55
+# this command depends on the value:
56
+#
57
+# 1) A set of options (BlockdevOptions): the child is reopened with
58
+# the specified set of options.
59
+#
60
+# 2) A reference to the current child: the child is reopened using
61
+# its existing set of options.
62
+#
63
+# 3) A reference to a different node: the current child is replaced
64
+# with the specified one.
65
+#
66
+# 4) NULL: the current child (if any) is detached.
67
+#
68
+# Options (1) and (2) are supported in all cases, but at the moment
69
+# only @backing allows replacing or detaching an existing child.
70
+#
71
+# Unlike with blockdev-add, the @backing option must always be present
72
+# unless the node being reopened does not have a backing file and its
73
+# image does not have a default backing file name as part of its
74
+# metadata.
75
+#
76
+# Since: 4.0
77
+##
78
+{ 'command': 'x-blockdev-reopen',
79
+ 'data': 'BlockdevOptions', 'boxed': true }
80
+
81
##
82
# @blockdev-del:
83
#
29
#
84
diff --git a/blockdev.c b/blockdev.c
85
index XXXXXXX..XXXXXXX 100644
86
--- a/blockdev.c
87
+++ b/blockdev.c
88
@@ -XXX,XX +XXX,XX @@ fail:
89
visit_free(v);
90
}
91
92
+void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
93
+{
94
+ BlockDriverState *bs;
95
+ AioContext *ctx;
96
+ QObject *obj;
97
+ Visitor *v = qobject_output_visitor_new(&obj);
98
+ Error *local_err = NULL;
99
+ BlockReopenQueue *queue;
100
+ QDict *qdict;
101
+
102
+ /* Check for the selected node name */
103
+ if (!options->has_node_name) {
104
+ error_setg(errp, "Node name not specified");
105
+ goto fail;
106
+ }
107
+
108
+ bs = bdrv_find_node(options->node_name);
109
+ if (!bs) {
110
+ error_setg(errp, "Cannot find node named '%s'", options->node_name);
111
+ goto fail;
112
+ }
113
+
114
+ /* Put all options in a QDict and flatten it */
115
+ visit_type_BlockdevOptions(v, NULL, &options, &local_err);
116
+ if (local_err) {
117
+ error_propagate(errp, local_err);
118
+ goto fail;
119
+ }
120
+
121
+ visit_complete(v, &obj);
122
+ qdict = qobject_to(QDict, obj);
123
+
124
+ qdict_flatten(qdict);
125
+
126
+ /* Perform the reopen operation */
127
+ ctx = bdrv_get_aio_context(bs);
128
+ aio_context_acquire(ctx);
129
+ bdrv_subtree_drained_begin(bs);
130
+ queue = bdrv_reopen_queue(NULL, bs, qdict, false);
131
+ bdrv_reopen_multiple(queue, errp);
132
+ bdrv_subtree_drained_end(bs);
133
+ aio_context_release(ctx);
134
+
135
+fail:
136
+ visit_free(v);
137
+}
138
+
139
void qmp_blockdev_del(const char *node_name, Error **errp)
140
{
141
AioContext *aio_context;
142
--
30
--
143
2.20.1
31
2.13.6
144
32
145
33
diff view generated by jsdifflib
1
From: Keith Busch <keith.busch@intel.com>
1
From: Doug Gale <doug16k@gmail.com>
2
2
3
The implementation used blocks units rather than the expected bytes.
3
Add trace output for commands, errors, and undefined behavior.
4
Add guest error log output for undefined behavior.
5
Report invalid undefined accesses to MMIO.
6
Annotate unlikely error checks with unlikely.
4
7
5
Fixes: c03e7ef12a9 ("nvme: Implement Write Zeroes")
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
6
Reported-by: Ming Lei <ming.lei@redhat.com>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
7
Signed-off-by: Keith Busch <keith.busch@intel.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Christoph Hellwig <hch@lst.de>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
12
---
11
hw/block/nvme.c | 6 +++---
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
12
1 file changed, 3 insertions(+), 3 deletions(-)
14
hw/block/trace-events | 93 ++++++++++++++
15
2 files changed, 390 insertions(+), 52 deletions(-)
13
16
14
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
15
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/block/nvme.c
19
--- a/hw/block/nvme.c
17
+++ b/hw/block/nvme.c
20
+++ b/hw/block/nvme.c
21
@@ -XXX,XX +XXX,XX @@
22
#include "qapi/visitor.h"
23
#include "sysemu/block-backend.h"
24
25
+#include "qemu/log.h"
26
+#include "trace.h"
27
#include "nvme.h"
28
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
30
+ do { \
31
+ (trace_##trace)(__VA_ARGS__); \
32
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
33
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
34
+ } while (0)
35
+
36
static void nvme_process_sq(void *opaque);
37
38
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
39
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
40
{
41
if (cq->irq_enabled) {
42
if (msix_enabled(&(n->parent_obj))) {
43
+ trace_nvme_irq_msix(cq->vector);
44
msix_notify(&(n->parent_obj), cq->vector);
45
} else {
46
+ trace_nvme_irq_pin();
47
pci_irq_pulse(&n->parent_obj);
48
}
49
+ } else {
50
+ trace_nvme_irq_masked();
51
}
52
}
53
54
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
55
trans_len = MIN(len, trans_len);
56
int num_prps = (len >> n->page_bits) + 1;
57
58
- if (!prp1) {
59
+ if (unlikely(!prp1)) {
60
+ trace_nvme_err_invalid_prp();
61
return NVME_INVALID_FIELD | NVME_DNR;
62
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
63
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
64
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
65
}
66
len -= trans_len;
67
if (len) {
68
- if (!prp2) {
69
+ if (unlikely(!prp2)) {
70
+ trace_nvme_err_invalid_prp2_missing();
71
goto unmap;
72
}
73
if (len > n->page_size) {
74
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
76
77
if (i == n->max_prp_ents - 1 && len > n->page_size) {
78
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
79
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
80
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
81
goto unmap;
82
}
83
84
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
85
prp_ent = le64_to_cpu(prp_list[i]);
86
}
87
88
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
89
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
90
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
91
goto unmap;
92
}
93
94
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
95
i++;
96
}
97
} else {
98
- if (prp2 & (n->page_size - 1)) {
99
+ if (unlikely(prp2 & (n->page_size - 1))) {
100
+ trace_nvme_err_invalid_prp2_align(prp2);
101
goto unmap;
102
}
103
if (qsg->nsg) {
104
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
105
QEMUIOVector iov;
106
uint16_t status = NVME_SUCCESS;
107
108
+ trace_nvme_dma_read(prp1, prp2);
109
+
110
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
111
return NVME_INVALID_FIELD | NVME_DNR;
112
}
113
if (qsg.nsg > 0) {
114
- if (dma_buf_read(ptr, len, &qsg)) {
115
+ if (unlikely(dma_buf_read(ptr, len, &qsg))) {
116
+ trace_nvme_err_invalid_dma();
117
status = NVME_INVALID_FIELD | NVME_DNR;
118
}
119
qemu_sglist_destroy(&qsg);
120
} else {
121
- if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
122
+ if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
123
+ trace_nvme_err_invalid_dma();
124
status = NVME_INVALID_FIELD | NVME_DNR;
125
}
126
qemu_iovec_destroy(&iov);
18
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
127
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
19
const uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds;
128
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
20
uint64_t slba = le64_to_cpu(rw->slba);
129
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
21
uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
130
22
- uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
131
- if (slba + nlb > ns->id_ns.nsze) {
23
- uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
132
+ if (unlikely(slba + nlb > ns->id_ns.nsze)) {
24
+ uint64_t offset = slba << data_shift;
133
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
25
+ uint32_t count = nlb << data_shift;
134
return NVME_LBA_RANGE | NVME_DNR;
26
135
}
27
if (unlikely(slba + nlb > ns->id_ns.nsze)) {
136
28
trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
137
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
29
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
138
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
30
req->has_sg = false;
139
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
31
block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0,
140
32
BLOCK_ACCT_WRITE);
141
- if ((slba + nlb) > ns->id_ns.nsze) {
33
- req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, aio_slba, aio_nlb,
142
+ trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
34
+ req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, offset, count,
143
+
35
BDRV_REQ_MAY_UNMAP, nvme_rw_cb, req);
144
+ if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
36
return NVME_NO_COMPLETE;
145
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
37
}
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"
38
--
784
--
39
2.20.1
785
2.13.6
40
786
41
787
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
Of all options of type BlockdevRef used to specify children in
3
Management tools create overlays of running guests with qemu-img:
4
BlockdevOptions, 'backing' is the only one that is optional.
5
4
6
For "x-blockdev-reopen" we want that if an option is omitted then it
5
$ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2
7
must be reset to its default value. The default value of 'backing'
8
means that QEMU opens the backing file specified in the image
9
metadata, but this is not something that we want to support for the
10
reopen operation.
11
6
12
Because of this the 'backing' option has to be specified during
7
but this doesn't work anymore due to image locking:
13
reopen, pointing to the existing backing file if we want to keep it,
14
or pointing to a different one (or NULL) if we want to replace it (to
15
be implemented in a subsequent patch).
16
8
17
In order to simplify things a bit and not to require that the user
9
qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock
18
passes the 'backing' option to every single block device even when
10
Is another process using the image?
19
it's clearly not necessary, this patch allows omitting this option if
11
Could not open backing image to determine size.
20
the block device being reopened doesn't have a backing file attached
12
Use the force share option to allow this use case again.
21
_and_ no default backing file is specified in the image metadata.
22
13
23
Signed-off-by: Alberto Garcia <berto@igalia.com>
14
Cc: qemu-stable@nongnu.org
15
Signed-off-by: Fam Zheng <famz@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
18
---
26
block.c | 8 +++++++-
19
block.c | 3 ++-
27
1 file changed, 7 insertions(+), 1 deletion(-)
20
1 file changed, 2 insertions(+), 1 deletion(-)
28
21
29
diff --git a/block.c b/block.c
22
diff --git a/block.c b/block.c
30
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
24
--- a/block.c
32
+++ b/block.c
25
+++ b/block.c
33
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
34
27
back_flags = flags;
35
drv_prepared = true;
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
36
29
37
- if (drv->supports_backing && reopen_state->backing_missing) {
30
+ backing_options = qdict_new();
38
+ /*
31
if (backing_fmt) {
39
+ * We must provide the 'backing' option if the BDS has a backing
32
- backing_options = qdict_new();
40
+ * file or if the image file has a backing file name as part of
33
qdict_put_str(backing_options, "driver", backing_fmt);
41
+ * its metadata. Otherwise the 'backing' option can be omitted.
34
}
42
+ */
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
43
+ if (drv->supports_backing && reopen_state->backing_missing &&
36
44
+ (backing_bs(reopen_state->bs) || reopen_state->bs->backing_file[0])) {
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
45
error_setg(errp, "backing is missing for '%s'",
38
&local_err);
46
reopen_state->bs->node_name);
47
ret = -EINVAL;
48
--
39
--
49
2.20.1
40
2.13.6
50
41
51
42
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
Drop x- and x_ prefixes for latency histograms and update version to
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
4
3.1
5
4
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Signed-off-by: Thomas Huth <thuth@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
---
9
qapi/block-core.json | 20 ++++++++++----------
10
blockdev.c | 11 -----------
10
block/qapi.c | 12 ++++++------
11
qemu-doc.texi | 6 ------
11
blockdev.c | 2 +-
12
2 files changed, 17 deletions(-)
12
3 files changed, 17 insertions(+), 17 deletions(-)
13
13
14
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/block-core.json
17
+++ b/qapi/block-core.json
18
@@ -XXX,XX +XXX,XX @@
19
# +------------------
20
# 10 50 100
21
#
22
-# Since: 2.12
23
+# Since: 4.0
24
##
25
{ 'struct': 'BlockLatencyHistogramInfo',
26
'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } }
27
28
##
29
-# @x-block-latency-histogram-set:
30
+# @block-latency-histogram-set:
31
#
32
# Manage read, write and flush latency histograms for the device.
33
#
34
@@ -XXX,XX +XXX,XX @@
35
#
36
# Returns: error if device is not found or any boundary arrays are invalid.
37
#
38
-# Since: 2.12
39
+# Since: 4.0
40
#
41
# Example: set new histograms for all io types with intervals
42
# [0, 10), [10, 50), [50, 100), [100, +inf):
43
@@ -XXX,XX +XXX,XX @@
44
# "arguments": { "device": "drive0" } }
45
# <- { "return": {} }
46
##
47
-{ 'command': 'x-block-latency-histogram-set',
48
+{ 'command': 'block-latency-histogram-set',
49
'data': {'id': 'str',
50
'*boundaries': ['uint64'],
51
'*boundaries-read': ['uint64'],
52
@@ -XXX,XX +XXX,XX @@
53
# @timed_stats: Statistics specific to the set of previously defined
54
# intervals of time (Since 2.5)
55
#
56
-# @x_rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
57
+# @rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
58
#
59
-# @x_wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
60
+# @wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
61
#
62
-# @x_flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
63
+# @flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0)
64
#
65
# Since: 0.14.0
66
##
67
@@ -XXX,XX +XXX,XX @@
68
'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
69
'account_invalid': 'bool', 'account_failed': 'bool',
70
'timed_stats': ['BlockDeviceTimedStats'],
71
- '*x_rd_latency_histogram': 'BlockLatencyHistogramInfo',
72
- '*x_wr_latency_histogram': 'BlockLatencyHistogramInfo',
73
- '*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
74
+ '*rd_latency_histogram': 'BlockLatencyHistogramInfo',
75
+ '*wr_latency_histogram': 'BlockLatencyHistogramInfo',
76
+ '*flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
77
78
##
79
# @BlockStats:
80
diff --git a/block/qapi.c b/block/qapi.c
81
index XXXXXXX..XXXXXXX 100644
82
--- a/block/qapi.c
83
+++ b/block/qapi.c
84
@@ -XXX,XX +XXX,XX @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
85
}
86
87
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
88
- &ds->has_x_rd_latency_histogram,
89
- &ds->x_rd_latency_histogram);
90
+ &ds->has_rd_latency_histogram,
91
+ &ds->rd_latency_histogram);
92
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
93
- &ds->has_x_wr_latency_histogram,
94
- &ds->x_wr_latency_histogram);
95
+ &ds->has_wr_latency_histogram,
96
+ &ds->wr_latency_histogram);
97
bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
98
- &ds->has_x_flush_latency_histogram,
99
- &ds->x_flush_latency_histogram);
100
+ &ds->has_flush_latency_histogram,
101
+ &ds->flush_latency_histogram);
102
}
103
104
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
105
diff --git a/blockdev.c b/blockdev.c
14
diff --git a/blockdev.c b/blockdev.c
106
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
107
--- a/blockdev.c
16
--- a/blockdev.c
108
+++ b/blockdev.c
17
+++ b/blockdev.c
109
@@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
110
aio_context_release(old_context);
19
.type = QEMU_OPT_STRING,
111
}
20
.help = "chs translation (auto, lba, none)",
112
21
},{
113
-void qmp_x_block_latency_histogram_set(
22
- .name = "boot",
114
+void qmp_block_latency_histogram_set(
23
- .type = QEMU_OPT_BOOL,
115
const char *id,
24
- .help = "(deprecated, ignored)",
116
bool has_boundaries, uint64List *boundaries,
25
- },{
117
bool has_boundaries_read, uint64List *boundaries_read,
26
.name = "addr",
27
.type = QEMU_OPT_STRING,
28
.help = "pci address (virtio only)",
29
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
30
goto fail;
31
}
32
33
- /* Deprecated option boot=[on|off] */
34
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
35
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
36
- "ignored. Future versions will reject this parameter. Please "
37
- "update your scripts.\n");
38
- }
39
-
40
/* Other deprecated options */
41
if (!qtest_enabled()) {
42
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
43
diff --git a/qemu-doc.texi b/qemu-doc.texi
44
index XXXXXXX..XXXXXXX 100644
45
--- a/qemu-doc.texi
46
+++ b/qemu-doc.texi
47
@@ -XXX,XX +XXX,XX @@ deprecated.
48
49
@section System emulator command line arguments
50
51
-@subsection -drive boot=on|off (since 1.3.0)
52
-
53
-The ``boot=on|off'' option to the ``-drive'' argument is
54
-ignored. Applications should use the ``bootindex=N'' parameter
55
-to set an absolute ordering between devices instead.
56
-
57
@subsection -tdf (since 1.3.0)
58
59
The ``-tdf'' argument is ignored. The behaviour implemented
118
--
60
--
119
2.20.1
61
2.13.6
120
62
121
63
diff view generated by jsdifflib
New patch
1
1
From: Thomas Huth <thuth@redhat.com>
2
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
complained that we should keep it, so let's remove this legacy option
5
now to simplify the code quite a bit.
6
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>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
vl.c | 86 ++-------------------------------------------------------
13
qemu-doc.texi | 8 ------
14
qemu-options.hx | 19 ++-----------
15
3 files changed, 4 insertions(+), 109 deletions(-)
16
17
diff --git a/vl.c b/vl.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/vl.c
20
+++ b/vl.c
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
22
const char *boot_order = NULL;
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)
152
-
153
-The ``-hdachs'' argument is now a synonym for setting
154
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
155
-on the ``ide-hd'' device using the ``-device'' argument.
156
-The new syntax allows different settings to be provided
157
-per disk.
158
-
159
@subsection -usbdevice (since 2.10.0)
160
161
The ``-usbdevice DEV'' argument is now a synonym for setting
162
diff --git a/qemu-options.hx b/qemu-options.hx
163
index XXXXXXX..XXXXXXX 100644
164
--- a/qemu-options.hx
165
+++ b/qemu-options.hx
166
@@ -XXX,XX +XXX,XX @@ of available connectors of a given interface type.
167
@item media=@var{media}
168
This option defines the type of the media: disk or cdrom.
169
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
170
-These options have the same definition as they have in @option{-hdachs}.
171
-These parameters are deprecated, use the corresponding parameters
172
+Force disk physical geometry and the optional BIOS translation (trans=none or
173
+lba). These parameters are deprecated, use the corresponding parameters
174
of @code{-device} instead.
175
@item snapshot=@var{snapshot}
176
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
177
@@ -XXX,XX +XXX,XX @@ the raw disk image you use is not written back. You can however force
178
the write back by pressing @key{C-a s} (@pxref{disk_images}).
179
ETEXI
180
181
-DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
182
- "-hdachs c,h,s[,t]\n" \
183
- " force hard disk 0 physical geometry and the optional BIOS\n" \
184
- " translation (t=none or lba) (usually QEMU can guess them)\n",
185
- QEMU_ARCH_ALL)
186
-STEXI
187
-@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
188
-@findex -hdachs
189
-Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
190
-@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
191
-translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
192
-all those parameters. This option is deprecated, please use
193
-@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead.
194
-ETEXI
195
-
196
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
197
"-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n"
198
" [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n"
199
--
200
2.13.6
201
202
diff view generated by jsdifflib
New patch
1
From: Thomas Huth <thuth@redhat.com>
1
2
3
Looks like we forgot to announce the deprecation of these options in
4
the corresponding chapter of the qemu-doc text, so let's do that now.
5
6
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
qemu-doc.texi | 15 +++++++++++++++
12
1 file changed, 15 insertions(+)
13
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qemu-doc.texi
17
+++ b/qemu-doc.texi
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
19
The ``-drive if=scsi'' argument is replaced by the the
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
21
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
23
+
24
+The drive geometry arguments are replaced by the the geometry arguments
25
+that can be specified with the ``-device'' parameter.
26
+
27
+@subsection -drive serial=... (since 2.10.0)
28
+
29
+The drive serial argument is replaced by the the serial argument
30
+that can be specified with the ``-device'' parameter.
31
+
32
+@subsection -drive addr=... (since 2.10.0)
33
+
34
+The drive addr argument is replaced by the the addr argument
35
+that can be specified with the ``-device'' parameter.
36
+
37
@subsection -net dump (since 2.10.0)
38
39
The ``--net dump'' argument is now replaced with the
40
--
41
2.13.6
42
43
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
If we reopen a BlockDriverState and there is an option that is present
3
Signed-off-by: Fam Zheng <famz@redhat.com>
4
in bs->options but missing from the new set of options then we have to
5
return an error unless the driver is able to reset it to its default
6
value.
7
8
This patch adds a new 'mutable_opts' field to BlockDriver. This is
9
a list of runtime options that can be modified during reopen. If an
10
option in this list is unspecified on reopen then it must be reset (or
11
return an error).
12
13
Signed-off-by: Alberto Garcia <berto@igalia.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
5
---
16
include/block/block_int.h | 8 ++++++++
6
include/block/block_int.h | 1 -
17
block/file-posix.c | 6 ++++++
7
block/io.c | 18 ------------------
18
block/qcow2.c | 25 +++++++++++++++++++++++++
8
2 files changed, 19 deletions(-)
19
block/raw-format.c | 3 +++
20
4 files changed, 42 insertions(+)
21
9
22
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
23
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/block_int.h
12
--- a/include/block/block_int.h
25
+++ b/include/block/block_int.h
13
+++ b/include/block/block_int.h
26
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
27
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
28
/* List of options for creating images, terminated by name == NULL */
16
29
QemuOptsList *create_opts;
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
30
+ /*
18
-bool bdrv_requests_pending(BlockDriverState *bs);
31
+ * If this driver supports reopening images this contains a
19
32
+ * NULL-terminated list of the runtime options that can be
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
33
+ * modified. If an option in this list is unspecified during
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
34
+ * reopen then it _must_ be reset to its default value or return
22
diff --git a/block/io.c b/block/io.c
35
+ * an error.
36
+ */
37
+ const char *const *mutable_opts;
38
39
/*
40
* Returns 0 for completed check, -errno for internal errors.
41
diff --git a/block/file-posix.c b/block/file-posix.c
42
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
43
--- a/block/file-posix.c
24
--- a/block/io.c
44
+++ b/block/file-posix.c
25
+++ b/block/io.c
45
@@ -XXX,XX +XXX,XX @@ static QemuOptsList raw_runtime_opts = {
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
46
},
27
assert(old >= 1);
47
};
48
49
+static const char *const mutable_opts[] = { "x-check-cache-dropped", NULL };
50
+
51
static int raw_open_common(BlockDriverState *bs, QDict *options,
52
int bdrv_flags, int open_flags,
53
bool device, Error **errp)
54
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
55
.bdrv_set_perm = raw_set_perm,
56
.bdrv_abort_perm_update = raw_abort_perm_update,
57
.create_opts = &raw_create_opts,
58
+ .mutable_opts = mutable_opts,
59
};
60
61
/***********************************************/
62
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
63
.bdrv_reopen_abort = raw_reopen_abort,
64
.bdrv_co_create_opts = hdev_co_create_opts,
65
.create_opts = &raw_create_opts,
66
+ .mutable_opts = mutable_opts,
67
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
68
.bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
69
70
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
71
.bdrv_reopen_abort = raw_reopen_abort,
72
.bdrv_co_create_opts = hdev_co_create_opts,
73
.create_opts = &raw_create_opts,
74
+ .mutable_opts = mutable_opts,
75
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
76
77
78
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
79
.bdrv_reopen_abort = raw_reopen_abort,
80
.bdrv_co_create_opts = hdev_co_create_opts,
81
.create_opts = &raw_create_opts,
82
+ .mutable_opts = mutable_opts,
83
84
.bdrv_co_preadv = raw_co_preadv,
85
.bdrv_co_pwritev = raw_co_pwritev,
86
diff --git a/block/qcow2.c b/block/qcow2.c
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/qcow2.c
89
+++ b/block/qcow2.c
90
@@ -XXX,XX +XXX,XX @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
91
return 0;
92
}
28
}
93
29
94
+static const char *const mutable_opts[] = {
30
-/* Check if any requests are in-flight (including throttled requests) */
95
+ QCOW2_OPT_LAZY_REFCOUNTS,
31
-bool bdrv_requests_pending(BlockDriverState *bs)
96
+ QCOW2_OPT_DISCARD_REQUEST,
32
-{
97
+ QCOW2_OPT_DISCARD_SNAPSHOT,
33
- BdrvChild *child;
98
+ QCOW2_OPT_DISCARD_OTHER,
34
-
99
+ QCOW2_OPT_OVERLAP,
35
- if (atomic_read(&bs->in_flight)) {
100
+ QCOW2_OPT_OVERLAP_TEMPLATE,
36
- return true;
101
+ QCOW2_OPT_OVERLAP_MAIN_HEADER,
37
- }
102
+ QCOW2_OPT_OVERLAP_ACTIVE_L1,
38
-
103
+ QCOW2_OPT_OVERLAP_ACTIVE_L2,
39
- QLIST_FOREACH(child, &bs->children, next) {
104
+ QCOW2_OPT_OVERLAP_REFCOUNT_TABLE,
40
- if (bdrv_requests_pending(child->bs)) {
105
+ QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK,
41
- return true;
106
+ QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE,
42
- }
107
+ QCOW2_OPT_OVERLAP_INACTIVE_L1,
43
- }
108
+ QCOW2_OPT_OVERLAP_INACTIVE_L2,
44
-
109
+ QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY,
45
- return false;
110
+ QCOW2_OPT_CACHE_SIZE,
46
-}
111
+ QCOW2_OPT_L2_CACHE_SIZE,
47
-
112
+ QCOW2_OPT_L2_CACHE_ENTRY_SIZE,
48
typedef struct {
113
+ QCOW2_OPT_REFCOUNT_CACHE_SIZE,
49
Coroutine *co;
114
+ QCOW2_OPT_CACHE_CLEAN_INTERVAL,
50
BlockDriverState *bs;
115
+ NULL
116
+};
117
+
118
static QemuOptsList qcow2_runtime_opts = {
119
.name = "qcow2",
120
.head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
121
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
122
123
.create_opts = &qcow2_create_opts,
124
.strong_runtime_opts = qcow2_strong_runtime_opts,
125
+ .mutable_opts = mutable_opts,
126
.bdrv_co_check = qcow2_co_check,
127
.bdrv_amend_options = qcow2_amend_options,
128
129
diff --git a/block/raw-format.c b/block/raw-format.c
130
index XXXXXXX..XXXXXXX 100644
131
--- a/block/raw-format.c
132
+++ b/block/raw-format.c
133
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
134
bool has_size;
135
} BDRVRawState;
136
137
+static const char *const mutable_opts[] = { "offset", "size", NULL };
138
+
139
static QemuOptsList raw_runtime_opts = {
140
.name = "raw",
141
.head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head),
142
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
143
.create_opts = &raw_create_opts,
144
.bdrv_has_zero_init = &raw_has_zero_init,
145
.strong_runtime_opts = raw_strong_runtime_opts,
146
+ .mutable_opts = mutable_opts,
147
};
148
149
static void bdrv_raw_init(void)
150
--
51
--
151
2.20.1
52
2.13.6
152
53
153
54
diff view generated by jsdifflib
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Eric Blake <eblake@redhat.com>
2
Reviewed-by: Fam Zheng <famz@redhat.com>
3
---
3
---
4
tests/qemu-iotests/232 | 31 +++++++++++++++++++++++++++++++
4
block/io.c | 6 ++++++
5
tests/qemu-iotests/232.out | 20 ++++++++++++++++++++
5
1 file changed, 6 insertions(+)
6
2 files changed, 51 insertions(+)
7
6
8
diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232
7
diff --git a/block/io.c b/block/io.c
9
index XXXXXXX..XXXXXXX 100755
8
index XXXXXXX..XXXXXXX 100644
10
--- a/tests/qemu-iotests/232
9
--- a/block/io.c
11
+++ b/tests/qemu-iotests/232
10
+++ b/block/io.c
12
@@ -XXX,XX +XXX,XX @@ status=1    # failure is the default!
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
13
_cleanup()
12
BdrvNextIterator it;
14
{
13
GSList *aio_ctxs = NULL, *ctx;
15
_cleanup_test_img
14
16
+ rm -f $TEST_IMG.[01234]
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
17
}
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
18
trap "_cleanup; exit \$status" 0 1 2 3 15
17
+ * nodes in several different AioContexts, so make sure we're in the main
19
18
+ * context. */
20
@@ -XXX,XX +XXX,XX @@ run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,a
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
21
run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on
22
run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0
23
24
+echo
25
+echo "=== Try commit to backing file with auto-read-only ==="
26
+echo
27
+
20
+
28
+TEST_IMG="$TEST_IMG.0" _make_test_img $size
21
block_job_pause_all();
29
+TEST_IMG="$TEST_IMG.1" _make_test_img $size
22
30
+TEST_IMG="$TEST_IMG.2" _make_test_img $size
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
31
+TEST_IMG="$TEST_IMG.3" _make_test_img $size
32
+TEST_IMG="$TEST_IMG.4" _make_test_img $size
33
+
34
+(cat <<EOF
35
+{"execute":"qmp_capabilities"}
36
+{"execute":"block-commit",
37
+ "arguments":{"device":"format-4", "top-node": "format-2", "base-node":"format-0", "job-id":"job0"}}
38
+EOF
39
+sleep 1
40
+echo '{"execute":"quit"}'
41
+) | $QEMU -qmp stdio -nographic -nodefaults \
42
+ -blockdev file,node-name=file-0,filename=$TEST_IMG.0,auto-read-only=on \
43
+ -blockdev qcow2,node-name=format-0,file=file-0,read-only=on \
44
+ -blockdev file,node-name=file-1,filename=$TEST_IMG.1,auto-read-only=on \
45
+ -blockdev qcow2,node-name=format-1,file=file-1,read-only=on,backing=format-0 \
46
+ -blockdev file,node-name=file-2,filename=$TEST_IMG.2,auto-read-only=on \
47
+ -blockdev qcow2,node-name=format-2,file=file-2,read-only=on,backing=format-1 \
48
+ -blockdev file,node-name=file-3,filename=$TEST_IMG.3,auto-read-only=on \
49
+ -blockdev qcow2,node-name=format-3,file=file-3,read-only=on,backing=format-2 \
50
+ -blockdev file,node-name=file-4,filename=$TEST_IMG.4,auto-read-only=on \
51
+ -blockdev qcow2,node-name=format-4,file=file-4,read-only=on,backing=format-3 |
52
+ _filter_qmp
53
+
54
# success, all done
55
echo "*** done"
56
rm -f $seq.full
57
diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out
58
index XXXXXXX..XXXXXXX 100644
59
--- a/tests/qemu-iotests/232.out
60
+++ b/tests/qemu-iotests/232.out
61
@@ -XXX,XX +XXX,XX @@ QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read
62
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
63
node0: TEST_DIR/t.IMGFMT (file, read-only)
64
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
65
+
66
+=== Try commit to backing file with auto-read-only ===
67
+
68
+Formatting 'TEST_DIR/t.IMGFMT.0', fmt=IMGFMT size=134217728
69
+Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=134217728
70
+Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728
71
+Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=134217728
72
+Formatting 'TEST_DIR/t.IMGFMT.4', fmt=IMGFMT size=134217728
73
+QMP_VERSION
74
+{"return": {}}
75
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
76
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
77
+{"return": {}}
78
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
79
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
80
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}}
81
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
82
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
83
+{"return": {}}
84
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
85
*** done
86
--
24
--
87
2.20.1
25
2.13.6
88
26
89
27
diff view generated by jsdifflib
1
Until now, with auto-read-only=on we tried to open the file read-write
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
first and if that failed, read-only was tried. This is actually not good
2
and also doesn't notify other parent nodes of children, which both means
3
enough for libvirt, which gives QEMU SELinux permissions for read-write
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
only as soon as it actually intends to write to the image. So we need to
4
is providing useful functionality only on a single node.
5
be able to switch between read-only and read-write at runtime.
6
5
7
This patch makes auto-read-only dynamic, i.e. the file is opened
6
To keep things consistent, we also shouldn't call the block driver
8
read-only as long as no user of the node has requested write
7
callbacks recursively.
9
permissions, but it is automatically reopened read-write as soon as the
10
first writer is attached. Conversely, if the last writer goes away, the
11
file is reopened read-only again.
12
8
13
bs->read_only is no longer set for auto-read-only=on files even if the
9
A proper recursive drain version that provides an actually working
14
file descriptor is opened read-only because it will be transparently
10
drained section for child nodes will be introduced later.
15
upgraded as soon as a writer is attached. This changes the output of
16
qemu-iotests 232.
17
11
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
19
---
14
---
20
block/file-posix.c | 36 +++++++++++++++++-------------------
15
block/io.c | 16 +++++++++-------
21
tests/qemu-iotests/232.out | 12 ++++++------
16
1 file changed, 9 insertions(+), 7 deletions(-)
22
2 files changed, 23 insertions(+), 25 deletions(-)
23
17
24
diff --git a/block/file-posix.c b/block/file-posix.c
18
diff --git a/block/io.c b/block/io.c
25
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
26
--- a/block/file-posix.c
20
--- a/block/io.c
27
+++ b/block/file-posix.c
21
+++ b/block/io.c
28
@@ -XXX,XX +XXX,XX @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
22
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
23
}
24
25
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
26
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
27
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
28
{
29
BdrvChild *child, *tmp;
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
32
bdrv_coroutine_enter(bs, data.co);
33
BDRV_POLL_WHILE(bs, !data.done);
34
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
36
- bdrv_drain_invoke(child->bs, begin);
37
+ if (recursive) {
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
39
+ bdrv_drain_invoke(child->bs, begin, true);
40
+ }
29
}
41
}
30
}
42
}
31
43
32
-static void raw_parse_flags(int bdrv_flags, int *open_flags)
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
33
+static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers)
45
bdrv_parent_drained_begin(bs);
34
{
46
}
35
+ bool read_write = false;
47
36
assert(open_flags != NULL);
48
- bdrv_drain_invoke(bs, true);
37
49
+ bdrv_drain_invoke(bs, true, false);
38
*open_flags |= O_BINARY;
50
bdrv_drain_recurse(bs);
39
*open_flags &= ~O_ACCMODE;
40
- if (bdrv_flags & BDRV_O_RDWR) {
41
+
42
+ if (bdrv_flags & BDRV_O_AUTO_RDONLY) {
43
+ read_write = has_writers;
44
+ } else if (bdrv_flags & BDRV_O_RDWR) {
45
+ read_write = true;
46
+ }
47
+
48
+ if (read_write) {
49
*open_flags |= O_RDWR;
50
} else {
51
*open_flags |= O_RDONLY;
52
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
53
false);
54
55
s->open_flags = open_flags;
56
- raw_parse_flags(bdrv_flags, &s->open_flags);
57
+ raw_parse_flags(bdrv_flags, &s->open_flags, false);
58
59
s->fd = -1;
60
fd = qemu_open(filename, s->open_flags, 0644);
61
ret = fd < 0 ? -errno : 0;
62
63
- if (ret == -EACCES || ret == -EROFS) {
64
- /* Try to degrade to read-only, but if it doesn't work, still use the
65
- * normal error message. */
66
- if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
67
- bdrv_flags &= ~BDRV_O_RDWR;
68
- raw_parse_flags(bdrv_flags, &s->open_flags);
69
- assert(!(s->open_flags & O_CREAT));
70
- fd = qemu_open(filename, s->open_flags);
71
- ret = fd < 0 ? -errno : 0;
72
- }
73
- }
74
-
75
if (ret < 0) {
76
error_setg_errno(errp, -ret, "Could not open '%s'", filename);
77
if (ret == -EROFS) {
78
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
79
}
51
}
80
52
81
static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
53
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
82
- int *open_flags, bool force_dup,
83
+ int *open_flags, uint64_t perm, bool force_dup,
84
Error **errp)
85
{
86
BDRVRawState *s = bs->opaque;
87
int fd = -1;
88
int ret;
89
+ bool has_writers = perm &
90
+ (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_RESIZE);
91
int fcntl_flags = O_APPEND | O_NONBLOCK;
92
#ifdef O_NOATIME
93
fcntl_flags |= O_NOATIME;
94
@@ -XXX,XX +XXX,XX @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
95
*open_flags |= O_NONBLOCK;
96
}
54
}
97
55
98
- raw_parse_flags(flags, open_flags);
56
/* Re-enable things in child-to-parent order */
99
+ raw_parse_flags(flags, open_flags, has_writers);
57
- bdrv_drain_invoke(bs, false);
100
58
+ bdrv_drain_invoke(bs, false, false);
101
#ifdef O_ASYNC
59
bdrv_parent_drained_end(bs);
102
/* Not all operating systems have O_ASYNC, and those that don't
60
aio_enable_external(bdrv_get_aio_context(bs));
103
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
61
}
104
qemu_opts_to_qdict(opts, state->options);
62
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
105
63
aio_context_acquire(aio_context);
106
rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
64
aio_disable_external(aio_context);
107
- true, &local_err);
65
bdrv_parent_drained_begin(bs);
108
+ state->perm, true, &local_err);
66
- bdrv_drain_invoke(bs, true);
109
if (local_err) {
67
+ bdrv_drain_invoke(bs, true, true);
110
error_propagate(errp, local_err);
68
aio_context_release(aio_context);
111
ret = -1;
69
112
@@ -XXX,XX +XXX,XX @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
70
if (!g_slist_find(aio_ctxs, aio_context)) {
113
s->perm_change_fd = rs->fd;
71
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
114
} else {
72
115
/* We may need a new fd if auto-read-only switches the mode */
73
/* Re-enable things in child-to-parent order */
116
- ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags,
74
aio_context_acquire(aio_context);
117
+ ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm,
75
- bdrv_drain_invoke(bs, false);
118
false, errp);
76
+ bdrv_drain_invoke(bs, false, true);
119
if (ret < 0) {
77
bdrv_parent_drained_end(bs);
120
return ret;
78
aio_enable_external(aio_context);
121
diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out
79
aio_context_release(aio_context);
122
index XXXXXXX..XXXXXXX 100644
123
--- a/tests/qemu-iotests/232.out
124
+++ b/tests/qemu-iotests/232.out
125
@@ -XXX,XX +XXX,XX @@ NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
126
NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
127
128
QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
129
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
130
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
131
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
132
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
133
134
QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
135
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
136
-NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
137
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
138
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
139
140
=== -blockdev with read-write image: read-only/auto-read-only combinations ===
141
142
@@ -XXX,XX +XXX,XX @@ node0: TEST_DIR/t.IMGFMT (file, read-only)
143
node0: TEST_DIR/t.IMGFMT (file, read-only)
144
145
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
146
-node0: TEST_DIR/t.IMGFMT (file, read-only)
147
+node0: TEST_DIR/t.IMGFMT (file)
148
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
149
150
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
151
-node0: TEST_DIR/t.IMGFMT (file, read-only)
152
+node0: TEST_DIR/t.IMGFMT (file)
153
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
154
155
=== Try commit to backing file with auto-read-only ===
156
--
80
--
157
2.20.1
81
2.13.6
158
82
159
83
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.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
Signed-off-by: Alberto Garcia <berto@igalia.com>
6
Also, add a backing file to the test node to test whether the operations
7
work recursively.
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
10
---
6
block/stream.c | 21 +++++++++++++++++++++
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
7
1 file changed, 21 insertions(+)
12
1 file changed, 62 insertions(+), 7 deletions(-)
8
13
9
diff --git a/block/stream.c b/block/stream.c
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
10
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
11
--- a/block/stream.c
16
--- a/tests/test-bdrv-drain.c
12
+++ b/block/stream.c
17
+++ b/tests/test-bdrv-drain.c
13
@@ -XXX,XX +XXX,XX @@ typedef struct StreamBlockJob {
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
14
BlockdevOnError on_error;
19
15
char *backing_file_str;
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
16
bool bs_read_only;
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
17
+ bool chain_frozen;
22
+
18
} StreamBlockJob;
23
+ .bdrv_child_perm = bdrv_format_default_perms,
19
24
};
20
static int coroutine_fn stream_populate(BlockBackend *blk,
25
21
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_populate(BlockBackend *blk,
26
static void aio_ret_cb(void *opaque, int ret)
22
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
28
*aio_ret = ret;
23
}
29
}
24
30
25
+static void stream_abort(Job *job)
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)
26
+{
38
+{
27
+ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
39
+ switch (drain_type) {
28
+
40
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
29
+ if (s->chain_frozen) {
41
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
30
+ BlockJob *bjob = &s->common;
42
+ default: g_assert_not_reached();
31
+ bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->base);
32
+ }
43
+ }
33
+}
44
+}
34
+
45
+
35
static int stream_prepare(Job *job)
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)
36
{
56
{
37
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
57
BlockBackend *blk;
38
@@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job)
58
- BlockDriverState *bs;
39
Error *local_err = NULL;
59
- BDRVTestState *s;
40
int ret = 0;
60
+ BlockDriverState *bs, *backing;
41
61
+ BDRVTestState *s, *backing_s;
42
+ bdrv_unfreeze_backing_chain(bs, base);
62
BlockAIOCB *acb;
43
+ s->chain_frozen = false;
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);
44
+
72
+
45
if (bs->backing) {
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
46
const char *base_id = NULL, *base_fmt = NULL;
74
g_assert_cmpint(s->drain_count, ==, 0);
47
if (base) {
75
- bdrv_drain_all_begin();
48
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
49
.free = block_job_free,
50
.run = stream_run,
51
.prepare = stream_prepare,
52
+ .abort = stream_abort,
53
.clean = stream_clean,
54
.user_resume = block_job_user_resume,
55
.drain = block_job_drain,
56
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
57
&error_abort);
58
}
59
60
+ if (bdrv_freeze_backing_chain(bs, base, errp) < 0) {
61
+ job_early_fail(&s->common.job);
62
+ goto fail;
63
+ }
64
+
77
+
65
s->base = base;
78
+ do_drain_begin(drain_type, bs);
66
s->backing_file_str = g_strdup(backing_file_str);
79
+
67
s->bs_read_only = bs_read_only;
80
g_assert_cmpint(s->drain_count, ==, 1);
68
+ s->chain_frozen = true;
81
- bdrv_drain_all_end();
69
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
70
s->on_error = on_error;
83
+
71
trace_stream_start(bs, base, s);
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
}
72
--
136
--
73
2.20.1
137
2.13.6
74
138
75
139
diff view generated by jsdifflib
1
Using a different read-only setting for bs->open_flags than for the
1
This is currently only working correctly for bdrv_drain(), not for
2
flags to the driver's open function is just inconsistent and a bad idea.
2
bdrv_drain_all(). Leave a comment for the drain_all case, we'll address
3
After this patch, the temporary snapshot keeps being opened read-only if
3
it later.
4
read-only=on,snapshot=on is passed.
5
6
If we wanted to change this behaviour to make only the orginal image
7
file read-only, but the temporary overlay read-write (as the comment in
8
the removed code suggests), that change would have to be made in
9
bdrv_temp_snapshot_options() (where the comment suggests otherwise).
10
11
Addressing this inconsistency before introducing dynamic auto-read-only
12
is important because otherwise we would immediately try to reopen the
13
temporary overlay even though the file is already unlinked.
14
4
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
6
---
17
block.c | 7 -------
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
18
tests/qemu-iotests/051 | 7 +++++++
8
1 file changed, 45 insertions(+)
19
tests/qemu-iotests/051.out | 9 +++++++++
20
tests/qemu-iotests/051.pc.out | 9 +++++++++
21
4 files changed, 25 insertions(+), 7 deletions(-)
22
9
23
diff --git a/block.c b/block.c
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
24
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
25
--- a/block.c
12
--- a/tests/test-bdrv-drain.c
26
+++ b/block.c
13
+++ b/tests/test-bdrv-drain.c
27
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
28
*/
15
test_drv_cb_common(BDRV_DRAIN, false);
29
open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL);
30
31
- /*
32
- * Snapshots should be writable.
33
- */
34
- if (flags & BDRV_O_TEMPORARY) {
35
- open_flags |= BDRV_O_RDWR;
36
- }
37
-
38
return open_flags;
39
}
16
}
40
17
41
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
42
index XXXXXXX..XXXXXXX 100755
19
+{
43
--- a/tests/qemu-iotests/051
20
+ BlockBackend *blk;
44
+++ b/tests/qemu-iotests/051
21
+ BlockDriverState *bs, *backing;
45
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io
46
# Using snapshot=on with a non-existent TMPDIR
47
TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on
48
49
+# Using snapshot=on together with read-only=on
50
+echo "info block" |
51
+ run_qemu -drive file="$TEST_IMG",snapshot=on,read-only=on,if=none,id=$device_id |
52
+ _filter_qemu_io |
53
+ sed -e 's#"/[^"]*/vl\.[A-Za-z]\{6\}"#SNAPSHOT_PATH#g'
54
+
22
+
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
25
+ &error_abort);
26
+ blk_insert_bs(blk, bs, &error_abort);
55
+
27
+
56
# success, all done
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
57
echo "*** done"
29
+ bdrv_set_backing_hd(bs, backing, &error_abort);
58
rm -f $seq.full
59
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
60
index XXXXXXX..XXXXXXX 100644
61
--- a/tests/qemu-iotests/051.out
62
+++ b/tests/qemu-iotests/051.out
63
@@ -XXX,XX +XXX,XX @@ read 4096/4096 bytes at offset 0
64
Testing: -drive driver=null-co,snapshot=on
65
QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory
66
67
+Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0
68
+QEMU X.Y.Z monitor - type 'help' for more information
69
+(qemu) info block
70
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, read-only)
71
+ Removable device: not locked, tray closed
72
+ Cache mode: writeback, ignore flushes
73
+ Backing file: TEST_DIR/t.qcow2 (chain depth: 1)
74
+(qemu) quit
75
+
30
+
76
*** done
31
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
77
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
32
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
78
index XXXXXXX..XXXXXXX 100644
79
--- a/tests/qemu-iotests/051.pc.out
80
+++ b/tests/qemu-iotests/051.pc.out
81
@@ -XXX,XX +XXX,XX @@ read 4096/4096 bytes at offset 0
82
Testing: -drive driver=null-co,snapshot=on
83
QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory
84
85
+Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0
86
+QEMU X.Y.Z monitor - type 'help' for more information
87
+(qemu) info block
88
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, read-only)
89
+ Removable device: not locked, tray closed
90
+ Cache mode: writeback, ignore flushes
91
+ Backing file: TEST_DIR/t.qcow2 (chain depth: 1)
92
+(qemu) quit
93
+
33
+
94
*** done
34
+ do_drain_begin(drain_type, bs);
35
+
36
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
37
+ g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
38
+
39
+ do_drain_end(drain_type, bs);
40
+
41
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
43
+
44
+ bdrv_unref(backing);
45
+ bdrv_unref(bs);
46
+ blk_unref(blk);
47
+}
48
+
49
+static void test_quiesce_drain_all(void)
50
+{
51
+ // XXX drain_all doesn't quiesce
52
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
53
+}
54
+
55
+static void test_quiesce_drain(void)
56
+{
57
+ test_quiesce_common(BDRV_DRAIN, false);
58
+}
59
+
60
int main(int argc, char **argv)
61
{
62
bdrv_init();
63
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
64
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
65
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
66
67
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
68
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
69
+
70
return g_test_run();
71
}
95
--
72
--
96
2.20.1
73
2.13.6
97
74
98
75
diff view generated by jsdifflib
1
We'll want to access the file descriptor in the reopen_state while
1
Block jobs already paused themselves when their main BlockBackend
2
processing permission changes in the context of the repoen.
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.
5
6
This implements .drained_begin/end callbacks in child_job in order to
7
consider all block nodes related to the job, and removes the
8
BlockBackend callbacks which are unnecessary now because the root of the
9
job main BlockBackend is always referenced with a child_job, too.
3
10
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
12
---
6
block/file-posix.c | 10 ++++++++++
13
blockjob.c | 22 +++++++++-------------
7
1 file changed, 10 insertions(+)
14
1 file changed, 9 insertions(+), 13 deletions(-)
8
15
9
diff --git a/block/file-posix.c b/block/file-posix.c
16
diff --git a/blockjob.c b/blockjob.c
10
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
11
--- a/block/file-posix.c
18
--- a/blockjob.c
12
+++ b/block/file-posix.c
19
+++ b/blockjob.c
13
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState {
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
14
uint64_t locked_perm;
21
job->id);
15
uint64_t locked_shared_perm;
16
17
+ BDRVReopenState *reopen_state;
18
+
19
#ifdef CONFIG_XFS
20
bool is_xfs:1;
21
#endif
22
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
23
}
24
}
25
26
+ s->reopen_state = state;
27
out:
28
qemu_opts_del(opts);
29
return ret;
30
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
31
32
g_free(state->opaque);
33
state->opaque = NULL;
34
+
35
+ assert(s->reopen_state == state);
36
+ s->reopen_state = NULL;
37
}
22
}
38
23
39
24
-static const BdrvChildRole child_job = {
40
static void raw_reopen_abort(BDRVReopenState *state)
25
- .get_parent_desc = child_job_get_parent_desc,
26
- .stay_at_node = true,
27
-};
28
-
29
-static void block_job_drained_begin(void *opaque)
30
+static void child_job_drained_begin(BdrvChild *c)
41
{
31
{
42
BDRVRawReopenState *rs = state->opaque;
32
- BlockJob *job = opaque;
43
+ BDRVRawState *s = state->bs->opaque;
33
+ BlockJob *job = c->opaque;
44
34
block_job_pause(job);
45
/* nothing to do if NULL, we didn't get far enough */
46
if (rs == NULL) {
47
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_abort(BDRVReopenState *state)
48
}
49
g_free(state->opaque);
50
state->opaque = NULL;
51
+
52
+ assert(s->reopen_state == state);
53
+ s->reopen_state = NULL;
54
}
35
}
55
36
56
static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd)
37
-static void block_job_drained_end(void *opaque)
38
+static void child_job_drained_end(BdrvChild *c)
39
{
40
- BlockJob *job = opaque;
41
+ BlockJob *job = c->opaque;
42
block_job_resume(job);
43
}
44
45
-static const BlockDevOps block_job_dev_ops = {
46
- .drained_begin = block_job_drained_begin,
47
- .drained_end = block_job_drained_end,
48
+static const BdrvChildRole child_job = {
49
+ .get_parent_desc = child_job_get_parent_desc,
50
+ .drained_begin = child_job_drained_begin,
51
+ .drained_end = child_job_drained_end,
52
+ .stay_at_node = true,
53
};
54
55
void block_job_remove_all_bdrv(BlockJob *job)
56
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
57
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
58
bs->job = job;
59
60
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
61
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
62
63
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
57
--
64
--
58
2.20.1
65
2.13.6
59
66
60
67
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
Block jobs must be paused if any of the involved nodes are drained.
2
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
4
---
6
block/mirror.c | 8 ++++++++
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
7
1 file changed, 8 insertions(+)
6
1 file changed, 121 insertions(+)
8
7
9
diff --git a/block/mirror.c b/block/mirror.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
10
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
11
--- a/block/mirror.c
10
--- a/tests/test-bdrv-drain.c
12
+++ b/block/mirror.c
11
+++ b/tests/test-bdrv-drain.c
13
@@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job)
12
@@ -XXX,XX +XXX,XX @@
14
}
13
15
s->prepared = true;
14
#include "qemu/osdep.h"
16
15
#include "block/block.h"
17
+ if (bdrv_chain_contains(src, target_bs)) {
16
+#include "block/blockjob_int.h"
18
+ bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
17
#include "sysemu/block-backend.h"
18
#include "qapi/error.h"
19
20
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
21
test_quiesce_common(BDRV_DRAIN, false);
22
}
23
24
+
25
+typedef struct TestBlockJob {
26
+ BlockJob common;
27
+ bool should_complete;
28
+} TestBlockJob;
29
+
30
+static void test_job_completed(BlockJob *job, void *opaque)
31
+{
32
+ block_job_completed(job, 0);
33
+}
34
+
35
+static void coroutine_fn test_job_start(void *opaque)
36
+{
37
+ TestBlockJob *s = opaque;
38
+
39
+ while (!s->should_complete) {
40
+ block_job_sleep_ns(&s->common, 100000);
19
+ }
41
+ }
20
+
42
+
21
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
22
44
+}
23
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
24
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
25
goto fail;
26
}
27
}
28
+
45
+
29
+ if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
46
+static void test_job_complete(BlockJob *job, Error **errp)
30
+ goto fail;
47
+{
31
+ }
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
32
}
49
+ s->should_complete = true;
33
50
+}
34
QTAILQ_INIT(&s->ops_in_flight);
51
+
52
+BlockJobDriver test_job_driver = {
53
+ .instance_size = sizeof(TestBlockJob),
54
+ .start = test_job_start,
55
+ .complete = test_job_complete,
56
+};
57
+
58
+static void test_blockjob_common(enum drain_type drain_type)
59
+{
60
+ BlockBackend *blk_src, *blk_target;
61
+ BlockDriverState *src, *target;
62
+ BlockJob *job;
63
+ int ret;
64
+
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
66
+ &error_abort);
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
68
+ blk_insert_bs(blk_src, src, &error_abort);
69
+
70
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
71
+ &error_abort);
72
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
73
+ blk_insert_bs(blk_target, target, &error_abort);
74
+
75
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
76
+ 0, NULL, NULL, &error_abort);
77
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
78
+ block_job_start(job);
79
+
80
+ g_assert_cmpint(job->pause_count, ==, 0);
81
+ g_assert_false(job->paused);
82
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
83
+
84
+ do_drain_begin(drain_type, src);
85
+
86
+ if (drain_type == BDRV_DRAIN_ALL) {
87
+ /* bdrv_drain_all() drains both src and target, and involves an
88
+ * additional block_job_pause_all() */
89
+ g_assert_cmpint(job->pause_count, ==, 3);
90
+ } else {
91
+ g_assert_cmpint(job->pause_count, ==, 1);
92
+ }
93
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
94
+ /* g_assert_true(job->paused); */
95
+ g_assert_false(job->busy); /* The job is paused */
96
+
97
+ do_drain_end(drain_type, src);
98
+
99
+ g_assert_cmpint(job->pause_count, ==, 0);
100
+ g_assert_false(job->paused);
101
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
102
+
103
+ do_drain_begin(drain_type, target);
104
+
105
+ if (drain_type == BDRV_DRAIN_ALL) {
106
+ /* bdrv_drain_all() drains both src and target, and involves an
107
+ * additional block_job_pause_all() */
108
+ g_assert_cmpint(job->pause_count, ==, 3);
109
+ } else {
110
+ g_assert_cmpint(job->pause_count, ==, 1);
111
+ }
112
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
113
+ /* g_assert_true(job->paused); */
114
+ g_assert_false(job->busy); /* The job is paused */
115
+
116
+ do_drain_end(drain_type, target);
117
+
118
+ g_assert_cmpint(job->pause_count, ==, 0);
119
+ g_assert_false(job->paused);
120
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
121
+
122
+ ret = block_job_complete_sync(job, &error_abort);
123
+ g_assert_cmpint(ret, ==, 0);
124
+
125
+ blk_unref(blk_src);
126
+ blk_unref(blk_target);
127
+ bdrv_unref(src);
128
+ bdrv_unref(target);
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
}
35
--
153
--
36
2.20.1
154
2.13.6
37
155
38
156
diff view generated by jsdifflib
1
The way that reopen interacts with permission changes has one big
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
problem: Both operations are recursive, and the permissions are changes
2
so we don't need an additional block_job_pause_all() call.
3
for each node in the reopen queue.
4
5
For a simple graph that consists just of parent and child,
6
.bdrv_check_perm will be called twice for the child, once recursively
7
when adjusting the permissions of parent, and once again when the child
8
itself is reopened.
9
10
Even worse, the first .bdrv_check_perm call happens before
11
.bdrv_reopen_prepare was called for the child and the second one is
12
called afterwards.
13
14
Making sure that .bdrv_check_perm (and the other permission callbacks)
15
are called only once is hard. We can cope with multiple calls right now,
16
but as soon as file-posix gets a dynamic auto-read-only that may need to
17
open a new file descriptor, we get the additional requirement that all
18
of them are after the .bdrv_reopen_prepare call.
19
20
So reorder things in bdrv_reopen_multiple() to first call
21
.bdrv_reopen_prepare for all involved nodes and only then adjust
22
permissions.
23
3
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
5
---
26
block.c | 35 ++++++++++++++++++++++++-----------
6
block/io.c | 4 ----
27
1 file changed, 24 insertions(+), 11 deletions(-)
7
tests/test-bdrv-drain.c | 10 ++++------
8
2 files changed, 4 insertions(+), 10 deletions(-)
28
9
29
diff --git a/block.c b/block.c
10
diff --git a/block/io.c b/block/io.c
30
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
12
--- a/block/io.c
32
+++ b/block.c
13
+++ b/block/io.c
33
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
14
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
34
15
* context. */
35
typedef struct BlockReopenQueueEntry {
16
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
36
bool prepared;
17
37
+ bool perms_checked;
18
- block_job_pause_all();
38
BDRVReopenState state;
39
QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry;
40
} BlockReopenQueueEntry;
41
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
42
bs_entry->prepared = true;
43
}
44
45
+ QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
46
+ BDRVReopenState *state = &bs_entry->state;
47
+ ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
48
+ state->shared_perm, NULL, errp);
49
+ if (ret < 0) {
50
+ goto cleanup_perm;
51
+ }
52
+ bs_entry->perms_checked = true;
53
+ }
54
+
55
/* If we reach this point, we have success and just need to apply the
56
* changes
57
*/
58
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
59
}
60
61
ret = 0;
62
+cleanup_perm:
63
+ QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
64
+ BDRVReopenState *state = &bs_entry->state;
65
+
66
+ if (!bs_entry->perms_checked) {
67
+ continue;
68
+ }
69
70
+ if (ret == 0) {
71
+ bdrv_set_perm(state->bs, state->perm, state->shared_perm);
72
+ } else {
73
+ bdrv_abort_perm_update(state->bs);
74
+ }
75
+ }
76
cleanup:
77
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
78
if (ret) {
79
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
80
} while ((entry = qdict_next(reopen_state->options, entry)));
81
}
82
83
- ret = bdrv_check_perm(reopen_state->bs, queue, reopen_state->perm,
84
- reopen_state->shared_perm, NULL, errp);
85
- if (ret < 0) {
86
- goto error;
87
- }
88
-
19
-
89
ret = 0;
20
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
90
21
AioContext *aio_context = bdrv_get_aio_context(bs);
91
/* Restore the original reopen_state->options QDict */
22
92
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
23
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
93
24
aio_enable_external(aio_context);
94
bdrv_refresh_limits(bs, NULL);
25
aio_context_release(aio_context);
95
96
- bdrv_set_perm(reopen_state->bs, reopen_state->perm,
97
- reopen_state->shared_perm);
98
-
99
new_can_write =
100
!bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
101
if (!old_can_write && new_can_write && drv->bdrv_reopen_bitmaps_rw) {
102
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
103
if (drv->bdrv_reopen_abort) {
104
drv->bdrv_reopen_abort(reopen_state);
105
}
26
}
106
-
27
-
107
- bdrv_abort_perm_update(reopen_state->bs);
28
- block_job_resume_all();
108
}
29
}
109
30
110
31
void bdrv_drain_all(void)
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/tests/test-bdrv-drain.c
35
+++ b/tests/test-bdrv-drain.c
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
37
do_drain_begin(drain_type, src);
38
39
if (drain_type == BDRV_DRAIN_ALL) {
40
- /* bdrv_drain_all() drains both src and target, and involves an
41
- * additional block_job_pause_all() */
42
- g_assert_cmpint(job->pause_count, ==, 3);
43
+ /* bdrv_drain_all() drains both src and target */
44
+ g_assert_cmpint(job->pause_count, ==, 2);
45
} else {
46
g_assert_cmpint(job->pause_count, ==, 1);
47
}
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
49
do_drain_begin(drain_type, target);
50
51
if (drain_type == BDRV_DRAIN_ALL) {
52
- /* bdrv_drain_all() drains both src and target, and involves an
53
- * additional block_job_pause_all() */
54
- g_assert_cmpint(job->pause_count, ==, 3);
55
+ /* bdrv_drain_all() drains both src and target */
56
+ g_assert_cmpint(job->pause_count, ==, 2);
57
} else {
58
g_assert_cmpint(job->pause_count, ==, 1);
59
}
111
--
60
--
112
2.20.1
61
2.13.6
113
62
114
63
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
aio_disable_external() to the outermost drain section, but the block
3
driver callbacks are always called. bdrv_do_drained_end() must match
4
this behaviour, otherwise nodes stay drained even if begin/end calls
5
were balanced.
2
6
3
The bdrv_reopen_queue() function is used to create a queue with
4
the BDSs that are going to be reopened and their new options. Once
5
the queue is ready bdrv_reopen_multiple() is called to perform the
6
operation.
7
8
The original options from each one of the BDSs are kept, with the new
9
options passed to bdrv_reopen_queue() applied on top of them.
10
11
For "x-blockdev-reopen" we want a function that behaves much like
12
"blockdev-add". We want to ignore the previous set of options so that
13
only the ones actually specified by the user are applied, with the
14
rest having their default values.
15
16
One of the things that we need is a way to tell bdrv_reopen_queue()
17
whether we want to keep the old set of options or not, and that's what
18
this patch does. All current callers are setting this new parameter to
19
true and x-blockdev-reopen will set it to false.
20
21
Signed-off-by: Alberto Garcia <berto@igalia.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
8
---
24
include/block/block.h | 3 ++-
9
block/io.c | 12 +++++++-----
25
block.c | 34 +++++++++++++++++++---------------
10
1 file changed, 7 insertions(+), 5 deletions(-)
26
block/replication.c | 4 ++--
27
qemu-io-cmds.c | 2 +-
28
4 files changed, 24 insertions(+), 19 deletions(-)
29
11
30
diff --git a/include/block/block.h b/include/block/block.h
12
diff --git a/block/io.c b/block/io.c
31
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
32
--- a/include/block/block.h
14
--- a/block/io.c
33
+++ b/include/block/block.h
15
+++ b/block/io.c
34
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
35
BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
17
36
int flags, Error **errp);
18
void bdrv_drained_end(BlockDriverState *bs)
37
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
38
- BlockDriverState *bs, QDict *options);
39
+ BlockDriverState *bs, QDict *options,
40
+ bool keep_old_opts);
41
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp);
42
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
43
Error **errp);
44
diff --git a/block.c b/block.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/block.c
47
+++ b/block.c
48
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
49
QDict *options,
50
const BdrvChildRole *role,
51
QDict *parent_options,
52
- int parent_flags)
53
+ int parent_flags,
54
+ bool keep_old_opts)
55
{
19
{
56
assert(bs != NULL);
20
+ int old_quiesce_counter;
57
21
+
58
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
22
if (qemu_in_coroutine()) {
59
*/
23
bdrv_co_yield_to_drain(bs, false);
60
24
return;
61
/* Old explicitly set values (don't overwrite by inherited value) */
62
- if (bs_entry) {
63
- old_options = qdict_clone_shallow(bs_entry->state.explicit_options);
64
- } else {
65
- old_options = qdict_clone_shallow(bs->explicit_options);
66
+ if (bs_entry || keep_old_opts) {
67
+ old_options = qdict_clone_shallow(bs_entry ?
68
+ bs_entry->state.explicit_options :
69
+ bs->explicit_options);
70
+ bdrv_join_options(bs, options, old_options);
71
+ qobject_unref(old_options);
72
}
25
}
73
- bdrv_join_options(bs, options, old_options);
26
assert(bs->quiesce_counter > 0);
74
- qobject_unref(old_options);
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
75
28
- return;
76
explicit_options = qdict_clone_shallow(options);
29
- }
77
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
78
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
31
79
flags = bdrv_get_flags(bs);
32
/* Re-enable things in child-to-parent order */
80
}
33
bdrv_drain_invoke(bs, false, false);
81
34
- bdrv_parent_drained_end(bs);
82
- /* Old values are used for options that aren't set yet */
35
- aio_enable_external(bdrv_get_aio_context(bs));
83
- old_options = qdict_clone_shallow(bs->options);
36
+ if (old_quiesce_counter == 1) {
84
- bdrv_join_options(bs, options, old_options);
37
+ bdrv_parent_drained_end(bs);
85
- qobject_unref(old_options);
38
+ aio_enable_external(bdrv_get_aio_context(bs));
86
+ if (keep_old_opts) {
87
+ /* Old values are used for options that aren't set yet */
88
+ old_options = qdict_clone_shallow(bs->options);
89
+ bdrv_join_options(bs, options, old_options);
90
+ qobject_unref(old_options);
91
+ }
39
+ }
92
93
/* We have the final set of options so let's update the flags */
94
options_copy = qdict_clone_shallow(options);
95
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
96
g_free(child_key_dot);
97
98
bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options,
99
- child->role, options, flags);
100
+ child->role, options, flags, keep_old_opts);
101
}
102
103
return bs_queue;
104
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
105
106
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
107
BlockDriverState *bs,
108
- QDict *options)
109
+ QDict *options, bool keep_old_opts)
110
{
111
- return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0);
112
+ return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0,
113
+ keep_old_opts);
114
}
40
}
115
41
116
/*
42
/*
117
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
118
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only);
119
120
bdrv_subtree_drained_begin(bs);
121
- queue = bdrv_reopen_queue(NULL, bs, opts);
122
+ queue = bdrv_reopen_queue(NULL, bs, opts, true);
123
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, errp);
124
bdrv_subtree_drained_end(bs);
125
126
diff --git a/block/replication.c b/block/replication.c
127
index XXXXXXX..XXXXXXX 100644
128
--- a/block/replication.c
129
+++ b/block/replication.c
130
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
131
QDict *opts = qdict_new();
132
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
133
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs,
134
- opts);
135
+ opts, true);
136
}
137
138
if (s->orig_secondary_read_only) {
139
QDict *opts = qdict_new();
140
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
141
reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs,
142
- opts);
143
+ opts, true);
144
}
145
146
if (reopen_queue) {
147
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
148
index XXXXXXX..XXXXXXX 100644
149
--- a/qemu-io-cmds.c
150
+++ b/qemu-io-cmds.c
151
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
152
}
153
154
bdrv_subtree_drained_begin(bs);
155
- brq = bdrv_reopen_queue(NULL, bs, opts);
156
+ brq = bdrv_reopen_queue(NULL, bs, opts, true);
157
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
158
bdrv_subtree_drained_end(bs);
159
160
--
43
--
161
2.20.1
44
2.13.6
162
45
163
46
diff view generated by jsdifflib
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
---
2
---
3
block/file-posix.c | 107 ++++++++++++++++++++++++++-------------------
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
4
1 file changed, 62 insertions(+), 45 deletions(-)
4
1 file changed, 57 insertions(+)
5
5
6
diff --git a/block/file-posix.c b/block/file-posix.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
7
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
8
--- a/block/file-posix.c
8
--- a/tests/test-bdrv-drain.c
9
+++ b/block/file-posix.c
9
+++ b/tests/test-bdrv-drain.c
10
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
11
return ret;
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);
12
}
20
}
13
21
14
+static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
22
+static void test_nested(void)
15
+ int *open_flags, Error **errp)
16
+{
23
+{
17
+ BDRVRawState *s = bs->opaque;
24
+ BlockBackend *blk;
18
+ int fd = -1;
25
+ BlockDriverState *bs, *backing;
19
+ int ret;
26
+ BDRVTestState *s, *backing_s;
20
+ int fcntl_flags = O_APPEND | O_NONBLOCK;
27
+ enum drain_type outer, inner;
21
+#ifdef O_NOATIME
22
+ fcntl_flags |= O_NOATIME;
23
+#endif
24
+
28
+
25
+ *open_flags = 0;
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
26
+ if (s->type == FTYPE_CD) {
30
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
27
+ *open_flags |= O_NONBLOCK;
31
+ &error_abort);
28
+ }
32
+ s = bs->opaque;
33
+ blk_insert_bs(blk, bs, &error_abort);
29
+
34
+
30
+ raw_parse_flags(flags, open_flags);
35
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
36
+ backing_s = backing->opaque;
37
+ bdrv_set_backing_hd(bs, backing, &error_abort);
31
+
38
+
32
+#ifdef O_ASYNC
39
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
33
+ /* Not all operating systems have O_ASYNC, and those that don't
40
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
34
+ * will not let us track the state into rs->open_flags (typically
41
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
35
+ * you achieve the same effect with an ioctl, for example I_SETSIG
42
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
36
+ * on Solaris). But we do not use O_ASYNC, so that's fine.
43
+ (inner != BDRV_DRAIN_ALL);
37
+ */
44
+ int backing_quiesce = 0;
38
+ assert((s->open_flags & O_ASYNC) == 0);
45
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
39
+#endif
46
+ (inner != BDRV_DRAIN);
40
+
47
+
41
+ if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
48
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
42
+ /* dup the original fd */
49
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
43
+ fd = qemu_dup(s->fd);
50
+ g_assert_cmpint(s->drain_count, ==, 0);
44
+ if (fd >= 0) {
51
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
45
+ ret = fcntl_setfl(fd, *open_flags);
52
+
46
+ if (ret) {
53
+ do_drain_begin(outer, bs);
47
+ qemu_close(fd);
54
+ do_drain_begin(inner, bs);
48
+ fd = -1;
55
+
49
+ }
56
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
57
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
58
+ g_assert_cmpint(s->drain_count, ==, 2);
59
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
60
+
61
+ do_drain_end(inner, bs);
62
+ do_drain_end(outer, bs);
63
+
64
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
65
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
66
+ g_assert_cmpint(s->drain_count, ==, 0);
67
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
50
+ }
68
+ }
51
+ }
69
+ }
52
+
70
+
53
+ /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
71
+ bdrv_unref(backing);
54
+ if (fd == -1) {
72
+ bdrv_unref(bs);
55
+ const char *normalized_filename = bs->filename;
73
+ blk_unref(blk);
56
+ ret = raw_normalize_devicepath(&normalized_filename, errp);
57
+ if (ret >= 0) {
58
+ assert(!(*open_flags & O_CREAT));
59
+ fd = qemu_open(normalized_filename, *open_flags);
60
+ if (fd == -1) {
61
+ error_setg_errno(errp, errno, "Could not reopen file");
62
+ return -1;
63
+ }
64
+ }
65
+ }
66
+
67
+ return fd;
68
+}
74
+}
69
+
75
+
70
static int raw_reopen_prepare(BDRVReopenState *state,
76
71
BlockReopenQueue *queue, Error **errp)
77
typedef struct TestBlockJob {
72
{
78
BlockJob common;
73
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
74
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
75
state->opaque = g_new0(BDRVRawReopenState, 1);
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
76
rs = state->opaque;
82
77
- rs->fd = -1;
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
78
84
+
79
/* Handle options changes */
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
80
opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
81
@@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state,
87
82
* bdrv_reopen_prepare() will detect changes and complain. */
83
qemu_opts_to_qdict(opts, state->options);
84
85
- if (s->type == FTYPE_CD) {
86
- rs->open_flags |= O_NONBLOCK;
87
- }
88
-
89
- raw_parse_flags(state->flags, &rs->open_flags);
90
-
91
- int fcntl_flags = O_APPEND | O_NONBLOCK;
92
-#ifdef O_NOATIME
93
- fcntl_flags |= O_NOATIME;
94
-#endif
95
-
96
-#ifdef O_ASYNC
97
- /* Not all operating systems have O_ASYNC, and those that don't
98
- * will not let us track the state into rs->open_flags (typically
99
- * you achieve the same effect with an ioctl, for example I_SETSIG
100
- * on Solaris). But we do not use O_ASYNC, so that's fine.
101
- */
102
- assert((s->open_flags & O_ASYNC) == 0);
103
-#endif
104
-
105
- if ((rs->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
106
- /* dup the original fd */
107
- rs->fd = qemu_dup(s->fd);
108
- if (rs->fd >= 0) {
109
- ret = fcntl_setfl(rs->fd, rs->open_flags);
110
- if (ret) {
111
- qemu_close(rs->fd);
112
- rs->fd = -1;
113
- }
114
- }
115
- }
116
-
117
- /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
118
- if (rs->fd == -1) {
119
- const char *normalized_filename = state->bs->filename;
120
- ret = raw_normalize_devicepath(&normalized_filename, errp);
121
- if (ret >= 0) {
122
- assert(!(rs->open_flags & O_CREAT));
123
- rs->fd = qemu_open(normalized_filename, rs->open_flags);
124
- if (rs->fd == -1) {
125
- error_setg_errno(errp, errno, "Could not reopen file");
126
- ret = -1;
127
- }
128
- }
129
+ rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
130
+ &local_err);
131
+ if (local_err) {
132
+ error_propagate(errp, local_err);
133
+ ret = -1;
134
+ goto out;
135
}
136
137
/* Fail already reopen_prepare() if we can't get a working O_DIRECT
138
--
88
--
139
2.20.1
89
2.13.6
140
90
141
91
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.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
Children in QMP are specified with BlockdevRef / BlockdevRefOrNull,
3
4
which can contain a set of child options, a child reference, or
4
Calling the parent callbacks for drain is pointless when we just came
5
NULL. In optional attributes like "backing" it can also be missing.
5
from that parent node recursively and leads to multiple increases of
6
6
bs->quiesce_counter in a single drain call. Don't do it.
7
Only the first case (set of child options) is being handled properly
7
8
by bdrv_reopen_queue(). This patch deals with all the others.
8
In order for this to work correctly, the parent callback must be called
9
9
for every bdrv_drain_begin/end() call, not only for the outermost one:
10
Here's how these cases should be handled when bdrv_reopen_queue() is
10
11
deciding what to do with each child of a BlockDriverState:
11
If we have a node N with two parents A and B, recursive draining of A
12
12
should cause the quiesce_counter of B to increase because its child N is
13
1) Set of child options: if the child was implicitly created (i.e
13
drained independently of B. If now B is recursively drained, too, A must
14
inherits_from points to the parent) then the options are removed
14
increase its quiesce_counter because N is drained independently of A
15
from the parent's options QDict and are passed to the child with
15
only now, even if N is going from quiesce_counter 1 to 2.
16
a recursive bdrv_reopen_queue() call. This case was already
16
17
working fine.
18
19
2) Child reference: there's two possibilites here.
20
21
2a) Reference to the current child: if the child was implicitly
22
created then it is put in the reopen queue, keeping its
23
current set of options (since this was a child reference
24
there was no way to specify a different set of options).
25
If the child is not implicit then it keeps its current set
26
of options but it is not reopened (and therefore does not
27
inherit any new option from the parent).
28
29
2b) Reference to a different BDS: the current child is not put
30
in the reopen queue at all. Passing a reference to a
31
different BDS can be used to replace a child, although at
32
the moment no driver implements this, so it results in an
33
error. In any case, the current child is not going to be
34
reopened (and might in fact disappear if it's replaced)
35
36
3) NULL: This is similar to (2b). Although no driver allows this
37
yet it can be used to detach the current child so it should not
38
be put in the reopen queue.
39
40
4) Missing option: at the moment "backing" is the only case where
41
this can happen. With "blockdev-add", leaving "backing" out
42
means that the default backing file is opened. We don't want to
43
open a new image during reopen, so we require that "backing" is
44
always present. We'll relax this requirement a bit in the next
45
patch. If keep_old_opts is true and "backing" is missing then
46
this behaves like 2a (the current child is reopened).
47
48
Signed-off-by: Alberto Garcia <berto@igalia.com>
49
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
50
---
18
---
51
include/block/block.h | 1 +
19
include/block/block.h | 4 ++--
52
block.c | 52 +++++++++++++++++++++++++++++++++++++------
20
block.c | 13 +++++++++----
53
2 files changed, 46 insertions(+), 7 deletions(-)
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
22
3 files changed, 45 insertions(+), 19 deletions(-)
54
23
55
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
56
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
57
--- a/include/block/block.h
26
--- a/include/block/block.h
58
+++ b/include/block/block.h
27
+++ b/include/block/block.h
59
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState {
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
60
BlockDriverState *bs;
29
* Begin a quiesced section of all users of @bs. This is part of
61
int flags;
30
* bdrv_drained_begin.
62
BlockdevDetectZeroesOptions detect_zeroes;
31
*/
63
+ bool backing_missing;
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
64
uint64_t perm, shared_perm;
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
65
QDict *options;
34
66
QDict *explicit_options;
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:
67
diff --git a/block.c b/block.c
46
diff --git a/block.c b/block.c
68
index XXXXXXX..XXXXXXX 100644
47
index XXXXXXX..XXXXXXX 100644
69
--- a/block.c
48
--- a/block.c
70
+++ b/block.c
49
+++ b/block.c
71
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
72
bs_entry->state.perm = UINT64_MAX;
51
BlockDriverState *new_bs)
73
bs_entry->state.shared_perm = 0;
52
{
74
53
BlockDriverState *old_bs = child->bs;
75
+ /*
54
+ int i;
76
+ * If keep_old_opts is false then it means that unspecified
55
77
+ * options must be reset to their original value. We don't allow
56
if (old_bs && new_bs) {
78
+ * resetting 'backing' but we need to know if the option is
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
79
+ * missing in order to decide if we have to return an error.
58
}
80
+ */
59
if (old_bs) {
81
+ if (!keep_old_opts) {
60
if (old_bs->quiesce_counter && child->role->drained_end) {
82
+ bs_entry->state.backing_missing =
61
- child->role->drained_end(child);
83
+ !qdict_haskey(options, "backing") &&
62
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
84
+ !qdict_haskey(options, "backing.driver");
63
+ child->role->drained_end(child);
85
+ }
64
+ }
65
}
66
if (child->role->detach) {
67
child->role->detach(child);
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
69
if (new_bs) {
70
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
71
if (new_bs->quiesce_counter && child->role->drained_begin) {
72
- child->role->drained_begin(child);
73
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
74
+ child->role->drained_begin(child);
75
+ }
76
}
77
78
if (child->role->attach) {
79
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
80
AioContext *ctx = bdrv_get_aio_context(bs);
81
82
aio_disable_external(ctx);
83
- bdrv_parent_drained_begin(bs);
84
+ bdrv_parent_drained_begin(bs, NULL);
85
bdrv_drain(bs); /* ensure there are no in-flight requests */
86
87
while (aio_poll(ctx, false)) {
88
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
89
*/
90
aio_context_acquire(new_context);
91
bdrv_attach_aio_context(bs, new_context);
92
- bdrv_parent_drained_end(bs);
93
+ bdrv_parent_drained_end(bs, NULL);
94
aio_enable_external(ctx);
95
aio_context_release(new_context);
96
}
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);
86
+
146
+
87
QLIST_FOREACH(child, &bs->children, next) {
147
static void bdrv_co_drain_bh_cb(void *opaque)
88
- QDict *new_child_options;
148
{
89
- char *child_key_dot;
149
BdrvCoDrainData *data = opaque;
90
+ QDict *new_child_options = NULL;
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
91
+ bool child_keep_old = keep_old_opts;
151
92
152
bdrv_dec_in_flight(bs);
93
/* reopen can only change the options of block devices that were
153
if (data->begin) {
94
* implicitly created and inherited options. For other (referenced)
154
- bdrv_drained_begin(bs);
95
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
155
+ bdrv_do_drained_begin(bs, data->parent);
96
continue;
156
} else {
97
}
157
- bdrv_drained_end(bs);
98
158
+ bdrv_do_drained_end(bs, data->parent);
99
- child_key_dot = g_strdup_printf("%s.", child->name);
159
}
100
- qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
160
101
- qdict_extract_subqdict(options, &new_child_options, child_key_dot);
161
data->done = true;
102
- g_free(child_key_dot);
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
103
+ /* Check if the options contain a child reference */
163
}
104
+ if (qdict_haskey(options, child->name)) {
164
105
+ const char *childref = qdict_get_try_str(options, child->name);
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
106
+ /*
166
- bool begin)
107
+ * The current child must not be reopened if the child
167
+ bool begin, BdrvChild *parent)
108
+ * reference is null or points to a different node.
168
{
109
+ */
169
BdrvCoDrainData data;
110
+ if (g_strcmp0(childref, child->bs->node_name)) {
170
111
+ continue;
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
112
+ }
172
.bs = bs,
113
+ /*
173
.done = false,
114
+ * If the child reference points to the current child then
174
.begin = begin,
115
+ * reopen it with its existing set of options (note that
175
+ .parent = parent,
116
+ * it can still inherit new options from the parent).
176
};
117
+ */
177
bdrv_inc_in_flight(bs);
118
+ child_keep_old = true;
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
119
+ } else {
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
120
+ /* Extract child options ("child-name.*") */
180
assert(data.done);
121
+ char *child_key_dot = g_strdup_printf("%s.", child->name);
181
}
122
+ qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
182
123
+ qdict_extract_subqdict(options, &new_child_options, child_key_dot);
183
-void bdrv_drained_begin(BlockDriverState *bs)
124
+ g_free(child_key_dot);
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
125
+ }
185
{
126
186
if (qemu_in_coroutine()) {
127
bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options,
187
- bdrv_co_yield_to_drain(bs, true);
128
- child->role, options, flags, keep_old_opts);
188
+ bdrv_co_yield_to_drain(bs, true, parent);
129
+ child->role, options, flags, child_keep_old);
189
return;
130
}
190
}
131
191
132
return bs_queue;
192
/* Stop things in parent-to-child order */
133
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
134
194
aio_disable_external(bdrv_get_aio_context(bs));
135
drv_prepared = true;
195
- bdrv_parent_drained_begin(bs);
136
196
}
137
+ if (drv->supports_backing && reopen_state->backing_missing) {
197
138
+ error_setg(errp, "backing is missing for '%s'",
198
+ bdrv_parent_drained_begin(bs, parent);
139
+ reopen_state->bs->node_name);
199
bdrv_drain_invoke(bs, true, false);
140
+ ret = -EINVAL;
200
bdrv_drain_recurse(bs);
141
+ goto error;
201
}
142
+ }
202
203
-void bdrv_drained_end(BlockDriverState *bs)
204
+void bdrv_drained_begin(BlockDriverState *bs)
205
+{
206
+ bdrv_do_drained_begin(bs, NULL);
207
+}
143
+
208
+
144
/* Options that are not handled are only okay if they are unchanged
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
145
* compared to the old state. It is expected that some options are only
210
{
146
* used for the initial open, but not reopen (e.g. filename) */
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
}
147
--
256
--
148
2.20.1
257
2.13.6
149
258
150
259
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
until bdrv_drained_end().
2
4
3
This patch allows the user to change the backing file of an image that
5
Add a version that keeps the whole subtree drained. As of this commit,
4
is being reopened. Here's what it does:
6
graph changes cannot be allowed during a subtree drained section, but
7
this will be fixed soon.
5
8
6
- In bdrv_reopen_prepare(): check that the value of 'backing' points
7
to an existing node or is null. If it points to an existing node it
8
also needs to make sure that replacing the backing file will not
9
create a cycle in the node graph (i.e. you cannot reach the parent
10
from the new backing file).
11
12
- In bdrv_reopen_commit(): perform the actual node replacement by
13
calling bdrv_set_backing_hd().
14
15
There may be temporary implicit nodes between a BDS and its backing
16
file (e.g. a commit filter node). In these cases bdrv_reopen_prepare()
17
looks for the real (non-implicit) backing file and requires that the
18
'backing' option points to it. Replacing or detaching a backing file
19
is forbidden if there are implicit nodes in the middle.
20
21
Although x-blockdev-reopen is meant to be used like blockdev-add,
22
there's an important thing that must be taken into account: the only
23
way to set a new backing file is by using a reference to an existing
24
node (previously added with e.g. blockdev-add). If 'backing' contains
25
a dictionary with a new set of options ({"driver": "qcow2", "file": {
26
... }}) then it is interpreted that the _existing_ backing file must
27
be reopened with those options.
28
29
Signed-off-by: Alberto Garcia <berto@igalia.com>
30
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
31
---
10
---
32
include/block/block.h | 2 +
11
include/block/block.h | 13 +++++++++++++
33
block.c | 166 ++++++++++++++++++++++++++++++++++++++++++
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
34
2 files changed, 168 insertions(+)
13
2 files changed, 56 insertions(+), 11 deletions(-)
35
14
36
diff --git a/include/block/block.h b/include/block/block.h
15
diff --git a/include/block/block.h b/include/block/block.h
37
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
38
--- a/include/block/block.h
17
--- a/include/block/block.h
39
+++ b/include/block/block.h
18
+++ b/include/block/block.h
40
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVReopenState {
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
41
int flags;
20
void bdrv_drained_begin(BlockDriverState *bs);
42
BlockdevDetectZeroesOptions detect_zeroes;
21
43
bool backing_missing;
22
/**
44
+ bool replace_backing_bs; /* new_backing_bs is ignored if this is false */
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
45
+ BlockDriverState *new_backing_bs; /* If NULL then detach the current bs */
24
+ * exclusive access to all child nodes as well.
46
uint64_t perm, shared_perm;
25
+ *
47
QDict *options;
26
+ * Graph changes are not allowed during a subtree drain section.
48
QDict *explicit_options;
27
+ */
49
diff --git a/block.c b/block.c
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
50
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
51
--- a/block.c
47
--- a/block/io.c
52
+++ b/block.c
48
+++ b/block/io.c
53
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
49
@@ -XXX,XX +XXX,XX @@ typedef struct {
54
NULL, errp);
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;
55
}
59
}
56
60
57
+/*
61
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
58
+ * Returns true if @child can be reached recursively from @bs
62
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
59
+ */
63
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
60
+static bool bdrv_recurse_has_child(BlockDriverState *bs,
64
+ BdrvChild *parent);
61
+ BlockDriverState *child)
65
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
62
+{
66
+ BdrvChild *parent);
63
+ BdrvChild *c;
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;
64
+
109
+
65
+ if (bs == child) {
110
if (qemu_in_coroutine()) {
66
+ return true;
111
- bdrv_co_yield_to_drain(bs, true, parent);
67
+ }
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);
68
+
120
+
69
+ QLIST_FOREACH(c, &bs->children, next) {
121
+ if (recursive) {
70
+ if (bdrv_recurse_has_child(c->bs, child)) {
122
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
71
+ return true;
123
+ bdrv_do_drained_begin(child->bs, true, child);
72
+ }
124
+ }
73
+ }
125
+ }
74
+
126
}
75
+ return false;
127
128
void bdrv_drained_begin(BlockDriverState *bs)
129
{
130
- bdrv_do_drained_begin(bs, NULL);
131
+ bdrv_do_drained_begin(bs, false, NULL);
76
+}
132
+}
77
+
133
+
78
/*
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
79
* Adds a BlockDriverState to a simple queue for an atomic, transactional
135
+{
80
* reopen of multiple devices.
136
+ bdrv_do_drained_begin(bs, true, NULL);
81
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
137
}
82
if (ret < 0) {
138
83
goto cleanup_perm;
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
84
}
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
85
+ /* Check if new_backing_bs would accept the new permissions */
141
+ BdrvChild *parent)
86
+ if (state->replace_backing_bs && state->new_backing_bs) {
142
{
87
+ uint64_t nperm, nshared;
143
+ BdrvChild *child, *next;
88
+ bdrv_child_perm(state->bs, state->new_backing_bs,
144
int old_quiesce_counter;
89
+ NULL, &child_backing, bs_queue,
145
90
+ state->perm, state->shared_perm,
146
if (qemu_in_coroutine()) {
91
+ &nperm, &nshared);
147
- bdrv_co_yield_to_drain(bs, false, parent);
92
+ ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
93
+ nperm, nshared, NULL, errp);
149
return;
94
+ if (ret < 0) {
95
+ goto cleanup_perm;
96
+ }
97
+ }
98
bs_entry->perms_checked = true;
99
}
150
}
100
151
assert(bs->quiesce_counter > 0);
101
@@ -XXX,XX +XXX,XX @@ cleanup_perm:
152
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
102
bdrv_set_perm(state->bs, state->perm, state->shared_perm);
153
if (old_quiesce_counter == 1) {
103
} else {
154
aio_enable_external(bdrv_get_aio_context(bs));
104
bdrv_abort_perm_update(state->bs);
105
+ if (state->replace_backing_bs && state->new_backing_bs) {
106
+ bdrv_abort_perm_update(state->new_backing_bs);
107
+ }
108
}
109
}
155
}
110
cleanup:
111
@@ -XXX,XX +XXX,XX @@ cleanup:
112
qobject_unref(bs_entry->state.explicit_options);
113
qobject_unref(bs_entry->state.options);
114
}
115
+ if (bs_entry->state.new_backing_bs) {
116
+ bdrv_unref(bs_entry->state.new_backing_bs);
117
+ }
118
g_free(bs_entry);
119
}
120
g_free(bs_queue);
121
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
122
*shared = cumulative_shared_perms;
123
}
124
125
+/*
126
+ * Take a BDRVReopenState and check if the value of 'backing' in the
127
+ * reopen_state->options QDict is valid or not.
128
+ *
129
+ * If 'backing' is missing from the QDict then return 0.
130
+ *
131
+ * If 'backing' contains the node name of the backing file of
132
+ * reopen_state->bs then return 0.
133
+ *
134
+ * If 'backing' contains a different node name (or is null) then check
135
+ * whether the current backing file can be replaced with the new one.
136
+ * If that's the case then reopen_state->replace_backing_bs is set to
137
+ * true and reopen_state->new_backing_bs contains a pointer to the new
138
+ * backing BlockDriverState (or NULL).
139
+ *
140
+ * Return 0 on success, otherwise return < 0 and set @errp.
141
+ */
142
+static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
143
+ Error **errp)
144
+{
145
+ BlockDriverState *bs = reopen_state->bs;
146
+ BlockDriverState *overlay_bs, *new_backing_bs;
147
+ QObject *value;
148
+ const char *str;
149
+
156
+
150
+ value = qdict_get(reopen_state->options, "backing");
157
+ if (recursive) {
151
+ if (value == NULL) {
158
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
152
+ return 0;
159
+ bdrv_do_drained_end(child->bs, true, child);
153
+ }
154
+
155
+ switch (qobject_type(value)) {
156
+ case QTYPE_QNULL:
157
+ new_backing_bs = NULL;
158
+ break;
159
+ case QTYPE_QSTRING:
160
+ str = qobject_get_try_str(value);
161
+ new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
162
+ if (new_backing_bs == NULL) {
163
+ return -EINVAL;
164
+ } else if (bdrv_recurse_has_child(new_backing_bs, bs)) {
165
+ error_setg(errp, "Making '%s' a backing file of '%s' "
166
+ "would create a cycle", str, bs->node_name);
167
+ return -EINVAL;
168
+ }
169
+ break;
170
+ default:
171
+ /* 'backing' does not allow any other data type */
172
+ g_assert_not_reached();
173
+ }
174
+
175
+ /*
176
+ * TODO: before removing the x- prefix from x-blockdev-reopen we
177
+ * should move the new backing file into the right AioContext
178
+ * instead of returning an error.
179
+ */
180
+ if (new_backing_bs) {
181
+ if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
182
+ error_setg(errp, "Cannot use a new backing file "
183
+ "with a different AioContext");
184
+ return -EINVAL;
185
+ }
160
+ }
186
+ }
161
+ }
187
+
162
}
188
+ /*
163
189
+ * Find the "actual" backing file by skipping all links that point
164
void bdrv_drained_end(BlockDriverState *bs)
190
+ * to an implicit node, if any (e.g. a commit filter node).
165
{
191
+ */
166
- bdrv_do_drained_end(bs, NULL);
192
+ overlay_bs = bs;
167
+ bdrv_do_drained_end(bs, false, NULL);
193
+ while (backing_bs(overlay_bs) && backing_bs(overlay_bs)->implicit) {
194
+ overlay_bs = backing_bs(overlay_bs);
195
+ }
196
+
197
+ /* If we want to replace the backing file we need some extra checks */
198
+ if (new_backing_bs != backing_bs(overlay_bs)) {
199
+ /* Check for implicit nodes between bs and its backing file */
200
+ if (bs != overlay_bs) {
201
+ error_setg(errp, "Cannot change backing link if '%s' has "
202
+ "an implicit backing file", bs->node_name);
203
+ return -EPERM;
204
+ }
205
+ /* Check if the backing link that we want to replace is frozen */
206
+ if (bdrv_is_backing_chain_frozen(overlay_bs, backing_bs(overlay_bs),
207
+ errp)) {
208
+ return -EPERM;
209
+ }
210
+ reopen_state->replace_backing_bs = true;
211
+ if (new_backing_bs) {
212
+ bdrv_ref(new_backing_bs);
213
+ reopen_state->new_backing_bs = new_backing_bs;
214
+ }
215
+ }
216
+
217
+ return 0;
218
+}
168
+}
219
+
169
+
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
171
+{
172
+ bdrv_do_drained_end(bs, true, NULL);
173
}
174
220
/*
175
/*
221
* Prepares a BlockDriverState for reopen. All changes are staged in the
222
* 'opaque' field of the BDRVReopenState, which is used and allocated by
223
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
224
goto error;
225
}
226
227
+ /*
228
+ * Allow changing the 'backing' option. The new value can be
229
+ * either a reference to an existing node (using its node name)
230
+ * or NULL to simply detach the current backing file.
231
+ */
232
+ ret = bdrv_reopen_parse_backing(reopen_state, errp);
233
+ if (ret < 0) {
234
+ goto error;
235
+ }
236
+ qdict_del(reopen_state->options, "backing");
237
+
238
/* Options that are not handled are only okay if they are unchanged
239
* compared to the old state. It is expected that some options are only
240
* used for the initial open, but not reopen (e.g. filename) */
241
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
242
bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
243
bs->detect_zeroes = reopen_state->detect_zeroes;
244
245
+ if (reopen_state->replace_backing_bs) {
246
+ qdict_del(bs->explicit_options, "backing");
247
+ qdict_del(bs->options, "backing");
248
+ }
249
+
250
/* Remove child references from bs->options and bs->explicit_options.
251
* Child options were already removed in bdrv_reopen_queue_child() */
252
QLIST_FOREACH(child, &bs->children, next) {
253
@@ -XXX,XX +XXX,XX @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
254
qdict_del(bs->options, child->name);
255
}
256
257
+ /*
258
+ * Change the backing file if a new one was specified. We do this
259
+ * after updating bs->options, so bdrv_refresh_filename() (called
260
+ * from bdrv_set_backing_hd()) has the new values.
261
+ */
262
+ if (reopen_state->replace_backing_bs) {
263
+ BlockDriverState *old_backing_bs = backing_bs(bs);
264
+ assert(!old_backing_bs || !old_backing_bs->implicit);
265
+ /* Abort the permission update on the backing bs we're detaching */
266
+ if (old_backing_bs) {
267
+ bdrv_abort_perm_update(old_backing_bs);
268
+ }
269
+ bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
270
+ }
271
+
272
bdrv_refresh_limits(bs, NULL);
273
274
new_can_write =
275
--
176
--
276
2.20.1
177
2.13.6
277
178
278
179
diff view generated by jsdifflib
1
From: Niels de Vos <ndevos@redhat.com>
1
Add a subtree drain version to the existing test cases.
2
2
3
The glfs_*_async() functions do a callback once finished. This callback
4
has changed its arguments, pre- and post-stat structures have been
5
added. This makes it possible to improve caching, which is useful for
6
Samba and NFS-Ganesha, but not so much for QEMU. Gluster 6 is the first
7
release that includes these new arguments.
8
9
With an additional detection in ./configure, the new arguments can
10
conditionally get included in the glfs_io_cbk handler.
11
12
Signed-off-by: Niels de Vos <ndevos@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
4
---
15
configure | 24 ++++++++++++++++++++++++
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
16
block/gluster.c | 6 +++++-
6
1 file changed, 26 insertions(+), 1 deletion(-)
17
2 files changed, 29 insertions(+), 1 deletion(-)
18
7
19
diff --git a/configure b/configure
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
20
index XXXXXXX..XXXXXXX 100755
9
index XXXXXXX..XXXXXXX 100644
21
--- a/configure
10
--- a/tests/test-bdrv-drain.c
22
+++ b/configure
11
+++ b/tests/test-bdrv-drain.c
23
@@ -XXX,XX +XXX,XX @@ glusterfs_discard="no"
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
24
glusterfs_fallocate="no"
13
enum drain_type {
25
glusterfs_zerofill="no"
14
BDRV_DRAIN_ALL,
26
glusterfs_ftruncate_has_stat="no"
15
BDRV_DRAIN,
27
+glusterfs_iocb_has_stat="no"
16
+ BDRV_SUBTREE_DRAIN,
28
gtk=""
17
DRAIN_TYPE_MAX,
29
gtk_gl="no"
18
};
30
tls_priority="NORMAL"
19
31
@@ -XXX,XX +XXX,XX @@ EOF
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
32
if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
21
switch (drain_type) {
33
glusterfs_ftruncate_has_stat="yes"
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
34
fi
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
35
+ cat > $TMPC << EOF
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
36
+#include <glusterfs/api/glfs.h>
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
+}
37
+
44
+
38
+/* new glfs_io_cbk() passes two additional glfs_stat structs */
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
39
+static void
46
{
40
+glusterfs_iocb(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data)
47
BlockBackend *blk;
41
+{}
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
+}
42
+
56
+
43
+int
57
static void test_nested(void)
44
+main(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)
45
+{
75
+{
46
+    glfs_io_cbk iocb = &glusterfs_iocb;
76
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
47
+    iocb(NULL, 0 , NULL, NULL, NULL);
48
+    return 0;
49
+}
77
+}
50
+EOF
51
+ if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
52
+ glusterfs_iocb_has_stat="yes"
53
+ fi
54
else
55
if test "$glusterfs" = "yes" ; then
56
feature_not_found "GlusterFS backend support" \
57
@@ -XXX,XX +XXX,XX @@ if test "$glusterfs_ftruncate_has_stat" = "yes" ; then
58
echo "CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT=y" >> $config_host_mak
59
fi
60
61
+if test "$glusterfs_iocb_has_stat" = "yes" ; then
62
+ echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak
63
+fi
64
+
78
+
65
if test "$libssh2" = "yes" ; then
79
int main(int argc, char **argv)
66
echo "CONFIG_LIBSSH2=m" >> $config_host_mak
67
echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
68
diff --git a/block/gluster.c b/block/gluster.c
69
index XXXXXXX..XXXXXXX 100644
70
--- a/block/gluster.c
71
+++ b/block/gluster.c
72
@@ -XXX,XX +XXX,XX @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
73
/*
74
* AIO callback routine called from GlusterFS thread.
75
*/
76
-static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
77
+static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret,
78
+#ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT
79
+ struct glfs_stat *pre, struct glfs_stat *post,
80
+#endif
81
+ void *arg)
82
{
80
{
83
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
81
bdrv_init();
84
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
}
85
--
103
--
86
2.20.1
104
2.13.6
87
105
88
106
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.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
bdrv_reopen_prepare() receives a BDRVReopenState with (among other
4
things) a new set of options to be applied to that BlockDriverState.
5
6
If an option is missing then it means that we want to reset it to its
7
default value rather than keep the previous one. This way the state
8
of the block device after being reopened is comparable to that of a
9
device added with "blockdev-add" using the same set of options.
10
11
Not all options from all drivers can be changed this way, however.
12
If the user attempts to reset an immutable option to its default value
13
using this method then we must forbid it.
14
15
This new function takes a BlockDriverState and a new set of options
16
and checks if there's any option that was previously set but is
17
missing from the new set of options.
18
19
If the option is present in both sets we don't need to check that they
20
have the same value. The loop at the end of bdrv_reopen_prepare()
21
already takes care of that.
22
23
Signed-off-by: Alberto Garcia <berto@igalia.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
6
---
26
block.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
27
1 file changed, 58 insertions(+)
8
1 file changed, 59 insertions(+)
28
9
29
diff --git a/block.c b/block.c
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
30
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
31
--- a/block.c
12
--- a/tests/test-bdrv-drain.c
32
+++ b/block.c
13
+++ b/tests/test-bdrv-drain.c
33
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
34
NULL, errp);
15
*aio_ret = ret;
35
}
16
}
36
17
37
+/* Return true if the NULL-terminated @list contains @str */
18
+typedef struct CallInCoroutineData {
38
+static bool is_str_in_list(const char *str, const char *const *list)
19
+ void (*entry)(void);
20
+ bool done;
21
+} CallInCoroutineData;
22
+
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
39
+{
24
+{
40
+ if (str && list) {
25
+ CallInCoroutineData *data = opaque;
41
+ int i;
26
+
42
+ for (i = 0; list[i] != NULL; i++) {
27
+ data->entry();
43
+ if (!strcmp(str, list[i])) {
28
+ data->done = true;
44
+ return true;
45
+ }
46
+ }
47
+ }
48
+ return false;
49
+}
29
+}
50
+
30
+
51
+/*
31
+static void call_in_coroutine(void (*entry)(void))
52
+ * Check that every option set in @bs->options is also set in
53
+ * @new_opts.
54
+ *
55
+ * Options listed in the common_options list and in
56
+ * @bs->drv->mutable_opts are skipped.
57
+ *
58
+ * Return 0 on success, otherwise return -EINVAL and set @errp.
59
+ */
60
+static int bdrv_reset_options_allowed(BlockDriverState *bs,
61
+ const QDict *new_opts, Error **errp)
62
+{
32
+{
63
+ const QDictEntry *e;
33
+ Coroutine *co;
64
+ /* These options are common to all block drivers and are handled
34
+ CallInCoroutineData data = {
65
+ * in bdrv_reopen_prepare() so they can be left out of @new_opts */
35
+ .entry = entry,
66
+ const char *const common_options[] = {
36
+ .done = false,
67
+ "node-name", "discard", "cache.direct", "cache.no-flush",
68
+ "read-only", "auto-read-only", "detect-zeroes", NULL
69
+ };
37
+ };
70
+
38
+
71
+ for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
39
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
72
+ if (!qdict_haskey(new_opts, e->key) &&
40
+ qemu_coroutine_enter(co);
73
+ !is_str_in_list(e->key, common_options) &&
41
+ while (!data.done) {
74
+ !is_str_in_list(e->key, bs->drv->mutable_opts)) {
42
+ aio_poll(qemu_get_aio_context(), true);
75
+ error_setg(errp, "Option '%s' cannot be reset "
76
+ "to its default value", e->key);
77
+ return -EINVAL;
78
+ }
79
+ }
43
+ }
80
+
81
+ return 0;
82
+}
44
+}
83
+
45
+
84
/*
46
enum drain_type {
85
* Returns true if @child can be reached recursively from @bs
47
BDRV_DRAIN_ALL,
86
*/
48
BDRV_DRAIN,
87
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
49
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_subtree(void)
88
}
50
test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
89
51
}
90
if (drv->bdrv_reopen_prepare) {
52
91
+ /*
53
+static void test_drv_cb_co_drain(void)
92
+ * If a driver-specific option is missing, it means that we
54
+{
93
+ * should reset it to its default value.
55
+ call_in_coroutine(test_drv_cb_drain);
94
+ * But not all options allow that, so we need to check it first.
56
+}
95
+ */
96
+ ret = bdrv_reset_options_allowed(reopen_state->bs,
97
+ reopen_state->options, errp);
98
+ if (ret) {
99
+ goto error;
100
+ }
101
+
57
+
102
ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err);
58
+static void test_drv_cb_co_drain_subtree(void)
103
if (ret) {
59
+{
104
if (local_err != NULL) {
60
+ call_in_coroutine(test_drv_cb_drain_subtree);
61
+}
62
+
63
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
64
{
65
BlockBackend *blk;
66
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void)
67
test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
68
}
69
70
+static void test_quiesce_co_drain(void)
71
+{
72
+ call_in_coroutine(test_quiesce_drain);
73
+}
74
+
75
+static void test_quiesce_co_drain_subtree(void)
76
+{
77
+ call_in_coroutine(test_quiesce_drain_subtree);
78
+}
79
+
80
static void test_nested(void)
81
{
82
BlockBackend *blk;
83
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
84
g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
85
test_drv_cb_drain_subtree);
86
87
+ // XXX bdrv_drain_all() doesn't work in coroutine context
88
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
89
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
90
+ test_drv_cb_co_drain_subtree);
91
+
92
+
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
94
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
95
g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
96
test_quiesce_drain_subtree);
97
98
+ // XXX bdrv_drain_all() doesn't work in coroutine context
99
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
100
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
101
+ test_quiesce_co_drain_subtree);
102
+
103
g_test_add_func("/bdrv-drain/nested", test_nested);
104
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
105
--
106
--
106
2.20.1
107
2.13.6
107
108
108
109
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
Test that drain sections are correctly propagated through the graph.
2
2
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
4
---
6
qapi/block-core.json | 4 ++--
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
7
blockdev.c | 12 ++++++------
6
1 file changed, 74 insertions(+)
8
2 files changed, 8 insertions(+), 8 deletions(-)
9
7
10
diff --git a/qapi/block-core.json b/qapi/block-core.json
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
11
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
12
--- a/qapi/block-core.json
10
--- a/tests/test-bdrv-drain.c
13
+++ b/qapi/block-core.json
11
+++ b/tests/test-bdrv-drain.c
14
@@ -XXX,XX +XXX,XX @@
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
15
# If only @device parameter is specified, remove all present latency histograms
13
blk_unref(blk);
16
# for the device. Otherwise, add/reset some of (or all) latency histograms.
17
#
18
-# @device: device name to set latency histogram for.
19
+# @id: The name or QOM path of the guest device.
20
#
21
# @boundaries: list of interval boundary values (see description in
22
# BlockLatencyHistogramInfo definition). If specified, all
23
@@ -XXX,XX +XXX,XX @@
24
# <- { "return": {} }
25
##
26
{ 'command': 'x-block-latency-histogram-set',
27
- 'data': {'device': 'str',
28
+ 'data': {'id': 'str',
29
'*boundaries': ['uint64'],
30
'*boundaries-read': ['uint64'],
31
'*boundaries-write': ['uint64'],
32
diff --git a/blockdev.c b/blockdev.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/blockdev.c
35
+++ b/blockdev.c
36
@@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
37
}
14
}
38
15
39
void qmp_x_block_latency_histogram_set(
16
+static void test_multiparent(void)
40
- const char *device,
17
+{
41
+ const char *id,
18
+ BlockBackend *blk_a, *blk_b;
42
bool has_boundaries, uint64List *boundaries,
19
+ BlockDriverState *bs_a, *bs_b, *backing;
43
bool has_boundaries_read, uint64List *boundaries_read,
20
+ BDRVTestState *a_s, *b_s, *backing_s;
44
bool has_boundaries_write, uint64List *boundaries_write,
45
bool has_boundaries_flush, uint64List *boundaries_flush,
46
Error **errp)
47
{
48
- BlockBackend *blk = blk_by_name(device);
49
+ BlockBackend *blk = qmp_get_blk(NULL, id, errp);
50
BlockAcctStats *stats;
51
int ret;
52
53
if (!blk) {
54
- error_setg(errp, "Device '%s' not found", device);
55
return;
56
}
57
+
21
+
58
stats = blk_get_stats(blk);
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
59
23
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
60
if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
24
+ &error_abort);
61
@@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set(
25
+ a_s = bs_a->opaque;
62
stats, BLOCK_ACCT_READ,
26
+ blk_insert_bs(blk_a, bs_a, &error_abort);
63
has_boundaries_read ? boundaries_read : boundaries);
27
+
64
if (ret) {
28
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
65
- error_setg(errp, "Device '%s' set read boundaries fail", device);
29
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
66
+ error_setg(errp, "Device '%s' set read boundaries fail", id);
30
+ &error_abort);
67
return;
31
+ b_s = bs_b->opaque;
68
}
32
+ blk_insert_bs(blk_b, bs_b, &error_abort);
69
}
33
+
70
@@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set(
34
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
71
stats, BLOCK_ACCT_WRITE,
35
+ backing_s = backing->opaque;
72
has_boundaries_write ? boundaries_write : boundaries);
36
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
73
if (ret) {
37
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
74
- error_setg(errp, "Device '%s' set write boundaries fail", device);
38
+
75
+ error_setg(errp, "Device '%s' set write boundaries fail", id);
39
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
76
return;
40
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
77
}
41
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
78
}
42
+ g_assert_cmpint(a_s->drain_count, ==, 0);
79
@@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set(
43
+ g_assert_cmpint(b_s->drain_count, ==, 0);
80
stats, BLOCK_ACCT_FLUSH,
44
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
81
has_boundaries_flush ? boundaries_flush : boundaries);
45
+
82
if (ret) {
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
83
- error_setg(errp, "Device '%s' set flush boundaries fail", device);
47
+
84
+ error_setg(errp, "Device '%s' set flush boundaries fail", id);
48
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
85
return;
49
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
86
}
50
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
87
}
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);
87
+}
88
+
89
90
typedef struct TestBlockJob {
91
BlockJob common;
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
93
test_quiesce_co_drain_subtree);
94
95
g_test_add_func("/bdrv-drain/nested", test_nested);
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
97
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
88
--
100
--
89
2.20.1
101
2.13.6
90
102
91
103
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.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
Our permission system is useful to define what operations are allowed
3
can be correctly applied when children are added or removed during the
4
on a certain block node and includes things like BLK_PERM_WRITE or
4
drained section.
5
BLK_PERM_RESIZE among others.
5
6
6
With this change, it is safe to modify the graph even inside a
7
One of the permissions is BLK_PERM_GRAPH_MOD which allows "changing
7
bdrv_subtree_drained_begin/end() section.
8
the node that this BdrvChild points to". The exact meaning of this has
8
9
never been very clear, but it can be understood as "change any of the
10
links connected to the node". This can be used to prevent changing a
11
backing link, but it's too coarse.
12
13
This patch adds a new 'frozen' attribute to BdrvChild, which forbids
14
detaching the link from the node it points to, and new API to freeze
15
and unfreeze a backing chain.
16
17
After this change a few functions can fail, so they need additional
18
checks.
19
20
Signed-off-by: Alberto Garcia <berto@igalia.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
10
---
23
include/block/block.h | 5 +++
11
include/block/block.h | 2 --
24
include/block/block_int.h | 6 ++++
12
include/block/block_int.h | 5 +++++
25
block.c | 76 +++++++++++++++++++++++++++++++++++++++
13
block.c | 32 +++++++++++++++++++++++++++++---
26
3 files changed, 87 insertions(+)
14
block/io.c | 28 ++++++++++++++++++++++++----
15
4 files changed, 58 insertions(+), 9 deletions(-)
27
16
28
diff --git a/include/block/block.h b/include/block/block.h
17
diff --git a/include/block/block.h b/include/block/block.h
29
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
30
--- a/include/block/block.h
19
--- a/include/block/block.h
31
+++ b/include/block/block.h
20
+++ b/include/block/block.h
32
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
33
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
22
/**
34
BlockDriverState *bs);
23
* Like bdrv_drained_begin, but recursively begins a quiesced section for
35
BlockDriverState *bdrv_find_base(BlockDriverState *bs);
24
* exclusive access to all child nodes as well.
36
+bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
25
- *
37
+ Error **errp);
26
- * Graph changes are not allowed during a subtree drain section.
38
+int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
27
*/
39
+ Error **errp);
28
void bdrv_subtree_drained_begin(BlockDriverState *bs);
40
+void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base);
29
41
42
43
typedef struct BdrvCheckResult {
44
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
45
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
46
--- a/include/block/block_int.h
32
--- a/include/block/block_int.h
47
+++ b/include/block/block_int.h
33
+++ b/include/block/block_int.h
48
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
49
uint64_t backup_perm;
35
50
uint64_t backup_shared_perm;
36
/* Accessed with atomic ops. */
51
37
int quiesce_counter;
52
+ /*
38
+ int recursive_quiesce_counter;
53
+ * This link is frozen: the child can neither be replaced nor
39
+
54
+ * detached from the parent.
40
unsigned int write_gen; /* Current data generation */
55
+ */
41
56
+ bool frozen;
42
/* Protected by reqs_lock. */
57
+
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
58
QLIST_ENTRY(BdrvChild) next;
44
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
59
QLIST_ENTRY(BdrvChild) next_parent;
45
BdrvRequestFlags flags);
60
};
46
47
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
48
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
49
+
50
int get_tmp_filename(char *filename, int size);
51
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
52
const char *filename);
61
diff --git a/block.c b/block.c
53
diff --git a/block.c b/block.c
62
index XXXXXXX..XXXXXXX 100644
54
index XXXXXXX..XXXXXXX 100644
63
--- a/block.c
55
--- a/block.c
64
+++ b/block.c
56
+++ b/block.c
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
58
bdrv_drained_end(bs);
59
}
60
61
+static void bdrv_child_cb_attach(BdrvChild *child)
62
+{
63
+ BlockDriverState *bs = child->opaque;
64
+ bdrv_apply_subtree_drain(child, bs);
65
+}
66
+
67
+static void bdrv_child_cb_detach(BdrvChild *child)
68
+{
69
+ BlockDriverState *bs = child->opaque;
70
+ bdrv_unapply_subtree_drain(child, bs);
71
+}
72
+
73
static int bdrv_child_cb_inactivate(BdrvChild *child)
74
{
75
BlockDriverState *bs = child->opaque;
76
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_file = {
77
.inherit_options = bdrv_inherited_options,
78
.drained_begin = bdrv_child_cb_drained_begin,
79
.drained_end = bdrv_child_cb_drained_end,
80
+ .attach = bdrv_child_cb_attach,
81
+ .detach = bdrv_child_cb_detach,
82
.inactivate = bdrv_child_cb_inactivate,
83
};
84
85
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = {
86
.inherit_options = bdrv_inherited_fmt_options,
87
.drained_begin = bdrv_child_cb_drained_begin,
88
.drained_end = bdrv_child_cb_drained_end,
89
+ .attach = bdrv_child_cb_attach,
90
+ .detach = bdrv_child_cb_detach,
91
.inactivate = bdrv_child_cb_inactivate,
92
};
93
94
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_attach(BdrvChild *c)
95
parent->backing_blocker);
96
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
97
parent->backing_blocker);
98
+
99
+ bdrv_child_cb_attach(c);
100
}
101
102
static void bdrv_backing_detach(BdrvChild *c)
103
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_detach(BdrvChild *c)
104
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
105
error_free(parent->backing_blocker);
106
parent->backing_blocker = NULL;
107
+
108
+ bdrv_child_cb_detach(c);
109
}
110
111
/*
65
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
66
BlockDriverState *old_bs = child->bs;
67
int i;
68
69
+ assert(!child->frozen);
70
+
71
if (old_bs && new_bs) {
72
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
73
}
114
}
74
@@ -XXX,XX +XXX,XX @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
115
if (old_bs) {
75
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
116
+ /* Detach first so that the recursive drain sections coming from @child
76
bdrv_inherits_from_recursive(backing_hd, bs);
117
+ * are already gone and we only end the drain sections that came from
77
118
+ * elsewhere. */
78
+ if (bdrv_is_backing_chain_frozen(bs, backing_bs(bs), errp)) {
119
+ if (child->role->detach) {
79
+ return;
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);
80
+ }
195
+ }
81
+
196
+}
82
if (backing_hd) {
197
+
83
bdrv_ref(backing_hd);
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
84
}
199
+{
85
@@ -XXX,XX +XXX,XX @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
200
+ int i;
86
if (!should_update_child(c, to)) {
201
+
87
continue;
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
88
}
203
+ bdrv_do_drained_end(child->bs, true, child);
89
+ if (c->frozen) {
90
+ error_setg(errp, "Cannot change '%s' link to '%s'",
91
+ c->name, from->node_name);
92
+ goto out;
93
+ }
94
list = g_slist_prepend(list, c);
95
perm |= c->perm;
96
shared &= c->shared_perm;
97
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
98
return bdrv_find_overlay(bs, NULL);
99
}
100
101
+/*
102
+ * Return true if at least one of the backing links between @bs and
103
+ * @base is frozen. @errp is set if that's the case.
104
+ */
105
+bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
106
+ Error **errp)
107
+{
108
+ BlockDriverState *i;
109
+
110
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
111
+ if (i->backing->frozen) {
112
+ error_setg(errp, "Cannot change '%s' link from '%s' to '%s'",
113
+ i->backing->name, i->node_name,
114
+ backing_bs(i)->node_name);
115
+ return true;
116
+ }
117
+ }
204
+ }
118
+
119
+ return false;
120
+}
121
+
122
+/*
123
+ * Freeze all backing links between @bs and @base.
124
+ * If any of the links is already frozen the operation is aborted and
125
+ * none of the links are modified.
126
+ * Returns 0 on success. On failure returns < 0 and sets @errp.
127
+ */
128
+int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
129
+ Error **errp)
130
+{
131
+ BlockDriverState *i;
132
+
133
+ if (bdrv_is_backing_chain_frozen(bs, base, errp)) {
134
+ return -EPERM;
135
+ }
136
+
137
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
138
+ i->backing->frozen = true;
139
+ }
140
+
141
+ return 0;
142
+}
143
+
144
+/*
145
+ * Unfreeze all backing links between @bs and @base. The caller must
146
+ * ensure that all links are frozen before using this function.
147
+ */
148
+void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base)
149
+{
150
+ BlockDriverState *i;
151
+
152
+ for (i = bs; i != base && i->backing; i = backing_bs(i)) {
153
+ assert(i->backing->frozen);
154
+ i->backing->frozen = false;
155
+ }
156
+}
205
+}
157
+
206
+
158
/*
207
/*
159
* Drops images above 'base' up to and including 'top', and sets the image
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
160
* above 'top' to have base as its backing file.
209
* and suspend block driver's internal I/O until next request arrives.
161
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
162
goto exit;
163
}
164
165
+ /* This function changes all links that point to top and makes
166
+ * them point to base. Check that none of them is frozen. */
167
+ QLIST_FOREACH(c, &top->parents, next_parent) {
168
+ if (c->frozen) {
169
+ goto exit;
170
+ }
171
+ }
172
+
173
/* If 'base' recursively inherits from 'top' then we should set
174
* base->inherits_from to top->inherits_from after 'top' and all
175
* other intermediate nodes have been dropped.
176
--
210
--
177
2.20.1
211
2.13.6
178
212
179
213
diff view generated by jsdifflib
1
From: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
2
3
New versions of Glusters libgfapi.so have an updated glfs_ftruncate()
4
function that returns additional 'struct stat' structures to enable
5
advanced caching of attributes. This is useful for file servers, not so
6
much for QEMU. Nevertheless, the API has changed and needs to be
7
adopted.
8
9
Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
10
Signed-off-by: Niels de Vos <ndevos@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
2
---
13
configure | 18 ++++++++++++++++++
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
14
block/gluster.c | 4 ++++
4
1 file changed, 80 insertions(+)
15
2 files changed, 22 insertions(+)
16
5
17
diff --git a/configure b/configure
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/configure
8
--- a/tests/test-bdrv-drain.c
20
+++ b/configure
9
+++ b/tests/test-bdrv-drain.c
21
@@ -XXX,XX +XXX,XX @@ glusterfs_xlator_opt="no"
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
22
glusterfs_discard="no"
11
blk_unref(blk_b);
23
glusterfs_fallocate="no"
12
}
24
glusterfs_zerofill="no"
13
25
+glusterfs_ftruncate_has_stat="no"
14
+static void test_graph_change(void)
26
gtk=""
15
+{
27
gtk_gl="no"
16
+ BlockBackend *blk_a, *blk_b;
28
tls_priority="NORMAL"
17
+ BlockDriverState *bs_a, *bs_b, *backing;
29
@@ -XXX,XX +XXX,XX @@ if test "$glusterfs" != "no" ; then
18
+ BDRVTestState *a_s, *b_s, *backing_s;
30
glusterfs_fallocate="yes"
31
glusterfs_zerofill="yes"
32
fi
33
+ cat > $TMPC << EOF
34
+#include <glusterfs/api/glfs.h>
35
+
19
+
36
+int
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
37
+main(void)
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
38
+{
22
+ &error_abort);
39
+    /* new glfs_ftruncate() passes two additional args */
23
+ a_s = bs_a->opaque;
40
+    return glfs_ftruncate(NULL, 0, NULL, NULL);
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
25
+
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
28
+ &error_abort);
29
+ b_s = bs_b->opaque;
30
+ blk_insert_bs(blk_b, bs_b, &error_abort);
31
+
32
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
33
+ backing_s = backing->opaque;
34
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
35
+
36
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
37
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
38
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
39
+ g_assert_cmpint(a_s->drain_count, ==, 0);
40
+ g_assert_cmpint(b_s->drain_count, ==, 0);
41
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
42
+
43
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
44
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
45
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
47
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
48
+
49
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
50
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
51
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
52
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
53
+ g_assert_cmpint(a_s->drain_count, ==, 5);
54
+ g_assert_cmpint(b_s->drain_count, ==, 5);
55
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
56
+
57
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
58
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
59
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
61
+ g_assert_cmpint(a_s->drain_count, ==, 3);
62
+ g_assert_cmpint(b_s->drain_count, ==, 2);
63
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
64
+
65
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
69
+ g_assert_cmpint(a_s->drain_count, ==, 5);
70
+ g_assert_cmpint(b_s->drain_count, ==, 5);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
74
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
75
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
76
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
77
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
78
+
79
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
80
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
81
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
82
+ g_assert_cmpint(a_s->drain_count, ==, 0);
83
+ g_assert_cmpint(b_s->drain_count, ==, 0);
84
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
85
+
86
+ bdrv_unref(backing);
87
+ bdrv_unref(bs_a);
88
+ bdrv_unref(bs_b);
89
+ blk_unref(blk_a);
90
+ blk_unref(blk_b);
41
+}
91
+}
42
+EOF
43
+ if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then
44
+ glusterfs_ftruncate_has_stat="yes"
45
+ fi
46
else
47
if test "$glusterfs" = "yes" ; then
48
feature_not_found "GlusterFS backend support" \
49
@@ -XXX,XX +XXX,XX @@ if test "$glusterfs_zerofill" = "yes" ; then
50
echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
51
fi
52
53
+if test "$glusterfs_ftruncate_has_stat" = "yes" ; then
54
+ echo "CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT=y" >> $config_host_mak
55
+fi
56
+
92
+
57
if test "$libssh2" = "yes" ; then
93
58
echo "CONFIG_LIBSSH2=m" >> $config_host_mak
94
typedef struct TestBlockJob {
59
echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
95
BlockJob common;
60
diff --git a/block/gluster.c b/block/gluster.c
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
61
index XXXXXXX..XXXXXXX 100644
97
62
--- a/block/gluster.c
98
g_test_add_func("/bdrv-drain/nested", test_nested);
63
+++ b/block/gluster.c
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
64
@@ -XXX,XX +XXX,XX @@
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
65
#include "qemu/option.h"
101
66
#include "qemu/cutils.h"
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
67
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
68
+#ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT
69
+# define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL)
70
+#endif
71
+
72
#define GLUSTER_OPT_FILENAME "filename"
73
#define GLUSTER_OPT_VOLUME "volume"
74
#define GLUSTER_OPT_PATH "path"
75
--
104
--
76
2.20.1
105
2.13.6
77
106
78
107
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
Since commit bde70715, base is the only node that is reopened in
2
commit_start(). This means that the code, which still involves an
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
2
4
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
5
---
7
---
6
block/commit.c | 16 ++++++++++++++++
8
block/commit.c | 8 +-------
7
1 file changed, 16 insertions(+)
9
1 file changed, 1 insertion(+), 7 deletions(-)
8
10
9
diff --git a/block/commit.c b/block/commit.c
11
diff --git a/block/commit.c b/block/commit.c
10
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
11
--- a/block/commit.c
13
--- a/block/commit.c
12
+++ b/block/commit.c
14
+++ b/block/commit.c
13
@@ -XXX,XX +XXX,XX @@ typedef struct CommitBlockJob {
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
14
BlockDriverState *base_bs;
16
const char *filter_node_name, Error **errp)
15
BlockdevOnError on_error;
16
bool base_read_only;
17
+ bool chain_frozen;
18
char *backing_file_str;
19
} CommitBlockJob;
20
21
@@ -XXX,XX +XXX,XX @@ static int commit_prepare(Job *job)
22
{
17
{
23
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
18
CommitBlockJob *s;
24
19
- BlockReopenQueue *reopen_queue = NULL;
25
+ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
20
int orig_base_flags;
26
+ s->chain_frozen = false;
21
BlockDriverState *iter;
27
+
22
BlockDriverState *commit_top_bs = NULL;
28
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
29
* the normal backing chain can be restored. */
30
blk_unref(s->base);
31
@@ -XXX,XX +XXX,XX @@ static void commit_abort(Job *job)
32
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
33
BlockDriverState *top_bs = blk_bs(s->top);
34
35
+ if (s->chain_frozen) {
36
+ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs);
37
+ }
38
+
39
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
40
bdrv_ref(top_bs);
41
bdrv_ref(s->commit_top_bs);
42
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
43
}
24
/* convert base to r/w, if necessary */
44
}
25
orig_base_flags = bdrv_get_flags(base);
45
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
46
+ if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) {
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
47
+ goto fail;
28
- orig_base_flags | BDRV_O_RDWR);
48
+ }
29
- }
49
+ s->chain_frozen = true;
30
-
50
+
31
- if (reopen_queue) {
51
ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
52
if (ret < 0) {
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
53
goto fail;
34
if (local_err != NULL) {
54
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
35
error_propagate(errp, local_err);
55
return;
36
goto fail;
56
57
fail:
58
+ if (s->chain_frozen) {
59
+ bdrv_unfreeze_backing_chain(commit_top_bs, base);
60
+ }
61
if (s->base) {
62
blk_unref(s->base);
63
}
64
--
37
--
65
2.20.1
38
2.13.6
66
39
67
40
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.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
This parameter has been unused since 1a63a907507fbbcfaee3f622907ec244b
5
So instead of draining the device only in bdrv_reopen_multiple(),
6
require that callers already drained all affected nodes, and assert this
7
in bdrv_reopen_queue().
4
8
5
Signed-off-by: Alberto Garcia <berto@igalia.com>
6
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>
7
---
11
---
8
include/block/block.h | 2 +-
12
block.c | 23 ++++++++++++++++-------
9
block.c | 4 ++--
13
block/replication.c | 6 ++++++
10
block/replication.c | 3 +--
14
qemu-io-cmds.c | 3 +++
11
qemu-io-cmds.c | 2 +-
15
3 files changed, 25 insertions(+), 7 deletions(-)
12
4 files changed, 5 insertions(+), 6 deletions(-)
13
16
14
diff --git a/include/block/block.h b/include/block/block.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/block.h
17
+++ b/include/block/block.h
18
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
19
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
20
BlockDriverState *bs, QDict *options,
21
bool keep_old_opts);
22
-int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp);
23
+int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
24
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
25
Error **errp);
26
int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
27
diff --git a/block.c b/block.c
17
diff --git a/block.c b/block.c
28
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
29
--- a/block.c
19
--- a/block.c
30
+++ b/block.c
20
+++ b/block.c
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
22
* returns a pointer to bs_queue, which is either the newly allocated
23
* bs_queue, or the existing bs_queue being used.
24
*
25
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
26
*/
27
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
28
BlockDriverState *bs,
29
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
30
BdrvChild *child;
31
QDict *old_options, *explicit_options;
32
33
+ /* Make sure that the caller remembered to use a drained section. This is
34
+ * important to avoid graph changes between the recursive queuing here and
35
+ * bdrv_reopen_multiple(). */
36
+ assert(bs->quiesce_counter > 0);
37
+
38
if (bs_queue == NULL) {
39
bs_queue = g_new0(BlockReopenQueue, 1);
40
QSIMPLEQ_INIT(bs_queue);
31
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
41
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
32
* All affected nodes must be drained between bdrv_reopen_queue() and
42
* If all devices prepare successfully, then the changes are committed
33
* bdrv_reopen_multiple().
43
* to all devices.
44
*
45
+ * All affected nodes must be drained between bdrv_reopen_queue() and
46
+ * bdrv_reopen_multiple().
34
*/
47
*/
35
-int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
48
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
36
+int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
49
{
50
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
51
52
assert(bs_queue != NULL);
53
54
- aio_context_release(ctx);
55
- bdrv_drain_all_begin();
56
- aio_context_acquire(ctx);
57
-
58
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
59
+ assert(bs_entry->state.bs->quiesce_counter > 0);
60
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
61
error_propagate(errp, local_err);
62
goto cleanup;
63
@@ -XXX,XX +XXX,XX @@ cleanup:
64
}
65
g_free(bs_queue);
66
67
- bdrv_drain_all_end();
68
-
69
return ret;
70
}
71
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
37
{
73
{
38
int ret = -1;
74
int ret = -1;
39
BlockReopenQueueEntry *bs_entry, *next;
75
Error *local_err = NULL;
40
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
41
77
+ BlockReopenQueue *queue;
42
bdrv_subtree_drained_begin(bs);
78
43
queue = bdrv_reopen_queue(NULL, bs, opts, true);
79
+ bdrv_subtree_drained_begin(bs);
44
- ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, errp);
80
+
45
+ ret = bdrv_reopen_multiple(queue, errp);
81
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
46
bdrv_subtree_drained_end(bs);
82
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
47
83
if (local_err != NULL) {
84
error_propagate(errp, local_err);
85
}
86
+
87
+ bdrv_subtree_drained_end(bs);
88
+
48
return ret;
89
return ret;
90
}
91
49
diff --git a/block/replication.c b/block/replication.c
92
diff --git a/block/replication.c b/block/replication.c
50
index XXXXXXX..XXXXXXX 100644
93
index XXXXXXX..XXXXXXX 100644
51
--- a/block/replication.c
94
--- a/block/replication.c
52
+++ b/block/replication.c
95
+++ b/block/replication.c
53
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
96
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
97
new_secondary_flags = s->orig_secondary_flags;
54
}
98
}
55
99
56
if (reopen_queue) {
100
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
57
- bdrv_reopen_multiple(bdrv_get_aio_context(bs),
101
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
58
- reopen_queue, &local_err);
102
+
59
+ bdrv_reopen_multiple(reopen_queue, &local_err);
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);
60
error_propagate(errp, local_err);
108
error_propagate(errp, local_err);
61
}
109
}
62
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)
63
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
116
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
64
index XXXXXXX..XXXXXXX 100644
117
index XXXXXXX..XXXXXXX 100644
65
--- a/qemu-io-cmds.c
118
--- a/qemu-io-cmds.c
66
+++ b/qemu-io-cmds.c
119
+++ b/qemu-io-cmds.c
67
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
120
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
68
121
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
69
bdrv_subtree_drained_begin(bs);
122
qemu_opts_reset(&reopen_opts);
70
brq = bdrv_reopen_queue(NULL, bs, opts, true);
123
71
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
124
+ bdrv_subtree_drained_begin(bs);
72
+ bdrv_reopen_multiple(brq, &local_err);
125
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
73
bdrv_subtree_drained_end(bs);
126
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
74
127
+ bdrv_subtree_drained_end(bs);
128
+
75
if (local_err) {
129
if (local_err) {
130
error_report_err(local_err);
131
} else {
76
--
132
--
77
2.20.1
133
2.13.6
78
134
79
135
diff view generated by jsdifflib