1
The following changes since commit d9bbfea646e86426d549bd612cd9f91e49aa50c2:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
Merge remote-tracking branch 'remotes/riscv/tags/riscv-qemu-upstream-v8.2' into staging (2018-03-09 10:58:57 +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 a1be5921e35dcf84ce9e3c9a5c3029cea530a60b:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
Merge remote-tracking branch 'mreitz/tags/pull-block-2018-03-09' into queue-block (2018-03-09 16:09:06 +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
15
16
----------------------------------------------------------------
16
----------------------------------------------------------------
17
Alberto Garcia (8):
17
Doug Gale (1):
18
qcow2: Generalize validate_table_offset() into qcow2_validate_table()
18
nvme: Add tracing
19
qcow2: Check L1 table offset in qcow2_snapshot_load_tmp()
20
qcow2: Check L1 table parameters in qcow2_expand_zero_clusters()
21
qcow2: Check snapshot L1 tables in qcow2_check_metadata_overlap()
22
qcow2: Check snapshot L1 table in qcow2_snapshot_goto()
23
qcow2: Check snapshot L1 table in qcow2_snapshot_delete()
24
qcow2: Make qemu-img check detect corrupted L1 tables in snapshots
25
iotests: Tweak 030 in order to trigger a race condition with parallel jobs
26
19
27
Daniel P. Berrangé (1):
20
Edgar Kaziakhmedov (1):
28
block: implement the bdrv_reopen_prepare helper for LUKS driver
21
qcow2: get rid of qcow2_backing_read1 routine
29
30
Eric Blake (1):
31
iotests: Mark all tests executable
32
22
33
Fam Zheng (2):
23
Fam Zheng (2):
34
iotests: Test creating overlay when guest running
24
block: Open backing image in force share mode for size probe
35
iotests: Skip test for ENOMEM error
25
block: Remove unused bdrv_requests_pending
36
26
37
Kevin Wolf (38):
27
John Snow (1):
38
block/qapi: Introduce BlockdevCreateOptions
28
iotests: fix 197 for vpc
39
block/qapi: Add qcow2 create options to schema
40
qcow2: Rename qcow2_co_create2() to qcow2_co_create()
41
qcow2: Let qcow2_create() handle protocol layer
42
qcow2: Pass BlockdevCreateOptions to qcow2_co_create()
43
qcow2: Use BlockdevRef in qcow2_co_create()
44
qcow2: Use QCryptoBlockCreateOptions in qcow2_co_create()
45
qcow2: Handle full/falloc preallocation in qcow2_co_create()
46
util: Add qemu_opts_to_qdict_filtered()
47
test-qemu-opts: Test qemu_opts_append()
48
test-qemu-opts: Test qemu_opts_to_qdict_filtered()
49
qdict: Introduce qdict_rename_keys()
50
qcow2: Use visitor for options in qcow2_create()
51
block: Make bdrv_is_whitelisted() public
52
block: x-blockdev-create QMP command
53
file-posix: Support .bdrv_co_create
54
file-win32: Support .bdrv_co_create
55
gluster: Support .bdrv_co_create
56
rbd: Fix use after free in qemu_rbd_set_keypairs() error path
57
rbd: Factor out qemu_rbd_connect()
58
rbd: Remove non-schema options from runtime_opts
59
rbd: Pass BlockdevOptionsRbd to qemu_rbd_connect()
60
rbd: Support .bdrv_co_create
61
rbd: Assign s->snap/image_name in qemu_rbd_open()
62
rbd: Use qemu_rbd_connect() in qemu_rbd_do_create()
63
nfs: Use QAPI options in nfs_client_open()
64
nfs: Support .bdrv_co_create
65
sheepdog: QAPIfy "redundancy" create option
66
sheepdog: Support .bdrv_co_create
67
ssh: Use QAPI BlockdevOptionsSsh object
68
ssh: QAPIfy host-key-check option
69
ssh: Pass BlockdevOptionsSsh to connect_to_ssh()
70
ssh: Support .bdrv_co_create
71
file-posix: Fix no-op bdrv_truncate() with falloc preallocation
72
block: Fail bdrv_truncate() with negative size
73
qemu-iotests: Test qcow2 over file image creation with QMP
74
qemu-iotests: Test ssh image creation over QMP
75
Merge remote-tracking branch 'mreitz/tags/pull-block-2018-03-09' into queue-block
76
29
77
Paolo Bonzini (6):
30
Kevin Wolf (27):
78
qcow2: introduce qcow2_write_caches and qcow2_flush_caches
31
block: Formats don't need CONSISTENT_READ with NO_IO
79
qcow2: fix flushing after dirty bitmap metadata writes
32
block: Make bdrv_drain_invoke() recursive
80
qcow2: make qcow2_do_open a coroutine_fn
33
block: Call .drain_begin only once in bdrv_drain_all_begin()
81
qed: make bdrv_qed_do_open a coroutine_fn
34
test-bdrv-drain: Test BlockDriver callbacks for drain
82
block: convert bdrv_invalidate_cache callback to coroutine_fn
35
block: bdrv_drain_recurse(): Remove unused begin parameter
83
block: convert bdrv_check callback to coroutine_fn
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
84
58
85
Stefan Hajnoczi (1):
59
Thomas Huth (3):
86
qemu-iotests: fix 203 migration completion race
60
block: Remove the obsolete -drive boot=on|off parameter
61
block: Remove the deprecated -hdachs option
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
87
63
88
qapi/block-core.json | 326 +++++++++++++++++++++++++-
64
qapi/block-core.json | 4 +
89
block/qcow2.h | 12 +-
65
block/qcow2.h | 3 -
90
include/block/block.h | 2 +
66
include/block/block.h | 15 +-
91
include/block/block_int.h | 13 +-
67
include/block/block_int.h | 6 +-
92
include/qapi/qmp/qdict.h | 6 +
68
block.c | 75 ++++-
93
include/qemu/option.h | 2 +
69
block/commit.c | 8 +-
94
block.c | 138 +++++++++++-
70
block/io.c | 164 +++++++---
95
block/create.c | 76 +++++++
71
block/qcow2.c | 51 +--
96
block/crypto.c | 7 +
72
block/replication.c | 6 +
97
block/file-posix.c | 93 +++++---
73
blockdev.c | 11 -
98
block/file-win32.c | 47 +++-
74
blockjob.c | 22 +-
99
block/gluster.c | 135 +++++++----
75
hmp.c | 6 -
100
block/iscsi.c | 6 +-
76
hw/block/nvme.c | 349 +++++++++++++++++----
101
block/nfs.c | 244 +++++++++-----------
77
qemu-io-cmds.c | 3 +
102
block/parallels.c | 17 +-
78
tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++
103
block/qcow2-bitmap.c | 4 +-
79
vl.c | 86 +-----
104
block/qcow2-cluster.c | 24 +-
80
hw/block/trace-events | 93 ++++++
105
block/qcow2-refcount.c | 52 ++++-
81
qemu-doc.texi | 29 +-
106
block/qcow2-snapshot.c | 24 +-
82
qemu-options.hx | 19 +-
107
block/qcow2.c | 552 ++++++++++++++++++++++++++++-----------------
83
tests/Makefile.include | 2 +
108
block/qed-check.c | 1 +
84
tests/qemu-iotests/197 | 4 +
109
block/qed-table.c | 26 +--
85
tests/qemu-iotests/common.filter | 3 +-
110
block/qed.c | 66 ++++--
86
22 files changed, 1294 insertions(+), 316 deletions(-)
111
block/rbd.c | 403 +++++++++++++++++----------------
87
create mode 100644 tests/test-bdrv-drain.c
112
block/sheepdog.c | 321 ++++++++++++++++++--------
113
block/ssh.c | 290 +++++++++++++-----------
114
block/vdi.c | 6 +-
115
block/vhdx.c | 7 +-
116
block/vmdk.c | 7 +-
117
qobject/qdict.c | 34 +++
118
tests/check-qdict.c | 129 +++++++++++
119
tests/test-qemu-opts.c | 253 +++++++++++++++++++++
120
util/qemu-option.c | 42 +++-
121
block/Makefile.objs | 2 +-
122
tests/qemu-iotests/030 | 52 ++++-
123
tests/qemu-iotests/030.out | 4 +-
124
tests/qemu-iotests/049.out | 8 +-
125
tests/qemu-iotests/059 | 5 +-
126
tests/qemu-iotests/080 | 22 +-
127
tests/qemu-iotests/080.out | 58 ++++-
128
tests/qemu-iotests/096 | 0
129
tests/qemu-iotests/112.out | 4 +-
130
tests/qemu-iotests/124 | 0
131
tests/qemu-iotests/129 | 0
132
tests/qemu-iotests/132 | 0
133
tests/qemu-iotests/136 | 0
134
tests/qemu-iotests/139 | 0
135
tests/qemu-iotests/148 | 0
136
tests/qemu-iotests/152 | 0
137
tests/qemu-iotests/153 | 8 +-
138
tests/qemu-iotests/153.out | 7 +-
139
tests/qemu-iotests/163 | 0
140
tests/qemu-iotests/203 | 15 +-
141
tests/qemu-iotests/203.out | 5 +
142
tests/qemu-iotests/205 | 0
143
tests/qemu-iotests/206 | 436 +++++++++++++++++++++++++++++++++++
144
tests/qemu-iotests/206.out | 209 +++++++++++++++++
145
tests/qemu-iotests/207 | 261 +++++++++++++++++++++
146
tests/qemu-iotests/207.out | 75 ++++++
147
tests/qemu-iotests/group | 2 +
148
60 files changed, 3574 insertions(+), 964 deletions(-)
149
create mode 100644 block/create.c
150
mode change 100644 => 100755 tests/qemu-iotests/096
151
mode change 100644 => 100755 tests/qemu-iotests/124
152
mode change 100644 => 100755 tests/qemu-iotests/129
153
mode change 100644 => 100755 tests/qemu-iotests/132
154
mode change 100644 => 100755 tests/qemu-iotests/136
155
mode change 100644 => 100755 tests/qemu-iotests/139
156
mode change 100644 => 100755 tests/qemu-iotests/148
157
mode change 100644 => 100755 tests/qemu-iotests/152
158
mode change 100644 => 100755 tests/qemu-iotests/163
159
mode change 100644 => 100755 tests/qemu-iotests/205
160
create mode 100755 tests/qemu-iotests/206
161
create mode 100644 tests/qemu-iotests/206.out
162
create mode 100755 tests/qemu-iotests/207
163
create mode 100644 tests/qemu-iotests/207.out
164
88
diff view generated by jsdifflib
Deleted patch
1
From: "Daniel P. Berrange" <berrange@redhat.com>
2
1
3
If the bdrv_reopen_prepare helper isn't provided, the qemu-img commit
4
command fails to re-open the base layer after committing changes into
5
it. Provide a no-op implementation for the LUKS driver, since there
6
is not any custom work that needs doing to re-open it.
7
8
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block/crypto.c | 7 +++++++
12
1 file changed, 7 insertions(+)
13
14
diff --git a/block/crypto.c b/block/crypto.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/crypto.c
17
+++ b/block/crypto.c
18
@@ -XXX,XX +XXX,XX @@ static void block_crypto_close(BlockDriverState *bs)
19
qcrypto_block_free(crypto->block);
20
}
21
22
+static int block_crypto_reopen_prepare(BDRVReopenState *state,
23
+ BlockReopenQueue *queue, Error **errp)
24
+{
25
+ /* nothing needs checking */
26
+ return 0;
27
+}
28
29
/*
30
* 1 MB bounce buffer gives good performance / memory tradeoff
31
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_crypto_luks = {
32
.bdrv_truncate = block_crypto_truncate,
33
.create_opts = &block_crypto_create_opts_luks,
34
35
+ .bdrv_reopen_prepare = block_crypto_reopen_prepare,
36
.bdrv_refresh_limits = block_crypto_refresh_limits,
37
.bdrv_co_preadv = block_crypto_co_preadv,
38
.bdrv_co_pwritev = block_crypto_co_pwritev,
39
--
40
2.13.6
41
42
diff view generated by jsdifflib
1
Most callers have their own checks, but something like this should also
1
Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently
2
be checked centrally. As it happens, x-blockdev-create can pass negative
2
in use as a mirror target. It is not enough for image formats, though,
3
image sizes to format drivers (because there is no QAPI type that would
3
as these still unconditionally request BLK_PERM_CONSISTENT_READ.
4
reject negative numbers) and triggers the check added by this patch.
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.
5
10
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
---
12
---
10
block.c | 5 +++++
13
block.c | 6 +++++-
11
1 file changed, 5 insertions(+)
14
1 file changed, 5 insertions(+), 1 deletion(-)
12
15
13
diff --git a/block.c b/block.c
16
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
18
--- a/block.c
16
+++ b/block.c
19
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
18
error_setg(errp, "No medium inserted");
21
assert(role == &child_backing || role == &child_file);
19
return -ENOMEDIUM;
22
20
}
23
if (!backing) {
21
+ if (offset < 0) {
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
22
+ error_setg(errp, "Image size cannot be negative");
23
+ return -EINVAL;
24
+ }
25
+
25
+
26
if (!drv->bdrv_truncate) {
26
/* Apart from the modifications below, the same permissions are
27
if (bs->file && drv->is_filter) {
27
* forwarded and left alone as for filters */
28
return bdrv_truncate(bs->file, offset, prealloc, errp);
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
30
31
/* bs->file always needs to be consistent because of the metadata. We
32
* can never allow other users to resize or write to it. */
33
- perm |= BLK_PERM_CONSISTENT_READ;
34
+ if (!(flags & BDRV_O_NO_IO)) {
35
+ perm |= BLK_PERM_CONSISTENT_READ;
36
+ }
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
38
} else {
39
/* We want consistent read from backing files if the parent needs it.
29
--
40
--
30
2.13.6
41
2.13.6
31
42
32
43
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to ssh, which enables
1
From: John Snow <jsnow@redhat.com>
2
image creation over QMP.
3
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>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
6
---
13
---
7
qapi/block-core.json | 16 +++++++++-
14
tests/qemu-iotests/197 | 4 ++++
8
block/ssh.c | 83 ++++++++++++++++++++++++++++++----------------------
15
tests/qemu-iotests/common.filter | 3 ++-
9
2 files changed, 63 insertions(+), 36 deletions(-)
16
2 files changed, 6 insertions(+), 1 deletion(-)
10
17
11
diff --git a/qapi/block-core.json b/qapi/block-core.json
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
12
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
35
--- a/tests/qemu-iotests/common.filter
14
+++ b/qapi/block-core.json
36
+++ b/tests/qemu-iotests/common.filter
15
@@ -XXX,XX +XXX,XX @@
37
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
16
'*object-size': 'size' } }
38
-e "s# log_size=[0-9]\\+##g" \
17
39
-e "s# refcount_bits=[0-9]\\+##g" \
18
##
40
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
19
+# @BlockdevCreateOptionsSsh:
41
- -e "s# iter-time=[0-9]\\+##g"
20
+#
42
+ -e "s# iter-time=[0-9]\\+##g" \
21
+# Driver specific image creation options for SSH.
43
+ -e "s# force_size=\\(on\\|off\\)##g"
22
+#
23
+# @location Where to store the new image file
24
+# @size Size of the virtual disk in bytes
25
+#
26
+# Since: 2.12
27
+##
28
+{ 'struct': 'BlockdevCreateOptionsSsh',
29
+ 'data': { 'location': 'BlockdevOptionsSsh',
30
+ 'size': 'size' } }
31
+
32
+##
33
# @BlockdevCreateNotSupported:
34
#
35
# This is used for all drivers that don't support creating images.
36
@@ -XXX,XX +XXX,XX @@
37
'rbd': 'BlockdevCreateOptionsRbd',
38
'replication': 'BlockdevCreateNotSupported',
39
'sheepdog': 'BlockdevCreateOptionsSheepdog',
40
- 'ssh': 'BlockdevCreateNotSupported',
41
+ 'ssh': 'BlockdevCreateOptionsSsh',
42
'throttle': 'BlockdevCreateNotSupported',
43
'vdi': 'BlockdevCreateNotSupported',
44
'vhdx': 'BlockdevCreateNotSupported',
45
diff --git a/block/ssh.c b/block/ssh.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/block/ssh.c
48
+++ b/block/ssh.c
49
@@ -XXX,XX +XXX,XX @@ static QemuOptsList ssh_create_opts = {
50
}
51
};
52
53
+static int ssh_co_create(BlockdevCreateOptions *options, Error **errp)
54
+{
55
+ BlockdevCreateOptionsSsh *opts = &options->u.ssh;
56
+ BDRVSSHState s;
57
+ int ret;
58
+
59
+ assert(options->driver == BLOCKDEV_DRIVER_SSH);
60
+
61
+ ssh_state_init(&s);
62
+
63
+ ret = connect_to_ssh(&s, opts->location,
64
+ LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
65
+ LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
66
+ 0644, errp);
67
+ if (ret < 0) {
68
+ goto fail;
69
+ }
70
+
71
+ if (opts->size > 0) {
72
+ ret = ssh_grow_file(&s, opts->size, errp);
73
+ if (ret < 0) {
74
+ goto fail;
75
+ }
76
+ }
77
+
78
+ ret = 0;
79
+fail:
80
+ ssh_state_free(&s);
81
+ return ret;
82
+}
83
+
84
static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
85
Error **errp)
86
{
87
- int r, ret;
88
- int64_t total_size = 0;
89
+ BlockdevCreateOptions *create_options;
90
+ BlockdevCreateOptionsSsh *ssh_opts;
91
+ int ret;
92
QDict *uri_options = NULL;
93
- BlockdevOptionsSsh *ssh_opts = NULL;
94
- BDRVSSHState s;
95
96
- ssh_state_init(&s);
97
+ create_options = g_new0(BlockdevCreateOptions, 1);
98
+ create_options->driver = BLOCKDEV_DRIVER_SSH;
99
+ ssh_opts = &create_options->u.ssh;
100
101
/* Get desired file size. */
102
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
103
- BDRV_SECTOR_SIZE);
104
- DPRINTF("total_size=%" PRIi64, total_size);
105
+ ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
106
+ BDRV_SECTOR_SIZE);
107
+ DPRINTF("total_size=%" PRIi64, ssh_opts->size);
108
109
uri_options = qdict_new();
110
- r = parse_uri(filename, uri_options, errp);
111
- if (r < 0) {
112
- ret = r;
113
+ ret = parse_uri(filename, uri_options, errp);
114
+ if (ret < 0) {
115
goto out;
116
}
117
118
- ssh_opts = ssh_parse_options(uri_options, errp);
119
- if (ssh_opts == NULL) {
120
+ ssh_opts->location = ssh_parse_options(uri_options, errp);
121
+ if (ssh_opts->location == NULL) {
122
ret = -EINVAL;
123
goto out;
124
}
125
126
- r = connect_to_ssh(&s, ssh_opts,
127
- LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
128
- LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
129
- 0644, errp);
130
- if (r < 0) {
131
- ret = r;
132
- goto out;
133
- }
134
-
135
- if (total_size > 0) {
136
- ret = ssh_grow_file(&s, total_size, errp);
137
- if (ret < 0) {
138
- goto out;
139
- }
140
- }
141
-
142
- ret = 0;
143
+ ret = ssh_co_create(create_options, errp);
144
145
out:
146
- ssh_state_free(&s);
147
- if (uri_options != NULL) {
148
- QDECREF(uri_options);
149
- }
150
- qapi_free_BlockdevOptionsSsh(ssh_opts);
151
+ QDECREF(uri_options);
152
+ qapi_free_BlockdevCreateOptions(create_options);
153
return ret;
154
}
44
}
155
45
156
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_ssh = {
46
_filter_img_info()
157
.instance_size = sizeof(BDRVSSHState),
158
.bdrv_parse_filename = ssh_parse_filename,
159
.bdrv_file_open = ssh_file_open,
160
+ .bdrv_co_create = ssh_co_create,
161
.bdrv_co_create_opts = ssh_co_create_opts,
162
.bdrv_close = ssh_close,
163
.bdrv_has_zero_init = ssh_has_zero_init,
164
--
47
--
165
2.13.6
48
2.13.6
166
49
167
50
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
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.
2
4
3
It is called from qcow2_invalidate_cache in coroutine context (incoming
5
One reason for this is that bdrv_drain_recurse() can be called multiple
4
migration runs in a coroutine), so it's cleaner if metadata is always
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
5
loaded from a coroutine.
7
once. The separation is necessary to fix this bug.
6
8
7
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9
The other reason is that we intend to go to a model where we call all
8
Message-Id: <1516279431-30424-4-git-send-email-pbonzini@redhat.com>
10
driver callbacks first, and only then start polling. This is not fully
9
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
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
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
19
---
12
block/qcow2.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
20
block/io.c | 14 +++++++++++---
13
1 file changed, 41 insertions(+), 5 deletions(-)
21
1 file changed, 11 insertions(+), 3 deletions(-)
14
22
15
diff --git a/block/qcow2.c b/block/qcow2.c
23
diff --git a/block/io.c b/block/io.c
16
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
17
--- a/block/qcow2.c
25
--- a/block/io.c
18
+++ b/block/qcow2.c
26
+++ b/block/io.c
19
@@ -XXX,XX +XXX,XX @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options,
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
20
return ret;
28
bdrv_wakeup(bs);
21
}
29
}
22
30
23
-static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
24
- Error **errp)
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
25
+/* Called with s->lock held. */
26
+static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
27
+ int flags, Error **errp)
28
{
33
{
29
BDRVQcow2State *s = bs->opaque;
34
+ BdrvChild *child, *tmp;
30
unsigned int len, i;
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
31
@@ -XXX,XX +XXX,XX @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
36
32
}
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);
33
}
61
}
34
62
35
- /* Initialise locks */
63
+ bdrv_drain_invoke(bs, true);
36
- qemu_co_mutex_init(&s->lock);
64
bdrv_drain_recurse(bs, true);
37
bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0;
38
39
/* Repair image if dirty */
40
@@ -XXX,XX +XXX,XX @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
41
return ret;
42
}
65
}
43
66
44
+typedef struct QCow2OpenCo {
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
45
+ BlockDriverState *bs;
46
+ QDict *options;
47
+ int flags;
48
+ Error **errp;
49
+ int ret;
50
+} QCow2OpenCo;
51
+
52
+static void coroutine_fn qcow2_open_entry(void *opaque)
53
+{
54
+ QCow2OpenCo *qoc = opaque;
55
+ BDRVQcow2State *s = qoc->bs->opaque;
56
+
57
+ qemu_co_mutex_lock(&s->lock);
58
+ qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
59
+ qemu_co_mutex_unlock(&s->lock);
60
+}
61
+
62
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
63
Error **errp)
64
{
65
+ BDRVQcow2State *s = bs->opaque;
66
+ QCow2OpenCo qoc = {
67
+ .bs = bs,
68
+ .options = options,
69
+ .flags = flags,
70
+ .errp = errp,
71
+ .ret = -EINPROGRESS
72
+ };
73
+
74
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
75
false, errp);
76
if (!bs->file) {
77
return -EINVAL;
78
}
68
}
79
69
80
- return qcow2_do_open(bs, options, flags, errp);
70
bdrv_parent_drained_end(bs);
81
+ /* Initialise locks */
71
+ bdrv_drain_invoke(bs, false);
82
+ qemu_co_mutex_init(&s->lock);
72
bdrv_drain_recurse(bs, false);
83
+
73
aio_enable_external(bdrv_get_aio_context(bs));
84
+ if (qemu_in_coroutine()) {
85
+ /* From bdrv_co_create. */
86
+ qcow2_open_entry(&qoc);
87
+ } else {
88
+ qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc));
89
+ BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
90
+ }
91
+ return qoc.ret;
92
}
74
}
93
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
94
static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
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
}
95
--
92
--
96
2.13.6
93
2.13.6
97
94
98
95
diff view generated by jsdifflib
1
If we want to include the invalid option name in the error message, we
1
bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver
2
can't free the string earlier than that.
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.
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.
3
10
4
Cc: qemu-stable@nongnu.org
11
Cc: qemu-stable@nongnu.org
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
14
---
9
block/rbd.c | 3 ++-
15
block/io.c | 3 +--
10
1 file changed, 2 insertions(+), 1 deletion(-)
16
1 file changed, 1 insertion(+), 2 deletions(-)
11
17
12
diff --git a/block/rbd.c b/block/rbd.c
18
diff --git a/block/io.c b/block/io.c
13
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
14
--- a/block/rbd.c
20
--- a/block/io.c
15
+++ b/block/rbd.c
21
+++ b/block/io.c
16
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
17
key = qstring_get_str(name);
23
aio_context_acquire(aio_context);
18
24
bdrv_parent_drained_begin(bs);
19
ret = rados_conf_set(cluster, key, qstring_get_str(value));
25
aio_disable_external(aio_context);
20
- QDECREF(name);
26
+ bdrv_drain_invoke(bs, true);
21
QDECREF(value);
27
aio_context_release(aio_context);
22
if (ret < 0) {
28
23
error_setg_errno(errp, -ret, "invalid conf option %s", key);
29
if (!g_slist_find(aio_ctxs, aio_context)) {
24
+ QDECREF(name);
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
25
ret = -EINVAL;
31
aio_context_acquire(aio_context);
26
break;
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
27
}
33
if (aio_context == bdrv_get_aio_context(bs)) {
28
+ QDECREF(name);
34
- /* FIXME Calling this multiple times is wrong */
29
}
35
- bdrv_drain_invoke(bs, true);
30
36
waited |= bdrv_drain_recurse(bs, true);
31
QDECREF(keypairs);
37
}
38
}
32
--
39
--
33
2.13.6
40
2.13.6
34
41
35
42
diff view generated by jsdifflib
1
This adds a synchronous x-blockdev-create QMP command that can create
1
This adds a test case that the BlockDriver callbacks for drain are
2
qcow2 images on a given node name.
2
called in bdrv_drained_all_begin/end(), and that both of them are called
3
3
exactly once.
4
We don't want to block while creating an image, so this is not the final
5
interface in all aspects, but BlockdevCreateOptionsQcow2 and
6
.bdrv_co_create() are what they actually might look like in the end. In
7
any case, this should be good enough to test whether we interpret
8
BlockdevCreateOptions as we should.
9
4
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
13
---
8
---
14
qapi/block-core.json | 12 ++++++++
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
15
include/block/block_int.h | 5 +++-
10
tests/Makefile.include | 2 +
16
block/create.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++
11
2 files changed, 139 insertions(+)
17
block/qcow2.c | 1 +
12
create mode 100644 tests/test-bdrv-drain.c
18
block/Makefile.objs | 2 +-
19
5 files changed, 94 insertions(+), 2 deletions(-)
20
create mode 100644 block/create.c
21
13
22
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/qapi/block-core.json
25
+++ b/qapi/block-core.json
26
@@ -XXX,XX +XXX,XX @@
27
} }
28
29
##
30
+# @x-blockdev-create:
31
+#
32
+# Create an image format on a given node.
33
+# TODO Replace with something asynchronous (block job?)
34
+#
35
+# Since: 2.12
36
+##
37
+{ 'command': 'x-blockdev-create',
38
+ 'data': 'BlockdevCreateOptions',
39
+ 'boxed': true }
40
+
41
+##
42
# @blockdev-open-tray:
43
#
44
# Opens a block device's tray. If there is a block driver state tree inserted as
45
diff --git a/include/block/block_int.h b/include/block/block_int.h
46
index XXXXXXX..XXXXXXX 100644
47
--- a/include/block/block_int.h
48
+++ b/include/block/block_int.h
49
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
50
int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
51
Error **errp);
52
void (*bdrv_close)(BlockDriverState *bs);
53
- int coroutine_fn (*bdrv_co_create_opts)(const char *filename, QemuOpts *opts,
54
+ int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts,
55
Error **errp);
56
+ int coroutine_fn (*bdrv_co_create_opts)(const char *filename,
57
+ QemuOpts *opts,
58
+ Error **errp);
59
int (*bdrv_make_empty)(BlockDriverState *bs);
60
61
void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options);
62
diff --git a/block/create.c b/block/create.c
63
new file mode 100644
15
new file mode 100644
64
index XXXXXXX..XXXXXXX
16
index XXXXXXX..XXXXXXX
65
--- /dev/null
17
--- /dev/null
66
+++ b/block/create.c
18
+++ b/tests/test-bdrv-drain.c
67
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
68
+/*
20
+/*
69
+ * Block layer code related to image creation
21
+ * Block node draining tests
70
+ *
22
+ *
71
+ * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
23
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
72
+ *
24
+ *
73
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
74
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * of this software and associated documentation files (the "Software"), to deal
75
+ * in the Software without restriction, including without limitation the rights
27
+ * in the Software without restriction, including without limitation the rights
76
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
...
...
88
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
89
+ * THE SOFTWARE.
41
+ * THE SOFTWARE.
90
+ */
42
+ */
91
+
43
+
92
+#include "qemu/osdep.h"
44
+#include "qemu/osdep.h"
93
+#include "block/block_int.h"
45
+#include "block/block.h"
94
+#include "qapi/qapi-commands-block-core.h"
46
+#include "sysemu/block-backend.h"
95
+#include "qapi/error.h"
47
+#include "qapi/error.h"
96
+
48
+
97
+typedef struct BlockdevCreateCo {
49
+typedef struct BDRVTestState {
98
+ BlockDriver *drv;
50
+ int drain_count;
99
+ BlockdevCreateOptions *opts;
51
+} BDRVTestState;
100
+ int ret;
101
+ Error **errp;
102
+} BlockdevCreateCo;
103
+
52
+
104
+static void coroutine_fn bdrv_co_create_co_entry(void *opaque)
53
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
105
+{
54
+{
106
+ BlockdevCreateCo *cco = opaque;
55
+ BDRVTestState *s = bs->opaque;
107
+ cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
56
+ s->drain_count++;
108
+}
57
+}
109
+
58
+
110
+void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
59
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
111
+{
60
+{
112
+ const char *fmt = BlockdevDriver_str(options->driver);
61
+ BDRVTestState *s = bs->opaque;
113
+ BlockDriver *drv = bdrv_find_format(fmt);
62
+ s->drain_count--;
114
+ Coroutine *co;
63
+}
115
+ BlockdevCreateCo cco;
116
+
64
+
117
+ /* If the driver is in the schema, we know that it exists. But it may not
65
+static void bdrv_test_close(BlockDriverState *bs)
118
+ * be whitelisted. */
66
+{
119
+ assert(drv);
67
+ BDRVTestState *s = bs->opaque;
120
+ if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) {
68
+ g_assert_cmpint(s->drain_count, >, 0);
121
+ error_setg(errp, "Driver is not whitelisted");
69
+}
122
+ return;
123
+ }
124
+
70
+
125
+ /* Call callback if it exists */
71
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
126
+ if (!drv->bdrv_co_create) {
72
+ uint64_t offset, uint64_t bytes,
127
+ error_setg(errp, "Driver does not support blockdev-create");
73
+ QEMUIOVector *qiov, int flags)
128
+ return;
74
+{
129
+ }
75
+ /* We want this request to stay until the polling loop in drain waits for
76
+ * it to complete. We need to sleep a while as bdrv_drain_invoke() comes
77
+ * first and polls its result, too, but it shouldn't accidentally complete
78
+ * this request yet. */
79
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
130
+
80
+
131
+ cco = (BlockdevCreateCo) {
81
+ return 0;
132
+ .drv = drv,
82
+}
133
+ .opts = options,
83
+
134
+ .ret = -EINPROGRESS,
84
+static BlockDriver bdrv_test = {
135
+ .errp = errp,
85
+ .format_name = "test",
86
+ .instance_size = sizeof(BDRVTestState),
87
+
88
+ .bdrv_close = bdrv_test_close,
89
+ .bdrv_co_preadv = bdrv_test_co_preadv,
90
+
91
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
92
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
93
+};
94
+
95
+static void aio_ret_cb(void *opaque, int ret)
96
+{
97
+ int *aio_ret = opaque;
98
+ *aio_ret = ret;
99
+}
100
+
101
+static void test_drv_cb_drain_all(void)
102
+{
103
+ BlockBackend *blk;
104
+ BlockDriverState *bs;
105
+ BDRVTestState *s;
106
+ BlockAIOCB *acb;
107
+ int aio_ret;
108
+
109
+ QEMUIOVector qiov;
110
+ struct iovec iov = {
111
+ .iov_base = NULL,
112
+ .iov_len = 0,
136
+ };
113
+ };
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
137
+
115
+
138
+ co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco);
116
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
139
+ qemu_coroutine_enter(co);
117
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
140
+ while (cco.ret == -EINPROGRESS) {
118
+ &error_abort);
141
+ aio_poll(qemu_get_aio_context(), true);
119
+ s = bs->opaque;
142
+ }
120
+ blk_insert_bs(blk, bs, &error_abort);
121
+
122
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
123
+ g_assert_cmpint(s->drain_count, ==, 0);
124
+ bdrv_drain_all_begin();
125
+ g_assert_cmpint(s->drain_count, ==, 1);
126
+ bdrv_drain_all_end();
127
+ g_assert_cmpint(s->drain_count, ==, 0);
128
+
129
+ /* Now do the same while a request is pending */
130
+ aio_ret = -EINPROGRESS;
131
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
132
+ g_assert(acb != NULL);
133
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
134
+
135
+ g_assert_cmpint(s->drain_count, ==, 0);
136
+ bdrv_drain_all_begin();
137
+ g_assert_cmpint(aio_ret, ==, 0);
138
+ g_assert_cmpint(s->drain_count, ==, 1);
139
+ bdrv_drain_all_end();
140
+ g_assert_cmpint(s->drain_count, ==, 0);
141
+
142
+ bdrv_unref(bs);
143
+ blk_unref(blk);
143
+}
144
+}
144
diff --git a/block/qcow2.c b/block/qcow2.c
145
+
146
+int main(int argc, char **argv)
147
+{
148
+ bdrv_init();
149
+ qemu_init_main_loop(&error_abort);
150
+
151
+ g_test_init(&argc, &argv, NULL);
152
+
153
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
154
+
155
+ return g_test_run();
156
+}
157
diff --git a/tests/Makefile.include b/tests/Makefile.include
145
index XXXXXXX..XXXXXXX 100644
158
index XXXXXXX..XXXXXXX 100644
146
--- a/block/qcow2.c
159
--- a/tests/Makefile.include
147
+++ b/block/qcow2.c
160
+++ b/tests/Makefile.include
148
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
149
.bdrv_join_options = qcow2_join_options,
162
gcov-files-test-hbitmap-y = util/hbitmap.c
150
.bdrv_child_perm = bdrv_format_default_perms,
163
check-unit-y += tests/test-hbitmap$(EXESUF)
151
.bdrv_co_create_opts = qcow2_co_create_opts,
164
gcov-files-test-hbitmap-y = blockjob.c
152
+ .bdrv_co_create = qcow2_co_create,
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
153
.bdrv_has_zero_init = bdrv_has_zero_init_1,
166
check-unit-y += tests/test-blockjob$(EXESUF)
154
.bdrv_co_block_status = qcow2_co_block_status,
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
155
168
check-unit-y += tests/test-x86-cpuid$(EXESUF)
156
diff --git a/block/Makefile.objs b/block/Makefile.objs
169
@@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
157
index XXXXXXX..XXXXXXX 100644
170
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
158
--- a/block/Makefile.objs
171
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
159
+++ b/block/Makefile.objs
172
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
160
@@ -XXX,XX +XXX,XX @@ block-obj-y += block-backend.o snapshot.o qapi.o
173
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
161
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
174
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
162
block-obj-$(CONFIG_POSIX) += file-posix.o
175
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
163
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
176
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
164
-block-obj-y += null.o mirror.o commit.o io.o
165
+block-obj-y += null.o mirror.o commit.o io.o create.o
166
block-obj-y += throttle-groups.o
167
block-obj-$(CONFIG_LINUX) += nvme.o
168
169
--
177
--
170
2.13.6
178
2.13.6
171
179
172
180
diff view generated by jsdifflib
1
This makes the host-key-check option available in blockdev-add.
1
Now that the bdrv_drain_invoke() calls are pulled up to the callers of
2
bdrv_drain_recurse(), the 'begin' parameter isn't needed any more.
2
3
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
---
6
---
6
qapi/block-core.json | 63 +++++++++++++++++++++++++++++++++++--
7
block/io.c | 12 ++++++------
7
block/ssh.c | 88 +++++++++++++++++++++++++++++++++-------------------
8
1 file changed, 6 insertions(+), 6 deletions(-)
8
2 files changed, 117 insertions(+), 34 deletions(-)
9
9
10
diff --git a/qapi/block-core.json b/qapi/block-core.json
10
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
12
--- a/qapi/block-core.json
12
--- a/block/io.c
13
+++ b/qapi/block-core.json
13
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
15
'*encrypt': 'BlockdevQcow2Encryption' } }
15
}
16
17
##
18
+# @SshHostKeyCheckMode:
19
+#
20
+# @none Don't check the host key at all
21
+# @hash Compare the host key with a given hash
22
+# @known_hosts Check the host key against the known_hosts file
23
+#
24
+# Since: 2.12
25
+##
26
+{ 'enum': 'SshHostKeyCheckMode',
27
+ 'data': [ 'none', 'hash', 'known_hosts' ] }
28
+
29
+##
30
+# @SshHostKeyCheckHashType:
31
+#
32
+# @md5 The given hash is an md5 hash
33
+# @sha1 The given hash is an sha1 hash
34
+#
35
+# Since: 2.12
36
+##
37
+{ 'enum': 'SshHostKeyCheckHashType',
38
+ 'data': [ 'md5', 'sha1' ] }
39
+
40
+##
41
+# @SshHostKeyHash:
42
+#
43
+# @type The hash algorithm used for the hash
44
+# @hash The expected hash value
45
+#
46
+# Since: 2.12
47
+##
48
+{ 'struct': 'SshHostKeyHash',
49
+ 'data': { 'type': 'SshHostKeyCheckHashType',
50
+ 'hash': 'str' }}
51
+
52
+##
53
+# @SshHostKeyDummy:
54
+#
55
+# For those union branches that don't need additional fields.
56
+#
57
+# Since: 2.12
58
+##
59
+{ 'struct': 'SshHostKeyDummy',
60
+ 'data': {} }
61
+
62
+##
63
+# @SshHostKeyCheck:
64
+#
65
+# Since: 2.12
66
+##
67
+{ 'union': 'SshHostKeyCheck',
68
+ 'base': { 'mode': 'SshHostKeyCheckMode' },
69
+ 'discriminator': 'mode',
70
+ 'data': { 'none': 'SshHostKeyDummy',
71
+ 'hash': 'SshHostKeyHash',
72
+ 'known_hosts': 'SshHostKeyDummy' } }
73
+
74
+##
75
# @BlockdevOptionsSsh:
76
#
77
# @server: host address
78
@@ -XXX,XX +XXX,XX @@
79
# @user: user as which to connect, defaults to current
80
# local user name
81
#
82
-# TODO: Expose the host_key_check option in QMP
83
+# @host-key-check: Defines how and what to check the host key against
84
+# (default: known_hosts)
85
#
86
# Since: 2.9
87
##
88
{ 'struct': 'BlockdevOptionsSsh',
89
'data': { 'server': 'InetSocketAddress',
90
'path': 'str',
91
- '*user': 'str' } }
92
+ '*user': 'str',
93
+ '*host-key-check': 'SshHostKeyCheck' } }
94
95
96
##
97
diff --git a/block/ssh.c b/block/ssh.c
98
index XXXXXXX..XXXXXXX 100644
99
--- a/block/ssh.c
100
+++ b/block/ssh.c
101
@@ -XXX,XX +XXX,XX @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
102
}
16
}
103
17
104
static int check_host_key(BDRVSSHState *s, const char *host, int port,
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
105
- const char *host_key_check, Error **errp)
19
+static bool bdrv_drain_recurse(BlockDriverState *bs)
106
+ SshHostKeyCheck *hkc, Error **errp)
107
{
20
{
108
- /* host_key_check=no */
21
BdrvChild *child, *tmp;
109
- if (strcmp(host_key_check, "no") == 0) {
22
bool waited;
110
- return 0;
23
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
111
- }
24
*/
112
+ SshHostKeyCheckMode mode;
25
bdrv_ref(bs);
113
26
}
114
- /* host_key_check=md5:xx:yy:zz:... */
27
- waited |= bdrv_drain_recurse(bs, begin);
115
- if (strncmp(host_key_check, "md5:", 4) == 0) {
28
+ waited |= bdrv_drain_recurse(bs);
116
- return check_host_key_hash(s, &host_key_check[4],
29
if (in_main_loop) {
117
- LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
30
bdrv_unref(bs);
118
- }
31
}
119
-
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
120
- /* host_key_check=sha1:xx:yy:zz:... */
121
- if (strncmp(host_key_check, "sha1:", 5) == 0) {
122
- return check_host_key_hash(s, &host_key_check[5],
123
- LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
124
+ if (hkc) {
125
+ mode = hkc->mode;
126
+ } else {
127
+ mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS;
128
}
33
}
129
34
130
- /* host_key_check=yes */
35
bdrv_drain_invoke(bs, true);
131
- if (strcmp(host_key_check, "yes") == 0) {
36
- bdrv_drain_recurse(bs, true);
132
+ switch (mode) {
37
+ bdrv_drain_recurse(bs);
133
+ case SSH_HOST_KEY_CHECK_MODE_NONE:
38
}
134
+ return 0;
39
135
+ case SSH_HOST_KEY_CHECK_MODE_HASH:
40
void bdrv_drained_end(BlockDriverState *bs)
136
+ if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
41
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
137
+ return check_host_key_hash(s, hkc->u.hash.hash,
42
138
+ LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
43
bdrv_parent_drained_end(bs);
139
+ } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
44
bdrv_drain_invoke(bs, false);
140
+ return check_host_key_hash(s, hkc->u.hash.hash,
45
- bdrv_drain_recurse(bs, false);
141
+ LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
46
+ bdrv_drain_recurse(bs);
142
+ }
47
aio_enable_external(bdrv_get_aio_context(bs));
143
+ g_assert_not_reached();
48
}
144
+ break;
49
145
+ case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
146
return check_host_key_knownhosts(s, host, port, errp);
51
aio_context_acquire(aio_context);
147
+ default:
52
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
148
+ g_assert_not_reached();
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);
149
}
66
}
150
67
151
- error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
152
return -EINVAL;
153
}
154
155
@@ -XXX,XX +XXX,XX @@ static QemuOptsList ssh_runtime_opts = {
156
.type = QEMU_OPT_NUMBER,
157
.help = "Port to connect to",
158
},
159
+ {
160
+ .name = "host_key_check",
161
+ .type = QEMU_OPT_STRING,
162
+ .help = "Defines how and what to check the host key against",
163
+ },
164
{ /* end of list */ }
165
},
166
};
167
168
-static bool ssh_process_legacy_socket_options(QDict *output_opts,
169
- QemuOpts *legacy_opts,
170
- Error **errp)
171
+static bool ssh_process_legacy_options(QDict *output_opts,
172
+ QemuOpts *legacy_opts,
173
+ Error **errp)
174
{
175
const char *host = qemu_opt_get(legacy_opts, "host");
176
const char *port = qemu_opt_get(legacy_opts, "port");
177
+ const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check");
178
179
if (!host && port) {
180
error_setg(errp, "port may not be used without host");
181
@@ -XXX,XX +XXX,XX @@ static bool ssh_process_legacy_socket_options(QDict *output_opts,
182
qdict_put_str(output_opts, "server.port", port ?: stringify(22));
183
}
184
185
+ if (host_key_check) {
186
+ if (strcmp(host_key_check, "no") == 0) {
187
+ qdict_put_str(output_opts, "host-key-check.mode", "none");
188
+ } else if (strncmp(host_key_check, "md5:", 4) == 0) {
189
+ qdict_put_str(output_opts, "host-key-check.mode", "hash");
190
+ qdict_put_str(output_opts, "host-key-check.type", "md5");
191
+ qdict_put_str(output_opts, "host-key-check.hash",
192
+ &host_key_check[4]);
193
+ } else if (strncmp(host_key_check, "sha1:", 5) == 0) {
194
+ qdict_put_str(output_opts, "host-key-check.mode", "hash");
195
+ qdict_put_str(output_opts, "host-key-check.type", "sha1");
196
+ qdict_put_str(output_opts, "host-key-check.hash",
197
+ &host_key_check[5]);
198
+ } else if (strcmp(host_key_check, "yes") == 0) {
199
+ qdict_put_str(output_opts, "host-key-check.mode", "known_hosts");
200
+ } else {
201
+ error_setg(errp, "unknown host_key_check setting (%s)",
202
+ host_key_check);
203
+ return false;
204
+ }
205
+ }
206
+
207
return true;
208
}
209
210
@@ -XXX,XX +XXX,XX @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
211
goto fail;
212
}
213
214
- if (!ssh_process_legacy_socket_options(options, opts, errp)) {
215
+ if (!ssh_process_legacy_options(options, opts, errp)) {
216
goto fail;
217
}
218
219
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
220
{
221
BlockdevOptionsSsh *opts;
222
int r, ret;
223
- const char *user, *host_key_check;
224
+ const char *user;
225
long port = 0;
226
227
- host_key_check = qdict_get_try_str(options, "host_key_check");
228
- if (!host_key_check) {
229
- host_key_check = "yes";
230
- } else {
231
- qdict_del(options, "host_key_check");
232
- }
233
-
234
opts = ssh_parse_options(options, errp);
235
if (opts == NULL) {
236
return -EINVAL;
237
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
238
}
239
240
/* Check the remote host's key against known_hosts. */
241
- ret = check_host_key(s, s->inet->host, port, host_key_check,
242
- errp);
243
+ ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
244
if (ret < 0) {
245
goto err;
246
}
247
--
68
--
248
2.13.6
69
2.13.6
249
70
250
71
diff view generated by jsdifflib
1
Create a BlockdevOptionsSsh object in connect_to_ssh() and take the
1
The device is drained, so there is no point in waiting for requests at
2
options from there. 'host_key_check' is still processed separately
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
because it's not in the schema yet.
3
there.
4
5
The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e
6
in order to call the .bdrv_co_drain_end() driver callback. This is now
7
done by a separate bdrv_drain_invoke() call.
4
8
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
---
12
---
8
block/ssh.c | 137 +++++++++++++++++++++++++++---------------------------------
13
block/io.c | 2 --
9
1 file changed, 62 insertions(+), 75 deletions(-)
14
1 file changed, 2 deletions(-)
10
15
11
diff --git a/block/ssh.c b/block/ssh.c
16
diff --git a/block/io.c b/block/io.c
12
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
13
--- a/block/ssh.c
18
--- a/block/io.c
14
+++ b/block/ssh.c
19
+++ b/block/io.c
15
@@ -XXX,XX +XXX,XX @@
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
16
#include "qemu/sockets.h"
21
17
#include "qemu/uri.h"
22
bdrv_parent_drained_end(bs);
18
#include "qapi/qapi-visit-sockets.h"
23
bdrv_drain_invoke(bs, false);
19
+#include "qapi/qapi-visit-block-core.h"
24
- bdrv_drain_recurse(bs);
20
#include "qapi/qmp/qdict.h"
25
aio_enable_external(bdrv_get_aio_context(bs));
21
#include "qapi/qmp/qstring.h"
22
#include "qapi/qobject-input-visitor.h"
23
@@ -XXX,XX +XXX,XX @@ static QemuOptsList ssh_runtime_opts = {
24
.type = QEMU_OPT_NUMBER,
25
.help = "Port to connect to",
26
},
27
- {
28
- .name = "path",
29
- .type = QEMU_OPT_STRING,
30
- .help = "Path of the image on the host",
31
- },
32
- {
33
- .name = "user",
34
- .type = QEMU_OPT_STRING,
35
- .help = "User as which to connect",
36
- },
37
- {
38
- .name = "host_key_check",
39
- .type = QEMU_OPT_STRING,
40
- .help = "Defines how and what to check the host key against",
41
- },
42
{ /* end of list */ }
43
},
44
};
45
@@ -XXX,XX +XXX,XX @@ static bool ssh_process_legacy_socket_options(QDict *output_opts,
46
return true;
47
}
26
}
48
27
49
-static InetSocketAddress *ssh_config(QDict *options, Error **errp)
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
50
+static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
29
aio_enable_external(aio_context);
51
{
30
bdrv_parent_drained_end(bs);
52
- InetSocketAddress *inet = NULL;
31
bdrv_drain_invoke(bs, false);
53
- QDict *addr = NULL;
32
- bdrv_drain_recurse(bs);
54
- QObject *crumpled_addr = NULL;
33
aio_context_release(aio_context);
55
- Visitor *iv = NULL;
56
- Error *local_error = NULL;
57
-
58
- qdict_extract_subqdict(options, &addr, "server.");
59
- if (!qdict_size(addr)) {
60
- error_setg(errp, "SSH server address missing");
61
- goto out;
62
+ BlockdevOptionsSsh *result = NULL;
63
+ QemuOpts *opts = NULL;
64
+ Error *local_err = NULL;
65
+ QObject *crumpled;
66
+ const QDictEntry *e;
67
+ Visitor *v;
68
+
69
+ /* Translate legacy options */
70
+ opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
71
+ qemu_opts_absorb_qdict(opts, options, &local_err);
72
+ if (local_err) {
73
+ error_propagate(errp, local_err);
74
+ goto fail;
75
}
34
}
76
35
77
- crumpled_addr = qdict_crumple(addr, errp);
78
- if (!crumpled_addr) {
79
- goto out;
80
+ if (!ssh_process_legacy_socket_options(options, opts, errp)) {
81
+ goto fail;
82
+ }
83
+
84
+ /* Create the QAPI object */
85
+ crumpled = qdict_crumple(options, errp);
86
+ if (crumpled == NULL) {
87
+ goto fail;
88
}
89
90
/*
91
@@ -XXX,XX +XXX,XX @@ static InetSocketAddress *ssh_config(QDict *options, Error **errp)
92
* but when they come from -drive, they're all QString. The
93
* visitor expects the former.
94
*/
95
- iv = qobject_input_visitor_new(crumpled_addr);
96
- visit_type_InetSocketAddress(iv, NULL, &inet, &local_error);
97
- if (local_error) {
98
- error_propagate(errp, local_error);
99
- goto out;
100
+ v = qobject_input_visitor_new(crumpled);
101
+ visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
102
+ visit_free(v);
103
+ qobject_decref(crumpled);
104
+
105
+ if (local_err) {
106
+ error_propagate(errp, local_err);
107
+ goto fail;
108
}
109
110
-out:
111
- QDECREF(addr);
112
- qobject_decref(crumpled_addr);
113
- visit_free(iv);
114
- return inet;
115
+ /* Remove the processed options from the QDict (the visitor processes
116
+ * _all_ options in the QDict) */
117
+ while ((e = qdict_first(options))) {
118
+ qdict_del(options, e->key);
119
+ }
120
+
121
+fail:
122
+ qemu_opts_del(opts);
123
+ return result;
124
}
125
126
static int connect_to_ssh(BDRVSSHState *s, QDict *options,
127
int ssh_flags, int creat_mode, Error **errp)
128
{
129
+ BlockdevOptionsSsh *opts;
130
int r, ret;
131
- QemuOpts *opts = NULL;
132
- Error *local_err = NULL;
133
- const char *user, *path, *host_key_check;
134
+ const char *user, *host_key_check;
135
long port = 0;
136
137
- opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
138
- qemu_opts_absorb_qdict(opts, options, &local_err);
139
- if (local_err) {
140
- ret = -EINVAL;
141
- error_propagate(errp, local_err);
142
- goto err;
143
- }
144
-
145
- if (!ssh_process_legacy_socket_options(options, opts, errp)) {
146
- ret = -EINVAL;
147
- goto err;
148
+ host_key_check = qdict_get_try_str(options, "host_key_check");
149
+ if (!host_key_check) {
150
+ host_key_check = "yes";
151
+ } else {
152
+ qdict_del(options, "host_key_check");
153
}
154
155
- path = qemu_opt_get(opts, "path");
156
- if (!path) {
157
- ret = -EINVAL;
158
- error_setg(errp, "No path was specified");
159
- goto err;
160
+ opts = ssh_parse_options(options, errp);
161
+ if (opts == NULL) {
162
+ return -EINVAL;
163
}
164
165
- user = qemu_opt_get(opts, "user");
166
- if (!user) {
167
+ if (opts->has_user) {
168
+ user = opts->user;
169
+ } else {
170
user = g_get_user_name();
171
if (!user) {
172
error_setg_errno(errp, errno, "Can't get user name");
173
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
174
}
175
}
176
177
- host_key_check = qemu_opt_get(opts, "host_key_check");
178
- if (!host_key_check) {
179
- host_key_check = "yes";
180
- }
181
-
182
/* Pop the config into our state object, Exit if invalid */
183
- s->inet = ssh_config(options, errp);
184
- if (!s->inet) {
185
- ret = -EINVAL;
186
- goto err;
187
- }
188
+ s->inet = opts->server;
189
+ opts->server = NULL;
190
191
if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
192
error_setg(errp, "Use only numeric port value");
193
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
194
195
/* Open the remote file. */
196
DPRINTF("opening file %s flags=0x%x creat_mode=0%o",
197
- path, ssh_flags, creat_mode);
198
- s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
199
+ opts->path, ssh_flags, creat_mode);
200
+ s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
201
+ creat_mode);
202
if (!s->sftp_handle) {
203
- session_error_setg(errp, s, "failed to open remote file '%s'", path);
204
+ session_error_setg(errp, s, "failed to open remote file '%s'",
205
+ opts->path);
206
ret = -EINVAL;
207
goto err;
208
}
209
210
- qemu_opts_del(opts);
211
+ qapi_free_BlockdevOptionsSsh(opts);
212
213
r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
214
if (r < 0) {
215
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
216
}
217
s->session = NULL;
218
219
- qemu_opts_del(opts);
220
+ qapi_free_BlockdevOptionsSsh(opts);
221
222
return ret;
223
}
224
--
36
--
225
2.13.6
37
2.13.6
226
38
227
39
diff view generated by jsdifflib
1
This is almost exactly the same code. The differences are that
1
Drain requests are propagated to child nodes, parent nodes and directly
2
qemu_rbd_connect() supports BlockdevOptionsRbd.server and that the cache
2
to the AioContext. The order in which this happened was different
3
mode is set explicitly.
3
between all combinations of drain/drain_all and begin/end.
4
4
5
Supporting 'server' is a welcome new feature for image creation.
5
The correct order is to keep children only drained when their parents
6
Caching is disabled by default, so leave it that way.
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.
7
13
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
16
---
11
block/rbd.c | 54 ++++++++++--------------------------------------------
17
block/io.c | 12 ++++++++----
12
1 file changed, 10 insertions(+), 44 deletions(-)
18
1 file changed, 8 insertions(+), 4 deletions(-)
13
19
14
diff --git a/block/rbd.c b/block/rbd.c
20
diff --git a/block/io.c b/block/io.c
15
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
16
--- a/block/rbd.c
22
--- a/block/io.c
17
+++ b/block/rbd.c
23
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRBDState {
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
19
char *snap;
25
return;
20
} BDRVRBDState;
21
22
+static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
23
+ BlockdevOptionsRbd *opts, bool cache,
24
+ const char *keypairs, const char *secretid,
25
+ Error **errp);
26
+
27
static char *qemu_rbd_next_tok(char *src, char delim, char **p)
28
{
29
char *end;
30
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_do_create(BlockdevCreateOptions *options,
31
return -EINVAL;
32
}
26
}
33
27
34
- /* TODO Remove the limitation */
28
+ /* Stop things in parent-to-child order */
35
- if (opts->location->has_server) {
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
36
- error_setg(errp, "Can't specify server for image creation");
30
aio_disable_external(bdrv_get_aio_context(bs));
37
- return -EINVAL;
31
bdrv_parent_drained_begin(bs);
38
- }
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
39
-
33
return;
40
if (opts->has_cluster_size) {
41
int64_t objsize = opts->cluster_size;
42
if ((objsize - 1) & objsize) { /* not a power of 2? */
43
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_do_create(BlockdevCreateOptions *options,
44
obj_order = ctz32(objsize);
45
}
34
}
46
35
47
- ret = rados_create(&cluster, opts->location->user);
36
- bdrv_parent_drained_end(bs);
48
+ ret = qemu_rbd_connect(&cluster, &io_ctx, opts->location, false, keypairs,
37
+ /* Re-enable things in child-to-parent order */
49
+ password_secret, errp);
38
bdrv_drain_invoke(bs, false);
50
if (ret < 0) {
39
+ bdrv_parent_drained_end(bs);
51
- error_setg_errno(errp, -ret, "error initializing");
40
aio_enable_external(bdrv_get_aio_context(bs));
52
return ret;
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);
53
}
67
}
54
68
55
- /* try default location when conf=NULL, but ignore failure */
56
- ret = rados_conf_read_file(cluster, opts->location->conf);
57
- if (opts->location->conf && ret < 0) {
58
- error_setg_errno(errp, -ret, "error reading conf file %s",
59
- opts->location->conf);
60
- ret = -EIO;
61
- goto shutdown;
62
- }
63
-
64
- ret = qemu_rbd_set_keypairs(cluster, keypairs, errp);
65
- if (ret < 0) {
66
- ret = -EIO;
67
- goto shutdown;
68
- }
69
-
70
- if (qemu_rbd_set_auth(cluster, password_secret, errp) < 0) {
71
- ret = -EIO;
72
- goto shutdown;
73
- }
74
-
75
- ret = rados_connect(cluster);
76
- if (ret < 0) {
77
- error_setg_errno(errp, -ret, "error connecting");
78
- goto shutdown;
79
- }
80
-
81
- ret = rados_ioctx_create(cluster, opts->location->pool, &io_ctx);
82
- if (ret < 0) {
83
- error_setg_errno(errp, -ret, "error opening pool %s",
84
- opts->location->pool);
85
- goto shutdown;
86
- }
87
-
88
ret = rbd_create(io_ctx, opts->location->image, opts->size, &obj_order);
89
if (ret < 0) {
90
error_setg_errno(errp, -ret, "error rbd create");
91
+ goto out;
92
}
93
94
- rados_ioctx_destroy(io_ctx);
95
-
96
ret = 0;
97
-shutdown:
98
+out:
99
+ rados_ioctx_destroy(io_ctx);
100
rados_shutdown(cluster);
101
return ret;
102
}
103
--
69
--
104
2.13.6
70
2.13.6
105
71
106
72
diff view generated by jsdifflib
1
Instead of the QemuOpts in qemu_rbd_connect(), we want to use QAPI
1
Commit 15afd94a047 added code to acquire and release the AioContext in
2
objects. As a preparation, fetch those options directly from the QDict
2
qemuio_command(). This means that the lock is taken twice now in the
3
that .bdrv_open() supports in the rbd driver and that are not in the
3
call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for
4
schema.
4
any requests issued to nodes in a non-mainloop AioContext.
5
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
5
7
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
10
---
9
block/rbd.c | 55 ++++++++++++++++++++++++-------------------------------
11
hmp.c | 6 ------
10
1 file changed, 24 insertions(+), 31 deletions(-)
12
1 file changed, 6 deletions(-)
11
13
12
diff --git a/block/rbd.c b/block/rbd.c
14
diff --git a/hmp.c b/hmp.c
13
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
14
--- a/block/rbd.c
16
--- a/hmp.c
15
+++ b/block/rbd.c
17
+++ b/hmp.c
16
@@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = {
18
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
17
/*
18
* server.* extracted manually, see qemu_rbd_mon_host()
19
*/
20
- {
21
- .name = "password-secret",
22
- .type = QEMU_OPT_STRING,
23
- .help = "ID of secret providing the password",
24
- },
25
-
26
- /*
27
- * Keys for qemu_rbd_parse_filename(), not in the QAPI schema
28
- */
29
- {
30
- /*
31
- * HACK: name starts with '=' so that qemu_opts_parse()
32
- * can't set it
33
- */
34
- .name = "=keyvalue-pairs",
35
- .type = QEMU_OPT_STRING,
36
- .help = "Legacy rados key/value option parameters",
37
- },
38
- {
39
- .name = "filename",
40
- .type = QEMU_OPT_STRING,
41
- },
42
{ /* end of list */ }
43
},
44
};
45
@@ -XXX,XX +XXX,XX @@ out:
46
47
static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
48
char **s_snap, char **s_image_name,
49
- QDict *options, bool cache, Error **errp)
50
+ QDict *options, bool cache,
51
+ const char *keypairs, const char *secretid,
52
+ Error **errp)
53
{
19
{
54
QemuOpts *opts;
20
BlockBackend *blk;
55
char *mon_host = NULL;
21
BlockBackend *local_blk = NULL;
56
- const char *pool, *snap, *conf, *user, *image_name, *keypairs;
22
- AioContext *aio_context;
57
- const char *secretid;
23
const char* device = qdict_get_str(qdict, "device");
58
+ const char *pool, *snap, *conf, *user, *image_name;
24
const char* command = qdict_get_str(qdict, "command");
59
Error *local_err = NULL;
25
Error *err = NULL;
60
int r;
26
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
61
62
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
63
goto failed_opts;
64
}
65
66
- secretid = qemu_opt_get(opts, "password-secret");
67
-
68
pool = qemu_opt_get(opts, "pool");
69
conf = qemu_opt_get(opts, "conf");
70
snap = qemu_opt_get(opts, "snapshot");
71
user = qemu_opt_get(opts, "user");
72
image_name = qemu_opt_get(opts, "image");
73
- keypairs = qemu_opt_get(opts, "=keyvalue-pairs");
74
75
if (!pool || !image_name) {
76
error_setg(errp, "Parameters 'pool' and 'image' are required");
77
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
78
BDRVRBDState *s = bs->opaque;
79
Error *local_err = NULL;
80
const char *filename;
81
+ char *keypairs, *secretid;
82
int r;
83
84
/* If we are given a filename, parse the filename, with precedence given to
85
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
86
"This is an unsupported option, and may be deprecated "
87
"in the future");
88
qemu_rbd_parse_filename(filename, options, &local_err);
89
+ qdict_del(options, "filename");
90
if (local_err) {
91
error_propagate(errp, local_err);
92
return -EINVAL;
93
}
27
}
94
}
28
}
95
29
96
+ keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
30
- aio_context = blk_get_aio_context(blk);
97
+ if (keypairs) {
31
- aio_context_acquire(aio_context);
98
+ qdict_del(options, "=keyvalue-pairs");
32
-
99
+ }
33
/*
100
+
34
* Notably absent: Proper permission management. This is sad, but it seems
101
+ secretid = g_strdup(qdict_get_try_str(options, "password-secret"));
35
* almost impossible to achieve without changing the semantics and thereby
102
+ if (secretid) {
36
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
103
+ qdict_del(options, "password-secret");
37
*/
104
+ }
38
qemuio_command(blk, command);
105
+
39
106
r = qemu_rbd_connect(&s->cluster, &s->io_ctx, &s->snap, &s->image_name,
40
- aio_context_release(aio_context);
107
- options, !(flags & BDRV_O_NOCACHE), errp);
41
-
108
+ options, !(flags & BDRV_O_NOCACHE), keypairs, secretid,
42
fail:
109
+ errp);
43
blk_unref(local_blk);
110
if (r < 0) {
44
hmp_handle_error(mon, &err);
111
- return r;
112
+ goto out;
113
}
114
115
/* rbd_open is always r/w */
116
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
117
}
118
}
119
120
- return 0;
121
+ r = 0;
122
+ goto out;
123
124
failed_open:
125
rados_ioctx_destroy(s->io_ctx);
126
g_free(s->snap);
127
g_free(s->image_name);
128
rados_shutdown(s->cluster);
129
+out:
130
+ g_free(keypairs);
131
+ g_free(secretid);
132
return r;
133
}
134
135
--
45
--
136
2.13.6
46
2.13.6
137
47
138
48
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
2
2
3
This function checks that the offset and size of a table are valid.
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.
4
6
5
While the offset checks are fine, the size check is too generic, since
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
6
it only verifies that the total size in bytes fits in a 64-bit
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
integer. In practice all tables used in qcow2 have much smaller size
8
limits, so the size needs to be checked again for each table using its
9
actual limit.
10
11
This patch generalizes this function by allowing the caller to specify
12
the maximum size for that table. In addition to that it allows passing
13
an Error variable.
14
15
The function is also renamed and made public since we're going to use
16
it in other parts of the code.
17
18
Signed-off-by: Alberto Garcia <berto@igalia.com>
19
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
---
11
---
22
block/qcow2.h | 10 +++---
12
block/qcow2.h | 3 ---
23
block/qcow2.c | 77 ++++++++++++++++++----------------------------
13
block/qcow2.c | 51 ++++++++-------------------------------------------
24
tests/qemu-iotests/080.out | 16 +++++-----
14
2 files changed, 8 insertions(+), 46 deletions(-)
25
3 files changed, 43 insertions(+), 60 deletions(-)
26
15
27
diff --git a/block/qcow2.h b/block/qcow2.h
16
diff --git a/block/qcow2.h b/block/qcow2.h
28
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
29
--- a/block/qcow2.h
18
--- a/block/qcow2.h
30
+++ b/block/qcow2.h
19
+++ b/block/qcow2.h
31
@@ -XXX,XX +XXX,XX @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
32
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
33
}
21
}
34
22
35
-static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s)
23
/* qcow2.c functions */
36
-{
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
37
- return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
25
- int64_t sector_num, int nb_sectors);
38
-}
39
-
26
-
40
static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry)
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
41
{
28
int refcount_order, bool generous_increase,
42
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
29
uint64_t *refblock_count);
43
@@ -XXX,XX +XXX,XX @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
44
int64_t size, const char *message_format, ...)
45
GCC_FMT_ATTR(5, 6);
46
47
+int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
48
+ uint64_t entries, size_t entry_len,
49
+ int64_t max_size_bytes, const char *table_name,
50
+ Error **errp);
51
+
52
/* qcow2-refcount.c functions */
53
int qcow2_refcount_init(BlockDriverState *bs);
54
void qcow2_refcount_close(BlockDriverState *bs);
55
diff --git a/block/qcow2.c b/block/qcow2.c
30
diff --git a/block/qcow2.c b/block/qcow2.c
56
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
57
--- a/block/qcow2.c
32
--- a/block/qcow2.c
58
+++ b/block/qcow2.c
33
+++ b/block/qcow2.c
59
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_check(BlockDriverState *bs,
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
60
return ret;
35
return status;
61
}
36
}
62
37
63
-static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
38
-/* handle reading after the end of the backing file */
64
- uint64_t entries, size_t entry_len)
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
65
+int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
40
- int64_t offset, int bytes)
66
+ uint64_t entries, size_t entry_len,
41
-{
67
+ int64_t max_size_bytes, const char *table_name,
42
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
68
+ Error **errp)
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)
69
{
63
{
70
BDRVQcow2State *s = bs->opaque;
64
BDRVQcow2State *s = bs->opaque;
71
- uint64_t size;
65
- int offset_in_cluster, n1;
72
66
+ int offset_in_cluster;
73
- /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
67
int ret;
74
- * because values will be passed to qemu functions taking int64_t. */
68
unsigned int cur_bytes; /* number of bytes in current iteration */
75
- if (entries > INT64_MAX / entry_len) {
69
uint64_t cluster_offset = 0;
76
- return -EINVAL;
70
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
77
- }
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;
78
-
79
-
79
- size = entries * entry_len;
80
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
81
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
80
-
82
-
81
- if (INT64_MAX - size < offset) {
83
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
82
- return -EINVAL;
84
- qemu_co_mutex_unlock(&s->lock);
83
+ if (entries > max_size_bytes / entry_len) {
85
- ret = bdrv_co_preadv(bs->backing, offset, n1,
84
+ error_setg(errp, "%s too large", table_name);
86
- &local_qiov, 0);
85
+ return -EFBIG;
87
- qemu_co_mutex_lock(&s->lock);
86
}
87
88
- /* Tables must be cluster aligned */
89
- if (offset_into_cluster(s, offset) != 0) {
90
+ /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
91
+ * because values will be passed to qemu functions taking int64_t. */
92
+ if ((INT64_MAX - entries * entry_len < offset) ||
93
+ (offset_into_cluster(s, offset) != 0)) {
94
+ error_setg(errp, "%s offset invalid", table_name);
95
return -EINVAL;
96
}
97
98
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
99
s->refcount_table_size =
100
header.refcount_table_clusters << (s->cluster_bits - 3);
101
102
- if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) {
103
- error_setg(errp, "Reference count table too large");
104
- ret = -EINVAL;
105
- goto fail;
106
- }
107
-
88
-
108
if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) {
89
- qemu_iovec_destroy(&local_qiov);
109
error_setg(errp, "Image does not contain a reference count table");
110
ret = -EINVAL;
111
goto fail;
112
}
113
114
- ret = validate_table_offset(bs, s->refcount_table_offset,
115
- s->refcount_table_size, sizeof(uint64_t));
116
+ ret = qcow2_validate_table(bs, s->refcount_table_offset,
117
+ header.refcount_table_clusters,
118
+ s->cluster_size, QCOW_MAX_REFTABLE_SIZE,
119
+ "Reference count table", errp);
120
if (ret < 0) {
121
- error_setg(errp, "Invalid reference count table offset");
122
goto fail;
123
}
124
125
- /* Snapshot table offset/length */
126
- if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) {
127
- error_setg(errp, "Too many snapshots");
128
- ret = -EINVAL;
129
- goto fail;
130
- }
131
-
90
-
132
- ret = validate_table_offset(bs, header.snapshots_offset,
91
- if (ret < 0) {
133
- header.nb_snapshots,
92
- goto fail;
134
- sizeof(QCowSnapshotHeader));
93
- }
135
+ /* The total size in bytes of the snapshot table is checked in
94
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
136
+ * qcow2_read_snapshots() because the size of each snapshot is
95
+ qemu_co_mutex_unlock(&s->lock);
137
+ * variable and we don't know it yet.
96
+ ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
138
+ * Here we only check the offset and number of snapshots. */
97
+ &hd_qiov, 0);
139
+ ret = qcow2_validate_table(bs, header.snapshots_offset,
98
+ qemu_co_mutex_lock(&s->lock);
140
+ header.nb_snapshots,
99
+ if (ret < 0) {
141
+ sizeof(QCowSnapshotHeader),
100
+ goto fail;
142
+ sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS,
101
}
143
+ "Snapshot table", errp);
102
} else {
144
if (ret < 0) {
103
/* Note: in this case, no need to wait */
145
- error_setg(errp, "Invalid snapshot table offset");
146
goto fail;
147
}
148
149
/* read the level 1 table */
150
- if (header.l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
151
- error_setg(errp, "Active L1 table too large");
152
- ret = -EFBIG;
153
+ ret = qcow2_validate_table(bs, header.l1_table_offset,
154
+ header.l1_size, sizeof(uint64_t),
155
+ QCOW_MAX_L1_SIZE, "Active L1 table", errp);
156
+ if (ret < 0) {
157
goto fail;
158
}
159
s->l1_size = header.l1_size;
160
+ s->l1_table_offset = header.l1_table_offset;
161
162
l1_vm_state_index = size_to_l1(s, header.size);
163
if (l1_vm_state_index > INT_MAX) {
164
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
165
goto fail;
166
}
167
168
- ret = validate_table_offset(bs, header.l1_table_offset,
169
- header.l1_size, sizeof(uint64_t));
170
- if (ret < 0) {
171
- error_setg(errp, "Invalid L1 table offset");
172
- goto fail;
173
- }
174
- s->l1_table_offset = header.l1_table_offset;
175
-
176
-
177
if (s->l1_size > 0) {
178
s->l1_table = qemu_try_blockalign(bs->file->bs,
179
ROUND_UP(s->l1_size * sizeof(uint64_t), 512));
180
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
181
index XXXXXXX..XXXXXXX 100644
182
--- a/tests/qemu-iotests/080.out
183
+++ b/tests/qemu-iotests/080.out
184
@@ -XXX,XX +XXX,XX @@ can't open device TEST_DIR/t.qcow2: Reference count table too large
185
186
== Misaligned refcount table ==
187
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
188
-can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
189
+can't open device TEST_DIR/t.qcow2: Reference count table offset invalid
190
191
== Huge refcount offset ==
192
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
193
-can't open device TEST_DIR/t.qcow2: Invalid reference count table offset
194
+can't open device TEST_DIR/t.qcow2: Reference count table offset invalid
195
196
== Invalid snapshot table ==
197
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
198
-can't open device TEST_DIR/t.qcow2: Too many snapshots
199
-can't open device TEST_DIR/t.qcow2: Too many snapshots
200
-can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset
201
-can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset
202
+can't open device TEST_DIR/t.qcow2: Snapshot table too large
203
+can't open device TEST_DIR/t.qcow2: Snapshot table too large
204
+can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid
205
+can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid
206
207
== Hitting snapshot table size limit ==
208
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
209
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 0
210
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
211
can't open device TEST_DIR/t.qcow2: Active L1 table too large
212
can't open device TEST_DIR/t.qcow2: Active L1 table too large
213
-can't open device TEST_DIR/t.qcow2: Invalid L1 table offset
214
-can't open device TEST_DIR/t.qcow2: Invalid L1 table offset
215
+can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid
216
+can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid
217
218
== Invalid L1 table (with internal snapshot in the image) ==
219
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
220
--
104
--
221
2.13.6
105
2.13.6
222
106
223
107
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to sheepdog, which enables
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
image creation over QMP.
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().
5
6
Document this problem so that we won't accidentally mark the command
7
stable without having addressed it.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
6
---
11
---
7
qapi/block-core.json | 24 ++++-
12
qapi/block-core.json | 4 ++++
8
block/sheepdog.c | 243 +++++++++++++++++++++++++++++++++++----------------
13
1 file changed, 4 insertions(+)
9
2 files changed, 192 insertions(+), 75 deletions(-)
10
14
11
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
12
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
17
--- a/qapi/block-core.json
14
+++ b/qapi/block-core.json
18
+++ b/qapi/block-core.json
15
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
16
'erasure-coded': 'SheepdogRedundancyErasureCoded' } }
20
# does not support all kinds of operations, all kinds of children, nor
17
21
# all block drivers.
18
##
22
#
19
+# @BlockdevCreateOptionsSheepdog:
23
+# FIXME Removing children from a quorum node means introducing gaps in the
24
+# child indices. This cannot be represented in the 'children' list of
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
20
+#
26
+#
21
+# Driver specific image creation options for Sheepdog.
27
# Warning: The data in a new quorum child MUST be consistent with that of
22
+#
28
# the rest of the array.
23
+# @location Where to store the new image file
24
+# @size Size of the virtual disk in bytes
25
+# @backing-file File name of a base image
26
+# @preallocation Preallocation mode (allowed values: off, full)
27
+# @redundancy Redundancy of the image
28
+# @object-size Object size of the image
29
+#
30
+# Since: 2.12
31
+##
32
+{ 'struct': 'BlockdevCreateOptionsSheepdog',
33
+ 'data': { 'location': 'BlockdevOptionsSheepdog',
34
+ 'size': 'size',
35
+ '*backing-file': 'str',
36
+ '*preallocation': 'PreallocMode',
37
+ '*redundancy': 'SheepdogRedundancy',
38
+ '*object-size': 'size' } }
39
+
40
+##
41
# @BlockdevCreateNotSupported:
42
#
29
#
43
# This is used for all drivers that don't support creating images.
44
@@ -XXX,XX +XXX,XX @@
45
'raw': 'BlockdevCreateNotSupported',
46
'rbd': 'BlockdevCreateOptionsRbd',
47
'replication': 'BlockdevCreateNotSupported',
48
- 'sheepdog': 'BlockdevCreateNotSupported',
49
+ 'sheepdog': 'BlockdevCreateOptionsSheepdog',
50
'ssh': 'BlockdevCreateNotSupported',
51
'throttle': 'BlockdevCreateNotSupported',
52
'vdi': 'BlockdevCreateNotSupported',
53
diff --git a/block/sheepdog.c b/block/sheepdog.c
54
index XXXXXXX..XXXXXXX 100644
55
--- a/block/sheepdog.c
56
+++ b/block/sheepdog.c
57
@@ -XXX,XX +XXX,XX @@
58
#include "qemu/osdep.h"
59
#include "qapi/error.h"
60
#include "qapi/qapi-visit-sockets.h"
61
+#include "qapi/qapi-visit-block-core.h"
62
#include "qapi/qmp/qdict.h"
63
#include "qapi/qobject-input-visitor.h"
64
+#include "qapi/qobject-output-visitor.h"
65
#include "qemu/uri.h"
66
#include "qemu/error-report.h"
67
#include "qemu/option.h"
68
@@ -XXX,XX +XXX,XX @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
69
qemu_co_mutex_unlock(&s->queue_lock);
70
}
71
72
-static SocketAddress *sd_socket_address(const char *path,
73
- const char *host, const char *port)
74
-{
75
- SocketAddress *addr = g_new0(SocketAddress, 1);
76
-
77
- if (path) {
78
- addr->type = SOCKET_ADDRESS_TYPE_UNIX;
79
- addr->u.q_unix.path = g_strdup(path);
80
- } else {
81
- addr->type = SOCKET_ADDRESS_TYPE_INET;
82
- addr->u.inet.host = g_strdup(host ?: SD_DEFAULT_ADDR);
83
- addr->u.inet.port = g_strdup(port ?: stringify(SD_DEFAULT_PORT));
84
- }
85
-
86
- return addr;
87
-}
88
-
89
static SocketAddress *sd_server_config(QDict *options, Error **errp)
90
{
91
QDict *server = NULL;
92
@@ -XXX,XX +XXX,XX @@ out_with_err_set:
93
return ret;
94
}
95
96
+static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
97
+ Error **errp)
98
+{
99
+ BlockDriverState *bs;
100
+ Visitor *v;
101
+ QObject *obj = NULL;
102
+ QDict *qdict;
103
+ Error *local_err = NULL;
104
+ int ret;
105
+
106
+ v = qobject_output_visitor_new(&obj);
107
+ visit_type_BlockdevOptionsSheepdog(v, NULL, &location, &local_err);
108
+ visit_free(v);
109
+
110
+ if (local_err) {
111
+ error_propagate(errp, local_err);
112
+ qobject_decref(obj);
113
+ return -EINVAL;
114
+ }
115
+
116
+ qdict = qobject_to_qdict(obj);
117
+ qdict_flatten(qdict);
118
+
119
+ qdict_put_str(qdict, "driver", "sheepdog");
120
+
121
+ bs = bdrv_open(NULL, NULL, qdict, BDRV_O_PROTOCOL | BDRV_O_RDWR, errp);
122
+ if (bs == NULL) {
123
+ ret = -EIO;
124
+ goto fail;
125
+ }
126
+
127
+ ret = sd_prealloc(bs, 0, size, errp);
128
+fail:
129
+ bdrv_unref(bs);
130
+ QDECREF(qdict);
131
+ return ret;
132
+}
133
+
134
static int parse_redundancy(BDRVSheepdogState *s, SheepdogRedundancy *opt)
135
{
136
struct SheepdogInode *inode = &s->inode;
137
@@ -XXX,XX +XXX,XX @@ static int parse_redundancy(BDRVSheepdogState *s, SheepdogRedundancy *opt)
138
* # create a erasure coded vdi with x data strips and y parity strips
139
* -o redundancy=x:y (x must be one of {2,4,8,16} and 1 <= y < SD_EC_MAX_STRIP)
140
*/
141
-static int parse_redundancy_str(BDRVSheepdogState *s, const char *opt)
142
+static SheepdogRedundancy *parse_redundancy_str(const char *opt)
143
{
144
- struct SheepdogRedundancy redundancy;
145
+ SheepdogRedundancy *redundancy;
146
const char *n1, *n2;
147
long copy, parity;
148
char p[10];
149
@@ -XXX,XX +XXX,XX @@ static int parse_redundancy_str(BDRVSheepdogState *s, const char *opt)
150
n2 = strtok(NULL, ":");
151
152
if (!n1) {
153
- return -EINVAL;
154
+ return NULL;
155
}
156
157
ret = qemu_strtol(n1, NULL, 10, &copy);
158
if (ret < 0) {
159
- return ret;
160
+ return NULL;
161
}
162
163
+ redundancy = g_new0(SheepdogRedundancy, 1);
164
if (!n2) {
165
- redundancy = (SheepdogRedundancy) {
166
+ *redundancy = (SheepdogRedundancy) {
167
.type = SHEEPDOG_REDUNDANCY_TYPE_FULL,
168
.u.full.copies = copy,
169
};
170
} else {
171
ret = qemu_strtol(n2, NULL, 10, &parity);
172
if (ret < 0) {
173
- return ret;
174
+ return NULL;
175
}
176
177
- redundancy = (SheepdogRedundancy) {
178
+ *redundancy = (SheepdogRedundancy) {
179
.type = SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED,
180
.u.erasure_coded = {
181
.data_strips = copy,
182
@@ -XXX,XX +XXX,XX @@ static int parse_redundancy_str(BDRVSheepdogState *s, const char *opt)
183
};
184
}
185
186
- return parse_redundancy(s, &redundancy);
187
+ return redundancy;
188
}
189
190
-static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
191
+static int parse_block_size_shift(BDRVSheepdogState *s,
192
+ BlockdevCreateOptionsSheepdog *opts)
193
{
194
struct SheepdogInode *inode = &s->inode;
195
uint64_t object_size;
196
int obj_order;
197
198
- object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0);
199
- if (object_size) {
200
+ if (opts->has_object_size) {
201
+ object_size = opts->object_size;
202
+
203
if ((object_size - 1) & object_size) { /* not a power of 2? */
204
return -EINVAL;
205
}
206
@@ -XXX,XX +XXX,XX @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
207
return 0;
208
}
209
210
-static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
211
- Error **errp)
212
+static int sd_co_create(BlockdevCreateOptions *options, Error **errp)
213
{
214
- Error *err = NULL;
215
+ BlockdevCreateOptionsSheepdog *opts = &options->u.sheepdog;
216
int ret = 0;
217
uint32_t vid = 0;
218
char *backing_file = NULL;
219
char *buf = NULL;
220
BDRVSheepdogState *s;
221
- SheepdogConfig cfg;
222
uint64_t max_vdi_size;
223
bool prealloc = false;
224
225
+ assert(options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
226
+
227
s = g_new0(BDRVSheepdogState, 1);
228
229
- if (strstr(filename, "://")) {
230
- sd_parse_uri(&cfg, filename, &err);
231
- } else {
232
- parse_vdiname(&cfg, filename, &err);
233
- }
234
- if (err) {
235
- error_propagate(errp, err);
236
+ /* Steal SocketAddress from QAPI, set NULL to prevent double free */
237
+ s->addr = opts->location->server;
238
+ opts->location->server = NULL;
239
+
240
+ if (strlen(opts->location->vdi) >= sizeof(s->name)) {
241
+ error_setg(errp, "'vdi' string too long");
242
+ ret = -EINVAL;
243
goto out;
244
}
245
+ pstrcpy(s->name, sizeof(s->name), opts->location->vdi);
246
247
- buf = cfg.port ? g_strdup_printf("%d", cfg.port) : NULL;
248
- s->addr = sd_socket_address(cfg.path, cfg.host, buf);
249
- g_free(buf);
250
- strcpy(s->name, cfg.vdi);
251
- sd_config_done(&cfg);
252
+ s->inode.vdi_size = opts->size;
253
+ backing_file = opts->backing_file;
254
255
- s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
256
- BDRV_SECTOR_SIZE);
257
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
258
- buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
259
- if (!buf || !strcmp(buf, "off")) {
260
+ if (!opts->has_preallocation) {
261
+ opts->preallocation = PREALLOC_MODE_OFF;
262
+ }
263
+ switch (opts->preallocation) {
264
+ case PREALLOC_MODE_OFF:
265
prealloc = false;
266
- } else if (!strcmp(buf, "full")) {
267
+ break;
268
+ case PREALLOC_MODE_FULL:
269
prealloc = true;
270
- } else {
271
- error_setg(errp, "Invalid preallocation mode: '%s'", buf);
272
+ break;
273
+ default:
274
+ error_setg(errp, "Preallocation mode not supported for Sheepdog");
275
ret = -EINVAL;
276
goto out;
277
}
278
279
- g_free(buf);
280
- buf = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
281
- if (buf) {
282
- ret = parse_redundancy_str(s, buf);
283
+ if (opts->has_redundancy) {
284
+ ret = parse_redundancy(s, opts->redundancy);
285
if (ret < 0) {
286
- error_setg(errp, "Invalid redundancy mode: '%s'", buf);
287
+ error_setg(errp, "Invalid redundancy mode");
288
goto out;
289
}
290
}
291
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
292
goto out;
293
}
294
295
- if (backing_file) {
296
+ if (opts->has_backing_file) {
297
BlockBackend *blk;
298
BDRVSheepdogState *base;
299
BlockDriver *drv;
300
301
/* Currently, only Sheepdog backing image is supported. */
302
- drv = bdrv_find_protocol(backing_file, true, NULL);
303
+ drv = bdrv_find_protocol(opts->backing_file, true, NULL);
304
if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
305
error_setg(errp, "backing_file must be a sheepdog image");
306
ret = -EINVAL;
307
goto out;
308
}
309
310
- blk = blk_new_open(backing_file, NULL, NULL,
311
+ blk = blk_new_open(opts->backing_file, NULL, NULL,
312
BDRV_O_PROTOCOL, errp);
313
if (blk == NULL) {
314
ret = -EIO;
315
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
316
}
317
318
if (prealloc) {
319
- BlockDriverState *bs;
320
- QDict *opts;
321
-
322
- opts = qdict_new();
323
- qdict_put_str(opts, "driver", "sheepdog");
324
- bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR,
325
- errp);
326
- if (!bs) {
327
- goto out;
328
- }
329
-
330
- ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp);
331
-
332
- bdrv_unref(bs);
333
+ ret = sd_create_prealloc(opts->location, opts->size, errp);
334
}
335
out:
336
g_free(backing_file);
337
g_free(buf);
338
+ g_free(s->addr);
339
g_free(s);
340
return ret;
341
}
342
343
+static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
344
+ Error **errp)
345
+{
346
+ BlockdevCreateOptions *create_options = NULL;
347
+ QDict *qdict, *location_qdict;
348
+ QObject *crumpled;
349
+ Visitor *v;
350
+ const char *redundancy;
351
+ Error *local_err = NULL;
352
+ int ret;
353
+
354
+ redundancy = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
355
+
356
+ qdict = qemu_opts_to_qdict(opts, NULL);
357
+ qdict_put_str(qdict, "driver", "sheepdog");
358
+
359
+ location_qdict = qdict_new();
360
+ qdict_put(qdict, "location", location_qdict);
361
+
362
+ sd_parse_filename(filename, location_qdict, &local_err);
363
+ if (local_err) {
364
+ error_propagate(errp, local_err);
365
+ ret = -EINVAL;
366
+ goto fail;
367
+ }
368
+
369
+ qdict_flatten(qdict);
370
+
371
+ /* Change legacy command line options into QMP ones */
372
+ static const QDictRenames opt_renames[] = {
373
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
374
+ { BLOCK_OPT_OBJECT_SIZE, "object-size" },
375
+ { NULL, NULL },
376
+ };
377
+
378
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
379
+ ret = -EINVAL;
380
+ goto fail;
381
+ }
382
+
383
+ /* Get the QAPI object */
384
+ crumpled = qdict_crumple(qdict, errp);
385
+ if (crumpled == NULL) {
386
+ ret = -EINVAL;
387
+ goto fail;
388
+ }
389
+
390
+ v = qobject_input_visitor_new_keyval(crumpled);
391
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
392
+ visit_free(v);
393
+ qobject_decref(crumpled);
394
+
395
+ if (local_err) {
396
+ error_propagate(errp, local_err);
397
+ ret = -EINVAL;
398
+ goto fail;
399
+ }
400
+
401
+ assert(create_options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
402
+ create_options->u.sheepdog.size =
403
+ ROUND_UP(create_options->u.sheepdog.size, BDRV_SECTOR_SIZE);
404
+
405
+ if (redundancy) {
406
+ create_options->u.sheepdog.has_redundancy = true;
407
+ create_options->u.sheepdog.redundancy =
408
+ parse_redundancy_str(redundancy);
409
+ if (create_options->u.sheepdog.redundancy == NULL) {
410
+ error_setg(errp, "Invalid redundancy mode");
411
+ ret = -EINVAL;
412
+ goto fail;
413
+ }
414
+ }
415
+
416
+ ret = sd_co_create(create_options, errp);
417
+fail:
418
+ qapi_free_BlockdevCreateOptions(create_options);
419
+ QDECREF(qdict);
420
+ return ret;
421
+}
422
+
423
static void sd_close(BlockDriverState *bs)
424
{
425
Error *local_err = NULL;
426
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog = {
427
.bdrv_reopen_commit = sd_reopen_commit,
428
.bdrv_reopen_abort = sd_reopen_abort,
429
.bdrv_close = sd_close,
430
+ .bdrv_co_create = sd_co_create,
431
.bdrv_co_create_opts = sd_co_create_opts,
432
.bdrv_has_zero_init = bdrv_has_zero_init_1,
433
.bdrv_getlength = sd_getlength,
434
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog_tcp = {
435
.bdrv_reopen_commit = sd_reopen_commit,
436
.bdrv_reopen_abort = sd_reopen_abort,
437
.bdrv_close = sd_close,
438
+ .bdrv_co_create = sd_co_create,
439
.bdrv_co_create_opts = sd_co_create_opts,
440
.bdrv_has_zero_init = bdrv_has_zero_init_1,
441
.bdrv_getlength = sd_getlength,
442
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog_unix = {
443
.bdrv_reopen_commit = sd_reopen_commit,
444
.bdrv_reopen_abort = sd_reopen_abort,
445
.bdrv_close = sd_close,
446
+ .bdrv_co_create = sd_co_create,
447
.bdrv_co_create_opts = sd_co_create_opts,
448
.bdrv_has_zero_init = bdrv_has_zero_init_1,
449
.bdrv_getlength = sd_getlength,
450
--
30
--
451
2.13.6
31
2.13.6
452
32
453
33
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Doug Gale <doug16k@gmail.com>
2
2
3
'qemu-img check' cannot detect if a snapshot's L1 table is corrupted.
3
Add trace output for commands, errors, and undefined behavior.
4
This patch checks the table's offset and size and reports corruption
4
Add guest error log output for undefined behavior.
5
if the values are not valid.
5
Report invalid undefined accesses to MMIO.
6
Annotate unlikely error checks with unlikely.
6
7
7
This patch doesn't add code to fix that corruption yet, only to detect
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
8
and report it.
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
9
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Alberto Garcia <berto@igalia.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
12
---
14
block/qcow2-refcount.c | 14 ++++++++++++++
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
15
tests/qemu-iotests/080 | 2 ++
14
hw/block/trace-events | 93 ++++++++++++++
16
tests/qemu-iotests/080.out | 20 ++++++++++++++++++++
15
2 files changed, 390 insertions(+), 52 deletions(-)
17
3 files changed, 36 insertions(+)
18
16
19
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
20
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
21
--- a/block/qcow2-refcount.c
19
--- a/hw/block/nvme.c
22
+++ b/block/qcow2-refcount.c
20
+++ b/hw/block/nvme.c
23
@@ -XXX,XX +XXX,XX @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
21
@@ -XXX,XX +XXX,XX @@
24
/* snapshots */
22
#include "qapi/visitor.h"
25
for (i = 0; i < s->nb_snapshots; i++) {
23
#include "sysemu/block-backend.h"
26
sn = s->snapshots + i;
24
27
+ if (offset_into_cluster(s, sn->l1_table_offset)) {
25
+#include "qemu/log.h"
28
+ fprintf(stderr, "ERROR snapshot %s (%s) l1_offset=%#" PRIx64 ": "
26
+#include "trace.h"
29
+ "L1 table is not cluster aligned; snapshot table entry "
27
#include "nvme.h"
30
+ "corrupted\n", sn->id_str, sn->name, sn->l1_table_offset);
28
31
+ res->corruptions++;
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
32
+ continue;
30
+ do { \
31
+ (trace_##trace)(__VA_ARGS__); \
32
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
33
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
34
+ } while (0)
35
+
36
static void nvme_process_sq(void *opaque);
37
38
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
39
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
40
{
41
if (cq->irq_enabled) {
42
if (msix_enabled(&(n->parent_obj))) {
43
+ trace_nvme_irq_msix(cq->vector);
44
msix_notify(&(n->parent_obj), cq->vector);
45
} else {
46
+ trace_nvme_irq_pin();
47
pci_irq_pulse(&n->parent_obj);
48
}
49
+ } else {
50
+ trace_nvme_irq_masked();
51
}
52
}
53
54
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
55
trans_len = MIN(len, trans_len);
56
int num_prps = (len >> n->page_bits) + 1;
57
58
- if (!prp1) {
59
+ if (unlikely(!prp1)) {
60
+ trace_nvme_err_invalid_prp();
61
return NVME_INVALID_FIELD | NVME_DNR;
62
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
63
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
64
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
65
}
66
len -= trans_len;
67
if (len) {
68
- if (!prp2) {
69
+ if (unlikely(!prp2)) {
70
+ trace_nvme_err_invalid_prp2_missing();
71
goto unmap;
72
}
73
if (len > n->page_size) {
74
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
76
77
if (i == n->max_prp_ents - 1 && len > n->page_size) {
78
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
79
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
80
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
81
goto unmap;
82
}
83
84
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
85
prp_ent = le64_to_cpu(prp_list[i]);
86
}
87
88
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
89
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
90
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
91
goto unmap;
92
}
93
94
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
95
i++;
96
}
97
} else {
98
- if (prp2 & (n->page_size - 1)) {
99
+ if (unlikely(prp2 & (n->page_size - 1))) {
100
+ trace_nvme_err_invalid_prp2_align(prp2);
101
goto unmap;
102
}
103
if (qsg->nsg) {
104
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
105
QEMUIOVector iov;
106
uint16_t status = NVME_SUCCESS;
107
108
+ trace_nvme_dma_read(prp1, prp2);
109
+
110
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
111
return NVME_INVALID_FIELD | NVME_DNR;
112
}
113
if (qsg.nsg > 0) {
114
- if (dma_buf_read(ptr, len, &qsg)) {
115
+ if (unlikely(dma_buf_read(ptr, len, &qsg))) {
116
+ trace_nvme_err_invalid_dma();
117
status = NVME_INVALID_FIELD | NVME_DNR;
118
}
119
qemu_sglist_destroy(&qsg);
120
} else {
121
- if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
122
+ if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
123
+ trace_nvme_err_invalid_dma();
124
status = NVME_INVALID_FIELD | NVME_DNR;
125
}
126
qemu_iovec_destroy(&iov);
127
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
128
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
129
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
130
131
- if (slba + nlb > ns->id_ns.nsze) {
132
+ if (unlikely(slba + nlb > ns->id_ns.nsze)) {
133
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
134
return NVME_LBA_RANGE | NVME_DNR;
135
}
136
137
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
138
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
139
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
140
141
- if ((slba + nlb) > ns->id_ns.nsze) {
142
+ trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
143
+
144
+ if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
145
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
146
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
147
return NVME_LBA_RANGE | NVME_DNR;
148
}
149
150
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
151
NvmeNamespace *ns;
152
uint32_t nsid = le32_to_cpu(cmd->nsid);
153
154
- if (nsid == 0 || nsid > n->num_namespaces) {
155
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
156
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
157
return NVME_INVALID_NSID | NVME_DNR;
158
}
159
160
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
161
case NVME_CMD_READ:
162
return nvme_rw(n, ns, cmd, req);
163
default:
164
+ trace_nvme_err_invalid_opc(cmd->opcode);
165
return NVME_INVALID_OPCODE | NVME_DNR;
166
}
167
}
168
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
169
NvmeCQueue *cq;
170
uint16_t qid = le16_to_cpu(c->qid);
171
172
- if (!qid || nvme_check_sqid(n, qid)) {
173
+ if (unlikely(!qid || nvme_check_sqid(n, qid))) {
174
+ trace_nvme_err_invalid_del_sq(qid);
175
return NVME_INVALID_QID | NVME_DNR;
176
}
177
178
+ trace_nvme_del_sq(qid);
179
+
180
sq = n->sq[qid];
181
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
182
req = QTAILQ_FIRST(&sq->out_req_list);
183
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
184
uint16_t qflags = le16_to_cpu(c->sq_flags);
185
uint64_t prp1 = le64_to_cpu(c->prp1);
186
187
- if (!cqid || nvme_check_cqid(n, cqid)) {
188
+ trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags);
189
+
190
+ if (unlikely(!cqid || nvme_check_cqid(n, cqid))) {
191
+ trace_nvme_err_invalid_create_sq_cqid(cqid);
192
return NVME_INVALID_CQID | NVME_DNR;
193
}
194
- if (!sqid || !nvme_check_sqid(n, sqid)) {
195
+ if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) {
196
+ trace_nvme_err_invalid_create_sq_sqid(sqid);
197
return NVME_INVALID_QID | NVME_DNR;
198
}
199
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
200
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
201
+ trace_nvme_err_invalid_create_sq_size(qsize);
202
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
203
}
204
- if (!prp1 || prp1 & (n->page_size - 1)) {
205
+ if (unlikely(!prp1 || prp1 & (n->page_size - 1))) {
206
+ trace_nvme_err_invalid_create_sq_addr(prp1);
207
return NVME_INVALID_FIELD | NVME_DNR;
208
}
209
- if (!(NVME_SQ_FLAGS_PC(qflags))) {
210
+ if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) {
211
+ trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags));
212
return NVME_INVALID_FIELD | NVME_DNR;
213
}
214
sq = g_malloc0(sizeof(*sq));
215
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
216
NvmeCQueue *cq;
217
uint16_t qid = le16_to_cpu(c->qid);
218
219
- if (!qid || nvme_check_cqid(n, qid)) {
220
+ if (unlikely(!qid || nvme_check_cqid(n, qid))) {
221
+ trace_nvme_err_invalid_del_cq_cqid(qid);
222
return NVME_INVALID_CQID | NVME_DNR;
223
}
224
225
cq = n->cq[qid];
226
- if (!QTAILQ_EMPTY(&cq->sq_list)) {
227
+ if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) {
228
+ trace_nvme_err_invalid_del_cq_notempty(qid);
229
return NVME_INVALID_QUEUE_DEL;
230
}
231
+ trace_nvme_del_cq(qid);
232
nvme_free_cq(cq, n);
233
return NVME_SUCCESS;
234
}
235
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
236
uint16_t qflags = le16_to_cpu(c->cq_flags);
237
uint64_t prp1 = le64_to_cpu(c->prp1);
238
239
- if (!cqid || !nvme_check_cqid(n, cqid)) {
240
+ trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags,
241
+ NVME_CQ_FLAGS_IEN(qflags) != 0);
242
+
243
+ if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) {
244
+ trace_nvme_err_invalid_create_cq_cqid(cqid);
245
return NVME_INVALID_CQID | NVME_DNR;
246
}
247
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
248
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
249
+ trace_nvme_err_invalid_create_cq_size(qsize);
250
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
251
}
252
- if (!prp1) {
253
+ if (unlikely(!prp1)) {
254
+ trace_nvme_err_invalid_create_cq_addr(prp1);
255
return NVME_INVALID_FIELD | NVME_DNR;
256
}
257
- if (vector > n->num_queues) {
258
+ if (unlikely(vector > n->num_queues)) {
259
+ trace_nvme_err_invalid_create_cq_vector(vector);
260
return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
261
}
262
- if (!(NVME_CQ_FLAGS_PC(qflags))) {
263
+ if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) {
264
+ trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags));
265
return NVME_INVALID_FIELD | NVME_DNR;
266
}
267
268
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c)
269
uint64_t prp1 = le64_to_cpu(c->prp1);
270
uint64_t prp2 = le64_to_cpu(c->prp2);
271
272
+ trace_nvme_identify_ctrl();
273
+
274
return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
275
prp1, prp2);
276
}
277
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c)
278
uint64_t prp1 = le64_to_cpu(c->prp1);
279
uint64_t prp2 = le64_to_cpu(c->prp2);
280
281
- if (nsid == 0 || nsid > n->num_namespaces) {
282
+ trace_nvme_identify_ns(nsid);
283
+
284
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
285
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
286
return NVME_INVALID_NSID | NVME_DNR;
287
}
288
289
ns = &n->namespaces[nsid - 1];
290
+
291
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
292
prp1, prp2);
293
}
294
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
295
uint16_t ret;
296
int i, j = 0;
297
298
+ trace_nvme_identify_nslist(min_nsid);
299
+
300
list = g_malloc0(data_len);
301
for (i = 0; i < n->num_namespaces; i++) {
302
if (i < min_nsid) {
303
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
304
case 0x02:
305
return nvme_identify_nslist(n, c);
306
default:
307
+ trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
308
return NVME_INVALID_FIELD | NVME_DNR;
309
}
310
}
311
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
312
switch (dw10) {
313
case NVME_VOLATILE_WRITE_CACHE:
314
result = blk_enable_write_cache(n->conf.blk);
315
+ trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
316
break;
317
case NVME_NUMBER_OF_QUEUES:
318
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
319
+ trace_nvme_getfeat_numq(result);
320
break;
321
default:
322
+ trace_nvme_err_invalid_getfeat(dw10);
323
return NVME_INVALID_FIELD | NVME_DNR;
324
}
325
326
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
327
blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
328
break;
329
case NVME_NUMBER_OF_QUEUES:
330
+ trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1,
331
+ ((dw11 >> 16) & 0xFFFF) + 1,
332
+ n->num_queues - 1, n->num_queues - 1);
333
req->cqe.result =
334
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
335
break;
336
default:
337
+ trace_nvme_err_invalid_setfeat(dw10);
338
return NVME_INVALID_FIELD | NVME_DNR;
339
}
340
return NVME_SUCCESS;
341
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
342
case NVME_ADM_CMD_GET_FEATURES:
343
return nvme_get_feature(n, cmd, req);
344
default:
345
+ trace_nvme_err_invalid_admin_opc(cmd->opcode);
346
return NVME_INVALID_OPCODE | NVME_DNR;
347
}
348
}
349
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
350
uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
351
uint32_t page_size = 1 << page_bits;
352
353
- if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
354
- n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
355
- NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
356
- NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
357
- NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
358
- NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
359
- NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
360
- NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
361
- !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) {
362
+ if (unlikely(n->cq[0])) {
363
+ trace_nvme_err_startfail_cq();
364
+ return -1;
365
+ }
366
+ if (unlikely(n->sq[0])) {
367
+ trace_nvme_err_startfail_sq();
368
+ return -1;
369
+ }
370
+ if (unlikely(!n->bar.asq)) {
371
+ trace_nvme_err_startfail_nbarasq();
372
+ return -1;
373
+ }
374
+ if (unlikely(!n->bar.acq)) {
375
+ trace_nvme_err_startfail_nbaracq();
376
+ return -1;
377
+ }
378
+ if (unlikely(n->bar.asq & (page_size - 1))) {
379
+ trace_nvme_err_startfail_asq_misaligned(n->bar.asq);
380
+ return -1;
381
+ }
382
+ if (unlikely(n->bar.acq & (page_size - 1))) {
383
+ trace_nvme_err_startfail_acq_misaligned(n->bar.acq);
384
+ return -1;
385
+ }
386
+ if (unlikely(NVME_CC_MPS(n->bar.cc) <
387
+ NVME_CAP_MPSMIN(n->bar.cap))) {
388
+ trace_nvme_err_startfail_page_too_small(
389
+ NVME_CC_MPS(n->bar.cc),
390
+ NVME_CAP_MPSMIN(n->bar.cap));
391
+ return -1;
392
+ }
393
+ if (unlikely(NVME_CC_MPS(n->bar.cc) >
394
+ NVME_CAP_MPSMAX(n->bar.cap))) {
395
+ trace_nvme_err_startfail_page_too_large(
396
+ NVME_CC_MPS(n->bar.cc),
397
+ NVME_CAP_MPSMAX(n->bar.cap));
398
+ return -1;
399
+ }
400
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) <
401
+ NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) {
402
+ trace_nvme_err_startfail_cqent_too_small(
403
+ NVME_CC_IOCQES(n->bar.cc),
404
+ NVME_CTRL_CQES_MIN(n->bar.cap));
405
+ return -1;
406
+ }
407
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) >
408
+ NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) {
409
+ trace_nvme_err_startfail_cqent_too_large(
410
+ NVME_CC_IOCQES(n->bar.cc),
411
+ NVME_CTRL_CQES_MAX(n->bar.cap));
412
+ return -1;
413
+ }
414
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) <
415
+ NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) {
416
+ trace_nvme_err_startfail_sqent_too_small(
417
+ NVME_CC_IOSQES(n->bar.cc),
418
+ NVME_CTRL_SQES_MIN(n->bar.cap));
419
+ return -1;
420
+ }
421
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) >
422
+ NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) {
423
+ trace_nvme_err_startfail_sqent_too_large(
424
+ NVME_CC_IOSQES(n->bar.cc),
425
+ NVME_CTRL_SQES_MAX(n->bar.cap));
426
+ return -1;
427
+ }
428
+ if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) {
429
+ trace_nvme_err_startfail_asqent_sz_zero();
430
+ return -1;
431
+ }
432
+ if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) {
433
+ trace_nvme_err_startfail_acqent_sz_zero();
434
return -1;
435
}
436
437
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
438
static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
439
unsigned size)
440
{
441
+ if (unlikely(offset & (sizeof(uint32_t) - 1))) {
442
+ NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32,
443
+ "MMIO write not 32-bit aligned,"
444
+ " offset=0x%"PRIx64"", offset);
445
+ /* should be ignored, fall through for now */
446
+ }
447
+
448
+ if (unlikely(size < sizeof(uint32_t))) {
449
+ NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall,
450
+ "MMIO write smaller than 32-bits,"
451
+ " offset=0x%"PRIx64", size=%u",
452
+ offset, size);
453
+ /* should be ignored, fall through for now */
454
+ }
455
+
456
switch (offset) {
457
- case 0xc:
458
+ case 0xc: /* INTMS */
459
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
460
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
461
+ "undefined access to interrupt mask set"
462
+ " when MSI-X is enabled");
463
+ /* should be ignored, fall through for now */
33
+ }
464
+ }
34
+ if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
465
n->bar.intms |= data & 0xffffffff;
35
+ fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": "
466
n->bar.intmc = n->bar.intms;
36
+ "L1 table is too large; snapshot table entry corrupted\n",
467
+ trace_nvme_mmio_intm_set(data & 0xffffffff,
37
+ sn->id_str, sn->name, sn->l1_size);
468
+ n->bar.intmc);
38
+ res->corruptions++;
469
break;
39
+ continue;
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 */
40
+ }
477
+ }
41
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
478
n->bar.intms &= ~(data & 0xffffffff);
42
sn->l1_table_offset, sn->l1_size, 0, fix);
479
n->bar.intmc = n->bar.intms;
43
if (ret < 0) {
480
+ trace_nvme_mmio_intm_clr(data & 0xffffffff,
44
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
481
+ n->bar.intmc);
45
index XXXXXXX..XXXXXXX 100755
482
break;
46
--- a/tests/qemu-iotests/080
483
- case 0x14:
47
+++ b/tests/qemu-iotests/080
484
+ case 0x14: /* CC */
48
@@ -XXX,XX +XXX,XX @@ poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x0
485
+ trace_nvme_mmio_cfg(data & 0xffffffff);
49
-c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
486
/* Windows first sends data, then sends enable bit */
50
{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
487
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
51
{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir
488
!NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
52
+_check_test_img
489
@@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
53
490
54
echo
491
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
55
echo "== Invalid snapshot L1 table size =="
492
n->bar.cc = data;
56
@@ -XXX,XX +XXX,XX @@ poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00"
493
- if (nvme_start_ctrl(n)) {
57
-c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
494
+ if (unlikely(nvme_start_ctrl(n))) {
58
{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
495
+ trace_nvme_err_startfail();
59
{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir
496
n->bar.csts = NVME_CSTS_FAILED;
60
+_check_test_img
497
} else {
61
498
+ trace_nvme_mmio_start_success();
62
# success, all done
499
n->bar.csts = NVME_CSTS_READY;
63
echo "*** done"
500
}
64
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
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
65
index XXXXXXX..XXXXXXX 100644
681
index XXXXXXX..XXXXXXX 100644
66
--- a/tests/qemu-iotests/080.out
682
--- a/hw/block/trace-events
67
+++ b/tests/qemu-iotests/080.out
683
+++ b/hw/block/trace-events
68
@@ -XXX,XX +XXX,XX @@ write failed: Invalid argument
684
@@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
69
qemu-img: Snapshot L1 table offset invalid
685
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
70
qemu-img: Could not apply snapshot 'test': Failed to load snapshot: Invalid argument
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"
71
qemu-img: Could not delete snapshot 'test': Snapshot L1 table offset invalid
687
72
+ERROR snapshot 1 (test) l1_offset=0x400200: L1 table is not cluster aligned; snapshot table entry corrupted
688
+# hw/block/nvme.c
73
+Leaked cluster 4 refcount=2 reference=1
689
+# nvme traces for successful events
74
+Leaked cluster 5 refcount=2 reference=1
690
+nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
75
+Leaked cluster 6 refcount=1 reference=0
691
+nvme_irq_pin(void) "pulsing IRQ pin"
76
+
692
+nvme_irq_masked(void) "IRQ is masked"
77
+1 errors were found on the image.
693
+nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
78
+Data may be corrupted, or further writes to the image may corrupt it.
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""
79
+
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""
80
+3 leaked clusters were found on the image.
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"
81
+This means waste of disk space, but no harm to data.
697
+nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
82
698
+nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
83
== Invalid snapshot L1 table size ==
699
+nvme_identify_ctrl(void) "identify controller"
84
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
700
+nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
85
@@ -XXX,XX +XXX,XX @@ write failed: File too large
701
+nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
86
qemu-img: Snapshot L1 table too large
702
+nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
87
qemu-img: Could not apply snapshot 'test': Failed to load snapshot: File too large
703
+nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
88
qemu-img: Could not delete snapshot 'test': Snapshot L1 table too large
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"
89
+ERROR snapshot 1 (test) l1_size=0x10000000: L1 table is too large; snapshot table entry corrupted
705
+nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
90
+Leaked cluster 4 refcount=2 reference=1
706
+nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
91
+Leaked cluster 5 refcount=2 reference=1
707
+nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
92
+Leaked cluster 6 refcount=1 reference=0
708
+nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
93
+
709
+nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
94
+1 errors were found on the image.
710
+nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
95
+Data may be corrupted, or further writes to the image may corrupt it.
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""
96
+
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""
97
+3 leaked clusters were found on the image.
713
+nvme_mmio_start_success(void) "setting controller enable bit succeeded"
98
+This means waste of disk space, but no harm to data.
714
+nvme_mmio_stopped(void) "cleared controller enable bit"
99
*** done
715
+nvme_mmio_shutdown_set(void) "shutdown bit set"
716
+nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
717
+
718
+# nvme traces for error conditions
719
+nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
720
+nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
721
+nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
722
+nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
723
+nvme_err_invalid_field(void) "invalid field"
724
+nvme_err_invalid_prp(void) "invalid PRP"
725
+nvme_err_invalid_sgl(void) "invalid SGL"
726
+nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
727
+nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
728
+nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
729
+nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
730
+nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
731
+nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
732
+nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
733
+nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
734
+nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
735
+nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
736
+nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
737
+nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
738
+nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
739
+nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
740
+nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
741
+nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
742
+nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
743
+nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
744
+nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
745
+nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
746
+nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
747
+nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
748
+nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
749
+nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
750
+nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
751
+nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
752
+nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u"
753
+nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u"
754
+nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u"
755
+nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u"
756
+nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u"
757
+nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u"
758
+nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero"
759
+nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero"
760
+nvme_err_startfail(void) "setting controller enable bit failed"
761
+
762
+# Traces for undefined behavior
763
+nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64""
764
+nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u"
765
+nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled"
766
+nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status"
767
+nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)"
768
+nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
769
+nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
770
+nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
771
+nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
772
+nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
773
+nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
774
+nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0"
775
+nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring"
776
+nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring"
777
+nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring"
778
+nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring"
779
+nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
780
+
781
# hw/block/xen_disk.c
782
xen_disk_alloc(char *name) "%s"
783
xen_disk_init(char *name) "%s"
100
--
784
--
101
2.13.6
785
2.13.6
102
786
103
787
diff view generated by jsdifflib
1
From: Fam Zheng <famz@redhat.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
The AFL image is to exercise the code validating image size, which
3
Management tools create overlays of running guests with qemu-img:
4
doesn't work on 32 bit or when out of memory (there is a large
5
allocation before the interesting point). So check that and skip the
6
test, instead of faking the result.
7
4
5
$ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2
6
7
but this doesn't work anymore due to image locking:
8
9
qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock
10
Is another process using the image?
11
Could not open backing image to determine size.
12
Use the force share option to allow this use case again.
13
14
Cc: qemu-stable@nongnu.org
8
Signed-off-by: Fam Zheng <famz@redhat.com>
15
Signed-off-by: Fam Zheng <famz@redhat.com>
9
Message-id: 20180301011413.11531-1-famz@redhat.com
10
Reviewed-by: Eric Blake <eblake@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
18
---
13
tests/qemu-iotests/059 | 5 ++---
19
block.c | 3 ++-
14
1 file changed, 2 insertions(+), 3 deletions(-)
20
1 file changed, 2 insertions(+), 1 deletion(-)
15
21
16
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
22
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100755
23
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qemu-iotests/059
24
--- a/block.c
19
+++ b/tests/qemu-iotests/059
25
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ done
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
21
echo
27
back_flags = flags;
22
echo "=== Testing afl image with a very large capacity ==="
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
23
_use_sample_img afl9.vmdk.bz2
29
24
-# The sed makes this test pass on machines with little RAM
30
+ backing_options = qdict_new();
25
-# (and also with 32 bit builds)
31
if (backing_fmt) {
26
-_img_info | sed -e 's/Cannot allocate memory/Invalid argument/'
32
- backing_options = qdict_new();
27
+_img_info | grep -q 'Cannot allocate memory' && _notrun "Insufficent memory, skipped test"
33
qdict_put_str(backing_options, "driver", backing_fmt);
28
+_img_info
34
}
29
_cleanup_test_img
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
30
36
31
# success, all done
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
38
&local_err);
32
--
39
--
33
2.13.6
40
2.13.6
34
41
35
42
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to nfs, which enables
1
From: Thomas Huth <thuth@redhat.com>
2
image creation over QMP.
3
2
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
4
5
Signed-off-by: Thomas Huth <thuth@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
9
---
7
qapi/block-core.json | 16 ++++++++++-
10
blockdev.c | 11 -----------
8
block/nfs.c | 76 +++++++++++++++++++++++++++++++++++++++++-----------
11
qemu-doc.texi | 6 ------
9
2 files changed, 75 insertions(+), 17 deletions(-)
12
2 files changed, 17 deletions(-)
10
13
11
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/blockdev.c b/blockdev.c
12
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
16
--- a/blockdev.c
14
+++ b/qapi/block-core.json
17
+++ b/blockdev.c
15
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
16
'*preallocation': 'PreallocMode' } }
19
.type = QEMU_OPT_STRING,
17
20
.help = "chs translation (auto, lba, none)",
18
##
21
},{
19
+# @BlockdevCreateOptionsNfs:
22
- .name = "boot",
20
+#
23
- .type = QEMU_OPT_BOOL,
21
+# Driver specific image creation options for NFS.
24
- .help = "(deprecated, ignored)",
22
+#
25
- },{
23
+# @location Where to store the new image file
26
.name = "addr",
24
+# @size Size of the virtual disk in bytes
27
.type = QEMU_OPT_STRING,
25
+#
28
.help = "pci address (virtio only)",
26
+# Since: 2.12
29
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
27
+##
28
+{ 'struct': 'BlockdevCreateOptionsNfs',
29
+ 'data': { 'location': 'BlockdevOptionsNfs',
30
+ 'size': 'size' } }
31
+
32
+##
33
# @BlockdevQcow2Version:
34
#
35
# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2)
36
@@ -XXX,XX +XXX,XX @@
37
'iscsi': 'BlockdevCreateNotSupported',
38
'luks': 'BlockdevCreateNotSupported',
39
'nbd': 'BlockdevCreateNotSupported',
40
- 'nfs': 'BlockdevCreateNotSupported',
41
+ 'nfs': 'BlockdevCreateOptionsNfs',
42
'null-aio': 'BlockdevCreateNotSupported',
43
'null-co': 'BlockdevCreateNotSupported',
44
'nvme': 'BlockdevCreateNotSupported',
45
diff --git a/block/nfs.c b/block/nfs.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/block/nfs.c
48
+++ b/block/nfs.c
49
@@ -XXX,XX +XXX,XX @@ out:
50
return ret;
51
}
52
53
-static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
54
- int flags, int open_flags, Error **errp)
55
+static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
56
+ Error **errp)
57
{
58
BlockdevOptionsNfs *opts = NULL;
59
QObject *crumpled = NULL;
60
Visitor *v;
61
Error *local_err = NULL;
62
- int ret;
63
64
crumpled = qdict_crumple(options, errp);
65
if (crumpled == NULL) {
66
- return -EINVAL;
67
+ return NULL;
68
}
69
70
v = qobject_input_visitor_new_keyval(crumpled);
71
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
72
visit_free(v);
73
+ qobject_decref(crumpled);
74
75
if (local_err) {
76
- error_propagate(errp, local_err);
77
+ return NULL;
78
+ }
79
+
80
+ return opts;
81
+}
82
+
83
+static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
84
+ int flags, int open_flags, Error **errp)
85
+{
86
+ BlockdevOptionsNfs *opts;
87
+ int ret;
88
+
89
+ opts = nfs_options_qdict_to_qapi(options, errp);
90
+ if (opts == NULL) {
91
ret = -EINVAL;
92
goto fail;
30
goto fail;
93
}
31
}
94
32
95
ret = nfs_client_open(client, opts, flags, open_flags, errp);
33
- /* Deprecated option boot=[on|off] */
96
fail:
34
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
97
- qobject_decref(crumpled);
35
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
98
qapi_free_BlockdevOptionsNfs(opts);
36
- "ignored. Future versions will reject this parameter. Please "
99
return ret;
37
- "update your scripts.\n");
100
}
38
- }
101
@@ -XXX,XX +XXX,XX @@ static QemuOptsList nfs_create_opts = {
39
-
102
}
40
/* Other deprecated options */
103
};
41
if (!qtest_enabled()) {
104
42
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
105
-static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
43
diff --git a/qemu-doc.texi b/qemu-doc.texi
106
- Error **errp)
44
index XXXXXXX..XXXXXXX 100644
107
+static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp)
45
--- a/qemu-doc.texi
108
{
46
+++ b/qemu-doc.texi
109
- int64_t ret, total_size;
47
@@ -XXX,XX +XXX,XX @@ deprecated.
110
+ BlockdevCreateOptionsNfs *opts = &options->u.nfs;
48
111
NFSClient *client = g_new0(NFSClient, 1);
49
@section System emulator command line arguments
112
- QDict *options = NULL;
50
113
+ int ret;
51
-@subsection -drive boot=on|off (since 1.3.0)
114
+
52
-
115
+ assert(options->driver == BLOCKDEV_DRIVER_NFS);
53
-The ``boot=on|off'' option to the ``-drive'' argument is
116
54
-ignored. Applications should use the ``bootindex=N'' parameter
117
client->aio_context = qemu_get_aio_context();
55
-to set an absolute ordering between devices instead.
118
56
-
119
+ ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp);
57
@subsection -tdf (since 1.3.0)
120
+ if (ret < 0) {
58
121
+ goto out;
59
The ``-tdf'' argument is ignored. The behaviour implemented
122
+ }
123
+ ret = nfs_ftruncate(client->context, client->fh, opts->size);
124
+ nfs_client_close(client);
125
+
126
+out:
127
+ g_free(client);
128
+ return ret;
129
+}
130
+
131
+static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
132
+ Error **errp)
133
+{
134
+ BlockdevCreateOptions *create_options;
135
+ BlockdevCreateOptionsNfs *nfs_opts;
136
+ QDict *options;
137
+ int ret;
138
+
139
+ create_options = g_new0(BlockdevCreateOptions, 1);
140
+ create_options->driver = BLOCKDEV_DRIVER_NFS;
141
+ nfs_opts = &create_options->u.nfs;
142
+
143
/* Read out options */
144
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
145
- BDRV_SECTOR_SIZE);
146
+ nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
147
+ BDRV_SECTOR_SIZE);
148
149
options = qdict_new();
150
ret = nfs_parse_uri(url, options, errp);
151
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
152
goto out;
153
}
154
155
- ret = nfs_client_open_qdict(client, options, O_CREAT, 0, errp);
156
+ nfs_opts->location = nfs_options_qdict_to_qapi(options, errp);
157
+ if (nfs_opts->location == NULL) {
158
+ ret = -EINVAL;
159
+ goto out;
160
+ }
161
+
162
+ ret = nfs_file_co_create(create_options, errp);
163
if (ret < 0) {
164
goto out;
165
}
166
- ret = nfs_ftruncate(client->context, client->fh, total_size);
167
- nfs_client_close(client);
168
+
169
+ ret = 0;
170
out:
171
QDECREF(options);
172
- g_free(client);
173
+ qapi_free_BlockdevCreateOptions(create_options);
174
return ret;
175
}
176
177
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nfs = {
178
179
.bdrv_file_open = nfs_file_open,
180
.bdrv_close = nfs_file_close,
181
+ .bdrv_co_create = nfs_file_co_create,
182
.bdrv_co_create_opts = nfs_file_co_create_opts,
183
.bdrv_reopen_prepare = nfs_reopen_prepare,
184
185
--
60
--
186
2.13.6
61
2.13.6
187
62
188
63
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
This function deletes a snapshot from disk, removing its entry from
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
the snapshot table, freeing its L1 table and decreasing the refcounts
4
complained that we should keep it, so let's remove this legacy option
5
of all clusters.
5
now to simplify the code quite a bit.
6
6
7
The L1 table offset and size are however not validated. If we use
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
invalid values in this function we'll probably corrupt the image even
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
more, so we should return an error instead.
9
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
11
We now have a function to take care of this, so let's use it.
12
13
Signed-off-by: Alberto Garcia <berto@igalia.com>
14
Reviewed-by: Eric Blake <eblake@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
11
---
17
block/qcow2-snapshot.c | 7 +++++++
12
vl.c | 86 ++-------------------------------------------------------
18
tests/qemu-iotests/080 | 2 ++
13
qemu-doc.texi | 8 ------
19
tests/qemu-iotests/080.out | 2 ++
14
qemu-options.hx | 19 ++-----------
20
3 files changed, 11 insertions(+)
15
3 files changed, 4 insertions(+), 109 deletions(-)
21
16
22
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
17
diff --git a/vl.c b/vl.c
23
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
24
--- a/block/qcow2-snapshot.c
19
--- a/vl.c
25
+++ b/block/qcow2-snapshot.c
20
+++ b/vl.c
26
@@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_delete(BlockDriverState *bs,
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
27
}
22
const char *boot_order = NULL;
28
sn = s->snapshots[snapshot_index];
23
const char *boot_once = NULL;
29
24
DisplayState *ds;
30
+ ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size,
25
- int cyls, heads, secs, translation;
31
+ sizeof(uint64_t), QCOW_MAX_L1_SIZE,
26
QemuOpts *opts, *machine_opts;
32
+ "Snapshot L1 table", errp);
27
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
33
+ if (ret < 0) {
28
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
34
+ return ret;
29
QemuOptsList *olist;
35
+ }
30
int optind;
36
+
31
const char *optarg;
37
/* Remove it from the snapshot list */
32
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
38
memmove(s->snapshots + snapshot_index,
33
39
s->snapshots + snapshot_index + 1,
34
cpu_model = NULL;
40
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
35
snapshot = 0;
41
index XXXXXXX..XXXXXXX 100755
36
- cyls = heads = secs = 0;
42
--- a/tests/qemu-iotests/080
37
- translation = BIOS_ATA_TRANSLATION_AUTO;
43
+++ b/tests/qemu-iotests/080
38
44
@@ -XXX,XX +XXX,XX @@ poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x0
39
nb_nics = 0;
45
{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
40
46
-c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
41
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
47
{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
42
if (optind >= argc)
48
+{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir
43
break;
49
44
if (argv[optind][0] != '-') {
50
echo
45
- hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
51
echo "== Invalid snapshot L1 table size =="
46
+ drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
52
@@ -XXX,XX +XXX,XX @@ poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00"
47
} else {
53
{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
48
const QEMUOption *popt;
54
-c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
49
55
{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
50
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
56
+{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir
51
cpu_model = optarg;
57
52
break;
58
# success, all done
53
case QEMU_OPTION_hda:
59
echo "*** done"
54
- {
60
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
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
61
index XXXXXXX..XXXXXXX 100644
144
index XXXXXXX..XXXXXXX 100644
62
--- a/tests/qemu-iotests/080.out
145
--- a/qemu-doc.texi
63
+++ b/tests/qemu-iotests/080.out
146
+++ b/qemu-doc.texi
64
@@ -XXX,XX +XXX,XX @@ Failed to flush the refcount block cache: Invalid argument
147
@@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the
65
write failed: Invalid argument
148
``-object filter-dump'' argument which works in combination
66
qemu-img: Snapshot L1 table offset invalid
149
with the modern ``-netdev`` backends instead.
67
qemu-img: Could not apply snapshot 'test': Failed to load snapshot: Invalid argument
150
68
+qemu-img: Could not delete snapshot 'test': Snapshot L1 table offset invalid
151
-@subsection -hdachs (since 2.10.0)
69
152
-
70
== Invalid snapshot L1 table size ==
153
-The ``-hdachs'' argument is now a synonym for setting
71
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
154
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
72
@@ -XXX,XX +XXX,XX @@ Failed to flush the refcount block cache: File too large
155
-on the ``ide-hd'' device using the ``-device'' argument.
73
write failed: File too large
156
-The new syntax allows different settings to be provided
74
qemu-img: Snapshot L1 table too large
157
-per disk.
75
qemu-img: Could not apply snapshot 'test': Failed to load snapshot: File too large
158
-
76
+qemu-img: Could not delete snapshot 'test': Snapshot L1 table too large
159
@subsection -usbdevice (since 2.10.0)
77
*** done
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"
78
--
199
--
79
2.13.6
200
2.13.6
80
201
81
202
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
This function copies a snapshot's L1 table into the active one without
3
Looks like we forgot to announce the deprecation of these options in
4
validating it first.
4
the corresponding chapter of the qemu-doc text, so let's do that now.
5
5
6
We now have a function to take care of this, so let's use it.
6
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Cc: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
9
Signed-off-by: Alberto Garcia <berto@igalia.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
10
---
13
block/qcow2-snapshot.c | 9 +++++++++
11
qemu-doc.texi | 15 +++++++++++++++
14
tests/qemu-iotests/080 | 2 ++
12
1 file changed, 15 insertions(+)
15
tests/qemu-iotests/080.out | 4 ++++
16
3 files changed, 15 insertions(+)
17
13
18
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
19
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
20
--- a/block/qcow2-snapshot.c
16
--- a/qemu-doc.texi
21
+++ b/block/qcow2-snapshot.c
17
+++ b/qemu-doc.texi
22
@@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
23
{
19
The ``-drive if=scsi'' argument is replaced by the the
24
BDRVQcow2State *s = bs->opaque;
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
25
QCowSnapshot *sn;
21
26
+ Error *local_err = NULL;
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
27
int i, snapshot_index;
28
int cur_l1_bytes, sn_l1_bytes;
29
int ret;
30
@@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
31
}
32
sn = &s->snapshots[snapshot_index];
33
34
+ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size,
35
+ sizeof(uint64_t), QCOW_MAX_L1_SIZE,
36
+ "Snapshot L1 table", &local_err);
37
+ if (ret < 0) {
38
+ error_report_err(local_err);
39
+ goto fail;
40
+ }
41
+
23
+
42
if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
24
+The drive geometry arguments are replaced by the the geometry arguments
43
error_report("qcow2: Loading snapshots with different disk "
25
+that can be specified with the ``-device'' parameter.
44
"size is not implemented");
26
+
45
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
27
+@subsection -drive serial=... (since 2.10.0)
46
index XXXXXXX..XXXXXXX 100755
28
+
47
--- a/tests/qemu-iotests/080
29
+The drive serial argument is replaced by the the serial argument
48
+++ b/tests/qemu-iotests/080
30
+that can be specified with the ``-device'' parameter.
49
@@ -XXX,XX +XXX,XX @@ poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x0
31
+
50
{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
32
+@subsection -drive addr=... (since 2.10.0)
51
{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
33
+
52
-c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
34
+The drive addr argument is replaced by the the addr argument
53
+{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
35
+that can be specified with the ``-device'' parameter.
54
36
+
55
echo
37
@subsection -net dump (since 2.10.0)
56
echo "== Invalid snapshot L1 table size =="
38
57
@@ -XXX,XX +XXX,XX @@ poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00"
39
The ``--net dump'' argument is now replaced with the
58
{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
59
{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
60
-c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
61
+{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir
62
63
# success, all done
64
echo "*** done"
65
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
66
index XXXXXXX..XXXXXXX 100644
67
--- a/tests/qemu-iotests/080.out
68
+++ b/tests/qemu-iotests/080.out
69
@@ -XXX,XX +XXX,XX @@ qemu-img: Snapshot L1 table offset invalid
70
qemu-img: Error while amending options: Invalid argument
71
Failed to flush the refcount block cache: Invalid argument
72
write failed: Invalid argument
73
+qemu-img: Snapshot L1 table offset invalid
74
+qemu-img: Could not apply snapshot 'test': Failed to load snapshot: Invalid argument
75
76
== Invalid snapshot L1 table size ==
77
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
78
@@ -XXX,XX +XXX,XX @@ qemu-img: Snapshot L1 table too large
79
qemu-img: Error while amending options: File too large
80
Failed to flush the refcount block cache: File too large
81
write failed: File too large
82
+qemu-img: Snapshot L1 table too large
83
+qemu-img: Could not apply snapshot 'test': Failed to load snapshot: File too large
84
*** done
85
--
40
--
86
2.13.6
41
2.13.6
87
42
88
43
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
Suggested-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Fam Zheng <famz@redhat.com>
4
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
5
Message-Id: <1516279431-30424-8-git-send-email-pbonzini@redhat.com>
6
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
5
---
9
include/block/block_int.h | 5 +++--
6
include/block/block_int.h | 1 -
10
block.c | 43 ++++++++++++++++++++++++++++++++++++++++---
7
block/io.c | 18 ------------------
11
block/parallels.c | 17 +++++++++++------
8
2 files changed, 19 deletions(-)
12
block/qcow2.c | 23 +++++++++++++++++++----
13
block/qed-check.c | 1 +
14
block/qed-table.c | 26 +++++++++-----------------
15
block/qed.c | 13 +++++++++----
16
block/vdi.c | 6 +++---
17
block/vhdx.c | 7 ++++---
18
block/vmdk.c | 7 ++++---
19
10 files changed, 103 insertions(+), 45 deletions(-)
20
9
21
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
22
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block_int.h
12
--- a/include/block/block_int.h
24
+++ b/include/block/block_int.h
13
+++ b/include/block/block_int.h
25
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
26
* Returns 0 for completed check, -errno for internal errors.
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
27
* The check results are stored in result.
16
28
*/
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
29
- int (*bdrv_check)(BlockDriverState *bs, BdrvCheckResult *result,
18
-bool bdrv_requests_pending(BlockDriverState *bs);
30
- BdrvCheckMode fix);
19
31
+ int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs,
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
32
+ BdrvCheckResult *result,
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
33
+ BdrvCheckMode fix);
22
diff --git a/block/io.c b/block/io.c
34
35
int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
36
BlockDriverAmendStatusCB *status_cb,
37
diff --git a/block.c b/block.c
38
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
39
--- a/block.c
24
--- a/block/io.c
40
+++ b/block.c
25
+++ b/block/io.c
41
@@ -XXX,XX +XXX,XX @@ static void bdrv_delete(BlockDriverState *bs)
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
42
* free of errors) or -errno when an internal error occurred. The results of the
27
assert(old >= 1);
43
* check are stored in res.
44
*/
45
-int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix)
46
+static int coroutine_fn bdrv_co_check(BlockDriverState *bs,
47
+ BdrvCheckResult *res, BdrvCheckMode fix)
48
{
49
if (bs->drv == NULL) {
50
return -ENOMEDIUM;
51
}
52
- if (bs->drv->bdrv_check == NULL) {
53
+ if (bs->drv->bdrv_co_check == NULL) {
54
return -ENOTSUP;
55
}
56
57
memset(res, 0, sizeof(*res));
58
- return bs->drv->bdrv_check(bs, res, fix);
59
+ return bs->drv->bdrv_co_check(bs, res, fix);
60
+}
61
+
62
+typedef struct CheckCo {
63
+ BlockDriverState *bs;
64
+ BdrvCheckResult *res;
65
+ BdrvCheckMode fix;
66
+ int ret;
67
+} CheckCo;
68
+
69
+static void bdrv_check_co_entry(void *opaque)
70
+{
71
+ CheckCo *cco = opaque;
72
+ cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
73
+}
74
+
75
+int bdrv_check(BlockDriverState *bs,
76
+ BdrvCheckResult *res, BdrvCheckMode fix)
77
+{
78
+ Coroutine *co;
79
+ CheckCo cco = {
80
+ .bs = bs,
81
+ .res = res,
82
+ .ret = -EINPROGRESS,
83
+ .fix = fix,
84
+ };
85
+
86
+ if (qemu_in_coroutine()) {
87
+ /* Fast-path if already in coroutine context */
88
+ bdrv_check_co_entry(&cco);
89
+ } else {
90
+ co = qemu_coroutine_create(bdrv_check_co_entry, &cco);
91
+ qemu_coroutine_enter(co);
92
+ BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS);
93
+ }
94
+
95
+ return cco.ret;
96
}
28
}
97
29
98
/*
30
-/* Check if any requests are in-flight (including throttled requests) */
99
diff --git a/block/parallels.c b/block/parallels.c
31
-bool bdrv_requests_pending(BlockDriverState *bs)
100
index XXXXXXX..XXXXXXX 100644
32
-{
101
--- a/block/parallels.c
33
- BdrvChild *child;
102
+++ b/block/parallels.c
34
-
103
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
35
- if (atomic_read(&bs->in_flight)) {
104
}
36
- return true;
105
106
107
-static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
108
- BdrvCheckMode fix)
109
+static int coroutine_fn parallels_co_check(BlockDriverState *bs,
110
+ BdrvCheckResult *res,
111
+ BdrvCheckMode fix)
112
{
113
BDRVParallelsState *s = bs->opaque;
114
int64_t size, prev_off, high_off;
115
@@ -XXX,XX +XXX,XX @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
116
return size;
117
}
118
119
+ qemu_co_mutex_lock(&s->lock);
120
if (s->header_unclean) {
121
fprintf(stderr, "%s image was not closed correctly\n",
122
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR");
123
@@ -XXX,XX +XXX,XX @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
124
prev_off = off;
125
}
126
127
+ ret = 0;
128
if (flush_bat) {
129
ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size);
130
if (ret < 0) {
131
res->check_errors++;
132
- return ret;
133
+ goto out;
134
}
135
}
136
137
@@ -XXX,XX +XXX,XX @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
138
if (ret < 0) {
139
error_report_err(local_err);
140
res->check_errors++;
141
- return ret;
142
+ goto out;
143
}
144
res->leaks_fixed += count;
145
}
146
}
147
148
- return 0;
149
+out:
150
+ qemu_co_mutex_unlock(&s->lock);
151
+ return ret;
152
}
153
154
155
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_parallels = {
156
.bdrv_co_writev = parallels_co_writev,
157
.supports_backing = true,
158
.bdrv_co_create_opts = parallels_co_create_opts,
159
- .bdrv_check = parallels_check,
160
+ .bdrv_co_check = parallels_co_check,
161
.create_opts = &parallels_create_opts,
162
};
163
164
diff --git a/block/qcow2.c b/block/qcow2.c
165
index XXXXXXX..XXXXXXX 100644
166
--- a/block/qcow2.c
167
+++ b/block/qcow2.c
168
@@ -XXX,XX +XXX,XX @@ int qcow2_mark_consistent(BlockDriverState *bs)
169
return 0;
170
}
171
172
-static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
173
- BdrvCheckMode fix)
174
+static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs,
175
+ BdrvCheckResult *result,
176
+ BdrvCheckMode fix)
177
{
178
int ret = qcow2_check_refcounts(bs, result, fix);
179
if (ret < 0) {
180
@@ -XXX,XX +XXX,XX @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
181
return ret;
182
}
183
184
+static int coroutine_fn qcow2_co_check(BlockDriverState *bs,
185
+ BdrvCheckResult *result,
186
+ BdrvCheckMode fix)
187
+{
188
+ BDRVQcow2State *s = bs->opaque;
189
+ int ret;
190
+
191
+ qemu_co_mutex_lock(&s->lock);
192
+ ret = qcow2_co_check_locked(bs, result, fix);
193
+ qemu_co_mutex_unlock(&s->lock);
194
+ return ret;
195
+}
196
+
197
static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
198
uint64_t entries, size_t entry_len)
199
{
200
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
201
(s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
202
BdrvCheckResult result = {0};
203
204
- ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
205
+ ret = qcow2_co_check_locked(bs, &result,
206
+ BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
207
if (ret < 0 || result.check_errors) {
208
if (ret >= 0) {
209
ret = -EIO;
210
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
211
.bdrv_inactivate = qcow2_inactivate,
212
213
.create_opts = &qcow2_create_opts,
214
- .bdrv_check = qcow2_check,
215
+ .bdrv_co_check = qcow2_co_check,
216
.bdrv_amend_options = qcow2_amend_options,
217
218
.bdrv_detach_aio_context = qcow2_detach_aio_context,
219
diff --git a/block/qed-check.c b/block/qed-check.c
220
index XXXXXXX..XXXXXXX 100644
221
--- a/block/qed-check.c
222
+++ b/block/qed-check.c
223
@@ -XXX,XX +XXX,XX @@ static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result)
224
qed_write_header_sync(s);
225
}
226
227
+/* Called with table_lock held. */
228
int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
229
{
230
QEDCheck check = {
231
diff --git a/block/qed-table.c b/block/qed-table.c
232
index XXXXXXX..XXXXXXX 100644
233
--- a/block/qed-table.c
234
+++ b/block/qed-table.c
235
@@ -XXX,XX +XXX,XX @@
236
#include "qed.h"
237
#include "qemu/bswap.h"
238
239
-/* Called either from qed_check or with table_lock held. */
240
+/* Called with table_lock held. */
241
static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
242
{
243
QEMUIOVector qiov;
244
@@ -XXX,XX +XXX,XX @@ static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
245
246
trace_qed_read_table(s, offset, table);
247
248
- if (qemu_in_coroutine()) {
249
- qemu_co_mutex_unlock(&s->table_lock);
250
- }
37
- }
251
+ qemu_co_mutex_unlock(&s->table_lock);
38
-
252
ret = bdrv_preadv(s->bs->file, offset, &qiov);
39
- QLIST_FOREACH(child, &bs->children, next) {
253
- if (qemu_in_coroutine()) {
40
- if (bdrv_requests_pending(child->bs)) {
254
- qemu_co_mutex_lock(&s->table_lock);
41
- return true;
42
- }
255
- }
43
- }
256
+ qemu_co_mutex_lock(&s->table_lock);
44
-
257
if (ret < 0) {
45
- return false;
258
goto out;
46
-}
259
}
47
-
260
@@ -XXX,XX +XXX,XX @@ out:
48
typedef struct {
261
* @n: Number of elements
49
Coroutine *co;
262
* @flush: Whether or not to sync to disk
50
BlockDriverState *bs;
263
*
264
- * Called either from qed_check or with table_lock held.
265
+ * Called with table_lock held.
266
*/
267
static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
268
unsigned int index, unsigned int n, bool flush)
269
@@ -XXX,XX +XXX,XX @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
270
/* Adjust for offset into table */
271
offset += start * sizeof(uint64_t);
272
273
- if (qemu_in_coroutine()) {
274
- qemu_co_mutex_unlock(&s->table_lock);
275
- }
276
+ qemu_co_mutex_unlock(&s->table_lock);
277
ret = bdrv_pwritev(s->bs->file, offset, &qiov);
278
- if (qemu_in_coroutine()) {
279
- qemu_co_mutex_lock(&s->table_lock);
280
- }
281
+ qemu_co_mutex_lock(&s->table_lock);
282
trace_qed_write_table_cb(s, table, flush, ret);
283
if (ret < 0) {
284
goto out;
285
@@ -XXX,XX +XXX,XX @@ int qed_read_l1_table_sync(BDRVQEDState *s)
286
return qed_read_table(s, s->header.l1_table_offset, s->l1_table);
287
}
288
289
-/* Called either from qed_check or with table_lock held. */
290
+/* Called with table_lock held. */
291
int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n)
292
{
293
BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
294
@@ -XXX,XX +XXX,XX @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
295
return qed_write_l1_table(s, index, n);
296
}
297
298
-/* Called either from qed_check or with table_lock held. */
299
+/* Called with table_lock held. */
300
int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
301
{
302
int ret;
303
@@ -XXX,XX +XXX,XX @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
304
return qed_read_l2_table(s, request, offset);
305
}
306
307
-/* Called either from qed_check or with table_lock held. */
308
+/* Called with table_lock held. */
309
int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
310
unsigned int index, unsigned int n, bool flush)
311
{
312
diff --git a/block/qed.c b/block/qed.c
313
index XXXXXXX..XXXXXXX 100644
314
--- a/block/qed.c
315
+++ b/block/qed.c
316
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
317
}
318
}
319
320
-static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
321
- BdrvCheckMode fix)
322
+static int bdrv_qed_co_check(BlockDriverState *bs, BdrvCheckResult *result,
323
+ BdrvCheckMode fix)
324
{
325
BDRVQEDState *s = bs->opaque;
326
+ int ret;
327
328
- return qed_check(s, result, !!fix);
329
+ qemu_co_mutex_lock(&s->table_lock);
330
+ ret = qed_check(s, result, !!fix);
331
+ qemu_co_mutex_unlock(&s->table_lock);
332
+
333
+ return ret;
334
}
335
336
static QemuOptsList qed_create_opts = {
337
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = {
338
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
339
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
340
.bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache,
341
- .bdrv_check = bdrv_qed_check,
342
+ .bdrv_co_check = bdrv_qed_co_check,
343
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
344
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
345
.bdrv_co_drain_begin = bdrv_qed_co_drain_begin,
346
diff --git a/block/vdi.c b/block/vdi.c
347
index XXXXXXX..XXXXXXX 100644
348
--- a/block/vdi.c
349
+++ b/block/vdi.c
350
@@ -XXX,XX +XXX,XX @@ static void vdi_header_print(VdiHeader *header)
351
}
352
#endif
353
354
-static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res,
355
- BdrvCheckMode fix)
356
+static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
357
+ BdrvCheckMode fix)
358
{
359
/* TODO: additional checks possible. */
360
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
361
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vdi = {
362
.bdrv_get_info = vdi_get_info,
363
364
.create_opts = &vdi_create_opts,
365
- .bdrv_check = vdi_check,
366
+ .bdrv_co_check = vdi_co_check,
367
};
368
369
static void bdrv_vdi_init(void)
370
diff --git a/block/vhdx.c b/block/vhdx.c
371
index XXXXXXX..XXXXXXX 100644
372
--- a/block/vhdx.c
373
+++ b/block/vhdx.c
374
@@ -XXX,XX +XXX,XX @@ exit:
375
* r/w and any log has already been replayed, so there is nothing (currently)
376
* for us to do here
377
*/
378
-static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result,
379
- BdrvCheckMode fix)
380
+static int coroutine_fn vhdx_co_check(BlockDriverState *bs,
381
+ BdrvCheckResult *result,
382
+ BdrvCheckMode fix)
383
{
384
BDRVVHDXState *s = bs->opaque;
385
386
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vhdx = {
387
.bdrv_co_writev = vhdx_co_writev,
388
.bdrv_co_create_opts = vhdx_co_create_opts,
389
.bdrv_get_info = vhdx_get_info,
390
- .bdrv_check = vhdx_check,
391
+ .bdrv_co_check = vhdx_co_check,
392
.bdrv_has_zero_init = bdrv_has_zero_init_1,
393
394
.create_opts = &vhdx_create_opts,
395
diff --git a/block/vmdk.c b/block/vmdk.c
396
index XXXXXXX..XXXXXXX 100644
397
--- a/block/vmdk.c
398
+++ b/block/vmdk.c
399
@@ -XXX,XX +XXX,XX @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
400
return info;
401
}
402
403
-static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result,
404
- BdrvCheckMode fix)
405
+static int coroutine_fn vmdk_co_check(BlockDriverState *bs,
406
+ BdrvCheckResult *result,
407
+ BdrvCheckMode fix)
408
{
409
BDRVVmdkState *s = bs->opaque;
410
VmdkExtent *extent = NULL;
411
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vmdk = {
412
.instance_size = sizeof(BDRVVmdkState),
413
.bdrv_probe = vmdk_probe,
414
.bdrv_open = vmdk_open,
415
- .bdrv_check = vmdk_check,
416
+ .bdrv_co_check = vmdk_co_check,
417
.bdrv_reopen_prepare = vmdk_reopen_prepare,
418
.bdrv_child_perm = bdrv_format_default_perms,
419
.bdrv_co_preadv = vmdk_co_preadv,
420
--
51
--
421
2.13.6
52
2.13.6
422
53
423
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: Max Reitz <mreitz@redhat.com>
2
Reviewed-by: Fam Zheng <famz@redhat.com>
3
---
3
---
4
tests/qemu-iotests/207 | 261 +++++++++++++++++++++++++++++++++++++++++++++
4
block/io.c | 6 ++++++
5
tests/qemu-iotests/207.out | 75 +++++++++++++
5
1 file changed, 6 insertions(+)
6
tests/qemu-iotests/group | 1 +
7
3 files changed, 337 insertions(+)
8
create mode 100755 tests/qemu-iotests/207
9
create mode 100644 tests/qemu-iotests/207.out
10
6
11
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
7
diff --git a/block/io.c b/block/io.c
12
new file mode 100755
8
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX
9
--- a/block/io.c
14
--- /dev/null
10
+++ b/block/io.c
15
+++ b/tests/qemu-iotests/207
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
16
@@ -XXX,XX +XXX,XX @@
12
BdrvNextIterator it;
17
+#!/bin/bash
13
GSList *aio_ctxs = NULL, *ctx;
18
+#
14
19
+# Test ssh image creation
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
20
+#
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
21
+# Copyright (C) 2018 Red Hat, Inc.
17
+ * nodes in several different AioContexts, so make sure we're in the main
22
+#
18
+ * context. */
23
+# This program is free software; you can redistribute it and/or modify
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
24
+# it under the terms of the GNU General Public License as published by
25
+# the Free Software Foundation; either version 2 of the License, or
26
+# (at your option) any later version.
27
+#
28
+# This program is distributed in the hope that it will be useful,
29
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
30
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
+# GNU General Public License for more details.
32
+#
33
+# You should have received a copy of the GNU General Public License
34
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
35
+#
36
+
20
+
37
+# creator
21
block_job_pause_all();
38
+owner=kwolf@redhat.com
22
39
+
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
40
+seq=`basename $0`
41
+echo "QA output created by $seq"
42
+
43
+here=`pwd`
44
+status=1    # failure is the default!
45
+
46
+# get standard environment, filters and checks
47
+. ./common.rc
48
+. ./common.filter
49
+
50
+_supported_fmt raw
51
+_supported_proto ssh
52
+_supported_os Linux
53
+
54
+function do_run_qemu()
55
+{
56
+ echo Testing: "$@"
57
+ $QEMU -nographic -qmp stdio -serial none "$@"
58
+ echo
59
+}
60
+
61
+function run_qemu()
62
+{
63
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
64
+ | _filter_qemu | _filter_imgfmt \
65
+ | _filter_actual_image_size
66
+}
67
+
68
+echo
69
+echo "=== Successful image creation (defaults) ==="
70
+echo
71
+
72
+run_qemu <<EOF
73
+{ "execute": "qmp_capabilities" }
74
+{ "execute": "x-blockdev-create",
75
+ "arguments": {
76
+ "driver": "ssh",
77
+ "location": {
78
+ "path": "$TEST_IMG_FILE",
79
+ "server": {
80
+ "host": "127.0.0.1",
81
+ "port": "22"
82
+ }
83
+ },
84
+ "size": 4194304
85
+ }
86
+}
87
+{ "execute": "quit" }
88
+EOF
89
+
90
+_img_info | _filter_img_info
91
+echo
92
+TEST_IMG=$TEST_IMG_FILE _img_info | _filter_img_info
93
+
94
+echo
95
+echo "=== Test host-key-check options ==="
96
+echo
97
+
98
+run_qemu <<EOF
99
+{ "execute": "qmp_capabilities" }
100
+{ "execute": "x-blockdev-create",
101
+ "arguments": {
102
+ "driver": "ssh",
103
+ "location": {
104
+ "path": "$TEST_IMG_FILE",
105
+ "server": {
106
+ "host": "127.0.0.1",
107
+ "port": "22"
108
+ },
109
+ "host-key-check": {
110
+ "mode": "none"
111
+ }
112
+ },
113
+ "size": 8388608
114
+ }
115
+}
116
+{ "execute": "quit" }
117
+EOF
118
+
119
+_img_info | _filter_img_info
120
+
121
+run_qemu <<EOF
122
+{ "execute": "qmp_capabilities" }
123
+{ "execute": "x-blockdev-create",
124
+ "arguments": {
125
+ "driver": "ssh",
126
+ "location": {
127
+ "path": "$TEST_IMG_FILE",
128
+ "server": {
129
+ "host": "127.0.0.1",
130
+ "port": "22"
131
+ },
132
+ "host-key-check": {
133
+ "mode": "known_hosts"
134
+ }
135
+ },
136
+ "size": 4194304
137
+ }
138
+}
139
+{ "execute": "quit" }
140
+EOF
141
+
142
+_img_info | _filter_img_info
143
+
144
+
145
+key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" |
146
+ cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1)
147
+
148
+run_qemu <<EOF
149
+{ "execute": "qmp_capabilities" }
150
+{ "execute": "x-blockdev-create",
151
+ "arguments": {
152
+ "driver": "ssh",
153
+ "location": {
154
+ "path": "$TEST_IMG_FILE",
155
+ "server": {
156
+ "host": "127.0.0.1",
157
+ "port": "22"
158
+ },
159
+ "host-key-check": {
160
+ "mode": "hash",
161
+ "type": "md5",
162
+ "hash": "wrong"
163
+ }
164
+ },
165
+ "size": 8388608
166
+ }
167
+}
168
+{ "execute": "x-blockdev-create",
169
+ "arguments": {
170
+ "driver": "ssh",
171
+ "location": {
172
+ "path": "$TEST_IMG_FILE",
173
+ "server": {
174
+ "host": "127.0.0.1",
175
+ "port": "22"
176
+ },
177
+ "host-key-check": {
178
+ "mode": "hash",
179
+ "type": "md5",
180
+ "hash": "$key"
181
+ }
182
+ },
183
+ "size": 8388608
184
+ }
185
+}
186
+{ "execute": "quit" }
187
+EOF
188
+
189
+_img_info | _filter_img_info
190
+
191
+
192
+key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" |
193
+ cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1)
194
+
195
+run_qemu <<EOF
196
+{ "execute": "qmp_capabilities" }
197
+{ "execute": "x-blockdev-create",
198
+ "arguments": {
199
+ "driver": "ssh",
200
+ "location": {
201
+ "path": "$TEST_IMG_FILE",
202
+ "server": {
203
+ "host": "127.0.0.1",
204
+ "port": "22"
205
+ },
206
+ "host-key-check": {
207
+ "mode": "hash",
208
+ "type": "sha1",
209
+ "hash": "wrong"
210
+ }
211
+ },
212
+ "size": 4194304
213
+ }
214
+}
215
+{ "execute": "x-blockdev-create",
216
+ "arguments": {
217
+ "driver": "ssh",
218
+ "location": {
219
+ "path": "$TEST_IMG_FILE",
220
+ "server": {
221
+ "host": "127.0.0.1",
222
+ "port": "22"
223
+ },
224
+ "host-key-check": {
225
+ "mode": "hash",
226
+ "type": "sha1",
227
+ "hash": "$key"
228
+ }
229
+ },
230
+ "size": 4194304
231
+ }
232
+}
233
+{ "execute": "quit" }
234
+EOF
235
+
236
+_img_info | _filter_img_info
237
+
238
+echo
239
+echo "=== Invalid path and user ==="
240
+echo
241
+
242
+run_qemu <<EOF
243
+{ "execute": "qmp_capabilities" }
244
+{ "execute": "x-blockdev-create",
245
+ "arguments": {
246
+ "driver": "ssh",
247
+ "location": {
248
+ "path": "/this/is/not/an/existing/path",
249
+ "server": {
250
+ "host": "127.0.0.1",
251
+ "port": "22"
252
+ }
253
+ },
254
+ "size": 4194304
255
+ }
256
+}
257
+{ "execute": "x-blockdev-create",
258
+ "arguments": {
259
+ "driver": "ssh",
260
+ "location": {
261
+ "path": "$TEST_IMG_FILE",
262
+ "user": "invalid user",
263
+ "server": {
264
+ "host": "127.0.0.1",
265
+ "port": "22"
266
+ }
267
+ },
268
+ "size": 4194304
269
+ }
270
+}
271
+{ "execute": "quit" }
272
+EOF
273
+
274
+# success, all done
275
+echo "*** done"
276
+rm -f $seq.full
277
+status=0
278
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
279
new file mode 100644
280
index XXXXXXX..XXXXXXX
281
--- /dev/null
282
+++ b/tests/qemu-iotests/207.out
283
@@ -XXX,XX +XXX,XX @@
284
+QA output created by 207
285
+
286
+=== Successful image creation (defaults) ===
287
+
288
+Testing:
289
+QMP_VERSION
290
+{"return": {}}
291
+{"return": {}}
292
+{"return": {}}
293
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
294
+
295
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
296
+file format: IMGFMT
297
+virtual size: 4.0M (4194304 bytes)
298
+
299
+image: TEST_DIR/t.IMGFMT
300
+file format: IMGFMT
301
+virtual size: 4.0M (4194304 bytes)
302
+
303
+=== Test host-key-check options ===
304
+
305
+Testing:
306
+QMP_VERSION
307
+{"return": {}}
308
+{"return": {}}
309
+{"return": {}}
310
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
311
+
312
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
313
+file format: IMGFMT
314
+virtual size: 8.0M (8388608 bytes)
315
+Testing:
316
+QMP_VERSION
317
+{"return": {}}
318
+{"return": {}}
319
+{"return": {}}
320
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
321
+
322
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
323
+file format: IMGFMT
324
+virtual size: 4.0M (4194304 bytes)
325
+Testing:
326
+QMP_VERSION
327
+{"return": {}}
328
+{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}}
329
+{"return": {}}
330
+{"return": {}}
331
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
332
+
333
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
334
+file format: IMGFMT
335
+virtual size: 8.0M (8388608 bytes)
336
+Testing:
337
+QMP_VERSION
338
+{"return": {}}
339
+{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}}
340
+{"return": {}}
341
+{"return": {}}
342
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
343
+
344
+image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}}
345
+file format: IMGFMT
346
+virtual size: 4.0M (4194304 bytes)
347
+
348
+=== Invalid path and user ===
349
+
350
+Testing:
351
+QMP_VERSION
352
+{"return": {}}
353
+{"error": {"class": "GenericError", "desc": "failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)"}}
354
+{"error": {"class": "GenericError", "desc": "failed to authenticate using publickey authentication and the identities held by your ssh-agent"}}
355
+{"return": {}}
356
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
357
+
358
+*** done
359
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
360
index XXXXXXX..XXXXXXX 100644
361
--- a/tests/qemu-iotests/group
362
+++ b/tests/qemu-iotests/group
363
@@ -XXX,XX +XXX,XX @@
364
204 rw auto quick
365
205 rw auto quick
366
206 rw auto
367
+207 rw auto
368
--
24
--
369
2.13.6
25
2.13.6
370
26
371
27
diff view generated by jsdifflib
1
With the conversion to a QAPI options object, the function is now
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
prepared to be used in a .bdrv_co_create implementation.
2
and also doesn't notify other parent nodes of children, which both means
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
is providing useful functionality only on a single node.
5
6
To keep things consistent, we also shouldn't call the block driver
7
callbacks recursively.
8
9
A proper recursive drain version that provides an actually working
10
drained section for child nodes will be introduced later.
3
11
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
6
---
14
---
7
block/rbd.c | 115 +++++++++++++++++++++++++++++-------------------------------
15
block/io.c | 16 +++++++++-------
8
1 file changed, 55 insertions(+), 60 deletions(-)
16
1 file changed, 9 insertions(+), 7 deletions(-)
9
17
10
diff --git a/block/rbd.c b/block/rbd.c
18
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
12
--- a/block/rbd.c
20
--- a/block/io.c
13
+++ b/block/rbd.c
21
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
15
#include "qapi/qmp/qdict.h"
16
#include "qapi/qmp/qjson.h"
17
#include "qapi/qmp/qlist.h"
18
+#include "qapi/qobject-input-visitor.h"
19
+#include "qapi/qapi-visit-block-core.h"
20
21
/*
22
* When specifying the image filename use:
23
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
24
qemu_aio_unref(acb);
25
}
23
}
26
24
27
-static char *qemu_rbd_mon_host(QDict *options, Error **errp)
25
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
28
+static char *qemu_rbd_mon_host(BlockdevOptionsRbd *opts, Error **errp)
26
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
27
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
29
{
28
{
30
- const char **vals = g_new(const char *, qdict_size(options) + 1);
29
BdrvChild *child, *tmp;
31
- char keybuf[32];
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
32
+ const char **vals;
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
33
const char *host, *port;
32
bdrv_coroutine_enter(bs, data.co);
34
char *rados_str;
33
BDRV_POLL_WHILE(bs, !data.done);
35
- int i;
34
36
-
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
37
- for (i = 0;; i++) {
36
- bdrv_drain_invoke(child->bs, begin);
38
- sprintf(keybuf, "server.%d.host", i);
37
+ if (recursive) {
39
- host = qdict_get_try_str(options, keybuf);
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
40
- qdict_del(options, keybuf);
39
+ bdrv_drain_invoke(child->bs, begin, true);
41
- sprintf(keybuf, "server.%d.port", i);
40
+ }
42
- port = qdict_get_try_str(options, keybuf);
43
- qdict_del(options, keybuf);
44
- if (!host && !port) {
45
- break;
46
- }
47
- if (!host) {
48
- error_setg(errp, "Parameter server.%d.host is missing", i);
49
- rados_str = NULL;
50
- goto out;
51
- }
52
+ InetSocketAddressBaseList *p;
53
+ int i, cnt;
54
+
55
+ if (!opts->has_server) {
56
+ return NULL;
57
+ }
58
+
59
+ for (cnt = 0, p = opts->server; p; p = p->next) {
60
+ cnt++;
61
+ }
62
+
63
+ vals = g_new(const char *, cnt + 1);
64
+
65
+ for (i = 0, p = opts->server; p; p = p->next, i++) {
66
+ host = p->value->host;
67
+ port = p->value->port;
68
69
if (strchr(host, ':')) {
70
- vals[i] = port ? g_strdup_printf("[%s]:%s", host, port)
71
- : g_strdup_printf("[%s]", host);
72
+ vals[i] = g_strdup_printf("[%s]:%s", host, port);
73
} else {
74
- vals[i] = port ? g_strdup_printf("%s:%s", host, port)
75
- : g_strdup(host);
76
+ vals[i] = g_strdup_printf("%s:%s", host, port);
77
}
78
}
41
}
79
vals[i] = NULL;
80
81
rados_str = i ? g_strjoinv(";", (char **)vals) : NULL;
82
-out:
83
g_strfreev((char **)vals);
84
return rados_str;
85
}
42
}
86
43
87
static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
88
char **s_snap, char **s_image_name,
45
bdrv_parent_drained_begin(bs);
89
- QDict *options, bool cache,
90
+ BlockdevOptionsRbd *opts, bool cache,
91
const char *keypairs, const char *secretid,
92
Error **errp)
93
{
94
- QemuOpts *opts;
95
char *mon_host = NULL;
96
- const char *pool, *snap, *conf, *user, *image_name;
97
Error *local_err = NULL;
98
int r;
99
100
- opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
101
- qemu_opts_absorb_qdict(opts, options, &local_err);
102
- if (local_err) {
103
- error_propagate(errp, local_err);
104
- r = -EINVAL;
105
- goto failed_opts;
106
- }
107
-
108
- mon_host = qemu_rbd_mon_host(options, &local_err);
109
+ mon_host = qemu_rbd_mon_host(opts, &local_err);
110
if (local_err) {
111
error_propagate(errp, local_err);
112
r = -EINVAL;
113
goto failed_opts;
114
}
46
}
115
47
116
- pool = qemu_opt_get(opts, "pool");
48
- bdrv_drain_invoke(bs, true);
117
- conf = qemu_opt_get(opts, "conf");
49
+ bdrv_drain_invoke(bs, true, false);
118
- snap = qemu_opt_get(opts, "snapshot");
50
bdrv_drain_recurse(bs);
119
- user = qemu_opt_get(opts, "user");
51
}
120
- image_name = qemu_opt_get(opts, "image");
52
121
-
53
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
122
- if (!pool || !image_name) {
123
- error_setg(errp, "Parameters 'pool' and 'image' are required");
124
- r = -EINVAL;
125
- goto failed_opts;
126
- }
127
-
128
- r = rados_create(cluster, user);
129
+ r = rados_create(cluster, opts->user);
130
if (r < 0) {
131
error_setg_errno(errp, -r, "error initializing");
132
goto failed_opts;
133
}
54
}
134
55
135
- *s_snap = g_strdup(snap);
56
/* Re-enable things in child-to-parent order */
136
- *s_image_name = g_strdup(image_name);
57
- bdrv_drain_invoke(bs, false);
137
+ *s_snap = g_strdup(opts->snapshot);
58
+ bdrv_drain_invoke(bs, false, false);
138
+ *s_image_name = g_strdup(opts->image);
59
bdrv_parent_drained_end(bs);
139
60
aio_enable_external(bdrv_get_aio_context(bs));
140
/* try default location when conf=NULL, but ignore failure */
141
- r = rados_conf_read_file(*cluster, conf);
142
- if (conf && r < 0) {
143
- error_setg_errno(errp, -r, "error reading conf file %s", conf);
144
+ r = rados_conf_read_file(*cluster, opts->conf);
145
+ if (opts->has_conf && r < 0) {
146
+ error_setg_errno(errp, -r, "error reading conf file %s", opts->conf);
147
goto failed_shutdown;
148
}
149
150
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
151
goto failed_shutdown;
152
}
153
154
- r = rados_ioctx_create(*cluster, pool, io_ctx);
155
+ r = rados_ioctx_create(*cluster, opts->pool, io_ctx);
156
if (r < 0) {
157
- error_setg_errno(errp, -r, "error opening pool %s", pool);
158
+ error_setg_errno(errp, -r, "error opening pool %s", opts->pool);
159
goto failed_shutdown;
160
}
161
162
- qemu_opts_del(opts);
163
return 0;
164
165
failed_shutdown:
166
@@ -XXX,XX +XXX,XX @@ failed_shutdown:
167
g_free(*s_snap);
168
g_free(*s_image_name);
169
failed_opts:
170
- qemu_opts_del(opts);
171
g_free(mon_host);
172
return r;
173
}
61
}
174
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
62
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
175
Error **errp)
63
aio_context_acquire(aio_context);
176
{
64
aio_disable_external(aio_context);
177
BDRVRBDState *s = bs->opaque;
65
bdrv_parent_drained_begin(bs);
178
+ BlockdevOptionsRbd *opts = NULL;
66
- bdrv_drain_invoke(bs, true);
179
+ Visitor *v;
67
+ bdrv_drain_invoke(bs, true, true);
180
+ QObject *crumpled = NULL;
68
aio_context_release(aio_context);
181
Error *local_err = NULL;
69
182
const char *filename;
70
if (!g_slist_find(aio_ctxs, aio_context)) {
183
char *keypairs, *secretid;
71
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
184
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
72
185
qdict_del(options, "password-secret");
73
/* Re-enable things in child-to-parent order */
186
}
74
aio_context_acquire(aio_context);
187
75
- bdrv_drain_invoke(bs, false);
188
+ /* Convert the remaining options into a QAPI object */
76
+ bdrv_drain_invoke(bs, false, true);
189
+ crumpled = qdict_crumple(options, errp);
77
bdrv_parent_drained_end(bs);
190
+ if (crumpled == NULL) {
78
aio_enable_external(aio_context);
191
+ r = -EINVAL;
79
aio_context_release(aio_context);
192
+ goto out;
193
+ }
194
+
195
+ v = qobject_input_visitor_new_keyval(crumpled);
196
+ visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err);
197
+ visit_free(v);
198
+ qobject_decref(crumpled);
199
+
200
+ if (local_err) {
201
+ error_propagate(errp, local_err);
202
+ r = -EINVAL;
203
+ goto out;
204
+ }
205
+
206
r = qemu_rbd_connect(&s->cluster, &s->io_ctx, &s->snap, &s->image_name,
207
- options, !(flags & BDRV_O_NOCACHE), keypairs, secretid,
208
+ opts, !(flags & BDRV_O_NOCACHE), keypairs, secretid,
209
errp);
210
if (r < 0) {
211
goto out;
212
@@ -XXX,XX +XXX,XX @@ failed_open:
213
g_free(s->image_name);
214
rados_shutdown(s->cluster);
215
out:
216
+ qapi_free_BlockdevOptionsRbd(opts);
217
g_free(keypairs);
218
g_free(secretid);
219
return r;
220
--
80
--
221
2.13.6
81
2.13.6
222
82
223
83
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to gluster, which enables
1
The existing test is for bdrv_drain_all_begin/end() only. Generalise the
2
image creation over QMP.
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.
5
6
Also, add a backing file to the test node to test whether the operations
7
work recursively.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
---
10
---
8
qapi/block-core.json | 18 ++++++-
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
9
block/gluster.c | 135 ++++++++++++++++++++++++++++++++++-----------------
12
1 file changed, 62 insertions(+), 7 deletions(-)
10
2 files changed, 108 insertions(+), 45 deletions(-)
11
13
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
13
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
16
--- a/tests/test-bdrv-drain.c
15
+++ b/qapi/block-core.json
17
+++ b/tests/test-bdrv-drain.c
16
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
17
'*nocow': 'bool' } }
19
18
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
19
##
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
20
+# @BlockdevCreateOptionsGluster:
21
+#
22
+# Driver specific image creation options for gluster.
23
+#
24
+# @location Where to store the new image file
25
+# @size Size of the virtual disk in bytes
26
+# @preallocation Preallocation mode for the new image (default: off)
27
+#
28
+# Since: 2.12
29
+##
30
+{ 'struct': 'BlockdevCreateOptionsGluster',
31
+ 'data': { 'location': 'BlockdevOptionsGluster',
32
+ 'size': 'size',
33
+ '*preallocation': 'PreallocMode' } }
34
+
22
+
35
+##
23
+ .bdrv_child_perm = bdrv_format_default_perms,
36
# @BlockdevQcow2Version:
24
};
37
#
25
38
# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2)
26
static void aio_ret_cb(void *opaque, int ret)
39
@@ -XXX,XX +XXX,XX @@
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
40
'file': 'BlockdevCreateOptionsFile',
28
*aio_ret = ret;
41
'ftp': 'BlockdevCreateNotSupported',
42
'ftps': 'BlockdevCreateNotSupported',
43
- 'gluster': 'BlockdevCreateNotSupported',
44
+ 'gluster': 'BlockdevCreateOptionsGluster',
45
'host_cdrom': 'BlockdevCreateNotSupported',
46
'host_device': 'BlockdevCreateNotSupported',
47
'http': 'BlockdevCreateNotSupported',
48
diff --git a/block/gluster.c b/block/gluster.c
49
index XXXXXXX..XXXXXXX 100644
50
--- a/block/gluster.c
51
+++ b/block/gluster.c
52
@@ -XXX,XX +XXX,XX @@ out:
53
return -errno;
54
}
29
}
55
30
56
-static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
31
-static void test_drv_cb_drain_all(void)
57
- const char *filename,
32
+enum drain_type {
58
- QDict *options, Error **errp)
33
+ BDRV_DRAIN_ALL,
59
+/* Converts options given in @filename and the @options QDict into the QAPI
34
+ BDRV_DRAIN,
60
+ * object @gconf. */
35
+};
61
+static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
36
+
62
+ const char *filename,
37
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
63
+ QDict *options, Error **errp)
38
+{
64
{
39
+ switch (drain_type) {
65
int ret;
40
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
66
if (filename) {
41
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
67
@@ -XXX,XX +XXX,XX @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
42
+ default: g_assert_not_reached();
68
"[host[:port]]volume/path[?socket=...]"
69
"[,file.debug=N]"
70
"[,file.logfile=/path/filename.log]\n");
71
- errno = -ret;
72
- return NULL;
73
+ return ret;
74
}
75
} else {
76
ret = qemu_gluster_parse_json(gconf, options, errp);
77
@@ -XXX,XX +XXX,XX @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
78
"file.server.1.transport=unix,"
79
"file.server.1.socket=/var/run/glusterd.socket ..."
80
"\n");
81
- errno = -ret;
82
- return NULL;
83
+ return ret;
84
}
85
+ }
43
+ }
86
87
+ return 0;
88
+}
44
+}
89
+
45
+
90
+static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
46
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
91
+ const char *filename,
92
+ QDict *options, Error **errp)
93
+{
47
+{
94
+ int ret;
48
+ switch (drain_type) {
95
+
49
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
96
+ ret = qemu_gluster_parse(gconf, filename, options, errp);
50
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
97
+ if (ret < 0) {
51
+ default: g_assert_not_reached();
98
+ errno = -ret;
99
+ return NULL;
100
}
101
102
return qemu_gluster_glfs_init(gconf, errp);
103
@@ -XXX,XX +XXX,XX @@ static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
104
return 0;
105
}
106
107
+static int qemu_gluster_co_create(BlockdevCreateOptions *options,
108
+ Error **errp)
109
+{
110
+ BlockdevCreateOptionsGluster *opts = &options->u.gluster;
111
+ struct glfs *glfs;
112
+ struct glfs_fd *fd = NULL;
113
+ int ret = 0;
114
+
115
+ assert(options->driver == BLOCKDEV_DRIVER_GLUSTER);
116
+
117
+ glfs = qemu_gluster_glfs_init(opts->location, errp);
118
+ if (!glfs) {
119
+ ret = -errno;
120
+ goto out;
121
+ }
52
+ }
122
+
123
+ fd = glfs_creat(glfs, opts->location->path,
124
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
125
+ if (!fd) {
126
+ ret = -errno;
127
+ goto out;
128
+ }
129
+
130
+ ret = qemu_gluster_do_truncate(fd, opts->size, opts->preallocation, errp);
131
+
132
+out:
133
+ if (fd) {
134
+ if (glfs_close(fd) != 0 && ret == 0) {
135
+ ret = -errno;
136
+ }
137
+ }
138
+ glfs_clear_preopened(glfs);
139
+ return ret;
140
+}
53
+}
141
+
54
+
142
static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
55
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
143
QemuOpts *opts,
144
Error **errp)
145
{
56
{
146
+ BlockdevCreateOptions *options;
57
BlockBackend *blk;
147
+ BlockdevCreateOptionsGluster *gopts;
58
- BlockDriverState *bs;
148
BlockdevOptionsGluster *gconf;
59
- BDRVTestState *s;
149
- struct glfs *glfs;
60
+ BlockDriverState *bs, *backing;
150
- struct glfs_fd *fd = NULL;
61
+ BDRVTestState *s, *backing_s;
151
- int ret = 0;
62
BlockAIOCB *acb;
152
- PreallocMode prealloc;
63
int aio_ret;
153
- int64_t total_size = 0;
64
154
char *tmp = NULL;
65
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
155
Error *local_err = NULL;
66
s = bs->opaque;
156
+ int ret;
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);
157
+
72
+
158
+ options = g_new0(BlockdevCreateOptions, 1);
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
159
+ options->driver = BLOCKDEV_DRIVER_GLUSTER;
74
g_assert_cmpint(s->drain_count, ==, 0);
160
+ gopts = &options->u.gluster;
75
- bdrv_drain_all_begin();
161
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
162
gconf = g_new0(BlockdevOptionsGluster, 1);
163
+ gopts->location = gconf;
164
+
77
+
165
+ gopts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
78
+ do_drain_begin(drain_type, bs);
166
+ BDRV_SECTOR_SIZE);
167
+
79
+
168
+ tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
80
g_assert_cmpint(s->drain_count, ==, 1);
169
+ gopts->preallocation = qapi_enum_parse(&PreallocMode_lookup, tmp,
81
- bdrv_drain_all_end();
170
+ PREALLOC_MODE_OFF, &local_err);
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
171
+ g_free(tmp);
172
+ if (local_err) {
173
+ error_propagate(errp, local_err);
174
+ ret = -EINVAL;
175
+ goto fail;
176
+ }
177
+
83
+
178
gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
84
+ do_drain_end(drain_type, bs);
179
GLUSTER_DEBUG_DEFAULT);
85
+
180
if (gconf->debug < 0) {
86
g_assert_cmpint(s->drain_count, ==, 0);
181
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
87
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
182
}
88
183
gconf->has_logfile = true;
89
/* Now do the same while a request is pending */
184
90
aio_ret = -EINPROGRESS;
185
- glfs = qemu_gluster_init(gconf, filename, NULL, errp);
91
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
186
- if (!glfs) {
92
g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
187
- ret = -errno;
93
188
- goto out;
94
g_assert_cmpint(s->drain_count, ==, 0);
189
- }
95
- bdrv_drain_all_begin();
190
-
96
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
191
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
97
+
192
- BDRV_SECTOR_SIZE);
98
+ do_drain_begin(drain_type, bs);
193
-
99
+
194
- tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
100
g_assert_cmpint(aio_ret, ==, 0);
195
- prealloc = qapi_enum_parse(&PreallocMode_lookup, tmp, PREALLOC_MODE_OFF,
101
g_assert_cmpint(s->drain_count, ==, 1);
196
- &local_err);
102
- bdrv_drain_all_end();
197
- g_free(tmp);
103
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
198
- if (local_err) {
104
+
199
- error_propagate(errp, local_err);
105
+ do_drain_end(drain_type, bs);
200
- ret = -EINVAL;
106
+
201
- goto out;
107
g_assert_cmpint(s->drain_count, ==, 0);
202
+ ret = qemu_gluster_parse(gconf, filename, NULL, errp);
108
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
203
+ if (ret < 0) {
109
204
+ goto fail;
110
+ bdrv_unref(backing);
205
}
111
bdrv_unref(bs);
206
112
blk_unref(blk);
207
- fd = glfs_creat(glfs, gconf->path,
208
- O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
209
- if (!fd) {
210
- ret = -errno;
211
- goto out;
212
+ ret = qemu_gluster_co_create(options, errp);
213
+ if (ret < 0) {
214
+ goto fail;
215
}
216
217
- ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp);
218
-
219
-out:
220
- if (fd) {
221
- if (glfs_close(fd) != 0 && ret == 0) {
222
- ret = -errno;
223
- }
224
- }
225
- qapi_free_BlockdevOptionsGluster(gconf);
226
- glfs_clear_preopened(glfs);
227
+ ret = 0;
228
+fail:
229
+ qapi_free_BlockdevCreateOptions(options);
230
return ret;
231
}
113
}
232
114
233
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster = {
115
+static void test_drv_cb_drain_all(void)
234
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
116
+{
235
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
117
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
236
.bdrv_close = qemu_gluster_close,
118
+}
237
+ .bdrv_co_create = qemu_gluster_co_create,
119
+
238
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
120
+static void test_drv_cb_drain(void)
239
.bdrv_getlength = qemu_gluster_getlength,
121
+{
240
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
122
+ test_drv_cb_common(BDRV_DRAIN, false);
241
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_tcp = {
123
+}
242
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
124
+
243
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
125
int main(int argc, char **argv)
244
.bdrv_close = qemu_gluster_close,
126
{
245
+ .bdrv_co_create = qemu_gluster_co_create,
127
bdrv_init();
246
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
128
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
247
.bdrv_getlength = qemu_gluster_getlength,
129
g_test_init(&argc, &argv, NULL);
248
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
130
249
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_unix = {
131
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
250
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
132
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
251
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
133
252
.bdrv_close = qemu_gluster_close,
134
return g_test_run();
253
+ .bdrv_co_create = qemu_gluster_co_create,
135
}
254
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
255
.bdrv_getlength = qemu_gluster_getlength,
256
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
257
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_rdma = {
258
.bdrv_reopen_commit = qemu_gluster_reopen_commit,
259
.bdrv_reopen_abort = qemu_gluster_reopen_abort,
260
.bdrv_close = qemu_gluster_close,
261
+ .bdrv_co_create = qemu_gluster_co_create,
262
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
263
.bdrv_getlength = qemu_gluster_getlength,
264
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
265
--
136
--
266
2.13.6
137
2.13.6
267
138
268
139
diff view generated by jsdifflib
1
This is currently only working correctly for bdrv_drain(), not for
2
bdrv_drain_all(). Leave a comment for the drain_all case, we'll address
3
it later.
4
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Eric Blake <eblake@redhat.com>
3
Reviewed-by: Max Reitz <mreitz@redhat.com>
4
---
6
---
5
tests/test-qemu-opts.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
6
1 file changed, 125 insertions(+)
8
1 file changed, 45 insertions(+)
7
9
8
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
9
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
10
--- a/tests/test-qemu-opts.c
12
--- a/tests/test-bdrv-drain.c
11
+++ b/tests/test-qemu-opts.c
13
+++ b/tests/test-bdrv-drain.c
12
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
13
#include "qemu/osdep.h"
15
test_drv_cb_common(BDRV_DRAIN, false);
14
#include "qemu/cutils.h"
15
#include "qemu/option.h"
16
+#include "qemu/option_int.h"
17
#include "qapi/error.h"
18
#include "qapi/qmp/qdict.h"
19
#include "qapi/qmp/qstring.h"
20
@@ -XXX,XX +XXX,XX @@ static void test_opts_append(void)
21
qemu_opts_free(merged);
22
}
16
}
23
17
24
+static void test_opts_to_qdict_basic(void)
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
25
+{
19
+{
26
+ QemuOpts *opts;
20
+ BlockBackend *blk;
27
+ QDict *dict;
21
+ BlockDriverState *bs, *backing;
28
+
22
+
29
+ opts = qemu_opts_parse(&opts_list_01, "str1=foo,str2=,str3=bar,number1=42",
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
30
+ false, &error_abort);
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
31
+ g_assert(opts != NULL);
25
+ &error_abort);
26
+ blk_insert_bs(blk, bs, &error_abort);
32
+
27
+
33
+ dict = qemu_opts_to_qdict(opts, NULL);
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
34
+ g_assert(dict != NULL);
29
+ bdrv_set_backing_hd(bs, backing, &error_abort);
35
+
30
+
36
+ g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
31
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
37
+ g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
32
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
38
+ g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
39
+ g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
40
+ g_assert_false(qdict_haskey(dict, "number2"));
41
+
33
+
42
+ QDECREF(dict);
34
+ do_drain_begin(drain_type, bs);
43
+ qemu_opts_del(opts);
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);
44
+}
47
+}
45
+
48
+
46
+static void test_opts_to_qdict_filtered(void)
49
+static void test_quiesce_drain_all(void)
47
+{
50
+{
48
+ QemuOptsList *first, *merged;
51
+ // XXX drain_all doesn't quiesce
49
+ QemuOpts *opts;
52
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
50
+ QDict *dict;
51
+
52
+ first = qemu_opts_append(NULL, &opts_list_02);
53
+ merged = qemu_opts_append(first, &opts_list_01);
54
+
55
+ opts = qemu_opts_parse(merged,
56
+ "str1=foo,str2=,str3=bar,bool1=off,number1=42",
57
+ false, &error_abort);
58
+ g_assert(opts != NULL);
59
+
60
+ /* Convert to QDict without deleting from opts */
61
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, false);
62
+ g_assert(dict != NULL);
63
+ g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
64
+ g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
65
+ g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
66
+ g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
67
+ g_assert_false(qdict_haskey(dict, "number2"));
68
+ g_assert_false(qdict_haskey(dict, "bool1"));
69
+ QDECREF(dict);
70
+
71
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false);
72
+ g_assert(dict != NULL);
73
+ g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
74
+ g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
75
+ g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off");
76
+ g_assert_false(qdict_haskey(dict, "str3"));
77
+ g_assert_false(qdict_haskey(dict, "number1"));
78
+ g_assert_false(qdict_haskey(dict, "number2"));
79
+ QDECREF(dict);
80
+
81
+ /* Now delete converted options from opts */
82
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true);
83
+ g_assert(dict != NULL);
84
+ g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo");
85
+ g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, "");
86
+ g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar");
87
+ g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42");
88
+ g_assert_false(qdict_haskey(dict, "number2"));
89
+ g_assert_false(qdict_haskey(dict, "bool1"));
90
+ QDECREF(dict);
91
+
92
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true);
93
+ g_assert(dict != NULL);
94
+ g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off");
95
+ g_assert_false(qdict_haskey(dict, "str1"));
96
+ g_assert_false(qdict_haskey(dict, "str2"));
97
+ g_assert_false(qdict_haskey(dict, "str3"));
98
+ g_assert_false(qdict_haskey(dict, "number1"));
99
+ g_assert_false(qdict_haskey(dict, "number2"));
100
+ QDECREF(dict);
101
+
102
+ g_assert_true(QTAILQ_EMPTY(&opts->head));
103
+
104
+ qemu_opts_del(opts);
105
+ qemu_opts_free(merged);
106
+}
53
+}
107
+
54
+
108
+static void test_opts_to_qdict_duplicates(void)
55
+static void test_quiesce_drain(void)
109
+{
56
+{
110
+ QemuOpts *opts;
57
+ test_quiesce_common(BDRV_DRAIN, false);
111
+ QemuOpt *opt;
58
+}
112
+ QDict *dict;
113
+
59
+
114
+ opts = qemu_opts_parse(&opts_list_03, "foo=a,foo=b", false, &error_abort);
60
int main(int argc, char **argv)
115
+ g_assert(opts != NULL);
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);
116
+
69
+
117
+ /* Verify that opts has two options with the same name */
70
return g_test_run();
118
+ opt = QTAILQ_FIRST(&opts->head);
119
+ g_assert_cmpstr(opt->name, ==, "foo");
120
+ g_assert_cmpstr(opt->str , ==, "a");
121
+
122
+ opt = QTAILQ_NEXT(opt, next);
123
+ g_assert_cmpstr(opt->name, ==, "foo");
124
+ g_assert_cmpstr(opt->str , ==, "b");
125
+
126
+ opt = QTAILQ_NEXT(opt, next);
127
+ g_assert(opt == NULL);
128
+
129
+ /* In the conversion to QDict, the last one wins */
130
+ dict = qemu_opts_to_qdict(opts, NULL);
131
+ g_assert(dict != NULL);
132
+ g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b");
133
+ QDECREF(dict);
134
+
135
+ /* The last one still wins if entries are deleted, and both are deleted */
136
+ dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true);
137
+ g_assert(dict != NULL);
138
+ g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b");
139
+ QDECREF(dict);
140
+
141
+ g_assert_true(QTAILQ_EMPTY(&opts->head));
142
+
143
+ qemu_opts_del(opts);
144
+}
145
146
int main(int argc, char *argv[])
147
{
148
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
149
g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size);
150
g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null);
151
g_test_add_func("/qemu-opts/append", test_opts_append);
152
+ g_test_add_func("/qemu-opts/to_qdict/basic", test_opts_to_qdict_basic);
153
+ g_test_add_func("/qemu-opts/to_qdict/filtered", test_opts_to_qdict_filtered);
154
+ g_test_add_func("/qemu-opts/to_qdict/duplicates", test_opts_to_qdict_duplicates);
155
g_test_run();
156
return 0;
157
}
71
}
158
--
72
--
159
2.13.6
73
2.13.6
160
74
161
75
diff view generated by jsdifflib
1
Using the QAPI visitor to turn all options into QAPI BlockdevOptionsNfs
1
Block jobs already paused themselves when their main BlockBackend
2
simplifies the code a lot. It will also be useful for implementing the
2
entered a drained section. This is not good enough: We also want to
3
QAPI based .bdrv_co_create callback.
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.
4
10
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
12
---
8
block/nfs.c | 176 ++++++++++++++++++------------------------------------------
13
blockjob.c | 22 +++++++++-------------
9
1 file changed, 53 insertions(+), 123 deletions(-)
14
1 file changed, 9 insertions(+), 13 deletions(-)
10
15
11
diff --git a/block/nfs.c b/block/nfs.c
16
diff --git a/blockjob.c b/blockjob.c
12
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
13
--- a/block/nfs.c
18
--- a/blockjob.c
14
+++ b/block/nfs.c
19
+++ b/blockjob.c
15
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
16
return task.ret;
21
job->id);
17
}
22
}
18
23
19
-static QemuOptsList runtime_opts = {
24
-static const BdrvChildRole child_job = {
20
- .name = "nfs",
25
- .get_parent_desc = child_job_get_parent_desc,
21
- .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
26
- .stay_at_node = true,
22
- .desc = {
23
- {
24
- .name = "path",
25
- .type = QEMU_OPT_STRING,
26
- .help = "Path of the image on the host",
27
- },
28
- {
29
- .name = "user",
30
- .type = QEMU_OPT_NUMBER,
31
- .help = "UID value to use when talking to the server",
32
- },
33
- {
34
- .name = "group",
35
- .type = QEMU_OPT_NUMBER,
36
- .help = "GID value to use when talking to the server",
37
- },
38
- {
39
- .name = "tcp-syn-count",
40
- .type = QEMU_OPT_NUMBER,
41
- .help = "Number of SYNs to send during the session establish",
42
- },
43
- {
44
- .name = "readahead-size",
45
- .type = QEMU_OPT_NUMBER,
46
- .help = "Set the readahead size in bytes",
47
- },
48
- {
49
- .name = "page-cache-size",
50
- .type = QEMU_OPT_NUMBER,
51
- .help = "Set the pagecache size in bytes",
52
- },
53
- {
54
- .name = "debug",
55
- .type = QEMU_OPT_NUMBER,
56
- .help = "Set the NFS debug level (max 2)",
57
- },
58
- { /* end of list */ }
59
- },
60
-};
27
-};
61
-
28
-
62
static void nfs_detach_aio_context(BlockDriverState *bs)
29
-static void block_job_drained_begin(void *opaque)
30
+static void child_job_drained_begin(BdrvChild *c)
63
{
31
{
64
NFSClient *client = bs->opaque;
32
- BlockJob *job = opaque;
65
@@ -XXX,XX +XXX,XX @@ static void nfs_file_close(BlockDriverState *bs)
33
+ BlockJob *job = c->opaque;
66
nfs_client_close(client);
34
block_job_pause(job);
67
}
35
}
68
36
69
-static NFSServer *nfs_config(QDict *options, Error **errp)
37
-static void block_job_drained_end(void *opaque)
70
-{
38
+static void child_job_drained_end(BdrvChild *c)
71
- NFSServer *server = NULL;
72
- QDict *addr = NULL;
73
- QObject *crumpled_addr = NULL;
74
- Visitor *iv = NULL;
75
- Error *local_error = NULL;
76
-
77
- qdict_extract_subqdict(options, &addr, "server.");
78
- if (!qdict_size(addr)) {
79
- error_setg(errp, "NFS server address missing");
80
- goto out;
81
- }
82
-
83
- crumpled_addr = qdict_crumple(addr, errp);
84
- if (!crumpled_addr) {
85
- goto out;
86
- }
87
-
88
- /*
89
- * Caution: this works only because all scalar members of
90
- * NFSServer are QString in @crumpled_addr. The visitor expects
91
- * @crumpled_addr to be typed according to the QAPI schema. It
92
- * is when @options come from -blockdev or blockdev_add. But when
93
- * they come from -drive, they're all QString.
94
- */
95
- iv = qobject_input_visitor_new(crumpled_addr);
96
- visit_type_NFSServer(iv, NULL, &server, &local_error);
97
- if (local_error) {
98
- error_propagate(errp, local_error);
99
- goto out;
100
- }
101
-
102
-out:
103
- QDECREF(addr);
104
- qobject_decref(crumpled_addr);
105
- visit_free(iv);
106
- return server;
107
-}
108
-
109
-
110
-static int64_t nfs_client_open(NFSClient *client, QDict *options,
111
+static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
112
int flags, int open_flags, Error **errp)
113
{
39
{
114
int64_t ret = -EINVAL;
40
- BlockJob *job = opaque;
115
- QemuOpts *opts = NULL;
41
+ BlockJob *job = c->opaque;
116
- Error *local_err = NULL;
42
block_job_resume(job);
117
struct stat st;
118
char *file = NULL, *strp = NULL;
119
120
qemu_mutex_init(&client->mutex);
121
- opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
122
- qemu_opts_absorb_qdict(opts, options, &local_err);
123
- if (local_err) {
124
- error_propagate(errp, local_err);
125
- ret = -EINVAL;
126
- goto fail;
127
- }
128
129
- client->path = g_strdup(qemu_opt_get(opts, "path"));
130
- if (!client->path) {
131
- ret = -EINVAL;
132
- error_setg(errp, "No path was specified");
133
- goto fail;
134
- }
135
+ client->path = g_strdup(opts->path);
136
137
strp = strrchr(client->path, '/');
138
if (strp == NULL) {
139
@@ -XXX,XX +XXX,XX @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
140
file = g_strdup(strp);
141
*strp = 0;
142
143
- /* Pop the config into our state object, Exit if invalid */
144
- client->server = nfs_config(options, errp);
145
- if (!client->server) {
146
- ret = -EINVAL;
147
- goto fail;
148
- }
149
+ /* Steal the NFSServer object from opts; set the original pointer to NULL
150
+ * to avoid use after free and double free. */
151
+ client->server = opts->server;
152
+ opts->server = NULL;
153
154
client->context = nfs_init_context();
155
if (client->context == NULL) {
156
@@ -XXX,XX +XXX,XX @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
157
goto fail;
158
}
159
160
- if (qemu_opt_get(opts, "user")) {
161
- client->uid = qemu_opt_get_number(opts, "user", 0);
162
+ if (opts->has_user) {
163
+ client->uid = opts->user;
164
nfs_set_uid(client->context, client->uid);
165
}
166
167
- if (qemu_opt_get(opts, "group")) {
168
- client->gid = qemu_opt_get_number(opts, "group", 0);
169
+ if (opts->has_group) {
170
+ client->gid = opts->group;
171
nfs_set_gid(client->context, client->gid);
172
}
173
174
- if (qemu_opt_get(opts, "tcp-syn-count")) {
175
- client->tcp_syncnt = qemu_opt_get_number(opts, "tcp-syn-count", 0);
176
+ if (opts->has_tcp_syn_count) {
177
+ client->tcp_syncnt = opts->tcp_syn_count;
178
nfs_set_tcp_syncnt(client->context, client->tcp_syncnt);
179
}
180
181
#ifdef LIBNFS_FEATURE_READAHEAD
182
- if (qemu_opt_get(opts, "readahead-size")) {
183
+ if (opts->has_readahead_size) {
184
if (open_flags & BDRV_O_NOCACHE) {
185
error_setg(errp, "Cannot enable NFS readahead "
186
"if cache.direct = on");
187
goto fail;
188
}
189
- client->readahead = qemu_opt_get_number(opts, "readahead-size", 0);
190
+ client->readahead = opts->readahead_size;
191
if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) {
192
warn_report("Truncating NFS readahead size to %d",
193
QEMU_NFS_MAX_READAHEAD_SIZE);
194
@@ -XXX,XX +XXX,XX @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
195
#endif
196
197
#ifdef LIBNFS_FEATURE_PAGECACHE
198
- if (qemu_opt_get(opts, "page-cache-size")) {
199
+ if (opts->has_page_cache_size) {
200
if (open_flags & BDRV_O_NOCACHE) {
201
error_setg(errp, "Cannot enable NFS pagecache "
202
"if cache.direct = on");
203
goto fail;
204
}
205
- client->pagecache = qemu_opt_get_number(opts, "page-cache-size", 0);
206
+ client->pagecache = opts->page_cache_size;
207
if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) {
208
warn_report("Truncating NFS pagecache size to %d pages",
209
QEMU_NFS_MAX_PAGECACHE_SIZE);
210
@@ -XXX,XX +XXX,XX @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
211
#endif
212
213
#ifdef LIBNFS_FEATURE_DEBUG
214
- if (qemu_opt_get(opts, "debug")) {
215
- client->debug = qemu_opt_get_number(opts, "debug", 0);
216
+ if (opts->has_debug) {
217
+ client->debug = opts->debug;
218
/* limit the maximum debug level to avoid potential flooding
219
* of our log files. */
220
if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) {
221
@@ -XXX,XX +XXX,XX @@ static int64_t nfs_client_open(NFSClient *client, QDict *options,
222
fail:
223
nfs_client_close(client);
224
out:
225
- qemu_opts_del(opts);
226
g_free(file);
227
return ret;
228
}
43
}
229
44
230
+static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options,
45
-static const BlockDevOps block_job_dev_ops = {
231
+ int flags, int open_flags, Error **errp)
46
- .drained_begin = block_job_drained_begin,
232
+{
47
- .drained_end = block_job_drained_end,
233
+ BlockdevOptionsNfs *opts = NULL;
48
+static const BdrvChildRole child_job = {
234
+ QObject *crumpled = NULL;
49
+ .get_parent_desc = child_job_get_parent_desc,
235
+ Visitor *v;
50
+ .drained_begin = child_job_drained_begin,
236
+ Error *local_err = NULL;
51
+ .drained_end = child_job_drained_end,
237
+ int ret;
52
+ .stay_at_node = true,
238
+
53
};
239
+ crumpled = qdict_crumple(options, errp);
54
240
+ if (crumpled == NULL) {
55
void block_job_remove_all_bdrv(BlockJob *job)
241
+ return -EINVAL;
56
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
242
+ }
57
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
243
+
58
bs->job = job;
244
+ v = qobject_input_visitor_new_keyval(crumpled);
59
245
+ visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
60
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
246
+ visit_free(v);
61
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
247
+
62
248
+ if (local_err) {
63
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
249
+ error_propagate(errp, local_err);
250
+ ret = -EINVAL;
251
+ goto fail;
252
+ }
253
+
254
+ ret = nfs_client_open(client, opts, flags, open_flags, errp);
255
+fail:
256
+ qobject_decref(crumpled);
257
+ qapi_free_BlockdevOptionsNfs(opts);
258
+ return ret;
259
+}
260
+
261
static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
262
Error **errp) {
263
NFSClient *client = bs->opaque;
264
@@ -XXX,XX +XXX,XX @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags,
265
266
client->aio_context = bdrv_get_aio_context(bs);
267
268
- ret = nfs_client_open(client, options,
269
- (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
270
- bs->open_flags, errp);
271
+ ret = nfs_client_open_qdict(client, options,
272
+ (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY,
273
+ bs->open_flags, errp);
274
if (ret < 0) {
275
return ret;
276
}
277
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
278
goto out;
279
}
280
281
- ret = nfs_client_open(client, options, O_CREAT, 0, errp);
282
+ ret = nfs_client_open_qdict(client, options, O_CREAT, 0, errp);
283
if (ret < 0) {
284
goto out;
285
}
286
--
64
--
287
2.13.6
65
2.13.6
288
66
289
67
diff view generated by jsdifflib
1
Basic test for merging two QemuOptsLists.
1
Block jobs must be paused if any of the involved nodes are drained.
2
2
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Eric Blake <eblake@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
4
---
7
tests/test-qemu-opts.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
8
1 file changed, 128 insertions(+)
6
1 file changed, 121 insertions(+)
9
7
10
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
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/tests/test-qemu-opts.c
10
--- a/tests/test-bdrv-drain.c
13
+++ b/tests/test-qemu-opts.c
11
+++ b/tests/test-bdrv-drain.c
14
@@ -XXX,XX +XXX,XX @@ static QemuOptsList opts_list_01 = {
12
@@ -XXX,XX +XXX,XX @@
15
{
13
16
.name = "str1",
14
#include "qemu/osdep.h"
17
.type = QEMU_OPT_STRING,
15
#include "block/block.h"
18
+ .help = "Help texts are preserved in qemu_opts_append",
16
+#include "block/blockjob_int.h"
19
+ .def_value_str = "default",
17
#include "sysemu/block-backend.h"
20
},{
18
#include "qapi/error.h"
21
.name = "str2",
19
22
.type = QEMU_OPT_STRING,
20
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
23
@@ -XXX,XX +XXX,XX @@ static QemuOptsList opts_list_01 = {
21
test_quiesce_common(BDRV_DRAIN, false);
24
},{
25
.name = "number1",
26
.type = QEMU_OPT_NUMBER,
27
+ .help = "Having help texts only for some options is okay",
28
},{
29
.name = "number2",
30
.type = QEMU_OPT_NUMBER,
31
@@ -XXX,XX +XXX,XX @@ static void test_opts_parse_size(void)
32
qemu_opts_reset(&opts_list_02);
33
}
22
}
34
23
35
+static void append_verify_list_01(QemuOptDesc *desc, bool with_overlapping)
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)
36
+{
31
+{
37
+ int i = 0;
32
+ block_job_completed(job, 0);
33
+}
38
+
34
+
39
+ if (with_overlapping) {
35
+static void coroutine_fn test_job_start(void *opaque)
40
+ g_assert_cmpstr(desc[i].name, ==, "str1");
36
+{
41
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
37
+ TestBlockJob *s = opaque;
42
+ g_assert_cmpstr(desc[i].help, ==,
43
+ "Help texts are preserved in qemu_opts_append");
44
+ g_assert_cmpstr(desc[i].def_value_str, ==, "default");
45
+ i++;
46
+
38
+
47
+ g_assert_cmpstr(desc[i].name, ==, "str2");
39
+ while (!s->should_complete) {
48
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
40
+ block_job_sleep_ns(&s->common, 100000);
49
+ g_assert_cmpstr(desc[i].help, ==, NULL);
50
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
51
+ i++;
52
+ }
41
+ }
53
+
42
+
54
+ g_assert_cmpstr(desc[i].name, ==, "str3");
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
55
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
56
+ g_assert_cmpstr(desc[i].help, ==, NULL);
57
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
58
+ i++;
59
+
60
+ g_assert_cmpstr(desc[i].name, ==, "number1");
61
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER);
62
+ g_assert_cmpstr(desc[i].help, ==,
63
+ "Having help texts only for some options is okay");
64
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
65
+ i++;
66
+
67
+ g_assert_cmpstr(desc[i].name, ==, "number2");
68
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER);
69
+ g_assert_cmpstr(desc[i].help, ==, NULL);
70
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
71
+ i++;
72
+
73
+ g_assert_cmpstr(desc[i].name, ==, NULL);
74
+}
44
+}
75
+
45
+
76
+static void append_verify_list_02(QemuOptDesc *desc)
46
+static void test_job_complete(BlockJob *job, Error **errp)
77
+{
47
+{
78
+ int i = 0;
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
79
+
49
+ s->should_complete = true;
80
+ g_assert_cmpstr(desc[i].name, ==, "str1");
81
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
82
+ g_assert_cmpstr(desc[i].help, ==, NULL);
83
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
84
+ i++;
85
+
86
+ g_assert_cmpstr(desc[i].name, ==, "str2");
87
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING);
88
+ g_assert_cmpstr(desc[i].help, ==, NULL);
89
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
90
+ i++;
91
+
92
+ g_assert_cmpstr(desc[i].name, ==, "bool1");
93
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL);
94
+ g_assert_cmpstr(desc[i].help, ==, NULL);
95
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
96
+ i++;
97
+
98
+ g_assert_cmpstr(desc[i].name, ==, "bool2");
99
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL);
100
+ g_assert_cmpstr(desc[i].help, ==, NULL);
101
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
102
+ i++;
103
+
104
+ g_assert_cmpstr(desc[i].name, ==, "size1");
105
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
106
+ g_assert_cmpstr(desc[i].help, ==, NULL);
107
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
108
+ i++;
109
+
110
+ g_assert_cmpstr(desc[i].name, ==, "size2");
111
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
112
+ g_assert_cmpstr(desc[i].help, ==, NULL);
113
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
114
+ i++;
115
+
116
+ g_assert_cmpstr(desc[i].name, ==, "size3");
117
+ g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE);
118
+ g_assert_cmpstr(desc[i].help, ==, NULL);
119
+ g_assert_cmpstr(desc[i].def_value_str, ==, NULL);
120
+}
50
+}
121
+
51
+
122
+static void test_opts_append_to_null(void)
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)
123
+{
59
+{
124
+ QemuOptsList *merged;
60
+ BlockBackend *blk_src, *blk_target;
61
+ BlockDriverState *src, *target;
62
+ BlockJob *job;
63
+ int ret;
125
+
64
+
126
+ merged = qemu_opts_append(NULL, &opts_list_01);
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
127
+ g_assert(merged != &opts_list_01);
66
+ &error_abort);
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
68
+ blk_insert_bs(blk_src, src, &error_abort);
128
+
69
+
129
+ g_assert_cmpstr(merged->name, ==, NULL);
70
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
130
+ g_assert_cmpstr(merged->implied_opt_name, ==, NULL);
71
+ &error_abort);
131
+ g_assert_false(merged->merge_lists);
72
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
73
+ blk_insert_bs(blk_target, target, &error_abort);
132
+
74
+
133
+ append_verify_list_01(merged->desc, true);
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);
134
+
79
+
135
+ qemu_opts_free(merged);
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);
136
+}
129
+}
137
+
130
+
138
+static void test_opts_append(void)
131
+static void test_blockjob_drain_all(void)
139
+{
132
+{
140
+ QemuOptsList *first, *merged;
133
+ test_blockjob_common(BDRV_DRAIN_ALL);
141
+
142
+ first = qemu_opts_append(NULL, &opts_list_02);
143
+ merged = qemu_opts_append(first, &opts_list_01);
144
+ g_assert(first != &opts_list_02);
145
+ g_assert(merged != &opts_list_01);
146
+
147
+ g_assert_cmpstr(merged->name, ==, NULL);
148
+ g_assert_cmpstr(merged->implied_opt_name, ==, NULL);
149
+ g_assert_false(merged->merge_lists);
150
+
151
+ append_verify_list_02(&merged->desc[0]);
152
+ append_verify_list_01(&merged->desc[7], false);
153
+
154
+ qemu_opts_free(merged);
155
+}
134
+}
156
+
135
+
136
+static void test_blockjob_drain(void)
137
+{
138
+ test_blockjob_common(BDRV_DRAIN);
139
+}
157
+
140
+
158
int main(int argc, char *argv[])
141
int main(int argc, char **argv)
159
{
142
{
160
register_opts();
143
bdrv_init();
161
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
144
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
162
g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool);
145
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
163
g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number);
146
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
164
g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size);
147
165
+ g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null);
148
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
166
+ g_test_add_func("/qemu-opts/append", test_opts_append);
149
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
167
g_test_run();
150
+
168
return 0;
151
return g_test_run();
169
}
152
}
170
--
153
--
171
2.13.6
154
2.13.6
172
155
173
156
diff view generated by jsdifflib
1
All of the simple options are now passed to qcow2_co_create() in a
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
BlockdevCreateOptions object. Still missing: node-name and the
2
so we don't need an additional block_job_pause_all() call.
3
encryption options.
4
3
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
5
---
9
block/qcow2.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++------------
6
block/io.c | 4 ----
10
1 file changed, 151 insertions(+), 38 deletions(-)
7
tests/test-bdrv-drain.c | 10 ++++------
8
2 files changed, 4 insertions(+), 10 deletions(-)
11
9
12
diff --git a/block/qcow2.c b/block/qcow2.c
10
diff --git a/block/io.c b/block/io.c
13
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
14
--- a/block/qcow2.c
12
--- a/block/io.c
15
+++ b/block/qcow2.c
13
+++ b/block/io.c
16
@@ -XXX,XX +XXX,XX @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size,
14
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
17
return meta_size + aligned_total_size;
15
* context. */
16
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
17
18
- block_job_pause_all();
19
-
20
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
21
AioContext *aio_context = bdrv_get_aio_context(bs);
22
23
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
24
aio_enable_external(aio_context);
25
aio_context_release(aio_context);
26
}
27
-
28
- block_job_resume_all();
18
}
29
}
19
30
20
-static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
31
void bdrv_drain_all(void)
21
+static bool validate_cluster_size(size_t cluster_size, Error **errp)
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
22
{
33
index XXXXXXX..XXXXXXX 100644
23
- size_t cluster_size;
34
--- a/tests/test-bdrv-drain.c
24
- int cluster_bits;
35
+++ b/tests/test-bdrv-drain.c
25
-
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
26
- cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
37
do_drain_begin(drain_type, src);
27
- DEFAULT_CLUSTER_SIZE);
38
28
- cluster_bits = ctz32(cluster_size);
39
if (drain_type == BDRV_DRAIN_ALL) {
29
+ int cluster_bits = ctz32(cluster_size);
40
- /* bdrv_drain_all() drains both src and target, and involves an
30
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
41
- * additional block_job_pause_all() */
31
(1 << cluster_bits) != cluster_size)
42
- g_assert_cmpint(job->pause_count, ==, 3);
32
{
43
+ /* bdrv_drain_all() drains both src and target */
33
error_setg(errp, "Cluster size must be a power of two between %d and "
44
+ g_assert_cmpint(job->pause_count, ==, 2);
34
"%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
45
} else {
35
+ return false;
46
g_assert_cmpint(job->pause_count, ==, 1);
36
+ }
37
+ return true;
38
+}
39
+
40
+static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp)
41
+{
42
+ size_t cluster_size;
43
+
44
+ cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
45
+ DEFAULT_CLUSTER_SIZE);
46
+ if (!validate_cluster_size(cluster_size, errp)) {
47
return 0;
48
}
47
}
49
return cluster_size;
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
50
@@ -XXX,XX +XXX,XX @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
49
do_drain_begin(drain_type, target);
51
}
50
52
51
if (drain_type == BDRV_DRAIN_ALL) {
53
static int coroutine_fn
52
- /* bdrv_drain_all() drains both src and target, and involves an
54
-qcow2_co_create(BlockDriverState *bs, int64_t total_size,
53
- * additional block_job_pause_all() */
55
- const char *backing_file, const char *backing_format,
54
- g_assert_cmpint(job->pause_count, ==, 3);
56
- int flags, size_t cluster_size, PreallocMode prealloc,
55
+ /* bdrv_drain_all() drains both src and target */
57
- QemuOpts *opts, int version, int refcount_order,
56
+ g_assert_cmpint(job->pause_count, ==, 2);
58
- const char *encryptfmt, Error **errp)
57
} else {
59
+qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
58
g_assert_cmpint(job->pause_count, ==, 1);
60
+ QemuOpts *opts, const char *encryptfmt, Error **errp)
61
{
62
+ BlockdevCreateOptionsQcow2 *qcow2_opts;
63
QDict *options;
64
65
/*
66
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, int64_t total_size,
67
*/
68
BlockBackend *blk;
69
QCowHeader *header;
70
+ size_t cluster_size;
71
+ int version;
72
+ int refcount_order;
73
uint64_t* refcount_table;
74
Error *local_err = NULL;
75
int ret;
76
77
+ /* Validate options and set default values */
78
+ assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
79
+ qcow2_opts = &create_options->u.qcow2;
80
+
81
+ if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) {
82
+ error_setg(errp, "Image size must be a multiple of 512 bytes");
83
+ ret = -EINVAL;
84
+ goto out;
85
+ }
86
+
87
+ if (qcow2_opts->has_version) {
88
+ switch (qcow2_opts->version) {
89
+ case BLOCKDEV_QCOW2_VERSION_V2:
90
+ version = 2;
91
+ break;
92
+ case BLOCKDEV_QCOW2_VERSION_V3:
93
+ version = 3;
94
+ break;
95
+ default:
96
+ g_assert_not_reached();
97
+ }
98
+ } else {
99
+ version = 3;
100
+ }
101
+
102
+ if (qcow2_opts->has_cluster_size) {
103
+ cluster_size = qcow2_opts->cluster_size;
104
+ } else {
105
+ cluster_size = DEFAULT_CLUSTER_SIZE;
106
+ }
107
+
108
+ if (!validate_cluster_size(cluster_size, errp)) {
109
+ return -EINVAL;
110
+ }
111
+
112
+ if (!qcow2_opts->has_preallocation) {
113
+ qcow2_opts->preallocation = PREALLOC_MODE_OFF;
114
+ }
115
+ if (qcow2_opts->has_backing_file &&
116
+ qcow2_opts->preallocation != PREALLOC_MODE_OFF)
117
+ {
118
+ error_setg(errp, "Backing file and preallocation cannot be used at "
119
+ "the same time");
120
+ return -EINVAL;
121
+ }
122
+ if (qcow2_opts->has_backing_fmt && !qcow2_opts->has_backing_file) {
123
+ error_setg(errp, "Backing format cannot be used without backing file");
124
+ return -EINVAL;
125
+ }
126
+
127
+ if (!qcow2_opts->has_lazy_refcounts) {
128
+ qcow2_opts->lazy_refcounts = false;
129
+ }
130
+ if (version < 3 && qcow2_opts->lazy_refcounts) {
131
+ error_setg(errp, "Lazy refcounts only supported with compatibility "
132
+ "level 1.1 and above (use compat=1.1 or greater)");
133
+ return -EINVAL;
134
+ }
135
+
136
+ if (!qcow2_opts->has_refcount_bits) {
137
+ qcow2_opts->refcount_bits = 16;
138
+ }
139
+ if (qcow2_opts->refcount_bits > 64 ||
140
+ !is_power_of_2(qcow2_opts->refcount_bits))
141
+ {
142
+ error_setg(errp, "Refcount width must be a power of two and may not "
143
+ "exceed 64 bits");
144
+ return -EINVAL;
145
+ }
146
+ if (version < 3 && qcow2_opts->refcount_bits != 16) {
147
+ error_setg(errp, "Different refcount widths than 16 bits require "
148
+ "compatibility level 1.1 or above (use compat=1.1 or "
149
+ "greater)");
150
+ return -EINVAL;
151
+ }
152
+ refcount_order = ctz32(qcow2_opts->refcount_bits);
153
+
154
+
155
+ /* Create BlockBackend to write to the image */
156
blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
157
ret = blk_insert_bs(blk, bs, errp);
158
if (ret < 0) {
159
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, int64_t total_size,
160
/* We'll update this to correct value later */
161
header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
162
163
- if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
164
+ if (qcow2_opts->lazy_refcounts) {
165
header->compatible_features |=
166
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
167
}
168
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, int64_t total_size,
169
}
170
171
/* Okay, now that we have a valid image, let's give it the right size */
172
- ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp);
173
+ ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp);
174
if (ret < 0) {
175
error_prepend(errp, "Could not resize image: ");
176
goto out;
177
}
178
179
/* Want a backing file? There you go.*/
180
- if (backing_file) {
181
- ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format);
182
+ if (qcow2_opts->has_backing_file) {
183
+ const char *backing_format = NULL;
184
+
185
+ if (qcow2_opts->has_backing_fmt) {
186
+ backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt);
187
+ }
188
+
189
+ ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file,
190
+ backing_format);
191
if (ret < 0) {
192
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
193
- "with format '%s'", backing_file, backing_format);
194
+ "with format '%s'", qcow2_opts->backing_file,
195
+ backing_format);
196
goto out;
197
}
198
}
199
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, int64_t total_size,
200
}
201
202
/* And if we're supposed to preallocate metadata, do that now */
203
- if (prealloc != PREALLOC_MODE_OFF) {
204
- ret = preallocate(blk_bs(blk), 0, total_size);
205
+ if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) {
206
+ ret = preallocate(blk_bs(blk), 0, qcow2_opts->size);
207
if (ret < 0) {
208
error_setg_errno(errp, -ret, "Could not preallocate metadata");
209
goto out;
210
@@ -XXX,XX +XXX,XX @@ out:
211
static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts,
212
Error **errp)
213
{
214
+ BlockdevCreateOptions create_options;
215
char *backing_file = NULL;
216
char *backing_fmt = NULL;
217
+ BlockdevDriver backing_drv;
218
char *buf = NULL;
219
uint64_t size = 0;
220
int flags = 0;
221
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
222
PreallocMode prealloc;
223
int version;
224
uint64_t refcount_bits;
225
- int refcount_order;
226
char *encryptfmt = NULL;
227
BlockDriverState *bs = NULL;
228
Error *local_err = NULL;
229
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
230
BDRV_SECTOR_SIZE);
231
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
232
backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
233
+ backing_drv = qapi_enum_parse(&BlockdevDriver_lookup, backing_fmt,
234
+ 0, &local_err);
235
+ if (local_err) {
236
+ error_propagate(errp, local_err);
237
+ ret = -EINVAL;
238
+ goto finish;
239
+ }
240
encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
241
if (encryptfmt) {
242
if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
243
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
244
flags |= BLOCK_FLAG_LAZY_REFCOUNTS;
245
}
246
247
- if (backing_file && prealloc != PREALLOC_MODE_OFF) {
248
- error_setg(errp, "Backing file and preallocation cannot be used at "
249
- "the same time");
250
- ret = -EINVAL;
251
- goto finish;
252
- }
253
-
254
- if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
255
- error_setg(errp, "Lazy refcounts only supported with compatibility "
256
- "level 1.1 and above (use compat=1.1 or greater)");
257
- ret = -EINVAL;
258
- goto finish;
259
- }
260
-
261
refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err);
262
if (local_err) {
263
error_propagate(errp, local_err);
264
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
265
goto finish;
266
}
267
268
- refcount_order = ctz32(refcount_bits);
269
270
/* Create and open the file (protocol layer) */
271
if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
272
+ int refcount_order = ctz32(refcount_bits);
273
int64_t prealloc_size =
274
qcow2_calc_prealloc_size(size, cluster_size, refcount_order);
275
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
276
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
277
}
278
279
/* Create the qcow2 image (format layer) */
280
- ret = qcow2_co_create(bs, size, backing_file, backing_fmt, flags,
281
- cluster_size, prealloc, opts, version, refcount_order,
282
- encryptfmt, errp);
283
+ create_options = (BlockdevCreateOptions) {
284
+ .driver = BLOCKDEV_DRIVER_QCOW2,
285
+ .u.qcow2 = {
286
+ .file = &(BlockdevRef) {
287
+ .type = QTYPE_QSTRING,
288
+ .u.reference = bs->node_name,
289
+ },
290
+ .size = size,
291
+ .has_version = true,
292
+ .version = version == 2
293
+ ? BLOCKDEV_QCOW2_VERSION_V2
294
+ : BLOCKDEV_QCOW2_VERSION_V3,
295
+ .has_backing_file = (backing_file != NULL),
296
+ .backing_file = backing_file,
297
+ .has_backing_fmt = (backing_fmt != NULL),
298
+ .backing_fmt = backing_drv,
299
+ .has_cluster_size = true,
300
+ .cluster_size = cluster_size,
301
+ .has_preallocation = true,
302
+ .preallocation = prealloc,
303
+ .has_lazy_refcounts = true,
304
+ .lazy_refcounts = (flags & BLOCK_FLAG_LAZY_REFCOUNTS),
305
+ .has_refcount_bits = true,
306
+ .refcount_bits = refcount_bits,
307
+ },
308
+ };
309
+ ret = qcow2_co_create(bs, &create_options, opts, encryptfmt, errp);
310
if (ret < 0) {
311
goto finish;
312
}
59
}
313
--
60
--
314
2.13.6
61
2.13.6
315
62
316
63
diff view generated by jsdifflib
1
This allows, given a QemuOpts for a QemuOptsList that was merged from
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
multiple QemuOptsList, to only consider those options that exist in one
2
aio_disable_external() to the outermost drain section, but the block
3
specific list. Block drivers need this to separate format-layer create
3
driver callbacks are always called. bdrv_do_drained_end() must match
4
options from protocol-level options.
4
this behaviour, otherwise nodes stay drained even if begin/end calls
5
were balanced.
5
6
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
8
---
10
include/qemu/option.h | 2 ++
9
block/io.c | 12 +++++++-----
11
util/qemu-option.c | 42 +++++++++++++++++++++++++++++++++++++-----
10
1 file changed, 7 insertions(+), 5 deletions(-)
12
2 files changed, 39 insertions(+), 5 deletions(-)
13
11
14
diff --git a/include/qemu/option.h b/include/qemu/option.h
12
diff --git a/block/io.c b/block/io.c
15
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
16
--- a/include/qemu/option.h
14
--- a/block/io.c
17
+++ b/include/qemu/option.h
15
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
19
int permit_abbrev);
17
20
QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
18
void bdrv_drained_end(BlockDriverState *bs)
21
Error **errp);
19
{
22
+QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict,
20
+ int old_quiesce_counter;
23
+ QemuOptsList *list, bool del);
21
+
24
QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict);
22
if (qemu_in_coroutine()) {
25
void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp);
23
bdrv_co_yield_to_drain(bs, false);
26
24
return;
27
diff --git a/util/qemu-option.c b/util/qemu-option.c
25
}
28
index XXXXXXX..XXXXXXX 100644
26
assert(bs->quiesce_counter > 0);
29
--- a/util/qemu-option.c
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
30
+++ b/util/qemu-option.c
28
- return;
31
@@ -XXX,XX +XXX,XX @@ void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp)
29
- }
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
31
32
/* Re-enable things in child-to-parent order */
33
bdrv_drain_invoke(bs, false, false);
34
- bdrv_parent_drained_end(bs);
35
- aio_enable_external(bdrv_get_aio_context(bs));
36
+ if (old_quiesce_counter == 1) {
37
+ bdrv_parent_drained_end(bs);
38
+ aio_enable_external(bdrv_get_aio_context(bs));
39
+ }
32
}
40
}
33
41
34
/*
42
/*
35
- * Convert from QemuOpts to QDict.
36
- * The QDict values are of type QString.
37
+ * Convert from QemuOpts to QDict. The QDict values are of type QString.
38
+ *
39
+ * If @list is given, only add those options to the QDict that are contained in
40
+ * the list. If @del is true, any options added to the QDict are removed from
41
+ * the QemuOpts, otherwise they remain there.
42
+ *
43
+ * If two options in @opts have the same name, they are processed in order
44
+ * so that the last one wins (consistent with the reverse iteration in
45
+ * qemu_opt_find()), but all of them are deleted if @del is true.
46
+ *
47
* TODO We'll want to use types appropriate for opt->desc->type, but
48
* this is enough for now.
49
*/
50
-QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
51
+QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict,
52
+ QemuOptsList *list, bool del)
53
{
54
- QemuOpt *opt;
55
+ QemuOpt *opt, *next;
56
57
if (!qdict) {
58
qdict = qdict_new();
59
@@ -XXX,XX +XXX,XX @@ QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
60
if (opts->id) {
61
qdict_put_str(qdict, "id", opts->id);
62
}
63
- QTAILQ_FOREACH(opt, &opts->head, next) {
64
+ QTAILQ_FOREACH_SAFE(opt, &opts->head, next, next) {
65
+ if (list) {
66
+ QemuOptDesc *desc;
67
+ bool found = false;
68
+ for (desc = list->desc; desc->name; desc++) {
69
+ if (!strcmp(desc->name, opt->name)) {
70
+ found = true;
71
+ break;
72
+ }
73
+ }
74
+ if (!found) {
75
+ continue;
76
+ }
77
+ }
78
qdict_put_str(qdict, opt->name, opt->str);
79
+ if (del) {
80
+ qemu_opt_del(opt);
81
+ }
82
}
83
return qdict;
84
}
85
86
+/* Copy all options in a QemuOpts to the given QDict. See
87
+ * qemu_opts_to_qdict_filtered() for details. */
88
+QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
89
+{
90
+ return qemu_opts_to_qdict_filtered(opts, qdict, NULL, false);
91
+}
92
+
93
/* Validate parsed opts against descriptions where no
94
* descriptions were provided in the QemuOptsList.
95
*/
96
--
43
--
97
2.13.6
44
2.13.6
98
45
99
46
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
3
They will be used to avoid recursively taking s->lock during
4
bdrv_open or bdrv_check.
5
6
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
7
Message-Id: <1516279431-30424-7-git-send-email-pbonzini@redhat.com>
8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
2
---
11
block/qcow2.h | 2 ++
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
12
block/qcow2-refcount.c | 28 ++++++++++++++++++++++++++++
4
1 file changed, 57 insertions(+)
13
block/qcow2.c | 20 ++++----------------
14
3 files changed, 34 insertions(+), 16 deletions(-)
15
5
16
diff --git a/block/qcow2.h b/block/qcow2.h
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
17
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2.h
8
--- a/tests/test-bdrv-drain.c
19
+++ b/block/qcow2.h
9
+++ b/tests/test-bdrv-drain.c
20
@@ -XXX,XX +XXX,XX @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
21
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
11
enum drain_type {
22
int64_t l1_table_offset, int l1_size, int addend);
12
BDRV_DRAIN_ALL,
23
13
BDRV_DRAIN,
24
+int coroutine_fn qcow2_flush_caches(BlockDriverState *bs);
14
+ DRAIN_TYPE_MAX,
25
+int coroutine_fn qcow2_write_caches(BlockDriverState *bs);
15
};
26
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
16
27
BdrvCheckMode fix);
17
static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
28
18
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
29
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
19
test_quiesce_common(BDRV_DRAIN, false);
30
index XXXXXXX..XXXXXXX 100644
31
--- a/block/qcow2-refcount.c
32
+++ b/block/qcow2-refcount.c
33
@@ -XXX,XX +XXX,XX @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
34
}
35
}
20
}
36
21
37
+int coroutine_fn qcow2_write_caches(BlockDriverState *bs)
22
+static void test_nested(void)
38
+{
23
+{
39
+ BDRVQcow2State *s = bs->opaque;
24
+ BlockBackend *blk;
40
+ int ret;
25
+ BlockDriverState *bs, *backing;
41
26
+ BDRVTestState *s, *backing_s;
42
+ ret = qcow2_cache_write(bs, s->l2_table_cache);
27
+ enum drain_type outer, inner;
43
+ if (ret < 0) {
44
+ return ret;
45
+ }
46
+
28
+
47
+ if (qcow2_need_accurate_refcounts(s)) {
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
48
+ ret = qcow2_cache_write(bs, s->refcount_block_cache);
30
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
49
+ if (ret < 0) {
31
+ &error_abort);
50
+ return ret;
32
+ s = bs->opaque;
33
+ blk_insert_bs(blk, bs, &error_abort);
34
+
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);
38
+
39
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
40
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
41
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
42
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
43
+ (inner != BDRV_DRAIN_ALL);
44
+ int backing_quiesce = 0;
45
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
46
+ (inner != BDRV_DRAIN);
47
+
48
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
49
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
50
+ g_assert_cmpint(s->drain_count, ==, 0);
51
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
52
+
53
+ do_drain_begin(outer, bs);
54
+ do_drain_begin(inner, bs);
55
+
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);
51
+ }
68
+ }
52
+ }
69
+ }
53
+
70
+
54
+ return 0;
71
+ bdrv_unref(backing);
72
+ bdrv_unref(bs);
73
+ blk_unref(blk);
55
+}
74
+}
56
+
75
+
57
+int coroutine_fn qcow2_flush_caches(BlockDriverState *bs)
76
58
+{
77
typedef struct TestBlockJob {
59
+ int ret = qcow2_write_caches(bs);
78
BlockJob common;
60
+ if (ret < 0) {
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
61
+ return ret;
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
62
+ }
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
82
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
63
+
84
+
64
+ return bdrv_flush(bs->file->bs);
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
65
+}
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
66
87
67
/*********************************************************/
68
/* snapshots and image creation */
69
diff --git a/block/qcow2.c b/block/qcow2.c
70
index XXXXXXX..XXXXXXX 100644
71
--- a/block/qcow2.c
72
+++ b/block/qcow2.c
73
@@ -XXX,XX +XXX,XX @@ static int qcow2_mark_clean(BlockDriverState *bs)
74
75
s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
76
77
- ret = bdrv_flush(bs);
78
+ ret = qcow2_flush_caches(bs);
79
if (ret < 0) {
80
return ret;
81
}
82
@@ -XXX,XX +XXX,XX @@ int qcow2_mark_consistent(BlockDriverState *bs)
83
BDRVQcow2State *s = bs->opaque;
84
85
if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
86
- int ret = bdrv_flush(bs);
87
+ int ret = qcow2_flush_caches(bs);
88
if (ret < 0) {
89
return ret;
90
}
91
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
92
int ret;
93
94
qemu_co_mutex_lock(&s->lock);
95
- ret = qcow2_cache_write(bs, s->l2_table_cache);
96
- if (ret < 0) {
97
- qemu_co_mutex_unlock(&s->lock);
98
- return ret;
99
- }
100
-
101
- if (qcow2_need_accurate_refcounts(s)) {
102
- ret = qcow2_cache_write(bs, s->refcount_block_cache);
103
- if (ret < 0) {
104
- qemu_co_mutex_unlock(&s->lock);
105
- return ret;
106
- }
107
- }
108
+ ret = qcow2_write_caches(bs);
109
qemu_co_mutex_unlock(&s->lock);
110
111
- return 0;
112
+ return ret;
113
}
114
115
static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
116
--
88
--
117
2.13.6
89
2.13.6
118
90
119
91
diff view generated by jsdifflib
Deleted patch
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
1
3
update_header_sync itself does not need to flush the caches to disk.
4
The only paths that allocate clusters are:
5
6
- bitmap_list_store with in_place=false, called by update_ext_header_and_dir
7
8
- store_bitmap_data, called by store_bitmap
9
10
- store_bitmap, called by qcow2_store_persistent_dirty_bitmaps and
11
followed by update_ext_header_and_dir
12
13
So in the end the central place where we need to flush the caches
14
is update_ext_header_and_dir.
15
16
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
19
block/qcow2-bitmap.c | 4 ++--
20
1 file changed, 2 insertions(+), 2 deletions(-)
21
22
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/block/qcow2-bitmap.c
25
+++ b/block/qcow2-bitmap.c
26
@@ -XXX,XX +XXX,XX @@ static int update_header_sync(BlockDriverState *bs)
27
return ret;
28
}
29
30
- return bdrv_flush(bs);
31
+ return bdrv_flush(bs->file->bs);
32
}
33
34
static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
35
@@ -XXX,XX +XXX,XX @@ static int update_ext_header_and_dir(BlockDriverState *bs,
36
return ret;
37
}
38
39
- ret = bdrv_flush(bs->file->bs);
40
+ ret = qcow2_flush_caches(bs);
41
if (ret < 0) {
42
goto fail;
43
}
44
--
45
2.13.6
46
47
diff view generated by jsdifflib
1
Instead of passing a separate BlockDriverState* into qcow2_co_create(),
1
This is in preparation for subtree drains, i.e. drained sections that
2
make use of the BlockdevRef that is included in BlockdevCreateOptions.
2
affect not only a single node, but recursively all child nodes, too.
3
4
Calling the parent callbacks for drain is pointless when we just came
5
from that parent node recursively and leads to multiple increases of
6
bs->quiesce_counter in a single drain call. Don't do it.
7
8
In order for this to work correctly, the parent callback must be called
9
for every bdrv_drain_begin/end() call, not only for the outermost one:
10
11
If we have a node N with two parents A and B, recursive draining of A
12
should cause the quiesce_counter of B to increase because its child N is
13
drained independently of B. If now B is recursively drained, too, A must
14
increase its quiesce_counter because N is drained independently of A
15
only now, even if N is going from quiesce_counter 1 to 2.
3
16
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
18
---
8
include/block/block.h | 1 +
19
include/block/block.h | 4 ++--
9
block.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
20
block.c | 13 +++++++++----
10
block/qcow2.c | 39 +++++++++++++++++++++++++--------------
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
11
3 files changed, 73 insertions(+), 14 deletions(-)
22
3 files changed, 45 insertions(+), 19 deletions(-)
12
23
13
diff --git a/include/block/block.h b/include/block/block.h
24
diff --git a/include/block/block.h b/include/block/block.h
14
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
15
--- a/include/block/block.h
26
--- a/include/block/block.h
16
+++ b/include/block/block.h
27
+++ b/include/block/block.h
17
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename,
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
18
BlockDriverState* parent,
29
* Begin a quiesced section of all users of @bs. This is part of
19
const BdrvChildRole *child_role,
30
* bdrv_drained_begin.
20
bool allow_none, Error **errp);
31
*/
21
+BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp);
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
22
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
23
Error **errp);
34
24
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_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:
25
diff --git a/block.c b/block.c
46
diff --git a/block.c b/block.c
26
index XXXXXXX..XXXXXXX 100644
47
index XXXXXXX..XXXXXXX 100644
27
--- a/block.c
48
--- a/block.c
28
+++ b/block.c
49
+++ b/block.c
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
51
BlockDriverState *new_bs)
52
{
53
BlockDriverState *old_bs = child->bs;
54
+ int i;
55
56
if (old_bs && new_bs) {
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
58
}
59
if (old_bs) {
60
if (old_bs->quiesce_counter && child->role->drained_end) {
61
- child->role->drained_end(child);
62
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
63
+ child->role->drained_end(child);
64
+ }
65
}
66
if (child->role->detach) {
67
child->role->detach(child);
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
69
if (new_bs) {
70
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
71
if (new_bs->quiesce_counter && child->role->drained_begin) {
72
- child->role->drained_begin(child);
73
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
74
+ child->role->drained_begin(child);
75
+ }
76
}
77
78
if (child->role->attach) {
79
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
80
AioContext *ctx = bdrv_get_aio_context(bs);
81
82
aio_disable_external(ctx);
83
- bdrv_parent_drained_begin(bs);
84
+ bdrv_parent_drained_begin(bs, NULL);
85
bdrv_drain(bs); /* ensure there are no in-flight requests */
86
87
while (aio_poll(ctx, false)) {
88
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
89
*/
90
aio_context_acquire(new_context);
91
bdrv_attach_aio_context(bs, new_context);
92
- bdrv_parent_drained_end(bs);
93
+ bdrv_parent_drained_end(bs, NULL);
94
aio_enable_external(ctx);
95
aio_context_release(new_context);
96
}
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
29
@@ -XXX,XX +XXX,XX @@
101
@@ -XXX,XX +XXX,XX @@
30
#include "qapi/qmp/qdict.h"
102
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
31
#include "qapi/qmp/qjson.h"
103
int64_t offset, int bytes, BdrvRequestFlags flags);
32
#include "qapi/qmp/qstring.h"
104
33
+#include "qapi/qobject-output-visitor.h"
105
-void bdrv_parent_drained_begin(BlockDriverState *bs)
34
+#include "qapi/qapi-visit-block-core.h"
106
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
35
#include "sysemu/block-backend.h"
107
{
36
#include "sysemu/sysemu.h"
108
BdrvChild *c, *next;
37
#include "qemu/notify.h"
109
38
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename,
110
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
39
return c;
111
+ if (c == ignore) {
40
}
112
+ continue;
41
113
+ }
42
+/* TODO Future callers may need to specify parent/child_role in order for
114
if (c->role->drained_begin) {
43
+ * option inheritance to work. Existing callers use it for the root node. */
115
c->role->drained_begin(c);
44
+BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
116
}
117
}
118
}
119
120
-void bdrv_parent_drained_end(BlockDriverState *bs)
121
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
122
{
123
BdrvChild *c, *next;
124
125
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
126
+ if (c == ignore) {
127
+ continue;
128
+ }
129
if (c->role->drained_end) {
130
c->role->drained_end(c);
131
}
132
@@ -XXX,XX +XXX,XX @@ typedef struct {
133
BlockDriverState *bs;
134
bool done;
135
bool begin;
136
+ BdrvChild *parent;
137
} BdrvCoDrainData;
138
139
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
140
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
141
return waited;
142
}
143
144
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
145
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
146
+
147
static void bdrv_co_drain_bh_cb(void *opaque)
148
{
149
BdrvCoDrainData *data = opaque;
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
151
152
bdrv_dec_in_flight(bs);
153
if (data->begin) {
154
- bdrv_drained_begin(bs);
155
+ bdrv_do_drained_begin(bs, data->parent);
156
} else {
157
- bdrv_drained_end(bs);
158
+ bdrv_do_drained_end(bs, data->parent);
159
}
160
161
data->done = true;
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
163
}
164
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
166
- bool begin)
167
+ bool begin, BdrvChild *parent)
168
{
169
BdrvCoDrainData data;
170
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
172
.bs = bs,
173
.done = false,
174
.begin = begin,
175
+ .parent = parent,
176
};
177
bdrv_inc_in_flight(bs);
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
180
assert(data.done);
181
}
182
183
-void bdrv_drained_begin(BlockDriverState *bs)
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
185
{
186
if (qemu_in_coroutine()) {
187
- bdrv_co_yield_to_drain(bs, true);
188
+ bdrv_co_yield_to_drain(bs, true, parent);
189
return;
190
}
191
192
/* Stop things in parent-to-child order */
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
194
aio_disable_external(bdrv_get_aio_context(bs));
195
- bdrv_parent_drained_begin(bs);
196
}
197
198
+ bdrv_parent_drained_begin(bs, parent);
199
bdrv_drain_invoke(bs, true, false);
200
bdrv_drain_recurse(bs);
201
}
202
203
-void bdrv_drained_end(BlockDriverState *bs)
204
+void bdrv_drained_begin(BlockDriverState *bs)
45
+{
205
+{
46
+ BlockDriverState *bs = NULL;
206
+ bdrv_do_drained_begin(bs, NULL);
47
+ Error *local_err = NULL;
48
+ QObject *obj = NULL;
49
+ QDict *qdict = NULL;
50
+ const char *reference = NULL;
51
+ Visitor *v = NULL;
52
+
53
+ if (ref->type == QTYPE_QSTRING) {
54
+ reference = ref->u.reference;
55
+ } else {
56
+ BlockdevOptions *options = &ref->u.definition;
57
+ assert(ref->type == QTYPE_QDICT);
58
+
59
+ v = qobject_output_visitor_new(&obj);
60
+ visit_type_BlockdevOptions(v, NULL, &options, &local_err);
61
+ if (local_err) {
62
+ error_propagate(errp, local_err);
63
+ goto fail;
64
+ }
65
+ visit_complete(v, &obj);
66
+
67
+ qdict = qobject_to_qdict(obj);
68
+ qdict_flatten(qdict);
69
+
70
+ /* bdrv_open_inherit() defaults to the values in bdrv_flags (for
71
+ * compatibility with other callers) rather than what we want as the
72
+ * real defaults. Apply the defaults here instead. */
73
+ qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
74
+ qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
75
+ qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off");
76
+ }
77
+
78
+ bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
79
+ obj = NULL;
80
+
81
+fail:
82
+ qobject_decref(obj);
83
+ visit_free(v);
84
+ return bs;
85
+}
207
+}
86
+
208
+
87
static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
88
int flags,
210
{
89
QDict *snapshot_options,
211
int old_quiesce_counter;
90
diff --git a/block/qcow2.c b/block/qcow2.c
212
91
index XXXXXXX..XXXXXXX 100644
213
if (qemu_in_coroutine()) {
92
--- a/block/qcow2.c
214
- bdrv_co_yield_to_drain(bs, false);
93
+++ b/block/qcow2.c
215
+ bdrv_co_yield_to_drain(bs, false, parent);
94
@@ -XXX,XX +XXX,XX @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
216
return;
95
}
217
}
96
218
assert(bs->quiesce_counter > 0);
97
static int coroutine_fn
219
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
98
-qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
220
99
- QemuOpts *opts, const char *encryptfmt, Error **errp)
221
/* Re-enable things in child-to-parent order */
100
+qcow2_co_create(BlockdevCreateOptions *create_options, QemuOpts *opts,
222
bdrv_drain_invoke(bs, false, false);
101
+ const char *encryptfmt, Error **errp)
223
+ bdrv_parent_drained_end(bs, parent);
102
{
224
if (old_quiesce_counter == 1) {
103
BlockdevCreateOptionsQcow2 *qcow2_opts;
225
- bdrv_parent_drained_end(bs);
104
QDict *options;
226
aio_enable_external(bdrv_get_aio_context(bs));
105
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
227
}
106
* 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
228
}
107
* size for any qcow2 image.
229
108
*/
230
+void bdrv_drained_end(BlockDriverState *bs)
109
- BlockBackend *blk;
231
+{
110
+ BlockBackend *blk = NULL;
232
+ bdrv_do_drained_end(bs, NULL);
111
+ BlockDriverState *bs = NULL;
233
+}
112
QCowHeader *header;
113
size_t cluster_size;
114
int version;
115
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
116
Error *local_err = NULL;
117
int ret;
118
119
- /* Validate options and set default values */
120
assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
121
qcow2_opts = &create_options->u.qcow2;
122
123
+ bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp);
124
+ if (bs == NULL) {
125
+ return -EIO;
126
+ }
127
+
234
+
128
+ /* Validate options and set default values */
235
/*
129
if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) {
236
* Wait for pending requests to complete on a single BlockDriverState subtree,
130
error_setg(errp, "Image size must be a multiple of 512 bytes");
237
* and suspend block driver's internal I/O until next request arrives.
131
ret = -EINVAL;
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
132
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
239
/* Stop things in parent-to-child order */
133
}
240
aio_context_acquire(aio_context);
134
241
aio_disable_external(aio_context);
135
if (!validate_cluster_size(cluster_size, errp)) {
242
- bdrv_parent_drained_begin(bs);
136
- return -EINVAL;
243
+ bdrv_parent_drained_begin(bs, NULL);
137
+ ret = -EINVAL;
244
bdrv_drain_invoke(bs, true, true);
138
+ goto out;
245
aio_context_release(aio_context);
139
}
246
140
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
141
if (!qcow2_opts->has_preallocation) {
248
/* Re-enable things in child-to-parent order */
142
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
249
aio_context_acquire(aio_context);
143
{
250
bdrv_drain_invoke(bs, false, true);
144
error_setg(errp, "Backing file and preallocation cannot be used at "
251
- bdrv_parent_drained_end(bs);
145
"the same time");
252
+ bdrv_parent_drained_end(bs, NULL);
146
- return -EINVAL;
253
aio_enable_external(aio_context);
147
+ ret = -EINVAL;
254
aio_context_release(aio_context);
148
+ goto out;
149
}
150
if (qcow2_opts->has_backing_fmt && !qcow2_opts->has_backing_file) {
151
error_setg(errp, "Backing format cannot be used without backing file");
152
- return -EINVAL;
153
+ ret = -EINVAL;
154
+ goto out;
155
}
156
157
if (!qcow2_opts->has_lazy_refcounts) {
158
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
159
if (version < 3 && qcow2_opts->lazy_refcounts) {
160
error_setg(errp, "Lazy refcounts only supported with compatibility "
161
"level 1.1 and above (use compat=1.1 or greater)");
162
- return -EINVAL;
163
+ ret = -EINVAL;
164
+ goto out;
165
}
166
167
if (!qcow2_opts->has_refcount_bits) {
168
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
169
{
170
error_setg(errp, "Refcount width must be a power of two and may not "
171
"exceed 64 bits");
172
- return -EINVAL;
173
+ ret = -EINVAL;
174
+ goto out;
175
}
176
if (version < 3 && qcow2_opts->refcount_bits != 16) {
177
error_setg(errp, "Different refcount widths than 16 bits require "
178
"compatibility level 1.1 or above (use compat=1.1 or "
179
"greater)");
180
- return -EINVAL;
181
+ ret = -EINVAL;
182
+ goto out;
183
}
184
refcount_order = ctz32(qcow2_opts->refcount_bits);
185
186
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockDriverState *bs, BlockdevCreateOptions *create_options,
187
188
ret = 0;
189
out:
190
- if (blk) {
191
- blk_unref(blk);
192
- }
193
+ blk_unref(blk);
194
+ bdrv_unref(bs);
195
return ret;
196
}
197
198
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
199
.refcount_bits = refcount_bits,
200
},
201
};
202
- ret = qcow2_co_create(bs, &create_options, opts, encryptfmt, errp);
203
+ ret = qcow2_co_create(&create_options, opts, encryptfmt, errp);
204
if (ret < 0) {
205
goto finish;
206
}
255
}
207
--
256
--
208
2.13.6
257
2.13.6
209
258
210
259
diff view generated by jsdifflib
1
The code to establish an RBD connection is duplicated between open and
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
create. In order to be able to share the code, factor out the code from
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
qemu_rbd_open() as a first step.
3
until bdrv_drained_end().
4
5
Add a version that keeps the whole subtree drained. As of this commit,
6
graph changes cannot be allowed during a subtree drained section, but
7
this will be fixed soon.
4
8
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
10
---
8
block/rbd.c | 100 ++++++++++++++++++++++++++++++++++++------------------------
11
include/block/block.h | 13 +++++++++++++
9
1 file changed, 60 insertions(+), 40 deletions(-)
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
13
2 files changed, 56 insertions(+), 11 deletions(-)
10
14
11
diff --git a/block/rbd.c b/block/rbd.c
15
diff --git a/include/block/block.h b/include/block/block.h
12
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
13
--- a/block/rbd.c
17
--- a/include/block/block.h
14
+++ b/block/rbd.c
18
+++ b/include/block/block.h
15
@@ -XXX,XX +XXX,XX @@ out:
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
16
return rados_str;
20
void bdrv_drained_begin(BlockDriverState *bs);
21
22
/**
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
24
+ * exclusive access to all child nodes as well.
25
+ *
26
+ * Graph changes are not allowed during a subtree drain section.
27
+ */
28
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
29
+
30
+/**
31
* bdrv_drained_end:
32
*
33
* End a quiescent section started by bdrv_drained_begin().
34
*/
35
void bdrv_drained_end(BlockDriverState *bs);
36
37
+/**
38
+ * End a quiescent section started by bdrv_subtree_drained_begin().
39
+ */
40
+void bdrv_subtree_drained_end(BlockDriverState *bs);
41
+
42
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
43
Error **errp);
44
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
45
diff --git a/block/io.c b/block/io.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/block/io.c
48
+++ b/block/io.c
49
@@ -XXX,XX +XXX,XX @@ typedef struct {
50
BlockDriverState *bs;
51
bool done;
52
bool begin;
53
+ bool recursive;
54
BdrvChild *parent;
55
} BdrvCoDrainData;
56
57
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
58
return waited;
17
}
59
}
18
60
19
-static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
61
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
20
- Error **errp)
62
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
21
+static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
63
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
22
+ char **s_snap, char **s_image_name,
64
+ BdrvChild *parent);
23
+ QDict *options, bool cache, Error **errp)
65
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
66
+ BdrvChild *parent);
67
68
static void bdrv_co_drain_bh_cb(void *opaque)
24
{
69
{
25
- BDRVRBDState *s = bs->opaque;
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
26
- const char *pool, *snap, *conf, *user, *image_name, *keypairs;
71
27
- const char *secretid, *filename;
72
bdrv_dec_in_flight(bs);
28
QemuOpts *opts;
73
if (data->begin) {
29
- Error *local_err = NULL;
74
- bdrv_do_drained_begin(bs, data->parent);
30
char *mon_host = NULL;
75
+ bdrv_do_drained_begin(bs, data->recursive, data->parent);
31
+ const char *pool, *snap, *conf, *user, *image_name, *keypairs;
76
} else {
32
+ const char *secretid;
77
- bdrv_do_drained_end(bs, data->parent);
33
+ Error *local_err = NULL;
78
+ bdrv_do_drained_end(bs, data->recursive, data->parent);
34
int r;
35
36
- /* If we are given a filename, parse the filename, with precedence given to
37
- * filename encoded options */
38
- filename = qdict_get_try_str(options, "filename");
39
- if (filename) {
40
- warn_report("'filename' option specified. "
41
- "This is an unsupported option, and may be deprecated "
42
- "in the future");
43
- qemu_rbd_parse_filename(filename, options, &local_err);
44
- if (local_err) {
45
- r = -EINVAL;
46
- error_propagate(errp, local_err);
47
- goto exit;
48
- }
49
- }
50
-
51
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
52
qemu_opts_absorb_qdict(opts, options, &local_err);
53
if (local_err) {
54
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
55
goto failed_opts;
56
}
79
}
57
80
58
- r = rados_create(&s->cluster, user);
81
data->done = true;
59
+ r = rados_create(cluster, user);
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
60
if (r < 0) {
83
}
61
error_setg_errno(errp, -r, "error initializing");
84
62
goto failed_opts;
85
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
86
- bool begin, BdrvChild *parent)
87
+ bool begin, bool recursive,
88
+ BdrvChild *parent)
89
{
90
BdrvCoDrainData data;
91
92
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
93
.bs = bs,
94
.done = false,
95
.begin = begin,
96
+ .recursive = recursive,
97
.parent = parent,
98
};
99
bdrv_inc_in_flight(bs);
100
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
101
assert(data.done);
102
}
103
104
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
105
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
106
+ BdrvChild *parent)
107
{
108
+ BdrvChild *child, *next;
109
+
110
if (qemu_in_coroutine()) {
111
- bdrv_co_yield_to_drain(bs, true, parent);
112
+ bdrv_co_yield_to_drain(bs, true, recursive, parent);
113
return;
63
}
114
}
64
115
65
- s->snap = g_strdup(snap);
116
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
66
- s->image_name = g_strdup(image_name);
117
bdrv_parent_drained_begin(bs, parent);
67
+ *s_snap = g_strdup(snap);
118
bdrv_drain_invoke(bs, true, false);
68
+ *s_image_name = g_strdup(image_name);
119
bdrv_drain_recurse(bs);
69
70
/* try default location when conf=NULL, but ignore failure */
71
- r = rados_conf_read_file(s->cluster, conf);
72
+ r = rados_conf_read_file(*cluster, conf);
73
if (conf && r < 0) {
74
error_setg_errno(errp, -r, "error reading conf file %s", conf);
75
goto failed_shutdown;
76
}
77
78
- r = qemu_rbd_set_keypairs(s->cluster, keypairs, errp);
79
+ r = qemu_rbd_set_keypairs(*cluster, keypairs, errp);
80
if (r < 0) {
81
goto failed_shutdown;
82
}
83
84
if (mon_host) {
85
- r = rados_conf_set(s->cluster, "mon_host", mon_host);
86
+ r = rados_conf_set(*cluster, "mon_host", mon_host);
87
if (r < 0) {
88
goto failed_shutdown;
89
}
90
}
91
92
- if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
93
+ if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
94
r = -EIO;
95
goto failed_shutdown;
96
}
97
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
98
* librbd defaults to no caching. If write through caching cannot
99
* be set up, fall back to no caching.
100
*/
101
- if (flags & BDRV_O_NOCACHE) {
102
- rados_conf_set(s->cluster, "rbd_cache", "false");
103
+ if (cache) {
104
+ rados_conf_set(*cluster, "rbd_cache", "true");
105
} else {
106
- rados_conf_set(s->cluster, "rbd_cache", "true");
107
+ rados_conf_set(*cluster, "rbd_cache", "false");
108
}
109
110
- r = rados_connect(s->cluster);
111
+ r = rados_connect(*cluster);
112
if (r < 0) {
113
error_setg_errno(errp, -r, "error connecting");
114
goto failed_shutdown;
115
}
116
117
- r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
118
+ r = rados_ioctx_create(*cluster, pool, io_ctx);
119
if (r < 0) {
120
error_setg_errno(errp, -r, "error opening pool %s", pool);
121
goto failed_shutdown;
122
}
123
124
+ qemu_opts_del(opts);
125
+ return 0;
126
+
120
+
127
+failed_shutdown:
121
+ if (recursive) {
128
+ rados_shutdown(*cluster);
122
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
129
+ g_free(*s_snap);
123
+ bdrv_do_drained_begin(child->bs, true, child);
130
+ g_free(*s_image_name);
124
+ }
131
+failed_opts:
125
+ }
132
+ qemu_opts_del(opts);
126
}
133
+ g_free(mon_host);
127
134
+ return r;
128
void bdrv_drained_begin(BlockDriverState *bs)
129
{
130
- bdrv_do_drained_begin(bs, NULL);
131
+ bdrv_do_drained_begin(bs, false, NULL);
135
+}
132
+}
136
+
133
+
137
+static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
138
+ Error **errp)
139
+{
135
+{
140
+ BDRVRBDState *s = bs->opaque;
136
+ bdrv_do_drained_begin(bs, true, NULL);
141
+ Error *local_err = NULL;
137
}
142
+ const char *filename;
138
143
+ int r;
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
141
+ BdrvChild *parent)
142
{
143
+ BdrvChild *child, *next;
144
int old_quiesce_counter;
145
146
if (qemu_in_coroutine()) {
147
- bdrv_co_yield_to_drain(bs, false, parent);
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
149
return;
150
}
151
assert(bs->quiesce_counter > 0);
152
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
153
if (old_quiesce_counter == 1) {
154
aio_enable_external(bdrv_get_aio_context(bs));
155
}
144
+
156
+
145
+ /* If we are given a filename, parse the filename, with precedence given to
157
+ if (recursive) {
146
+ * filename encoded options */
158
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
147
+ filename = qdict_get_try_str(options, "filename");
159
+ bdrv_do_drained_end(child->bs, true, child);
148
+ if (filename) {
149
+ warn_report("'filename' option specified. "
150
+ "This is an unsupported option, and may be deprecated "
151
+ "in the future");
152
+ qemu_rbd_parse_filename(filename, options, &local_err);
153
+ if (local_err) {
154
+ error_propagate(errp, local_err);
155
+ return -EINVAL;
156
+ }
160
+ }
157
+ }
161
+ }
162
}
163
164
void bdrv_drained_end(BlockDriverState *bs)
165
{
166
- bdrv_do_drained_end(bs, NULL);
167
+ bdrv_do_drained_end(bs, false, NULL);
168
+}
158
+
169
+
159
+ r = qemu_rbd_connect(&s->cluster, &s->io_ctx, &s->snap, &s->image_name,
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
160
+ options, !(flags & BDRV_O_NOCACHE), errp);
171
+{
161
+ if (r < 0) {
172
+ bdrv_do_drained_end(bs, true, NULL);
162
+ return r;
163
+ }
164
+
165
/* rbd_open is always r/w */
166
r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap);
167
if (r < 0) {
168
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
169
}
170
}
171
172
- qemu_opts_del(opts);
173
return 0;
174
175
failed_open:
176
rados_ioctx_destroy(s->io_ctx);
177
-failed_shutdown:
178
- rados_shutdown(s->cluster);
179
g_free(s->snap);
180
g_free(s->image_name);
181
-failed_opts:
182
- qemu_opts_del(opts);
183
- g_free(mon_host);
184
-exit:
185
+ rados_shutdown(s->cluster);
186
return r;
187
}
173
}
188
174
175
/*
189
--
176
--
190
2.13.6
177
2.13.6
191
178
192
179
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to rbd, which enables
1
Add a subtree drain version to the existing test cases.
2
image creation over QMP.
3
2
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
4
---
7
qapi/block-core.json | 19 ++++++-
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
8
block/rbd.c | 150 ++++++++++++++++++++++++++++++++++-----------------
6
1 file changed, 26 insertions(+), 1 deletion(-)
9
2 files changed, 118 insertions(+), 51 deletions(-)
10
7
11
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
12
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
10
--- a/tests/test-bdrv-drain.c
14
+++ b/qapi/block-core.json
11
+++ b/tests/test-bdrv-drain.c
15
@@ -XXX,XX +XXX,XX @@
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
16
'*refcount-bits': 'int' } }
13
enum drain_type {
17
14
BDRV_DRAIN_ALL,
18
##
15
BDRV_DRAIN,
19
+# @BlockdevCreateOptionsRbd:
16
+ BDRV_SUBTREE_DRAIN,
20
+#
17
DRAIN_TYPE_MAX,
21
+# Driver specific image creation options for rbd/Ceph.
22
+#
23
+# @location Where to store the new image file. This location cannot
24
+# point to a snapshot.
25
+# @size Size of the virtual disk in bytes
26
+# @cluster-size RBD object size
27
+#
28
+# Since: 2.12
29
+##
30
+{ 'struct': 'BlockdevCreateOptionsRbd',
31
+ 'data': { 'location': 'BlockdevOptionsRbd',
32
+ 'size': 'size',
33
+ '*cluster-size' : 'size' } }
34
+
35
+##
36
# @BlockdevCreateNotSupported:
37
#
38
# This is used for all drivers that don't support creating images.
39
@@ -XXX,XX +XXX,XX @@
40
'qed': 'BlockdevCreateNotSupported',
41
'quorum': 'BlockdevCreateNotSupported',
42
'raw': 'BlockdevCreateNotSupported',
43
- 'rbd': 'BlockdevCreateNotSupported',
44
+ 'rbd': 'BlockdevCreateOptionsRbd',
45
'replication': 'BlockdevCreateNotSupported',
46
'sheepdog': 'BlockdevCreateNotSupported',
47
'ssh': 'BlockdevCreateNotSupported',
48
diff --git a/block/rbd.c b/block/rbd.c
49
index XXXXXXX..XXXXXXX 100644
50
--- a/block/rbd.c
51
+++ b/block/rbd.c
52
@@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = {
53
},
54
};
18
};
55
19
56
-static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
57
- QemuOpts *opts,
21
switch (drain_type) {
58
- Error **errp)
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
59
+/* FIXME Deprecate and remove keypairs or make it available in QMP.
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
60
+ * password_secret should eventually be configurable in opts->location. Support
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
61
+ * for it in .bdrv_open will make it work here as well. */
25
default: g_assert_not_reached();
62
+static int qemu_rbd_do_create(BlockdevCreateOptions *options,
63
+ const char *keypairs, const char *password_secret,
64
+ Error **errp)
65
{
66
- Error *local_err = NULL;
67
- int64_t bytes = 0;
68
- int64_t objsize;
69
- int obj_order = 0;
70
- const char *pool, *image_name, *conf, *user, *keypairs;
71
- const char *secretid;
72
+ BlockdevCreateOptionsRbd *opts = &options->u.rbd;
73
rados_t cluster;
74
rados_ioctx_t io_ctx;
75
- QDict *options = NULL;
76
- int ret = 0;
77
+ int obj_order = 0;
78
+ int ret;
79
+
80
+ assert(options->driver == BLOCKDEV_DRIVER_RBD);
81
+ if (opts->location->has_snapshot) {
82
+ error_setg(errp, "Can't use snapshot name for image creation");
83
+ return -EINVAL;
84
+ }
85
86
- secretid = qemu_opt_get(opts, "password-secret");
87
+ /* TODO Remove the limitation */
88
+ if (opts->location->has_server) {
89
+ error_setg(errp, "Can't specify server for image creation");
90
+ return -EINVAL;
91
+ }
92
93
- /* Read out options */
94
- bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
95
- BDRV_SECTOR_SIZE);
96
- objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0);
97
- if (objsize) {
98
+ if (opts->has_cluster_size) {
99
+ int64_t objsize = opts->cluster_size;
100
if ((objsize - 1) & objsize) { /* not a power of 2? */
101
error_setg(errp, "obj size needs to be power of 2");
102
- ret = -EINVAL;
103
- goto exit;
104
+ return -EINVAL;
105
}
106
if (objsize < 4096) {
107
error_setg(errp, "obj size too small");
108
- ret = -EINVAL;
109
- goto exit;
110
+ return -EINVAL;
111
}
112
obj_order = ctz32(objsize);
113
}
26
}
114
27
}
115
- options = qdict_new();
28
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
116
- qemu_rbd_parse_filename(filename, options, &local_err);
29
switch (drain_type) {
117
- if (local_err) {
30
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
118
- ret = -EINVAL;
31
case BDRV_DRAIN: bdrv_drained_end(bs); break;
119
- error_propagate(errp, local_err);
32
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
120
- goto exit;
33
default: g_assert_not_reached();
121
- }
122
-
123
- /*
124
- * Caution: while qdict_get_try_str() is fine, getting non-string
125
- * types would require more care. When @options come from -blockdev
126
- * or blockdev_add, its members are typed according to the QAPI
127
- * schema, but when they come from -drive, they're all QString.
128
- */
129
- pool = qdict_get_try_str(options, "pool");
130
- conf = qdict_get_try_str(options, "conf");
131
- user = qdict_get_try_str(options, "user");
132
- image_name = qdict_get_try_str(options, "image");
133
- keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
134
-
135
- ret = rados_create(&cluster, user);
136
+ ret = rados_create(&cluster, opts->location->user);
137
if (ret < 0) {
138
error_setg_errno(errp, -ret, "error initializing");
139
- goto exit;
140
+ return ret;
141
}
34
}
142
35
}
143
/* try default location when conf=NULL, but ignore failure */
36
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
144
- ret = rados_conf_read_file(cluster, conf);
37
test_drv_cb_common(BDRV_DRAIN, false);
145
- if (conf && ret < 0) {
38
}
146
- error_setg_errno(errp, -ret, "error reading conf file %s", conf);
39
147
+ ret = rados_conf_read_file(cluster, opts->location->conf);
40
+static void test_drv_cb_drain_subtree(void)
148
+ if (opts->location->conf && ret < 0) {
41
+{
149
+ error_setg_errno(errp, -ret, "error reading conf file %s",
42
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
150
+ opts->location->conf);
151
ret = -EIO;
152
goto shutdown;
153
}
154
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
155
goto shutdown;
156
}
157
158
- if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
159
+ if (qemu_rbd_set_auth(cluster, password_secret, errp) < 0) {
160
ret = -EIO;
161
goto shutdown;
162
}
163
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
164
goto shutdown;
165
}
166
167
- ret = rados_ioctx_create(cluster, pool, &io_ctx);
168
+ ret = rados_ioctx_create(cluster, opts->location->pool, &io_ctx);
169
if (ret < 0) {
170
- error_setg_errno(errp, -ret, "error opening pool %s", pool);
171
+ error_setg_errno(errp, -ret, "error opening pool %s",
172
+ opts->location->pool);
173
goto shutdown;
174
}
175
176
- ret = rbd_create(io_ctx, image_name, bytes, &obj_order);
177
+ ret = rbd_create(io_ctx, opts->location->image, opts->size, &obj_order);
178
if (ret < 0) {
179
error_setg_errno(errp, -ret, "error rbd create");
180
}
181
182
rados_ioctx_destroy(io_ctx);
183
184
+ ret = 0;
185
shutdown:
186
rados_shutdown(cluster);
187
+ return ret;
188
+}
43
+}
189
+
44
+
190
+static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp)
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
46
{
47
BlockBackend *blk;
48
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
49
test_quiesce_common(BDRV_DRAIN, false);
50
}
51
52
+static void test_quiesce_drain_subtree(void)
191
+{
53
+{
192
+ return qemu_rbd_do_create(options, NULL, NULL, errp);
54
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
193
+}
55
+}
194
+
56
+
195
+static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
57
static void test_nested(void)
196
+ QemuOpts *opts,
58
{
197
+ Error **errp)
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)
198
+{
75
+{
199
+ BlockdevCreateOptions *create_options;
76
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
200
+ BlockdevCreateOptionsRbd *rbd_opts;
77
+}
201
+ BlockdevOptionsRbd *loc;
202
+ Error *local_err = NULL;
203
+ const char *keypairs, *password_secret;
204
+ QDict *options = NULL;
205
+ int ret = 0;
206
+
78
+
207
+ create_options = g_new0(BlockdevCreateOptions, 1);
79
int main(int argc, char **argv)
208
+ create_options->driver = BLOCKDEV_DRIVER_RBD;
80
{
209
+ rbd_opts = &create_options->u.rbd;
81
bdrv_init();
210
+
82
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
211
+ rbd_opts->location = g_new0(BlockdevOptionsRbd, 1);
83
212
+
84
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
213
+ password_secret = qemu_opt_get(opts, "password-secret");
85
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
214
+
86
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
215
+ /* Read out options */
87
+ test_drv_cb_drain_subtree);
216
+ rbd_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
88
217
+ BDRV_SECTOR_SIZE);
89
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
218
+ rbd_opts->cluster_size = qemu_opt_get_size_del(opts,
90
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
219
+ BLOCK_OPT_CLUSTER_SIZE, 0);
91
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
220
+ rbd_opts->has_cluster_size = (rbd_opts->cluster_size != 0);
92
+ test_quiesce_drain_subtree);
221
+
93
222
+ options = qdict_new();
94
g_test_add_func("/bdrv-drain/nested", test_nested);
223
+ qemu_rbd_parse_filename(filename, options, &local_err);
95
224
+ if (local_err) {
96
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
225
+ ret = -EINVAL;
97
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
226
+ error_propagate(errp, local_err);
98
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
227
+ goto exit;
99
+ test_blockjob_drain_subtree);
228
+ }
100
229
+
101
return g_test_run();
230
+ /*
231
+ * Caution: while qdict_get_try_str() is fine, getting non-string
232
+ * types would require more care. When @options come from -blockdev
233
+ * or blockdev_add, its members are typed according to the QAPI
234
+ * schema, but when they come from -drive, they're all QString.
235
+ */
236
+ loc = rbd_opts->location;
237
+ loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
238
+ loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
239
+ loc->has_conf = !!loc->conf;
240
+ loc->user = g_strdup(qdict_get_try_str(options, "user"));
241
+ loc->has_user = !!loc->user;
242
+ loc->image = g_strdup(qdict_get_try_str(options, "image"));
243
+ keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
244
+
245
+ ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp);
246
+ if (ret < 0) {
247
+ goto exit;
248
+ }
249
250
exit:
251
QDECREF(options);
252
+ qapi_free_BlockdevCreateOptions(create_options);
253
return ret;
254
}
102
}
255
256
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
257
.bdrv_file_open = qemu_rbd_open,
258
.bdrv_close = qemu_rbd_close,
259
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
260
+ .bdrv_co_create = qemu_rbd_co_create,
261
.bdrv_co_create_opts = qemu_rbd_co_create_opts,
262
.bdrv_has_zero_init = bdrv_has_zero_init_1,
263
.bdrv_get_info = qemu_rbd_getinfo,
264
--
103
--
265
2.13.6
104
2.13.6
266
105
267
106
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
If bdrv_do_drained_begin/end() are called in coroutine context, they
2
first use a BH to get out of the coroutine context. Call some existing
3
tests again from a coroutine to cover this code path.
2
4
3
QED's bdrv_invalidate_cache implementation would like to reuse functions
4
that acquire/release the metadata locks. Call it from coroutine context
5
to simplify the logic.
6
7
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
8
Message-Id: <1516279431-30424-6-git-send-email-pbonzini@redhat.com>
9
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
6
---
12
include/block/block_int.h | 3 ++-
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
13
block.c | 41 +++++++++++++++++++++++++++++++++++++----
8
1 file changed, 59 insertions(+)
14
block/iscsi.c | 6 +++---
15
block/nfs.c | 6 +++---
16
block/qcow2.c | 7 +++++--
17
block/qed.c | 13 +++++--------
18
block/rbd.c | 6 +++---
19
7 files changed, 58 insertions(+), 24 deletions(-)
20
9
21
diff --git a/include/block/block_int.h b/include/block/block_int.h
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
22
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block_int.h
12
--- a/tests/test-bdrv-drain.c
24
+++ b/include/block/block_int.h
13
+++ b/tests/test-bdrv-drain.c
25
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
26
/*
15
*aio_ret = ret;
27
* Invalidate any cached meta-data.
28
*/
29
- void (*bdrv_invalidate_cache)(BlockDriverState *bs, Error **errp);
30
+ void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs,
31
+ Error **errp);
32
int (*bdrv_inactivate)(BlockDriverState *bs);
33
34
/*
35
diff --git a/block.c b/block.c
36
index XXXXXXX..XXXXXXX 100644
37
--- a/block.c
38
+++ b/block.c
39
@@ -XXX,XX +XXX,XX @@ void bdrv_init_with_whitelist(void)
40
bdrv_init();
41
}
16
}
42
17
43
-void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
18
+typedef struct CallInCoroutineData {
44
+static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
19
+ void (*entry)(void);
45
+ Error **errp)
46
{
47
BdrvChild *child, *parent;
48
uint64_t perm, shared_perm;
49
@@ -XXX,XX +XXX,XX @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
50
}
51
52
QLIST_FOREACH(child, &bs->children, next) {
53
- bdrv_invalidate_cache(child->bs, &local_err);
54
+ bdrv_co_invalidate_cache(child->bs, &local_err);
55
if (local_err) {
56
error_propagate(errp, local_err);
57
return;
58
@@ -XXX,XX +XXX,XX @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
59
}
60
bdrv_set_perm(bs, perm, shared_perm);
61
62
- if (bs->drv->bdrv_invalidate_cache) {
63
- bs->drv->bdrv_invalidate_cache(bs, &local_err);
64
+ if (bs->drv->bdrv_co_invalidate_cache) {
65
+ bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
66
if (local_err) {
67
bs->open_flags |= BDRV_O_INACTIVE;
68
error_propagate(errp, local_err);
69
@@ -XXX,XX +XXX,XX @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
70
}
71
}
72
73
+typedef struct InvalidateCacheCo {
74
+ BlockDriverState *bs;
75
+ Error **errp;
76
+ bool done;
20
+ bool done;
77
+} InvalidateCacheCo;
21
+} CallInCoroutineData;
78
+
22
+
79
+static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
80
+{
24
+{
81
+ InvalidateCacheCo *ico = opaque;
25
+ CallInCoroutineData *data = opaque;
82
+ bdrv_co_invalidate_cache(ico->bs, ico->errp);
26
+
83
+ ico->done = true;
27
+ data->entry();
28
+ data->done = true;
84
+}
29
+}
85
+
30
+
86
+void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
31
+static void call_in_coroutine(void (*entry)(void))
87
+{
32
+{
88
+ Coroutine *co;
33
+ Coroutine *co;
89
+ InvalidateCacheCo ico = {
34
+ CallInCoroutineData data = {
90
+ .bs = bs,
35
+ .entry = entry,
91
+ .done = false,
36
+ .done = false,
92
+ .errp = errp
93
+ };
37
+ };
94
+
38
+
95
+ if (qemu_in_coroutine()) {
39
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
96
+ /* Fast-path if already in coroutine context */
40
+ qemu_coroutine_enter(co);
97
+ bdrv_invalidate_cache_co_entry(&ico);
41
+ while (!data.done) {
98
+ } else {
42
+ aio_poll(qemu_get_aio_context(), true);
99
+ co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico);
100
+ qemu_coroutine_enter(co);
101
+ BDRV_POLL_WHILE(bs, !ico.done);
102
+ }
43
+ }
103
+}
44
+}
104
+
45
+
105
void bdrv_invalidate_cache_all(Error **errp)
46
enum drain_type {
47
BDRV_DRAIN_ALL,
48
BDRV_DRAIN,
49
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_subtree(void)
50
test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
51
}
52
53
+static void test_drv_cb_co_drain(void)
54
+{
55
+ call_in_coroutine(test_drv_cb_drain);
56
+}
57
+
58
+static void test_drv_cb_co_drain_subtree(void)
59
+{
60
+ call_in_coroutine(test_drv_cb_drain_subtree);
61
+}
62
+
63
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
106
{
64
{
107
BlockDriverState *bs;
65
BlockBackend *blk;
108
diff --git a/block/iscsi.c b/block/iscsi.c
66
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void)
109
index XXXXXXX..XXXXXXX 100644
67
test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
110
--- a/block/iscsi.c
111
+++ b/block/iscsi.c
112
@@ -XXX,XX +XXX,XX @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
113
return 0;
114
}
68
}
115
69
116
-static void iscsi_invalidate_cache(BlockDriverState *bs,
70
+static void test_quiesce_co_drain(void)
117
- Error **errp)
71
+{
118
+static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
72
+ call_in_coroutine(test_quiesce_drain);
119
+ Error **errp)
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)
120
{
81
{
121
IscsiLun *iscsilun = bs->opaque;
82
BlockBackend *blk;
122
iscsi_allocmap_invalidate(iscsilun);
83
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
123
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_iscsi = {
84
g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
124
.create_opts = &iscsi_create_opts,
85
test_drv_cb_drain_subtree);
125
.bdrv_reopen_prepare = iscsi_reopen_prepare,
86
126
.bdrv_reopen_commit = iscsi_reopen_commit,
87
+ // XXX bdrv_drain_all() doesn't work in coroutine context
127
- .bdrv_invalidate_cache = iscsi_invalidate_cache,
88
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
128
+ .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
89
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
129
90
+ test_drv_cb_co_drain_subtree);
130
.bdrv_getlength = iscsi_getlength,
91
+
131
.bdrv_get_info = iscsi_get_info,
92
+
132
diff --git a/block/nfs.c b/block/nfs.c
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
133
index XXXXXXX..XXXXXXX 100644
94
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
134
--- a/block/nfs.c
95
g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
135
+++ b/block/nfs.c
96
test_quiesce_drain_subtree);
136
@@ -XXX,XX +XXX,XX @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
97
137
}
98
+ // XXX bdrv_drain_all() doesn't work in coroutine context
138
99
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
139
#ifdef LIBNFS_FEATURE_PAGECACHE
100
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
140
-static void nfs_invalidate_cache(BlockDriverState *bs,
101
+ test_quiesce_co_drain_subtree);
141
- Error **errp)
102
+
142
+static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
103
g_test_add_func("/bdrv-drain/nested", test_nested);
143
+ Error **errp)
104
144
{
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
145
NFSClient *client = bs->opaque;
146
nfs_pagecache_invalidate(client->context, client->fh);
147
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nfs = {
148
.bdrv_refresh_filename = nfs_refresh_filename,
149
150
#ifdef LIBNFS_FEATURE_PAGECACHE
151
- .bdrv_invalidate_cache = nfs_invalidate_cache,
152
+ .bdrv_co_invalidate_cache = nfs_co_invalidate_cache,
153
#endif
154
};
155
156
diff --git a/block/qcow2.c b/block/qcow2.c
157
index XXXXXXX..XXXXXXX 100644
158
--- a/block/qcow2.c
159
+++ b/block/qcow2.c
160
@@ -XXX,XX +XXX,XX @@ static void qcow2_close(BlockDriverState *bs)
161
qcow2_free_snapshots(bs);
162
}
163
164
-static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
165
+static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
166
+ Error **errp)
167
{
168
BDRVQcow2State *s = bs->opaque;
169
int flags = s->flags;
170
@@ -XXX,XX +XXX,XX @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
171
options = qdict_clone_shallow(bs->options);
172
173
flags &= ~BDRV_O_INACTIVE;
174
+ qemu_co_mutex_lock(&s->lock);
175
ret = qcow2_do_open(bs, options, flags, &local_err);
176
+ qemu_co_mutex_unlock(&s->lock);
177
QDECREF(options);
178
if (local_err) {
179
error_propagate(errp, local_err);
180
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
181
.bdrv_change_backing_file = qcow2_change_backing_file,
182
183
.bdrv_refresh_limits = qcow2_refresh_limits,
184
- .bdrv_invalidate_cache = qcow2_invalidate_cache,
185
+ .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache,
186
.bdrv_inactivate = qcow2_inactivate,
187
188
.create_opts = &qcow2_create_opts,
189
diff --git a/block/qed.c b/block/qed.c
190
index XXXXXXX..XXXXXXX 100644
191
--- a/block/qed.c
192
+++ b/block/qed.c
193
@@ -XXX,XX +XXX,XX @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
194
return ret;
195
}
196
197
-static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
198
+static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
199
+ Error **errp)
200
{
201
BDRVQEDState *s = bs->opaque;
202
Error *local_err = NULL;
203
@@ -XXX,XX +XXX,XX @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp)
204
bdrv_qed_close(bs);
205
206
bdrv_qed_init_state(bs);
207
- if (qemu_in_coroutine()) {
208
- qemu_co_mutex_lock(&s->table_lock);
209
- }
210
+ qemu_co_mutex_lock(&s->table_lock);
211
ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
212
- if (qemu_in_coroutine()) {
213
- qemu_co_mutex_unlock(&s->table_lock);
214
- }
215
+ qemu_co_mutex_unlock(&s->table_lock);
216
if (local_err) {
217
error_propagate(errp, local_err);
218
error_prepend(errp, "Could not reopen qed layer: ");
219
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = {
220
.bdrv_get_info = bdrv_qed_get_info,
221
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
222
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
223
- .bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
224
+ .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache,
225
.bdrv_check = bdrv_qed_check,
226
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
227
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
228
diff --git a/block/rbd.c b/block/rbd.c
229
index XXXXXXX..XXXXXXX 100644
230
--- a/block/rbd.c
231
+++ b/block/rbd.c
232
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs,
233
#endif
234
235
#ifdef LIBRBD_SUPPORTS_INVALIDATE
236
-static void qemu_rbd_invalidate_cache(BlockDriverState *bs,
237
- Error **errp)
238
+static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs,
239
+ Error **errp)
240
{
241
BDRVRBDState *s = bs->opaque;
242
int r = rbd_invalidate_cache(s->image);
243
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
244
.bdrv_snapshot_list = qemu_rbd_snap_list,
245
.bdrv_snapshot_goto = qemu_rbd_snap_rollback,
246
#ifdef LIBRBD_SUPPORTS_INVALIDATE
247
- .bdrv_invalidate_cache = qemu_rbd_invalidate_cache,
248
+ .bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache,
249
#endif
250
};
251
252
--
106
--
253
2.13.6
107
2.13.6
254
108
255
109
diff view generated by jsdifflib
1
Instead of passing the encryption format name and the QemuOpts down, use
1
Test that drain sections are correctly propagated through the graph.
2
the QCryptoBlockCreateOptions contained in BlockdevCreateOptions.
3
2
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
4
---
8
block/qcow2.c | 62 +++++++++++++++++++++++++++++++++++++++++++----------------
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
9
1 file changed, 45 insertions(+), 17 deletions(-)
6
1 file changed, 74 insertions(+)
10
7
11
diff --git a/block/qcow2.c b/block/qcow2.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
12
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
13
--- a/block/qcow2.c
10
--- a/tests/test-bdrv-drain.c
14
+++ b/block/qcow2.c
11
+++ b/tests/test-bdrv-drain.c
15
@@ -XXX,XX +XXX,XX @@ static int qcow2_crypt_method_from_format(const char *encryptfmt)
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
16
}
13
blk_unref(blk);
17
}
14
}
18
15
19
-static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
16
+static void test_multiparent(void)
20
- QemuOpts *opts, Error **errp)
17
+{
21
+static QCryptoBlockCreateOptions *
18
+ BlockBackend *blk_a, *blk_b;
22
+qcow2_parse_encryption(const char *encryptfmt, QemuOpts *opts, Error **errp)
19
+ BlockDriverState *bs_a, *bs_b, *backing;
23
{
20
+ BDRVTestState *a_s, *b_s, *backing_s;
24
- BDRVQcow2State *s = bs->opaque;
25
QCryptoBlockCreateOptions *cryptoopts = NULL;
26
- QCryptoBlock *crypto = NULL;
27
- int ret = -EINVAL;
28
QDict *options, *encryptopts;
29
int fmt;
30
31
@@ -XXX,XX +XXX,XX @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
32
error_setg(errp, "Unknown encryption format '%s'", encryptfmt);
33
break;
34
}
35
- if (!cryptoopts) {
36
- ret = -EINVAL;
37
- goto out;
38
+
21
+
39
+ QDECREF(encryptopts);
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
40
+ return cryptoopts;
23
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
24
+ &error_abort);
25
+ a_s = bs_a->opaque;
26
+ blk_insert_bs(blk_a, bs_a, &error_abort);
27
+
28
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
29
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
30
+ &error_abort);
31
+ b_s = bs_b->opaque;
32
+ blk_insert_bs(blk_b, bs_b, &error_abort);
33
+
34
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
35
+ backing_s = backing->opaque;
36
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
37
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
38
+
39
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
40
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
41
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(a_s->drain_count, ==, 0);
43
+ g_assert_cmpint(b_s->drain_count, ==, 0);
44
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
45
+
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
47
+
48
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
49
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
50
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
51
+ g_assert_cmpint(a_s->drain_count, ==, 1);
52
+ g_assert_cmpint(b_s->drain_count, ==, 1);
53
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
54
+
55
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
56
+
57
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
58
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
59
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(a_s->drain_count, ==, 2);
61
+ g_assert_cmpint(b_s->drain_count, ==, 2);
62
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
63
+
64
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
65
+
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
69
+ g_assert_cmpint(a_s->drain_count, ==, 1);
70
+ g_assert_cmpint(b_s->drain_count, ==, 1);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
74
+
75
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
76
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
77
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
78
+ g_assert_cmpint(a_s->drain_count, ==, 0);
79
+ g_assert_cmpint(b_s->drain_count, ==, 0);
80
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
81
+
82
+ bdrv_unref(backing);
83
+ bdrv_unref(bs_a);
84
+ bdrv_unref(bs_b);
85
+ blk_unref(blk_a);
86
+ blk_unref(blk_b);
41
+}
87
+}
42
+
88
+
43
+static int qcow2_set_up_encryption(BlockDriverState *bs,
89
44
+ QCryptoBlockCreateOptions *cryptoopts,
90
typedef struct TestBlockJob {
45
+ Error **errp)
91
BlockJob common;
46
+{
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
47
+ BDRVQcow2State *s = bs->opaque;
93
test_quiesce_co_drain_subtree);
48
+ QCryptoBlock *crypto = NULL;
94
49
+ int fmt, ret;
95
g_test_add_func("/bdrv-drain/nested", test_nested);
50
+
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
51
+ switch (cryptoopts->format) {
97
52
+ case Q_CRYPTO_BLOCK_FORMAT_LUKS:
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
53
+ fmt = QCOW_CRYPT_LUKS;
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
54
+ break;
55
+ case Q_CRYPTO_BLOCK_FORMAT_QCOW:
56
+ fmt = QCOW_CRYPT_AES;
57
+ break;
58
+ default:
59
+ error_setg(errp, "Crypto format not supported in qcow2");
60
+ return -EINVAL;
61
}
62
+
63
s->crypt_method_header = fmt;
64
65
crypto = qcrypto_block_create(cryptoopts, "encrypt.",
66
@@ -XXX,XX +XXX,XX @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
67
qcow2_crypto_hdr_write_func,
68
bs, errp);
69
if (!crypto) {
70
- ret = -EINVAL;
71
- goto out;
72
+ return -EINVAL;
73
}
74
75
ret = qcow2_update_header(bs);
76
@@ -XXX,XX +XXX,XX @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt,
77
goto out;
78
}
79
80
+ ret = 0;
81
out:
82
- QDECREF(encryptopts);
83
qcrypto_block_free(crypto);
84
- qapi_free_QCryptoBlockCreateOptions(cryptoopts);
85
return ret;
86
}
87
88
@@ -XXX,XX +XXX,XX @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
89
}
90
91
static int coroutine_fn
92
-qcow2_co_create(BlockdevCreateOptions *create_options, QemuOpts *opts,
93
- const char *encryptfmt, Error **errp)
94
+qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
95
{
96
BlockdevCreateOptionsQcow2 *qcow2_opts;
97
QDict *options;
98
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, QemuOpts *opts,
99
}
100
101
/* Want encryption? There you go. */
102
- if (encryptfmt) {
103
- ret = qcow2_set_up_encryption(blk_bs(blk), encryptfmt, opts, errp);
104
+ if (qcow2_opts->has_encrypt) {
105
+ ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp);
106
if (ret < 0) {
107
goto out;
108
}
109
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
110
int version;
111
uint64_t refcount_bits;
112
char *encryptfmt = NULL;
113
+ QCryptoBlockCreateOptions *cryptoopts = NULL;
114
BlockDriverState *bs = NULL;
115
Error *local_err = NULL;
116
int ret;
117
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
118
ret = -EINVAL;
119
goto finish;
120
}
121
+
122
encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
123
if (encryptfmt) {
124
if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
125
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
126
} else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
127
encryptfmt = g_strdup("aes");
128
}
129
+ if (encryptfmt) {
130
+ cryptoopts = qcow2_parse_encryption(encryptfmt, opts, errp);
131
+ if (cryptoopts == NULL) {
132
+ ret = -EINVAL;
133
+ goto finish;
134
+ }
135
+ }
136
+
137
cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
138
if (local_err) {
139
error_propagate(errp, local_err);
140
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
141
.backing_file = backing_file,
142
.has_backing_fmt = (backing_fmt != NULL),
143
.backing_fmt = backing_drv,
144
+ .has_encrypt = (encryptfmt != NULL),
145
+ .encrypt = cryptoopts,
146
.has_cluster_size = true,
147
.cluster_size = cluster_size,
148
.has_preallocation = true,
149
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
150
.refcount_bits = refcount_bits,
151
},
152
};
153
- ret = qcow2_co_create(&create_options, opts, encryptfmt, errp);
154
+ ret = qcow2_co_create(&create_options, errp);
155
if (ret < 0) {
156
goto finish;
157
}
158
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
159
finish:
160
bdrv_unref(bs);
161
162
+ qapi_free_QCryptoBlockCreateOptions(cryptoopts);
163
g_free(backing_file);
164
g_free(backing_fmt);
165
g_free(encryptfmt);
166
--
100
--
167
2.13.6
101
2.13.6
168
102
169
103
diff view generated by jsdifflib
1
We'll use a separate source file for image creation, and we need to
1
We need to remember how many of the drain sections in which a node is
2
check there whether the requested driver is whitelisted.
2
were recursive (i.e. subtree drain rather than node drain), so that they
3
can be correctly applied when children are added or removed during the
4
drained section.
5
6
With this change, it is safe to modify the graph even inside a
7
bdrv_subtree_drained_begin/end() section.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
---
10
---
8
include/block/block.h | 1 +
11
include/block/block.h | 2 --
9
block.c | 2 +-
12
include/block/block_int.h | 5 +++++
10
2 files changed, 2 insertions(+), 1 deletion(-)
13
block.c | 32 +++++++++++++++++++++++++++++---
14
block/io.c | 28 ++++++++++++++++++++++++----
15
4 files changed, 58 insertions(+), 9 deletions(-)
11
16
12
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
13
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
14
--- a/include/block/block.h
19
--- a/include/block/block.h
15
+++ b/include/block/block.h
20
+++ b/include/block/block.h
16
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm);
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
17
void bdrv_init(void);
22
/**
18
void bdrv_init_with_whitelist(void);
23
* Like bdrv_drained_begin, but recursively begins a quiesced section for
19
bool bdrv_uses_whitelist(void);
24
* exclusive access to all child nodes as well.
20
+int bdrv_is_whitelisted(BlockDriver *drv, bool read_only);
25
- *
21
BlockDriver *bdrv_find_protocol(const char *filename,
26
- * Graph changes are not allowed during a subtree drain section.
22
bool allow_protocol_prefix,
27
*/
23
Error **errp);
28
void bdrv_subtree_drained_begin(BlockDriverState *bs);
29
30
diff --git a/include/block/block_int.h b/include/block/block_int.h
31
index XXXXXXX..XXXXXXX 100644
32
--- a/include/block/block_int.h
33
+++ b/include/block/block_int.h
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
35
36
/* Accessed with atomic ops. */
37
int quiesce_counter;
38
+ int recursive_quiesce_counter;
39
+
40
unsigned int write_gen; /* Current data generation */
41
42
/* Protected by reqs_lock. */
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
44
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
45
BdrvRequestFlags flags);
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);
24
diff --git a/block.c b/block.c
53
diff --git a/block.c b/block.c
25
index XXXXXXX..XXXXXXX 100644
54
index XXXXXXX..XXXXXXX 100644
26
--- a/block.c
55
--- a/block.c
27
+++ b/block.c
56
+++ b/block.c
28
@@ -XXX,XX +XXX,XX @@ BlockDriver *bdrv_find_format(const char *format_name)
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
29
return bdrv_do_find_format(format_name);
58
bdrv_drained_end(bs);
30
}
59
}
31
60
32
-static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
61
+static void bdrv_child_cb_attach(BdrvChild *child)
33
+int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
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)
34
{
74
{
35
static const char *whitelist_rw[] = {
75
BlockDriverState *bs = child->opaque;
36
CONFIG_BDRV_RW_WHITELIST
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
/*
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
114
}
115
if (old_bs) {
116
+ /* Detach first so that the recursive drain sections coming from @child
117
+ * are already gone and we only end the drain sections that came from
118
+ * elsewhere. */
119
+ if (child->role->detach) {
120
+ child->role->detach(child);
121
+ }
122
if (old_bs->quiesce_counter && child->role->drained_end) {
123
for (i = 0; i < old_bs->quiesce_counter; i++) {
124
child->role->drained_end(child);
125
}
126
}
127
- if (child->role->detach) {
128
- child->role->detach(child);
129
- }
130
QLIST_REMOVE(child, next_parent);
131
}
132
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
134
}
135
}
136
137
+ /* Attach only after starting new drained sections, so that recursive
138
+ * drain sections coming from @child don't get an extra .drained_begin
139
+ * callback. */
140
if (child->role->attach) {
141
child->role->attach(child);
142
}
143
diff --git a/block/io.c b/block/io.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/io.c
146
+++ b/block/io.c
147
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
148
assert(data.done);
149
}
150
151
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
152
- BdrvChild *parent)
153
+void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
154
+ BdrvChild *parent)
155
{
156
BdrvChild *child, *next;
157
158
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
159
bdrv_drain_recurse(bs);
160
161
if (recursive) {
162
+ bs->recursive_quiesce_counter++;
163
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
164
bdrv_do_drained_begin(child->bs, true, child);
165
}
166
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
167
bdrv_do_drained_begin(bs, true, NULL);
168
}
169
170
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
171
- BdrvChild *parent)
172
+void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
173
+ BdrvChild *parent)
174
{
175
BdrvChild *child, *next;
176
int old_quiesce_counter;
177
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
178
}
179
180
if (recursive) {
181
+ bs->recursive_quiesce_counter--;
182
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
183
bdrv_do_drained_end(child->bs, true, child);
184
}
185
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs)
186
bdrv_do_drained_end(bs, true, NULL);
187
}
188
189
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
190
+{
191
+ int i;
192
+
193
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
194
+ bdrv_do_drained_begin(child->bs, true, child);
195
+ }
196
+}
197
+
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
199
+{
200
+ int i;
201
+
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
203
+ bdrv_do_drained_end(child->bs, true, child);
204
+ }
205
+}
206
+
207
/*
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
209
* and suspend block driver's internal I/O until next request arrives.
37
--
210
--
38
2.13.6
211
2.13.6
39
212
40
213
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
3
It is called from qed_invalidate_cache in coroutine context (incoming
4
migration runs in a coroutine), so it's cleaner if metadata is always
5
loaded from a coroutine.
6
7
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
8
Message-Id: <1516279431-30424-5-git-send-email-pbonzini@redhat.com>
9
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
2
---
12
block/qed.c | 40 +++++++++++++++++++++++++++++++++++++---
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
13
1 file changed, 37 insertions(+), 3 deletions(-)
4
1 file changed, 80 insertions(+)
14
5
15
diff --git a/block/qed.c b/block/qed.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
16
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
17
--- a/block/qed.c
8
--- a/tests/test-bdrv-drain.c
18
+++ b/block/qed.c
9
+++ b/tests/test-bdrv-drain.c
19
@@ -XXX,XX +XXX,XX @@ static void bdrv_qed_init_state(BlockDriverState *bs)
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
20
qemu_co_queue_init(&s->allocating_write_reqs);
11
blk_unref(blk_b);
21
}
12
}
22
13
23
-static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags,
14
+static void test_graph_change(void)
24
- Error **errp)
15
+{
25
+/* Called with table_lock held. */
16
+ BlockBackend *blk_a, *blk_b;
26
+static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
17
+ BlockDriverState *bs_a, *bs_b, *backing;
27
+ int flags, Error **errp)
18
+ BDRVTestState *a_s, *b_s, *backing_s;
28
{
29
BDRVQEDState *s = bs->opaque;
30
QEDHeader le_header;
31
@@ -XXX,XX +XXX,XX @@ out:
32
return ret;
33
}
34
35
+typedef struct QEDOpenCo {
36
+ BlockDriverState *bs;
37
+ QDict *options;
38
+ int flags;
39
+ Error **errp;
40
+ int ret;
41
+} QEDOpenCo;
42
+
19
+
43
+static void coroutine_fn bdrv_qed_open_entry(void *opaque)
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
44
+{
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
45
+ QEDOpenCo *qoc = opaque;
22
+ &error_abort);
46
+ BDRVQEDState *s = qoc->bs->opaque;
23
+ a_s = bs_a->opaque;
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
47
+
25
+
48
+ qemu_co_mutex_lock(&s->table_lock);
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
49
+ qoc->ret = bdrv_qed_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
50
+ qemu_co_mutex_unlock(&s->table_lock);
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);
51
+}
91
+}
52
+
92
+
53
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
93
54
Error **errp)
94
typedef struct TestBlockJob {
55
{
95
BlockJob common;
56
+ QEDOpenCo qoc = {
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
57
+ .bs = bs,
97
58
+ .options = options,
98
g_test_add_func("/bdrv-drain/nested", test_nested);
59
+ .flags = flags,
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
60
+ .errp = errp,
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
61
+ .ret = -EINPROGRESS
101
62
+ };
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
63
+
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
64
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
65
false, errp);
66
if (!bs->file) {
67
@@ -XXX,XX +XXX,XX @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
68
}
69
70
bdrv_qed_init_state(bs);
71
- return bdrv_qed_do_open(bs, options, flags, errp);
72
+ if (qemu_in_coroutine()) {
73
+ bdrv_qed_open_entry(&qoc);
74
+ } else {
75
+ qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc));
76
+ BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
77
+ }
78
+ BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
79
+ return qoc.ret;
80
}
81
82
static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp)
83
--
104
--
84
2.13.6
105
2.13.6
85
106
86
107
diff view generated by jsdifflib
Deleted patch
1
From: Alberto Garcia <berto@igalia.com>
2
1
3
This function checks that the size of a snapshot's L1 table is not too
4
large, but it doesn't validate the offset.
5
6
We now have a function to take care of this, so let's use it.
7
8
Signed-off-by: Alberto Garcia <berto@igalia.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/qcow2-snapshot.c | 8 +++++---
13
tests/qemu-iotests/080 | 10 +++++++++-
14
tests/qemu-iotests/080.out | 8 +++++++-
15
3 files changed, 21 insertions(+), 5 deletions(-)
16
17
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block/qcow2-snapshot.c
20
+++ b/block/qcow2-snapshot.c
21
@@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
22
sn = &s->snapshots[snapshot_index];
23
24
/* Allocate and read in the snapshot's L1 table */
25
- if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
26
- error_setg(errp, "Snapshot L1 table too large");
27
- return -EFBIG;
28
+ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size,
29
+ sizeof(uint64_t), QCOW_MAX_L1_SIZE,
30
+ "Snapshot L1 table", errp);
31
+ if (ret < 0) {
32
+ return ret;
33
}
34
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
35
new_l1_table = qemu_try_blockalign(bs->file->bs,
36
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
37
index XXXXXXX..XXXXXXX 100755
38
--- a/tests/qemu-iotests/080
39
+++ b/tests/qemu-iotests/080
40
@@ -XXX,XX +XXX,XX @@ poke_file "$TEST_IMG" "$offset_l2_table_0" "\x80\x00\x00\xff\xff\xff\x00\x00"
41
{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
42
43
echo
44
-echo "== Invalid snapshot L1 table =="
45
+echo "== Invalid snapshot L1 table offset =="
46
+_make_test_img 64M
47
+{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
48
+{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
49
+poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00"
50
+{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
51
+
52
+echo
53
+echo "== Invalid snapshot L1 table size =="
54
_make_test_img 64M
55
{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
56
{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
57
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
58
index XXXXXXX..XXXXXXX 100644
59
--- a/tests/qemu-iotests/080.out
60
+++ b/tests/qemu-iotests/080.out
61
@@ -XXX,XX +XXX,XX @@ wrote 512/512 bytes at offset 0
62
qemu-img: Could not create snapshot 'test': -27 (File too large)
63
qemu-img: Could not create snapshot 'test': -11 (Resource temporarily unavailable)
64
65
-== Invalid snapshot L1 table ==
66
+== Invalid snapshot L1 table offset ==
67
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
68
+wrote 512/512 bytes at offset 0
69
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
70
+qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid
71
+
72
+== Invalid snapshot L1 table size ==
73
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
74
wrote 512/512 bytes at offset 0
75
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
76
--
77
2.13.6
78
79
diff view generated by jsdifflib
Deleted patch
1
From: Alberto Garcia <berto@igalia.com>
2
1
3
This function iterates over all snapshots of a qcow2 file in order to
4
expand all zero clusters, but it does not validate the snapshots' L1
5
tables first.
6
7
We now have a function to take care of this, so let's use it.
8
9
We can also take the opportunity to replace the sector-based
10
bdrv_read() with bdrv_pread().
11
12
Cc: Eric Blake <eblake@redhat.com>
13
Signed-off-by: Alberto Garcia <berto@igalia.com>
14
Reviewed-by: Eric Blake <eblake@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
17
block/qcow2-cluster.c | 24 +++++++++++++++++-------
18
tests/qemu-iotests/080 | 2 ++
19
tests/qemu-iotests/080.out | 4 ++++
20
3 files changed, 23 insertions(+), 7 deletions(-)
21
22
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/block/qcow2-cluster.c
25
+++ b/block/qcow2-cluster.c
26
@@ -XXX,XX +XXX,XX @@
27
#include "qemu/osdep.h"
28
#include <zlib.h>
29
30
+#include "qapi/error.h"
31
#include "qemu-common.h"
32
#include "block/block_int.h"
33
#include "block/qcow2.h"
34
@@ -XXX,XX +XXX,XX @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
35
}
36
37
for (i = 0; i < s->nb_snapshots; i++) {
38
- int l1_sectors = DIV_ROUND_UP(s->snapshots[i].l1_size *
39
- sizeof(uint64_t), BDRV_SECTOR_SIZE);
40
+ int l1_size2;
41
+ uint64_t *new_l1_table;
42
+ Error *local_err = NULL;
43
+
44
+ ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset,
45
+ s->snapshots[i].l1_size, sizeof(uint64_t),
46
+ QCOW_MAX_L1_SIZE, "Snapshot L1 table",
47
+ &local_err);
48
+ if (ret < 0) {
49
+ error_report_err(local_err);
50
+ goto fail;
51
+ }
52
53
- uint64_t *new_l1_table =
54
- g_try_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
55
+ l1_size2 = s->snapshots[i].l1_size * sizeof(uint64_t);
56
+ new_l1_table = g_try_realloc(l1_table, l1_size2);
57
58
if (!new_l1_table) {
59
ret = -ENOMEM;
60
@@ -XXX,XX +XXX,XX @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
61
62
l1_table = new_l1_table;
63
64
- ret = bdrv_read(bs->file,
65
- s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE,
66
- (void *)l1_table, l1_sectors);
67
+ ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset,
68
+ l1_table, l1_size2);
69
if (ret < 0) {
70
goto fail;
71
}
72
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
73
index XXXXXXX..XXXXXXX 100755
74
--- a/tests/qemu-iotests/080
75
+++ b/tests/qemu-iotests/080
76
@@ -XXX,XX +XXX,XX @@ _make_test_img 64M
77
{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
78
poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00"
79
{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
80
+{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
81
82
echo
83
echo "== Invalid snapshot L1 table size =="
84
@@ -XXX,XX +XXX,XX @@ _make_test_img 64M
85
{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir
86
poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00"
87
{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
88
+{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
89
90
# success, all done
91
echo "*** done"
92
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
93
index XXXXXXX..XXXXXXX 100644
94
--- a/tests/qemu-iotests/080.out
95
+++ b/tests/qemu-iotests/080.out
96
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
97
wrote 512/512 bytes at offset 0
98
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
99
qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid
100
+qemu-img: Snapshot L1 table offset invalid
101
+qemu-img: Error while amending options: Invalid argument
102
103
== Invalid snapshot L1 table size ==
104
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
105
wrote 512/512 bytes at offset 0
106
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
107
qemu-img: Failed to load snapshot: Snapshot L1 table too large
108
+qemu-img: Snapshot L1 table too large
109
+qemu-img: Error while amending options: File too large
110
*** done
111
--
112
2.13.6
113
114
diff view generated by jsdifflib
Deleted patch
1
From: Alberto Garcia <berto@igalia.com>
2
1
3
The inactive-l2 overlap check iterates uses the L1 tables from all
4
snapshots, but it does not validate them first.
5
6
We now have a function to take care of this, so let's use it.
7
8
Signed-off-by: Alberto Garcia <berto@igalia.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/qcow2-refcount.c | 10 +++++++++-
13
tests/qemu-iotests/080 | 4 ++++
14
tests/qemu-iotests/080.out | 4 ++++
15
3 files changed, 17 insertions(+), 1 deletion(-)
16
17
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block/qcow2-refcount.c
20
+++ b/block/qcow2-refcount.c
21
@@ -XXX,XX +XXX,XX @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
22
uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
23
uint32_t l1_sz = s->snapshots[i].l1_size;
24
uint64_t l1_sz2 = l1_sz * sizeof(uint64_t);
25
- uint64_t *l1 = g_try_malloc(l1_sz2);
26
+ uint64_t *l1;
27
int ret;
28
29
+ ret = qcow2_validate_table(bs, l1_ofs, l1_sz, sizeof(uint64_t),
30
+ QCOW_MAX_L1_SIZE, "", NULL);
31
+ if (ret < 0) {
32
+ return ret;
33
+ }
34
+
35
+ l1 = g_try_malloc(l1_sz2);
36
+
37
if (l1_sz2 && l1 == NULL) {
38
return -ENOMEM;
39
}
40
diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080
41
index XXXXXXX..XXXXXXX 100755
42
--- a/tests/qemu-iotests/080
43
+++ b/tests/qemu-iotests/080
44
@@ -XXX,XX +XXX,XX @@ _make_test_img 64M
45
poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00"
46
{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
47
{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
48
+{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
49
+ -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
50
51
echo
52
echo "== Invalid snapshot L1 table size =="
53
@@ -XXX,XX +XXX,XX @@ _make_test_img 64M
54
poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00"
55
{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir
56
{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir
57
+{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \
58
+ -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir
59
60
# success, all done
61
echo "*** done"
62
diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out
63
index XXXXXXX..XXXXXXX 100644
64
--- a/tests/qemu-iotests/080.out
65
+++ b/tests/qemu-iotests/080.out
66
@@ -XXX,XX +XXX,XX @@ wrote 512/512 bytes at offset 0
67
qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid
68
qemu-img: Snapshot L1 table offset invalid
69
qemu-img: Error while amending options: Invalid argument
70
+Failed to flush the refcount block cache: Invalid argument
71
+write failed: Invalid argument
72
73
== Invalid snapshot L1 table size ==
74
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
75
@@ -XXX,XX +XXX,XX @@ wrote 512/512 bytes at offset 0
76
qemu-img: Failed to load snapshot: Snapshot L1 table too large
77
qemu-img: Snapshot L1 table too large
78
qemu-img: Error while amending options: File too large
79
+Failed to flush the refcount block cache: File too large
80
+write failed: File too large
81
*** done
82
--
83
2.13.6
84
85
diff view generated by jsdifflib
Deleted patch
1
This creates a BlockdevCreateOptions union type that will contain all of
2
the options for image creation. We'll start out with an empty struct
3
type BlockdevCreateNotSupported for all drivers.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
---
9
qapi/block-core.json | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++
10
1 file changed, 62 insertions(+)
11
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
13
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
15
+++ b/qapi/block-core.json
16
@@ -XXX,XX +XXX,XX @@
17
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } }
18
19
##
20
+# @BlockdevCreateNotSupported:
21
+#
22
+# This is used for all drivers that don't support creating images.
23
+#
24
+# Since: 2.12
25
+##
26
+{ 'struct': 'BlockdevCreateNotSupported', 'data': {}}
27
+
28
+##
29
+# @BlockdevCreateOptions:
30
+#
31
+# Options for creating an image format on a given node.
32
+#
33
+# @driver block driver to create the image format
34
+#
35
+# Since: 2.12
36
+##
37
+{ 'union': 'BlockdevCreateOptions',
38
+ 'base': {
39
+ 'driver': 'BlockdevDriver' },
40
+ 'discriminator': 'driver',
41
+ 'data': {
42
+ 'blkdebug': 'BlockdevCreateNotSupported',
43
+ 'blkverify': 'BlockdevCreateNotSupported',
44
+ 'bochs': 'BlockdevCreateNotSupported',
45
+ 'cloop': 'BlockdevCreateNotSupported',
46
+ 'dmg': 'BlockdevCreateNotSupported',
47
+ 'file': 'BlockdevCreateNotSupported',
48
+ 'ftp': 'BlockdevCreateNotSupported',
49
+ 'ftps': 'BlockdevCreateNotSupported',
50
+ 'gluster': 'BlockdevCreateNotSupported',
51
+ 'host_cdrom': 'BlockdevCreateNotSupported',
52
+ 'host_device': 'BlockdevCreateNotSupported',
53
+ 'http': 'BlockdevCreateNotSupported',
54
+ 'https': 'BlockdevCreateNotSupported',
55
+ 'iscsi': 'BlockdevCreateNotSupported',
56
+ 'luks': 'BlockdevCreateNotSupported',
57
+ 'nbd': 'BlockdevCreateNotSupported',
58
+ 'nfs': 'BlockdevCreateNotSupported',
59
+ 'null-aio': 'BlockdevCreateNotSupported',
60
+ 'null-co': 'BlockdevCreateNotSupported',
61
+ 'nvme': 'BlockdevCreateNotSupported',
62
+ 'parallels': 'BlockdevCreateNotSupported',
63
+ 'qcow2': 'BlockdevCreateNotSupported',
64
+ 'qcow': 'BlockdevCreateNotSupported',
65
+ 'qed': 'BlockdevCreateNotSupported',
66
+ 'quorum': 'BlockdevCreateNotSupported',
67
+ 'raw': 'BlockdevCreateNotSupported',
68
+ 'rbd': 'BlockdevCreateNotSupported',
69
+ 'replication': 'BlockdevCreateNotSupported',
70
+ 'sheepdog': 'BlockdevCreateNotSupported',
71
+ 'ssh': 'BlockdevCreateNotSupported',
72
+ 'throttle': 'BlockdevCreateNotSupported',
73
+ 'vdi': 'BlockdevCreateNotSupported',
74
+ 'vhdx': 'BlockdevCreateNotSupported',
75
+ 'vmdk': 'BlockdevCreateNotSupported',
76
+ 'vpc': 'BlockdevCreateNotSupported',
77
+ 'vvfat': 'BlockdevCreateNotSupported',
78
+ 'vxhs': 'BlockdevCreateNotSupported'
79
+ } }
80
+
81
+##
82
# @blockdev-open-tray:
83
#
84
# Opens a block device's tray. If there is a block driver state tree inserted as
85
--
86
2.13.6
87
88
diff view generated by jsdifflib
Deleted patch
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Eric Blake <eblake@redhat.com>
3
Reviewed-by: Max Reitz <mreitz@redhat.com>
4
---
5
qapi/block-core.json | 45 ++++++++++++++++++++++++++++++++++++++++++++-
6
1 file changed, 44 insertions(+), 1 deletion(-)
7
1
8
diff --git a/qapi/block-core.json b/qapi/block-core.json
9
index XXXXXXX..XXXXXXX 100644
10
--- a/qapi/block-core.json
11
+++ b/qapi/block-core.json
12
@@ -XXX,XX +XXX,XX @@
13
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } }
14
15
##
16
+# @BlockdevQcow2Version:
17
+#
18
+# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2)
19
+# @v3: The extended QCOW2 format as introduced in qemu 1.1 (version 3)
20
+#
21
+# Since: 2.12
22
+##
23
+{ 'enum': 'BlockdevQcow2Version',
24
+ 'data': [ 'v2', 'v3' ] }
25
+
26
+
27
+##
28
+# @BlockdevCreateOptionsQcow2:
29
+#
30
+# Driver specific image creation options for qcow2.
31
+#
32
+# @file Node to create the image format on
33
+# @size Size of the virtual disk in bytes
34
+# @version Compatibility level (default: v3)
35
+# @backing-file File name of the backing file if a backing file
36
+# should be used
37
+# @backing-fmt Name of the block driver to use for the backing file
38
+# @encrypt Encryption options if the image should be encrypted
39
+# @cluster-size qcow2 cluster size in bytes (default: 65536)
40
+# @preallocation Preallocation mode for the new image (default: off)
41
+# @lazy-refcounts True if refcounts may be updated lazily (default: off)
42
+# @refcount-bits Width of reference counts in bits (default: 16)
43
+#
44
+# Since: 2.12
45
+##
46
+{ 'struct': 'BlockdevCreateOptionsQcow2',
47
+ 'data': { 'file': 'BlockdevRef',
48
+ 'size': 'size',
49
+ '*version': 'BlockdevQcow2Version',
50
+ '*backing-file': 'str',
51
+ '*backing-fmt': 'BlockdevDriver',
52
+ '*encrypt': 'QCryptoBlockCreateOptions',
53
+ '*cluster-size': 'size',
54
+ '*preallocation': 'PreallocMode',
55
+ '*lazy-refcounts': 'bool',
56
+ '*refcount-bits': 'int' } }
57
+
58
+##
59
# @BlockdevCreateNotSupported:
60
#
61
# This is used for all drivers that don't support creating images.
62
@@ -XXX,XX +XXX,XX @@
63
'null-co': 'BlockdevCreateNotSupported',
64
'nvme': 'BlockdevCreateNotSupported',
65
'parallels': 'BlockdevCreateNotSupported',
66
- 'qcow2': 'BlockdevCreateNotSupported',
67
+ 'qcow2': 'BlockdevCreateOptionsQcow2',
68
'qcow': 'BlockdevCreateNotSupported',
69
'qed': 'BlockdevCreateNotSupported',
70
'quorum': 'BlockdevCreateNotSupported',
71
--
72
2.13.6
73
74
diff view generated by jsdifflib
Deleted patch
1
The functions originally known as qcow2_create() and qcow2_create2()
2
are now called qcow2_co_create_opts() and qcow2_co_create(), which
3
matches the names of the BlockDriver callbacks that they will implement
4
at the end of this patch series.
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
10
block/qcow2.c | 16 ++++++++--------
11
1 file changed, 8 insertions(+), 8 deletions(-)
12
13
diff --git a/block/qcow2.c b/block/qcow2.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block/qcow2.c
16
+++ b/block/qcow2.c
17
@@ -XXX,XX +XXX,XX @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
18
}
19
20
static int coroutine_fn
21
-qcow2_co_create2(const char *filename, int64_t total_size,
22
- const char *backing_file, const char *backing_format,
23
- int flags, size_t cluster_size, PreallocMode prealloc,
24
- QemuOpts *opts, int version, int refcount_order,
25
- const char *encryptfmt, Error **errp)
26
+qcow2_co_create(const char *filename, int64_t total_size,
27
+ const char *backing_file, const char *backing_format,
28
+ int flags, size_t cluster_size, PreallocMode prealloc,
29
+ QemuOpts *opts, int version, int refcount_order,
30
+ const char *encryptfmt, Error **errp)
31
{
32
QDict *options;
33
34
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
35
36
refcount_order = ctz32(refcount_bits);
37
38
- ret = qcow2_co_create2(filename, size, backing_file, backing_fmt, flags,
39
- cluster_size, prealloc, opts, version, refcount_order,
40
- encryptfmt, &local_err);
41
+ ret = qcow2_co_create(filename, size, backing_file, backing_fmt, flags,
42
+ cluster_size, prealloc, opts, version, refcount_order,
43
+ encryptfmt, &local_err);
44
error_propagate(errp, local_err);
45
46
finish:
47
--
48
2.13.6
49
50
diff view generated by jsdifflib
Deleted patch
1
Currently, qcow2_create() only parses the QemuOpts and then calls
2
qcow2_co_create() for the actual image creation, which includes both the
3
creation of the actual file on the file system and writing a valid empty
4
qcow2 image into that file.
5
1
6
The plan is that qcow2_co_create() becomes the function that implements
7
the functionality for a future 'blockdev-create' QMP command, which only
8
creates the qcow2 layer on an already opened file node.
9
10
This is a first step towards that goal: Let's move out anything that
11
deals with the protocol layer from qcow2_co_create() into
12
qcow2_create(). This means that qcow2_co_create() doesn't need a file
13
name any more.
14
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
17
Reviewed-by: Max Reitz <mreitz@redhat.com>
18
---
19
block/qcow2.c | 64 +++++++++++++++++++++++++++++++++++------------------------
20
1 file changed, 38 insertions(+), 26 deletions(-)
21
22
diff --git a/block/qcow2.c b/block/qcow2.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/block/qcow2.c
25
+++ b/block/qcow2.c
26
@@ -XXX,XX +XXX,XX @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
27
}
28
29
static int coroutine_fn
30
-qcow2_co_create(const char *filename, int64_t total_size,
31
+qcow2_co_create(BlockDriverState *bs, int64_t total_size,
32
const char *backing_file, const char *backing_format,
33
int flags, size_t cluster_size, PreallocMode prealloc,
34
QemuOpts *opts, int version, int refcount_order,
35
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(const char *filename, int64_t total_size,
36
Error *local_err = NULL;
37
int ret;
38
39
- if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
40
- int64_t prealloc_size =
41
- qcow2_calc_prealloc_size(total_size, cluster_size, refcount_order);
42
- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
43
- qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc),
44
- &error_abort);
45
- }
46
-
47
- ret = bdrv_create_file(filename, opts, &local_err);
48
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
49
+ ret = blk_insert_bs(blk, bs, errp);
50
if (ret < 0) {
51
- error_propagate(errp, local_err);
52
- return ret;
53
- }
54
-
55
- blk = blk_new_open(filename, NULL, NULL,
56
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
57
- &local_err);
58
- if (blk == NULL) {
59
- error_propagate(errp, local_err);
60
- return -EIO;
61
+ goto out;
62
}
63
-
64
blk_set_allow_write_beyond_eof(blk, true);
65
66
/* Write the header */
67
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(const char *filename, int64_t total_size,
68
*/
69
options = qdict_new();
70
qdict_put_str(options, "driver", "qcow2");
71
- blk = blk_new_open(filename, NULL, options,
72
+ qdict_put_str(options, "file", bs->node_name);
73
+ blk = blk_new_open(NULL, NULL, options,
74
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
75
&local_err);
76
if (blk == NULL) {
77
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(const char *filename, int64_t total_size,
78
*/
79
options = qdict_new();
80
qdict_put_str(options, "driver", "qcow2");
81
- blk = blk_new_open(filename, NULL, options,
82
+ qdict_put_str(options, "file", bs->node_name);
83
+ blk = blk_new_open(NULL, NULL, options,
84
BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO,
85
&local_err);
86
if (blk == NULL) {
87
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
88
uint64_t refcount_bits;
89
int refcount_order;
90
char *encryptfmt = NULL;
91
+ BlockDriverState *bs = NULL;
92
Error *local_err = NULL;
93
int ret;
94
95
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
96
97
refcount_order = ctz32(refcount_bits);
98
99
- ret = qcow2_co_create(filename, size, backing_file, backing_fmt, flags,
100
+ /* Create and open the file (protocol layer) */
101
+ if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
102
+ int64_t prealloc_size =
103
+ qcow2_calc_prealloc_size(size, cluster_size, refcount_order);
104
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
105
+ qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc),
106
+ &error_abort);
107
+ }
108
+
109
+ ret = bdrv_create_file(filename, opts, errp);
110
+ if (ret < 0) {
111
+ goto finish;
112
+ }
113
+
114
+ bs = bdrv_open(filename, NULL, NULL,
115
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
116
+ if (bs == NULL) {
117
+ ret = -EIO;
118
+ goto finish;
119
+ }
120
+
121
+ /* Create the qcow2 image (format layer) */
122
+ ret = qcow2_co_create(bs, size, backing_file, backing_fmt, flags,
123
cluster_size, prealloc, opts, version, refcount_order,
124
- encryptfmt, &local_err);
125
- error_propagate(errp, local_err);
126
+ encryptfmt, errp);
127
+ if (ret < 0) {
128
+ goto finish;
129
+ }
130
131
finish:
132
+ bdrv_unref(bs);
133
+
134
g_free(backing_file);
135
g_free(backing_fmt);
136
g_free(encryptfmt);
137
--
138
2.13.6
139
140
diff view generated by jsdifflib
Deleted patch
1
Once qcow2_co_create() can be called directly on an already existing
2
node, we must provide the 'full' and 'falloc' preallocation modes
3
outside of creating the image on the protocol layer. Fortunately, we
4
have preallocated truncate now which can provide this functionality.
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
10
block/qcow2.c | 28 +++++++++++++++++++---------
11
1 file changed, 19 insertions(+), 9 deletions(-)
12
13
diff --git a/block/qcow2.c b/block/qcow2.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block/qcow2.c
16
+++ b/block/qcow2.c
17
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
18
}
19
blk_set_allow_write_beyond_eof(blk, true);
20
21
+ /* Clear the protocol layer and preallocate it if necessary */
22
+ ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
23
+ if (ret < 0) {
24
+ goto out;
25
+ }
26
+
27
+ if (qcow2_opts->preallocation == PREALLOC_MODE_FULL ||
28
+ qcow2_opts->preallocation == PREALLOC_MODE_FALLOC)
29
+ {
30
+ int64_t prealloc_size =
31
+ qcow2_calc_prealloc_size(qcow2_opts->size, cluster_size,
32
+ refcount_order);
33
+
34
+ ret = blk_truncate(blk, prealloc_size, qcow2_opts->preallocation, errp);
35
+ if (ret < 0) {
36
+ goto out;
37
+ }
38
+ }
39
+
40
/* Write the header */
41
QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header));
42
header = g_malloc0(cluster_size);
43
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
44
45
46
/* Create and open the file (protocol layer) */
47
- if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) {
48
- int refcount_order = ctz32(refcount_bits);
49
- int64_t prealloc_size =
50
- qcow2_calc_prealloc_size(size, cluster_size, refcount_order);
51
- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
52
- qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc),
53
- &error_abort);
54
- }
55
-
56
ret = bdrv_create_file(filename, opts, errp);
57
if (ret < 0) {
58
goto finish;
59
--
60
2.13.6
61
62
diff view generated by jsdifflib
Deleted patch
1
A few block drivers will need to rename .bdrv_create options for their
2
QAPIfication, so let's have a helper function for that.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
---
8
include/qapi/qmp/qdict.h | 6 +++
9
qobject/qdict.c | 34 +++++++++++++
10
tests/check-qdict.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++
11
3 files changed, 169 insertions(+)
12
13
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
14
index XXXXXXX..XXXXXXX 100644
15
--- a/include/qapi/qmp/qdict.h
16
+++ b/include/qapi/qmp/qdict.h
17
@@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp);
18
19
void qdict_join(QDict *dest, QDict *src, bool overwrite);
20
21
+typedef struct QDictRenames {
22
+ const char *from;
23
+ const char *to;
24
+} QDictRenames;
25
+bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
26
+
27
#endif /* QDICT_H */
28
diff --git a/qobject/qdict.c b/qobject/qdict.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/qobject/qdict.c
31
+++ b/qobject/qdict.c
32
@@ -XXX,XX +XXX,XX @@ void qdict_join(QDict *dest, QDict *src, bool overwrite)
33
entry = next;
34
}
35
}
36
+
37
+/**
38
+ * qdict_rename_keys(): Rename keys in qdict according to the replacements
39
+ * specified in the array renames. The array must be terminated by an entry
40
+ * with from = NULL.
41
+ *
42
+ * The renames are performed individually in the order of the array, so entries
43
+ * may be renamed multiple times and may or may not conflict depending on the
44
+ * order of the renames array.
45
+ *
46
+ * Returns true for success, false in error cases.
47
+ */
48
+bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp)
49
+{
50
+ QObject *qobj;
51
+
52
+ while (renames->from) {
53
+ if (qdict_haskey(qdict, renames->from)) {
54
+ if (qdict_haskey(qdict, renames->to)) {
55
+ error_setg(errp, "'%s' and its alias '%s' can't be used at the "
56
+ "same time", renames->to, renames->from);
57
+ return false;
58
+ }
59
+
60
+ qobj = qdict_get(qdict, renames->from);
61
+ qobject_incref(qobj);
62
+ qdict_put_obj(qdict, renames->to, qobj);
63
+ qdict_del(qdict, renames->from);
64
+ }
65
+
66
+ renames++;
67
+ }
68
+ return true;
69
+}
70
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/tests/check-qdict.c
73
+++ b/tests/check-qdict.c
74
@@ -XXX,XX +XXX,XX @@ static void qdict_crumple_test_empty(void)
75
QDECREF(dst);
76
}
77
78
+static int qdict_count_entries(QDict *dict)
79
+{
80
+ const QDictEntry *e;
81
+ int count = 0;
82
+
83
+ for (e = qdict_first(dict); e; e = qdict_next(dict, e)) {
84
+ count++;
85
+ }
86
+
87
+ return count;
88
+}
89
+
90
+static void qdict_rename_keys_test(void)
91
+{
92
+ QDict *dict = qdict_new();
93
+ QDict *copy;
94
+ QDictRenames *renames;
95
+ Error *local_err = NULL;
96
+
97
+ qdict_put_str(dict, "abc", "foo");
98
+ qdict_put_str(dict, "abcdef", "bar");
99
+ qdict_put_int(dict, "number", 42);
100
+ qdict_put_bool(dict, "flag", true);
101
+ qdict_put_null(dict, "nothing");
102
+
103
+ /* Empty rename list */
104
+ renames = (QDictRenames[]) {
105
+ { NULL, "this can be anything" }
106
+ };
107
+ copy = qdict_clone_shallow(dict);
108
+ qdict_rename_keys(copy, renames, &error_abort);
109
+
110
+ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
111
+ g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
112
+ g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
113
+ g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
114
+ g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
115
+ g_assert_cmpint(qdict_count_entries(copy), ==, 5);
116
+
117
+ QDECREF(copy);
118
+
119
+ /* Simple rename of all entries */
120
+ renames = (QDictRenames[]) {
121
+ { "abc", "str1" },
122
+ { "abcdef", "str2" },
123
+ { "number", "int" },
124
+ { "flag", "bool" },
125
+ { "nothing", "null" },
126
+ { NULL , NULL }
127
+ };
128
+ copy = qdict_clone_shallow(dict);
129
+ qdict_rename_keys(copy, renames, &error_abort);
130
+
131
+ g_assert(!qdict_haskey(copy, "abc"));
132
+ g_assert(!qdict_haskey(copy, "abcdef"));
133
+ g_assert(!qdict_haskey(copy, "number"));
134
+ g_assert(!qdict_haskey(copy, "flag"));
135
+ g_assert(!qdict_haskey(copy, "nothing"));
136
+
137
+ g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo");
138
+ g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar");
139
+ g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42);
140
+ g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true);
141
+ g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL);
142
+ g_assert_cmpint(qdict_count_entries(copy), ==, 5);
143
+
144
+ QDECREF(copy);
145
+
146
+ /* Renames are processed top to bottom */
147
+ renames = (QDictRenames[]) {
148
+ { "abc", "tmp" },
149
+ { "abcdef", "abc" },
150
+ { "number", "abcdef" },
151
+ { "flag", "number" },
152
+ { "nothing", "flag" },
153
+ { "tmp", "nothing" },
154
+ { NULL , NULL }
155
+ };
156
+ copy = qdict_clone_shallow(dict);
157
+ qdict_rename_keys(copy, renames, &error_abort);
158
+
159
+ g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo");
160
+ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar");
161
+ g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42);
162
+ g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true);
163
+ g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL);
164
+ g_assert(!qdict_haskey(copy, "tmp"));
165
+ g_assert_cmpint(qdict_count_entries(copy), ==, 5);
166
+
167
+ QDECREF(copy);
168
+
169
+ /* Conflicting rename */
170
+ renames = (QDictRenames[]) {
171
+ { "abcdef", "abc" },
172
+ { NULL , NULL }
173
+ };
174
+ copy = qdict_clone_shallow(dict);
175
+ qdict_rename_keys(copy, renames, &local_err);
176
+
177
+ g_assert(local_err != NULL);
178
+ error_free(local_err);
179
+ local_err = NULL;
180
+
181
+ g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
182
+ g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
183
+ g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
184
+ g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
185
+ g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
186
+ g_assert_cmpint(qdict_count_entries(copy), ==, 5);
187
+
188
+ QDECREF(copy);
189
+
190
+ /* Renames in an empty dict */
191
+ renames = (QDictRenames[]) {
192
+ { "abcdef", "abc" },
193
+ { NULL , NULL }
194
+ };
195
+
196
+ QDECREF(dict);
197
+ dict = qdict_new();
198
+
199
+ qdict_rename_keys(dict, renames, &error_abort);
200
+ g_assert(qdict_first(dict) == NULL);
201
+
202
+ QDECREF(dict);
203
+}
204
+
205
static void qdict_crumple_test_bad_inputs(void)
206
{
207
QDict *src;
208
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
209
g_test_add_func("/public/crumple/bad_inputs",
210
qdict_crumple_test_bad_inputs);
211
212
+ g_test_add_func("/public/rename_keys", qdict_rename_keys_test);
213
+
214
/* The Big one */
215
if (g_test_slow()) {
216
g_test_add_func("/stress/test", qdict_stress_test);
217
--
218
2.13.6
219
220
diff view generated by jsdifflib
1
Instead of manually creating the BlockdevCreateOptions object, use a
1
Since commit bde70715, base is the only node that is reopened in
2
visitor to parse the given options into the QAPI object.
2
commit_start(). This means that the code, which still involves an
3
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
4
This involves translation from the old command line syntax to the syntax
5
mandated by the QAPI schema. Option names are still checked against
6
qcow2_create_opts, so only the old option names are allowed on the
7
command line, even if they are translated in qcow2_create().
8
9
In contrast, new option values are optionally recognised besides the old
10
values: 'compat' accepts 'v2'/'v3' as an alias for '0.10'/'1.1', and
11
'encrypt.format' accepts 'qcow' as an alias for 'aes' now.
12
4
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
15
Reviewed-by: Eric Blake <eblake@redhat.com>
16
---
7
---
17
block/qcow2.c | 218 ++++++++++++++++-----------------------------
8
block/commit.c | 8 +-------
18
tests/qemu-iotests/049.out | 8 +-
9
1 file changed, 1 insertion(+), 7 deletions(-)
19
tests/qemu-iotests/112.out | 4 +-
20
3 files changed, 84 insertions(+), 146 deletions(-)
21
10
22
diff --git a/block/qcow2.c b/block/qcow2.c
11
diff --git a/block/commit.c b/block/commit.c
23
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
24
--- a/block/qcow2.c
13
--- a/block/commit.c
25
+++ b/block/qcow2.c
14
+++ b/block/commit.c
26
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
27
#include "qemu/option_int.h"
16
const char *filter_node_name, Error **errp)
28
#include "qemu/cutils.h"
17
{
29
#include "qemu/bswap.h"
18
CommitBlockJob *s;
30
-#include "qapi/opts-visitor.h"
19
- BlockReopenQueue *reopen_queue = NULL;
31
+#include "qapi/qobject-input-visitor.h"
20
int orig_base_flags;
32
+#include "qapi/qapi-visit-block-core.h"
21
BlockDriverState *iter;
33
#include "block/crypto.h"
22
BlockDriverState *commit_top_bs = NULL;
34
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
35
/*
24
/* convert base to r/w, if necessary */
36
@@ -XXX,XX +XXX,XX @@ static int qcow2_crypt_method_from_format(const char *encryptfmt)
25
orig_base_flags = bdrv_get_flags(base);
37
}
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
38
}
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
39
28
- orig_base_flags | BDRV_O_RDWR);
40
-static QCryptoBlockCreateOptions *
41
-qcow2_parse_encryption(const char *encryptfmt, QemuOpts *opts, Error **errp)
42
-{
43
- QCryptoBlockCreateOptions *cryptoopts = NULL;
44
- QDict *options, *encryptopts;
45
- int fmt;
46
-
47
- options = qemu_opts_to_qdict(opts, NULL);
48
- qdict_extract_subqdict(options, &encryptopts, "encrypt.");
49
- QDECREF(options);
50
-
51
- fmt = qcow2_crypt_method_from_format(encryptfmt);
52
-
53
- switch (fmt) {
54
- case QCOW_CRYPT_LUKS:
55
- cryptoopts = block_crypto_create_opts_init(
56
- Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp);
57
- break;
58
- case QCOW_CRYPT_AES:
59
- cryptoopts = block_crypto_create_opts_init(
60
- Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
61
- break;
62
- default:
63
- error_setg(errp, "Unknown encryption format '%s'", encryptfmt);
64
- break;
65
- }
29
- }
66
-
30
-
67
- QDECREF(encryptopts);
31
- if (reopen_queue) {
68
- return cryptoopts;
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
69
-}
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
70
-
34
if (local_err != NULL) {
71
static int qcow2_set_up_encryption(BlockDriverState *bs,
35
error_propagate(errp, local_err);
72
QCryptoBlockCreateOptions *cryptoopts,
36
goto fail;
73
Error **errp)
74
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
75
}
76
if (version < 3 && qcow2_opts->lazy_refcounts) {
77
error_setg(errp, "Lazy refcounts only supported with compatibility "
78
- "level 1.1 and above (use compat=1.1 or greater)");
79
+ "level 1.1 and above (use version=v3 or greater)");
80
ret = -EINVAL;
81
goto out;
82
}
83
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
84
}
85
if (version < 3 && qcow2_opts->refcount_bits != 16) {
86
error_setg(errp, "Different refcount widths than 16 bits require "
87
- "compatibility level 1.1 or above (use compat=1.1 or "
88
+ "compatibility level 1.1 or above (use version=v3 or "
89
"greater)");
90
ret = -EINVAL;
91
goto out;
92
@@ -XXX,XX +XXX,XX @@ out:
93
static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts,
94
Error **errp)
95
{
96
- BlockdevCreateOptions create_options;
97
- char *backing_file = NULL;
98
- char *backing_fmt = NULL;
99
- BlockdevDriver backing_drv;
100
- char *buf = NULL;
101
- uint64_t size = 0;
102
- int flags = 0;
103
- size_t cluster_size = DEFAULT_CLUSTER_SIZE;
104
- PreallocMode prealloc;
105
- int version;
106
- uint64_t refcount_bits;
107
- char *encryptfmt = NULL;
108
- QCryptoBlockCreateOptions *cryptoopts = NULL;
109
+ BlockdevCreateOptions *create_options = NULL;
110
+ QDict *qdict = NULL;
111
+ QObject *qobj;
112
+ Visitor *v;
113
BlockDriverState *bs = NULL;
114
Error *local_err = NULL;
115
+ const char *val;
116
int ret;
117
118
- /* Read out options */
119
- size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
120
- BDRV_SECTOR_SIZE);
121
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
122
- backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
123
- backing_drv = qapi_enum_parse(&BlockdevDriver_lookup, backing_fmt,
124
- 0, &local_err);
125
- if (local_err) {
126
- error_propagate(errp, local_err);
127
+ /* Only the keyval visitor supports the dotted syntax needed for
128
+ * encryption, so go through a QDict before getting a QAPI type. Ignore
129
+ * options meant for the protocol layer so that the visitor doesn't
130
+ * complain. */
131
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts,
132
+ true);
133
+
134
+ /* Handle encryption options */
135
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
136
+ if (val && !strcmp(val, "on")) {
137
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
138
+ } else if (val && !strcmp(val, "off")) {
139
+ qdict_del(qdict, BLOCK_OPT_ENCRYPT);
140
+ }
141
+
142
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
143
+ if (val && !strcmp(val, "aes")) {
144
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
145
+ }
146
+
147
+ /* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into
148
+ * version=v2/v3 below. */
149
+ val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL);
150
+ if (val && !strcmp(val, "0.10")) {
151
+ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2");
152
+ } else if (val && !strcmp(val, "1.1")) {
153
+ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3");
154
+ }
155
+
156
+ /* Change legacy command line options into QMP ones */
157
+ static const QDictRenames opt_renames[] = {
158
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
159
+ { BLOCK_OPT_BACKING_FMT, "backing-fmt" },
160
+ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
161
+ { BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" },
162
+ { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" },
163
+ { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
164
+ { BLOCK_OPT_COMPAT_LEVEL, "version" },
165
+ { NULL, NULL },
166
+ };
167
+
168
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
169
ret = -EINVAL;
170
goto finish;
171
}
172
173
- encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
174
- if (encryptfmt) {
175
- if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
176
- error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
177
- BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
178
- ret = -EINVAL;
179
- goto finish;
180
- }
181
- } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
182
- encryptfmt = g_strdup("aes");
183
- }
184
- if (encryptfmt) {
185
- cryptoopts = qcow2_parse_encryption(encryptfmt, opts, errp);
186
- if (cryptoopts == NULL) {
187
- ret = -EINVAL;
188
- goto finish;
189
- }
190
- }
191
-
192
- cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
193
- if (local_err) {
194
- error_propagate(errp, local_err);
195
- ret = -EINVAL;
196
+ /* Create and open the file (protocol layer) */
197
+ ret = bdrv_create_file(filename, opts, errp);
198
+ if (ret < 0) {
199
goto finish;
200
}
201
- buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
202
- prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
203
- PREALLOC_MODE_OFF, &local_err);
204
- if (local_err) {
205
- error_propagate(errp, local_err);
206
- ret = -EINVAL;
207
+
208
+ bs = bdrv_open(filename, NULL, NULL,
209
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
210
+ if (bs == NULL) {
211
+ ret = -EIO;
212
goto finish;
213
}
214
215
- version = qcow2_opt_get_version_del(opts, &local_err);
216
- if (local_err) {
217
- error_propagate(errp, local_err);
218
+ /* Set 'driver' and 'node' options */
219
+ qdict_put_str(qdict, "driver", "qcow2");
220
+ qdict_put_str(qdict, "file", bs->node_name);
221
+
222
+ /* Now get the QAPI type BlockdevCreateOptions */
223
+ qobj = qdict_crumple(qdict, errp);
224
+ QDECREF(qdict);
225
+ qdict = qobject_to_qdict(qobj);
226
+ if (qdict == NULL) {
227
ret = -EINVAL;
228
goto finish;
229
}
230
231
- if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) {
232
- flags |= BLOCK_FLAG_LAZY_REFCOUNTS;
233
- }
234
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
235
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
236
+ visit_free(v);
237
238
- refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err);
239
if (local_err) {
240
error_propagate(errp, local_err);
241
ret = -EINVAL;
242
goto finish;
243
}
244
245
-
246
- /* Create and open the file (protocol layer) */
247
- ret = bdrv_create_file(filename, opts, errp);
248
- if (ret < 0) {
249
- goto finish;
250
- }
251
-
252
- bs = bdrv_open(filename, NULL, NULL,
253
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
254
- if (bs == NULL) {
255
- ret = -EIO;
256
- goto finish;
257
- }
258
+ /* Silently round up size */
259
+ create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size,
260
+ BDRV_SECTOR_SIZE);
261
262
/* Create the qcow2 image (format layer) */
263
- create_options = (BlockdevCreateOptions) {
264
- .driver = BLOCKDEV_DRIVER_QCOW2,
265
- .u.qcow2 = {
266
- .file = &(BlockdevRef) {
267
- .type = QTYPE_QSTRING,
268
- .u.reference = bs->node_name,
269
- },
270
- .size = size,
271
- .has_version = true,
272
- .version = version == 2
273
- ? BLOCKDEV_QCOW2_VERSION_V2
274
- : BLOCKDEV_QCOW2_VERSION_V3,
275
- .has_backing_file = (backing_file != NULL),
276
- .backing_file = backing_file,
277
- .has_backing_fmt = (backing_fmt != NULL),
278
- .backing_fmt = backing_drv,
279
- .has_encrypt = (encryptfmt != NULL),
280
- .encrypt = cryptoopts,
281
- .has_cluster_size = true,
282
- .cluster_size = cluster_size,
283
- .has_preallocation = true,
284
- .preallocation = prealloc,
285
- .has_lazy_refcounts = true,
286
- .lazy_refcounts = (flags & BLOCK_FLAG_LAZY_REFCOUNTS),
287
- .has_refcount_bits = true,
288
- .refcount_bits = refcount_bits,
289
- },
290
- };
291
- ret = qcow2_co_create(&create_options, errp);
292
+ ret = qcow2_co_create(create_options, errp);
293
if (ret < 0) {
294
goto finish;
295
}
296
297
+ ret = 0;
298
finish:
299
+ QDECREF(qdict);
300
bdrv_unref(bs);
301
-
302
- qapi_free_QCryptoBlockCreateOptions(cryptoopts);
303
- g_free(backing_file);
304
- g_free(backing_fmt);
305
- g_free(encryptfmt);
306
- g_free(buf);
307
+ qapi_free_BlockdevCreateOptions(create_options);
308
return ret;
309
}
310
311
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
312
index XXXXXXX..XXXXXXX 100644
313
--- a/tests/qemu-iotests/049.out
314
+++ b/tests/qemu-iotests/049.out
315
@@ -XXX,XX +XXX,XX @@ qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M
316
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16
317
318
qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
319
-qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42'
320
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42'
321
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.42 cluster_size=65536 lazy_refcounts=off refcount_bits=16
322
323
qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
324
-qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar'
325
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar'
326
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=foobar cluster_size=65536 lazy_refcounts=off refcount_bits=16
327
328
== Check preallocation option ==
329
@@ -XXX,XX +XXX,XX @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M
330
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=metadata lazy_refcounts=off refcount_bits=16
331
332
qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
333
-qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234
334
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234'
335
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=1234 lazy_refcounts=off refcount_bits=16
336
337
== Check encryption option ==
338
@@ -XXX,XX +XXX,XX @@ qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M
339
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16
340
341
qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M
342
-qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
343
+qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)
344
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=on refcount_bits=16
345
346
*** done
347
diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out
348
index XXXXXXX..XXXXXXX 100644
349
--- a/tests/qemu-iotests/112.out
350
+++ b/tests/qemu-iotests/112.out
351
@@ -XXX,XX +XXX,XX @@ refcount bits: 16
352
353
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
354
refcount bits: 16
355
-qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater)
356
+qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
357
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
358
-qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater)
359
+qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
360
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
361
362
=== Snapshot limit on refcount_bits=1 ===
363
--
37
--
364
2.13.6
38
2.13.6
365
39
366
40
diff view generated by jsdifflib
Deleted patch
1
This adds the .bdrv_co_create driver callback to file, which enables
2
image creation over QMP.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
---
8
qapi/block-core.json | 20 ++++++++++++-
9
block/file-posix.c | 79 +++++++++++++++++++++++++++++++++++++---------------
10
2 files changed, 75 insertions(+), 24 deletions(-)
11
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
13
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
15
+++ b/qapi/block-core.json
16
@@ -XXX,XX +XXX,XX @@
17
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } }
18
19
##
20
+# @BlockdevCreateOptionsFile:
21
+#
22
+# Driver specific image creation options for file.
23
+#
24
+# @filename Filename for the new image file
25
+# @size Size of the virtual disk in bytes
26
+# @preallocation Preallocation mode for the new image (default: off)
27
+# @nocow Turn off copy-on-write (valid only on btrfs; default: off)
28
+#
29
+# Since: 2.12
30
+##
31
+{ 'struct': 'BlockdevCreateOptionsFile',
32
+ 'data': { 'filename': 'str',
33
+ 'size': 'size',
34
+ '*preallocation': 'PreallocMode',
35
+ '*nocow': 'bool' } }
36
+
37
+##
38
# @BlockdevQcow2Version:
39
#
40
# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2)
41
@@ -XXX,XX +XXX,XX @@
42
'bochs': 'BlockdevCreateNotSupported',
43
'cloop': 'BlockdevCreateNotSupported',
44
'dmg': 'BlockdevCreateNotSupported',
45
- 'file': 'BlockdevCreateNotSupported',
46
+ 'file': 'BlockdevCreateOptionsFile',
47
'ftp': 'BlockdevCreateNotSupported',
48
'ftps': 'BlockdevCreateNotSupported',
49
'gluster': 'BlockdevCreateNotSupported',
50
diff --git a/block/file-posix.c b/block/file-posix.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/block/file-posix.c
53
+++ b/block/file-posix.c
54
@@ -XXX,XX +XXX,XX @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
55
return (int64_t)st.st_blocks * 512;
56
}
57
58
-static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
59
- Error **errp)
60
+static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
61
{
62
+ BlockdevCreateOptionsFile *file_opts;
63
int fd;
64
int result = 0;
65
- int64_t total_size = 0;
66
- bool nocow = false;
67
- PreallocMode prealloc;
68
- char *buf = NULL;
69
- Error *local_err = NULL;
70
71
- strstart(filename, "file:", &filename);
72
+ /* Validate options and set default values */
73
+ assert(options->driver == BLOCKDEV_DRIVER_FILE);
74
+ file_opts = &options->u.file;
75
76
- /* Read out options */
77
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
78
- BDRV_SECTOR_SIZE);
79
- nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
80
- buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
81
- prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
82
- PREALLOC_MODE_OFF, &local_err);
83
- g_free(buf);
84
- if (local_err) {
85
- error_propagate(errp, local_err);
86
- result = -EINVAL;
87
- goto out;
88
+ if (!file_opts->has_nocow) {
89
+ file_opts->nocow = false;
90
+ }
91
+ if (!file_opts->has_preallocation) {
92
+ file_opts->preallocation = PREALLOC_MODE_OFF;
93
}
94
95
- fd = qemu_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
96
+ /* Create file */
97
+ fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
98
0644);
99
if (fd < 0) {
100
result = -errno;
101
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
102
goto out;
103
}
104
105
- if (nocow) {
106
+ if (file_opts->nocow) {
107
#ifdef __linux__
108
/* Set NOCOW flag to solve performance issue on fs like btrfs.
109
* This is an optimisation. The FS_IOC_SETFLAGS ioctl return value
110
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
111
#endif
112
}
113
114
- result = raw_regular_truncate(fd, total_size, prealloc, errp);
115
+ result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation,
116
+ errp);
117
if (result < 0) {
118
goto out_close;
119
}
120
@@ -XXX,XX +XXX,XX @@ out:
121
return result;
122
}
123
124
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
125
+ Error **errp)
126
+{
127
+ BlockdevCreateOptions options;
128
+ int64_t total_size = 0;
129
+ bool nocow = false;
130
+ PreallocMode prealloc;
131
+ char *buf = NULL;
132
+ Error *local_err = NULL;
133
+
134
+ /* Skip file: protocol prefix */
135
+ strstart(filename, "file:", &filename);
136
+
137
+ /* Read out options */
138
+ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
139
+ BDRV_SECTOR_SIZE);
140
+ nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
141
+ buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
142
+ prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
143
+ PREALLOC_MODE_OFF, &local_err);
144
+ g_free(buf);
145
+ if (local_err) {
146
+ error_propagate(errp, local_err);
147
+ return -EINVAL;
148
+ }
149
+
150
+ options = (BlockdevCreateOptions) {
151
+ .driver = BLOCKDEV_DRIVER_FILE,
152
+ .u.file = {
153
+ .filename = (char *) filename,
154
+ .size = total_size,
155
+ .has_preallocation = true,
156
+ .preallocation = prealloc,
157
+ .has_nocow = true,
158
+ .nocow = nocow,
159
+ },
160
+ };
161
+ return raw_co_create(&options, errp);
162
+}
163
+
164
/*
165
* Find allocation range in @bs around offset @start.
166
* May change underlying file descriptor's file offset.
167
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
168
.bdrv_reopen_commit = raw_reopen_commit,
169
.bdrv_reopen_abort = raw_reopen_abort,
170
.bdrv_close = raw_close,
171
+ .bdrv_co_create = raw_co_create,
172
.bdrv_co_create_opts = raw_co_create_opts,
173
.bdrv_has_zero_init = bdrv_has_zero_init_1,
174
.bdrv_co_block_status = raw_co_block_status,
175
--
176
2.13.6
177
178
diff view generated by jsdifflib
Deleted patch
1
This adds the .bdrv_co_create driver callback to file-win32, which
2
enables image creation over QMP.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
---
8
block/file-win32.c | 47 ++++++++++++++++++++++++++++++++++++++---------
9
1 file changed, 38 insertions(+), 9 deletions(-)
10
11
diff --git a/block/file-win32.c b/block/file-win32.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block/file-win32.c
14
+++ b/block/file-win32.c
15
@@ -XXX,XX +XXX,XX @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
16
return st.st_size;
17
}
18
19
-static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
20
- Error **errp)
21
+static int raw_co_create(BlockdevCreateOptions *options, Error **errp)
22
{
23
+ BlockdevCreateOptionsFile *file_opts;
24
int fd;
25
- int64_t total_size = 0;
26
27
- strstart(filename, "file:", &filename);
28
+ assert(options->driver == BLOCKDEV_DRIVER_FILE);
29
+ file_opts = &options->u.file;
30
31
- /* Read out options */
32
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
33
- BDRV_SECTOR_SIZE);
34
+ if (file_opts->has_preallocation) {
35
+ error_setg(errp, "Preallocation is not supported on Windows");
36
+ return -EINVAL;
37
+ }
38
+ if (file_opts->has_nocow) {
39
+ error_setg(errp, "nocow is not supported on Windows");
40
+ return -EINVAL;
41
+ }
42
43
- fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
44
+ fd = qemu_open(file_opts->filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
45
0644);
46
if (fd < 0) {
47
error_setg_errno(errp, errno, "Could not create file");
48
return -EIO;
49
}
50
set_sparse(fd);
51
- ftruncate(fd, total_size);
52
+ ftruncate(fd, file_opts->size);
53
qemu_close(fd);
54
+
55
return 0;
56
}
57
58
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
59
+ Error **errp)
60
+{
61
+ BlockdevCreateOptions options;
62
+ int64_t total_size = 0;
63
+
64
+ strstart(filename, "file:", &filename);
65
+
66
+ /* Read out options */
67
+ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
68
+ BDRV_SECTOR_SIZE);
69
+
70
+ options = (BlockdevCreateOptions) {
71
+ .driver = BLOCKDEV_DRIVER_FILE,
72
+ .u.file = {
73
+ .filename = (char *) filename,
74
+ .size = total_size,
75
+ .has_preallocation = false,
76
+ .has_nocow = false,
77
+ },
78
+ };
79
+ return raw_co_create(&options, errp);
80
+}
81
82
static QemuOptsList raw_create_opts = {
83
.name = "raw-create-opts",
84
--
85
2.13.6
86
87
diff view generated by jsdifflib
Deleted patch
1
Now that the options are already available in qemu_rbd_open() and not
2
only parsed in qemu_rbd_connect(), we can assign s->snap and
3
s->image_name there instead of passing the fields by reference to
4
qemu_rbd_connect().
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
---
9
block/rbd.c | 14 +++++---------
10
1 file changed, 5 insertions(+), 9 deletions(-)
11
12
diff --git a/block/rbd.c b/block/rbd.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/block/rbd.c
15
+++ b/block/rbd.c
16
@@ -XXX,XX +XXX,XX @@ static char *qemu_rbd_mon_host(BlockdevOptionsRbd *opts, Error **errp)
17
}
18
19
static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
20
- char **s_snap, char **s_image_name,
21
BlockdevOptionsRbd *opts, bool cache,
22
const char *keypairs, const char *secretid,
23
Error **errp)
24
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
25
goto failed_opts;
26
}
27
28
- *s_snap = g_strdup(opts->snapshot);
29
- *s_image_name = g_strdup(opts->image);
30
-
31
/* try default location when conf=NULL, but ignore failure */
32
r = rados_conf_read_file(*cluster, opts->conf);
33
if (opts->has_conf && r < 0) {
34
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
35
36
failed_shutdown:
37
rados_shutdown(*cluster);
38
- g_free(*s_snap);
39
- g_free(*s_image_name);
40
failed_opts:
41
g_free(mon_host);
42
return r;
43
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
44
goto out;
45
}
46
47
- r = qemu_rbd_connect(&s->cluster, &s->io_ctx, &s->snap, &s->image_name,
48
- opts, !(flags & BDRV_O_NOCACHE), keypairs, secretid,
49
- errp);
50
+ r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts,
51
+ !(flags & BDRV_O_NOCACHE), keypairs, secretid, errp);
52
if (r < 0) {
53
goto out;
54
}
55
56
+ s->snap = g_strdup(opts->snapshot);
57
+ s->image_name = g_strdup(opts->image);
58
+
59
/* rbd_open is always r/w */
60
r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap);
61
if (r < 0) {
62
--
63
2.13.6
64
65
diff view generated by jsdifflib
Deleted patch
1
The "redundancy" option for Sheepdog image creation is currently a
2
string that can encode one or two integers depending on its format,
3
which at the same time implicitly selects a mode.
4
1
5
This patch turns it into a QAPI union and converts the string into such
6
a QAPI object before interpreting the values.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
---
11
qapi/block-core.json | 45 +++++++++++++++++++++++++
12
block/sheepdog.c | 94 +++++++++++++++++++++++++++++++++++++---------------
13
2 files changed, 112 insertions(+), 27 deletions(-)
14
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
index XXXXXXX..XXXXXXX 100644
17
--- a/qapi/block-core.json
18
+++ b/qapi/block-core.json
19
@@ -XXX,XX +XXX,XX @@
20
'*cluster-size' : 'size' } }
21
22
##
23
+# @SheepdogRedundancyType:
24
+#
25
+# @full Create a fully replicated vdi with x copies
26
+# @erasure-coded Create an erasure coded vdi with x data strips and
27
+# y parity strips
28
+#
29
+# Since: 2.12
30
+##
31
+{ 'enum': 'SheepdogRedundancyType',
32
+ 'data': [ 'full', 'erasure-coded' ] }
33
+
34
+##
35
+# @SheepdogRedundancyFull:
36
+#
37
+# @copies Number of copies to use (between 1 and 31)
38
+#
39
+# Since: 2.12
40
+##
41
+{ 'struct': 'SheepdogRedundancyFull',
42
+ 'data': { 'copies': 'int' }}
43
+
44
+##
45
+# @SheepdogRedundancyErasureCoded:
46
+#
47
+# @data-strips Number of data strips to use (one of {2,4,8,16})
48
+# @parity-strips Number of parity strips to use (between 1 and 15)
49
+#
50
+# Since: 2.12
51
+##
52
+{ 'struct': 'SheepdogRedundancyErasureCoded',
53
+ 'data': { 'data-strips': 'int',
54
+ 'parity-strips': 'int' }}
55
+
56
+##
57
+# @SheepdogRedundancy:
58
+#
59
+# Since: 2.12
60
+##
61
+{ 'union': 'SheepdogRedundancy',
62
+ 'base': { 'type': 'SheepdogRedundancyType' },
63
+ 'discriminator': 'type',
64
+ 'data': { 'full': 'SheepdogRedundancyFull',
65
+ 'erasure-coded': 'SheepdogRedundancyErasureCoded' } }
66
+
67
+##
68
# @BlockdevCreateNotSupported:
69
#
70
# This is used for all drivers that don't support creating images.
71
diff --git a/block/sheepdog.c b/block/sheepdog.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/block/sheepdog.c
74
+++ b/block/sheepdog.c
75
@@ -XXX,XX +XXX,XX @@ out_with_err_set:
76
return ret;
77
}
78
79
+static int parse_redundancy(BDRVSheepdogState *s, SheepdogRedundancy *opt)
80
+{
81
+ struct SheepdogInode *inode = &s->inode;
82
+
83
+ switch (opt->type) {
84
+ case SHEEPDOG_REDUNDANCY_TYPE_FULL:
85
+ if (opt->u.full.copies > SD_MAX_COPIES || opt->u.full.copies < 1) {
86
+ return -EINVAL;
87
+ }
88
+ inode->copy_policy = 0;
89
+ inode->nr_copies = opt->u.full.copies;
90
+ return 0;
91
+
92
+ case SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED:
93
+ {
94
+ int64_t copy = opt->u.erasure_coded.data_strips;
95
+ int64_t parity = opt->u.erasure_coded.parity_strips;
96
+
97
+ if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
98
+ return -EINVAL;
99
+ }
100
+
101
+ if (parity >= SD_EC_MAX_STRIP || parity < 1) {
102
+ return -EINVAL;
103
+ }
104
+
105
+ /*
106
+ * 4 bits for parity and 4 bits for data.
107
+ * We have to compress upper data bits because it can't represent 16
108
+ */
109
+ inode->copy_policy = ((copy / 2) << 4) + parity;
110
+ inode->nr_copies = copy + parity;
111
+ return 0;
112
+ }
113
+
114
+ default:
115
+ g_assert_not_reached();
116
+ }
117
+
118
+ return -EINVAL;
119
+}
120
+
121
/*
122
* Sheepdog support two kinds of redundancy, full replication and erasure
123
* coding.
124
@@ -XXX,XX +XXX,XX @@ out_with_err_set:
125
* # create a erasure coded vdi with x data strips and y parity strips
126
* -o redundancy=x:y (x must be one of {2,4,8,16} and 1 <= y < SD_EC_MAX_STRIP)
127
*/
128
-static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
129
+static int parse_redundancy_str(BDRVSheepdogState *s, const char *opt)
130
{
131
- struct SheepdogInode *inode = &s->inode;
132
+ struct SheepdogRedundancy redundancy;
133
const char *n1, *n2;
134
long copy, parity;
135
char p[10];
136
+ int ret;
137
138
pstrcpy(p, sizeof(p), opt);
139
n1 = strtok(p, ":");
140
@@ -XXX,XX +XXX,XX @@ static int parse_redundancy(BDRVSheepdogState *s, const char *opt)
141
return -EINVAL;
142
}
143
144
- copy = strtol(n1, NULL, 10);
145
- /* FIXME fix error checking by switching to qemu_strtol() */
146
- if (copy > SD_MAX_COPIES || copy < 1) {
147
- return -EINVAL;
148
- }
149
- if (!n2) {
150
- inode->copy_policy = 0;
151
- inode->nr_copies = copy;
152
- return 0;
153
+ ret = qemu_strtol(n1, NULL, 10, &copy);
154
+ if (ret < 0) {
155
+ return ret;
156
}
157
158
- if (copy != 2 && copy != 4 && copy != 8 && copy != 16) {
159
- return -EINVAL;
160
- }
161
+ if (!n2) {
162
+ redundancy = (SheepdogRedundancy) {
163
+ .type = SHEEPDOG_REDUNDANCY_TYPE_FULL,
164
+ .u.full.copies = copy,
165
+ };
166
+ } else {
167
+ ret = qemu_strtol(n2, NULL, 10, &parity);
168
+ if (ret < 0) {
169
+ return ret;
170
+ }
171
172
- parity = strtol(n2, NULL, 10);
173
- /* FIXME fix error checking by switching to qemu_strtol() */
174
- if (parity >= SD_EC_MAX_STRIP || parity < 1) {
175
- return -EINVAL;
176
+ redundancy = (SheepdogRedundancy) {
177
+ .type = SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED,
178
+ .u.erasure_coded = {
179
+ .data_strips = copy,
180
+ .parity_strips = parity,
181
+ },
182
+ };
183
}
184
185
- /*
186
- * 4 bits for parity and 4 bits for data.
187
- * We have to compress upper data bits because it can't represent 16
188
- */
189
- inode->copy_policy = ((copy / 2) << 4) + parity;
190
- inode->nr_copies = copy + parity;
191
-
192
- return 0;
193
+ return parse_redundancy(s, &redundancy);
194
}
195
196
static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
197
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
198
g_free(buf);
199
buf = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
200
if (buf) {
201
- ret = parse_redundancy(s, buf);
202
+ ret = parse_redundancy_str(s, buf);
203
if (ret < 0) {
204
error_setg(errp, "Invalid redundancy mode: '%s'", buf);
205
goto out;
206
--
207
2.13.6
208
209
diff view generated by jsdifflib
1
Move the parsing of the QDict options up to the callers, in preparation
1
The bdrv_reopen*() implementation doesn't like it if the graph is
2
for the .bdrv_co_create implementation that directly gets a QAPI type.
2
changed between queuing nodes for reopen and actually reopening them
3
(one of the reasons is that queuing can be recursive).
4
5
So instead of draining the device only in bdrv_reopen_multiple(),
6
require that callers already drained all affected nodes, and assert this
7
in bdrv_reopen_queue().
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
6
---
11
---
7
block/ssh.c | 34 +++++++++++++++++++++-------------
12
block.c | 23 ++++++++++++++++-------
8
1 file changed, 21 insertions(+), 13 deletions(-)
13
block/replication.c | 6 ++++++
14
qemu-io-cmds.c | 3 +++
15
3 files changed, 25 insertions(+), 7 deletions(-)
9
16
10
diff --git a/block/ssh.c b/block/ssh.c
17
diff --git a/block.c b/block.c
11
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
12
--- a/block/ssh.c
19
--- a/block.c
13
+++ b/block/ssh.c
20
+++ b/block.c
14
@@ -XXX,XX +XXX,XX @@ fail:
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
15
return result;
22
* returns a pointer to bs_queue, which is either the newly allocated
16
}
23
* bs_queue, or the existing bs_queue being used.
17
24
*
18
-static int connect_to_ssh(BDRVSSHState *s, QDict *options,
25
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
19
+static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
26
*/
20
int ssh_flags, int creat_mode, Error **errp)
27
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
28
BlockDriverState *bs,
29
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
30
BdrvChild *child;
31
QDict *old_options, *explicit_options;
32
33
+ /* Make sure that the caller remembered to use a drained section. This is
34
+ * important to avoid graph changes between the recursive queuing here and
35
+ * bdrv_reopen_multiple(). */
36
+ assert(bs->quiesce_counter > 0);
37
+
38
if (bs_queue == NULL) {
39
bs_queue = g_new0(BlockReopenQueue, 1);
40
QSIMPLEQ_INIT(bs_queue);
41
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
42
* If all devices prepare successfully, then the changes are committed
43
* to all devices.
44
*
45
+ * All affected nodes must be drained between bdrv_reopen_queue() and
46
+ * bdrv_reopen_multiple().
47
*/
48
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
21
{
49
{
22
- BlockdevOptionsSsh *opts;
50
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
23
int r, ret;
51
24
const char *user;
52
assert(bs_queue != NULL);
25
long port = 0;
53
26
54
- aio_context_release(ctx);
27
- opts = ssh_parse_options(options, errp);
55
- bdrv_drain_all_begin();
28
- if (opts == NULL) {
56
- aio_context_acquire(ctx);
29
- return -EINVAL;
30
- }
31
-
57
-
32
if (opts->has_user) {
58
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
33
user = opts->user;
59
+ assert(bs_entry->state.bs->quiesce_counter > 0);
34
} else {
60
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
35
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
61
error_propagate(errp, local_err);
36
goto err;
62
goto cleanup;
63
@@ -XXX,XX +XXX,XX @@ cleanup:
37
}
64
}
38
65
g_free(bs_queue);
39
- qapi_free_BlockdevOptionsSsh(opts);
66
40
-
67
- bdrv_drain_all_end();
41
r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
42
if (r < 0) {
43
sftp_error_setg(errp, s, "failed to read file attributes");
44
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
45
}
46
s->session = NULL;
47
48
- qapi_free_BlockdevOptionsSsh(opts);
49
-
68
-
50
return ret;
69
return ret;
51
}
70
}
52
71
53
@@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
54
Error **errp)
55
{
73
{
56
BDRVSSHState *s = bs->opaque;
74
int ret = -1;
57
+ BlockdevOptionsSsh *opts;
75
Error *local_err = NULL;
58
int ret;
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
59
int ssh_flags;
77
+ BlockReopenQueue *queue;
60
78
61
@@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
79
+ bdrv_subtree_drained_begin(bs);
62
ssh_flags |= LIBSSH2_FXF_WRITE;
80
+
81
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
82
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
83
if (local_err != NULL) {
84
error_propagate(errp, local_err);
63
}
85
}
64
65
+ opts = ssh_parse_options(options, errp);
66
+ if (opts == NULL) {
67
+ return -EINVAL;
68
+ }
69
+
86
+
70
/* Start up SSH. */
87
+ bdrv_subtree_drained_end(bs);
71
- ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
72
+ ret = connect_to_ssh(s, opts, ssh_flags, 0, errp);
73
if (ret < 0) {
74
goto err;
75
}
76
@@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
77
/* Go non-blocking. */
78
libssh2_session_set_blocking(s->session, 0);
79
80
+ qapi_free_BlockdevOptionsSsh(opts);
81
+
82
return 0;
83
84
err:
85
@@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
86
}
87
s->sock = -1;
88
89
+ qapi_free_BlockdevOptionsSsh(opts);
90
+
88
+
91
return ret;
89
return ret;
92
}
90
}
93
91
94
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
92
diff --git a/block/replication.c b/block/replication.c
95
int r, ret;
93
index XXXXXXX..XXXXXXX 100644
96
int64_t total_size = 0;
94
--- a/block/replication.c
97
QDict *uri_options = NULL;
95
+++ b/block/replication.c
98
+ BlockdevOptionsSsh *ssh_opts = NULL;
96
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
99
BDRVSSHState s;
97
new_secondary_flags = s->orig_secondary_flags;
100
101
ssh_state_init(&s);
102
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
103
goto out;
104
}
98
}
105
99
106
- r = connect_to_ssh(&s, uri_options,
100
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
107
+ ssh_opts = ssh_parse_options(uri_options, errp);
101
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
108
+ if (ssh_opts == NULL) {
109
+ ret = -EINVAL;
110
+ goto out;
111
+ }
112
+
102
+
113
+ r = connect_to_ssh(&s, ssh_opts,
103
if (orig_hidden_flags != new_hidden_flags) {
114
LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
104
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
115
LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
105
new_hidden_flags);
116
0644, errp);
106
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
117
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
107
reopen_queue, &local_err);
118
if (uri_options != NULL) {
108
error_propagate(errp, local_err);
119
QDECREF(uri_options);
120
}
109
}
121
+ qapi_free_BlockdevOptionsSsh(ssh_opts);
110
+
122
return ret;
111
+ bdrv_subtree_drained_end(s->hidden_disk->bs);
112
+ bdrv_subtree_drained_end(s->secondary_disk->bs);
123
}
113
}
124
114
115
static void backup_job_cleanup(BlockDriverState *bs)
116
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
117
index XXXXXXX..XXXXXXX 100644
118
--- a/qemu-io-cmds.c
119
+++ b/qemu-io-cmds.c
120
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
121
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
122
qemu_opts_reset(&reopen_opts);
123
124
+ bdrv_subtree_drained_begin(bs);
125
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
126
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
127
+ bdrv_subtree_drained_end(bs);
128
+
129
if (local_err) {
130
error_report_err(local_err);
131
} else {
125
--
132
--
126
2.13.6
133
2.13.6
127
134
128
135
diff view generated by jsdifflib
Deleted patch
1
If bdrv_truncate() is called, but the requested size is the same as
2
before, don't call posix_fallocate(), which returns -EINVAL for length
3
zero and would therefore make bdrv_truncate() fail.
4
1
5
The problem can be triggered by creating a zero-sized raw image with
6
'falloc' preallocation mode.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
---
12
block/file-posix.c | 14 +++++++++-----
13
1 file changed, 9 insertions(+), 5 deletions(-)
14
15
diff --git a/block/file-posix.c b/block/file-posix.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/file-posix.c
18
+++ b/block/file-posix.c
19
@@ -XXX,XX +XXX,XX @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
20
* file systems that do not support fallocate(), trying to check if a
21
* block is allocated before allocating it, so don't do that here.
22
*/
23
- result = -posix_fallocate(fd, current_length, offset - current_length);
24
- if (result != 0) {
25
- /* posix_fallocate() doesn't set errno. */
26
- error_setg_errno(errp, -result,
27
- "Could not preallocate new data");
28
+ if (offset != current_length) {
29
+ result = -posix_fallocate(fd, current_length, offset - current_length);
30
+ if (result != 0) {
31
+ /* posix_fallocate() doesn't set errno. */
32
+ error_setg_errno(errp, -result,
33
+ "Could not preallocate new data");
34
+ }
35
+ } else {
36
+ result = 0;
37
}
38
goto out;
39
#endif
40
--
41
2.13.6
42
43
diff view generated by jsdifflib
Deleted patch
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Max Reitz <mreitz@redhat.com>
3
---
4
tests/qemu-iotests/206 | 436 +++++++++++++++++++++++++++++++++++++++++++++
5
tests/qemu-iotests/206.out | 209 ++++++++++++++++++++++
6
tests/qemu-iotests/group | 1 +
7
3 files changed, 646 insertions(+)
8
create mode 100755 tests/qemu-iotests/206
9
create mode 100644 tests/qemu-iotests/206.out
10
1
11
diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206
12
new file mode 100755
13
index XXXXXXX..XXXXXXX
14
--- /dev/null
15
+++ b/tests/qemu-iotests/206
16
@@ -XXX,XX +XXX,XX @@
17
+#!/bin/bash
18
+#
19
+# Test qcow2 and file image creation
20
+#
21
+# Copyright (C) 2018 Red Hat, Inc.
22
+#
23
+# This program is free software; you can redistribute it and/or modify
24
+# it under the terms of the GNU General Public License as published by
25
+# the Free Software Foundation; either version 2 of the License, or
26
+# (at your option) any later version.
27
+#
28
+# This program is distributed in the hope that it will be useful,
29
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
30
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
+# GNU General Public License for more details.
32
+#
33
+# You should have received a copy of the GNU General Public License
34
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
35
+#
36
+
37
+# creator
38
+owner=kwolf@redhat.com
39
+
40
+seq=`basename $0`
41
+echo "QA output created by $seq"
42
+
43
+here=`pwd`
44
+status=1    # failure is the default!
45
+
46
+# get standard environment, filters and checks
47
+. ./common.rc
48
+. ./common.filter
49
+
50
+_supported_fmt qcow2
51
+_supported_proto file
52
+_supported_os Linux
53
+
54
+function do_run_qemu()
55
+{
56
+ echo Testing: "$@"
57
+ $QEMU -nographic -qmp stdio -serial none "$@"
58
+ echo
59
+}
60
+
61
+function run_qemu()
62
+{
63
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
64
+ | _filter_qemu | _filter_imgfmt \
65
+ | _filter_actual_image_size
66
+}
67
+
68
+echo
69
+echo "=== Successful image creation (defaults) ==="
70
+echo
71
+
72
+size=$((128 * 1024 * 1024))
73
+
74
+run_qemu <<EOF
75
+{ "execute": "qmp_capabilities" }
76
+{ "execute": "x-blockdev-create",
77
+ "arguments": {
78
+ "driver": "file",
79
+ "filename": "$TEST_IMG",
80
+ "size": 0
81
+ }
82
+}
83
+{ "execute": "blockdev-add",
84
+ "arguments": {
85
+ "driver": "file",
86
+ "node-name": "imgfile",
87
+ "filename": "$TEST_IMG"
88
+ }
89
+}
90
+{ "execute": "x-blockdev-create",
91
+ "arguments": {
92
+ "driver": "$IMGFMT",
93
+ "file": "imgfile",
94
+ "size": $size
95
+ }
96
+}
97
+{ "execute": "quit" }
98
+EOF
99
+
100
+_img_info --format-specific
101
+
102
+echo
103
+echo "=== Successful image creation (inline blockdev-add, explicit defaults) ==="
104
+echo
105
+
106
+# Choose a different size to show that we got a new image
107
+size=$((64 * 1024 * 1024))
108
+
109
+run_qemu <<EOF
110
+{ "execute": "qmp_capabilities" }
111
+{ "execute": "x-blockdev-create",
112
+ "arguments": {
113
+ "driver": "file",
114
+ "filename": "$TEST_IMG",
115
+ "size": 0,
116
+ "preallocation": "off",
117
+ "nocow": false
118
+ }
119
+}
120
+{ "execute": "x-blockdev-create",
121
+ "arguments": {
122
+ "driver": "$IMGFMT",
123
+ "file": {
124
+ "driver": "file",
125
+ "filename": "$TEST_IMG"
126
+ },
127
+ "size": $size,
128
+ "version": "v3",
129
+ "cluster-size": 65536,
130
+ "preallocation": "off",
131
+ "lazy-refcounts": false,
132
+ "refcount-bits": 16
133
+ }
134
+}
135
+{ "execute": "quit" }
136
+EOF
137
+
138
+_img_info --format-specific
139
+
140
+echo
141
+echo "=== Successful image creation (v3 non-default options) ==="
142
+echo
143
+
144
+# Choose a different size to show that we got a new image
145
+size=$((32 * 1024 * 1024))
146
+
147
+run_qemu <<EOF
148
+{ "execute": "qmp_capabilities" }
149
+{ "execute": "x-blockdev-create",
150
+ "arguments": {
151
+ "driver": "file",
152
+ "filename": "$TEST_IMG",
153
+ "size": 0,
154
+ "preallocation": "falloc",
155
+ "nocow": true
156
+ }
157
+}
158
+{ "execute": "x-blockdev-create",
159
+ "arguments": {
160
+ "driver": "$IMGFMT",
161
+ "file": {
162
+ "driver": "file",
163
+ "filename": "$TEST_IMG"
164
+ },
165
+ "size": $size,
166
+ "version": "v3",
167
+ "cluster-size": 2097152,
168
+ "preallocation": "metadata",
169
+ "lazy-refcounts": true,
170
+ "refcount-bits": 1
171
+ }
172
+}
173
+{ "execute": "quit" }
174
+EOF
175
+
176
+_img_info --format-specific
177
+
178
+echo
179
+echo "=== Successful image creation (v2 non-default options) ==="
180
+echo
181
+
182
+mv $TEST_IMG $TEST_IMG.base
183
+
184
+run_qemu <<EOF
185
+{ "execute": "qmp_capabilities" }
186
+{ "execute": "x-blockdev-create",
187
+ "arguments": {
188
+ "driver": "file",
189
+ "filename": "$TEST_IMG",
190
+ "size": 0
191
+ }
192
+}
193
+{ "execute": "x-blockdev-create",
194
+ "arguments": {
195
+ "driver": "$IMGFMT",
196
+ "file": {
197
+ "driver": "file",
198
+ "filename": "$TEST_IMG"
199
+ },
200
+ "size": $size,
201
+ "backing-file": "$TEST_IMG.base",
202
+ "backing-fmt": "qcow2",
203
+ "version": "v2",
204
+ "cluster-size": 512
205
+ }
206
+}
207
+{ "execute": "quit" }
208
+EOF
209
+
210
+_img_info --format-specific
211
+
212
+echo
213
+echo "=== Successful image creation (encrypted) ==="
214
+echo
215
+
216
+run_qemu -object secret,id=keysec0,data="foo" <<EOF
217
+{ "execute": "qmp_capabilities" }
218
+{ "execute": "x-blockdev-create",
219
+ "arguments": {
220
+ "driver": "$IMGFMT",
221
+ "file": {
222
+ "driver": "file",
223
+ "filename": "$TEST_IMG"
224
+ },
225
+ "size": $size,
226
+ "encrypt": {
227
+ "format": "luks",
228
+ "key-secret": "keysec0",
229
+ "cipher-alg": "twofish-128",
230
+ "cipher-mode": "ctr",
231
+ "ivgen-alg": "plain64",
232
+ "ivgen-hash-alg": "md5",
233
+ "hash-alg": "sha1",
234
+ "iter-time": 10
235
+ }
236
+ }
237
+}
238
+{ "execute": "quit" }
239
+EOF
240
+
241
+_img_info --format-specific | _filter_img_info --format-specific
242
+
243
+echo
244
+echo "=== Invalid BlockdevRef ==="
245
+echo
246
+
247
+run_qemu <<EOF
248
+{ "execute": "qmp_capabilities" }
249
+{ "execute": "x-blockdev-create",
250
+ "arguments": {
251
+ "driver": "$IMGFMT",
252
+ "file": "this doesn't exist",
253
+ "size": $size
254
+ }
255
+}
256
+{ "execute": "quit" }
257
+EOF
258
+
259
+
260
+echo
261
+echo "=== Invalid sizes ==="
262
+echo
263
+
264
+# TODO Negative image sizes aren't handled correctly, but this is a problem
265
+# with QAPI's implementation of the 'size' type and affects other commands as
266
+# well. Once this is fixed, we may want to add a test case here.
267
+
268
+# 1. Misaligned image size
269
+# 2. 2^64 - 512
270
+# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
271
+# 4. 2^63 - 512 (generally valid, but qcow2 can't handle images this size)
272
+
273
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
274
+{ "execute": "qmp_capabilities" }
275
+{ "execute": "x-blockdev-create",
276
+ "arguments": {
277
+ "driver": "$IMGFMT",
278
+ "file": "node0",
279
+ "size": 1234
280
+ }
281
+}
282
+{ "execute": "x-blockdev-create",
283
+ "arguments": {
284
+ "driver": "$IMGFMT",
285
+ "file": "node0",
286
+ "size": 18446744073709551104
287
+ }
288
+}
289
+{ "execute": "x-blockdev-create",
290
+ "arguments": {
291
+ "driver": "$IMGFMT",
292
+ "file": "node0",
293
+ "size": 9223372036854775808
294
+ }
295
+}
296
+{ "execute": "x-blockdev-create",
297
+ "arguments": {
298
+ "driver": "$IMGFMT",
299
+ "file": "node0",
300
+ "size": 9223372036854775296
301
+ }
302
+}
303
+{ "execute": "quit" }
304
+EOF
305
+
306
+echo
307
+echo "=== Invalid version ==="
308
+echo
309
+
310
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
311
+{ "execute": "qmp_capabilities" }
312
+{ "execute": "x-blockdev-create",
313
+ "arguments": {
314
+ "driver": "$IMGFMT",
315
+ "file": "node0",
316
+ "size": 67108864,
317
+ "version": "v1"
318
+ }
319
+}
320
+{ "execute": "x-blockdev-create",
321
+ "arguments": {
322
+ "driver": "$IMGFMT",
323
+ "file": "node0",
324
+ "size": 67108864,
325
+ "version": "v2",
326
+ "lazy-refcounts": true
327
+ }
328
+}
329
+{ "execute": "x-blockdev-create",
330
+ "arguments": {
331
+ "driver": "$IMGFMT",
332
+ "file": "node0",
333
+ "size": 67108864,
334
+ "version": "v2",
335
+ "refcount-bits": 8
336
+ }
337
+}
338
+{ "execute": "quit" }
339
+EOF
340
+
341
+echo
342
+echo "=== Invalid backing file options ==="
343
+echo
344
+
345
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
346
+{ "execute": "qmp_capabilities" }
347
+{ "execute": "x-blockdev-create",
348
+ "arguments": {
349
+ "driver": "$IMGFMT",
350
+ "file": "node0",
351
+ "size": 67108864,
352
+ "backing-file": "/dev/null",
353
+ "preallocation": "full"
354
+ }
355
+}
356
+{ "execute": "x-blockdev-create",
357
+ "arguments": {
358
+ "driver": "$IMGFMT",
359
+ "file": "node0",
360
+ "size": 67108864,
361
+ "backing-fmt": "$IMGFMT"
362
+ }
363
+}
364
+{ "execute": "quit" }
365
+EOF
366
+
367
+echo
368
+echo "=== Invalid cluster size ==="
369
+echo
370
+
371
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
372
+{ "execute": "qmp_capabilities" }
373
+{ "execute": "x-blockdev-create",
374
+ "arguments": {
375
+ "driver": "$IMGFMT",
376
+ "file": "node0",
377
+ "size": 67108864,
378
+ "cluster-size": 1234
379
+ }
380
+}
381
+{ "execute": "x-blockdev-create",
382
+ "arguments": {
383
+ "driver": "$IMGFMT",
384
+ "file": "node0",
385
+ "size": 67108864,
386
+ "cluster-size": 128
387
+ }
388
+}
389
+{ "execute": "x-blockdev-create",
390
+ "arguments": {
391
+ "driver": "$IMGFMT",
392
+ "file": "node0",
393
+ "size": 67108864,
394
+ "cluster-size": 4194304
395
+ }
396
+}
397
+{ "execute": "x-blockdev-create",
398
+ "arguments": {
399
+ "driver": "$IMGFMT",
400
+ "file": "node0",
401
+ "size": 67108864,
402
+ "cluster-size": 0
403
+ }
404
+}
405
+{ "execute": "x-blockdev-create",
406
+ "arguments": {
407
+ "driver": "$IMGFMT",
408
+ "file": "node0",
409
+ "size": 281474976710656,
410
+ "cluster-size": 512
411
+ }
412
+}
413
+{ "execute": "quit" }
414
+EOF
415
+
416
+echo
417
+echo "=== Invalid refcount width ==="
418
+echo
419
+
420
+run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF
421
+{ "execute": "qmp_capabilities" }
422
+{ "execute": "x-blockdev-create",
423
+ "arguments": {
424
+ "driver": "$IMGFMT",
425
+ "file": "node0",
426
+ "size": 67108864,
427
+ "refcount-bits": 128
428
+ }
429
+}
430
+{ "execute": "x-blockdev-create",
431
+ "arguments": {
432
+ "driver": "$IMGFMT",
433
+ "file": "node0",
434
+ "size": 67108864,
435
+ "refcount-bits": 0
436
+ }
437
+}
438
+{ "execute": "x-blockdev-create",
439
+ "arguments": {
440
+ "driver": "$IMGFMT",
441
+ "file": "node0",
442
+ "size": 67108864,
443
+ "refcount-bits": 7
444
+ }
445
+}
446
+{ "execute": "quit" }
447
+EOF
448
+
449
+# success, all done
450
+echo "*** done"
451
+rm -f $seq.full
452
+status=0
453
diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out
454
new file mode 100644
455
index XXXXXXX..XXXXXXX
456
--- /dev/null
457
+++ b/tests/qemu-iotests/206.out
458
@@ -XXX,XX +XXX,XX @@
459
+QA output created by 206
460
+
461
+=== Successful image creation (defaults) ===
462
+
463
+Testing:
464
+QMP_VERSION
465
+{"return": {}}
466
+{"return": {}}
467
+{"return": {}}
468
+{"return": {}}
469
+{"return": {}}
470
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
471
+
472
+image: TEST_DIR/t.IMGFMT
473
+file format: IMGFMT
474
+virtual size: 128M (134217728 bytes)
475
+cluster_size: 65536
476
+Format specific information:
477
+ compat: 1.1
478
+ lazy refcounts: false
479
+ refcount bits: 16
480
+ corrupt: false
481
+
482
+=== Successful image creation (inline blockdev-add, explicit defaults) ===
483
+
484
+Testing:
485
+QMP_VERSION
486
+{"return": {}}
487
+{"return": {}}
488
+{"return": {}}
489
+{"return": {}}
490
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
491
+
492
+image: TEST_DIR/t.IMGFMT
493
+file format: IMGFMT
494
+virtual size: 64M (67108864 bytes)
495
+cluster_size: 65536
496
+Format specific information:
497
+ compat: 1.1
498
+ lazy refcounts: false
499
+ refcount bits: 16
500
+ corrupt: false
501
+
502
+=== Successful image creation (v3 non-default options) ===
503
+
504
+Testing:
505
+QMP_VERSION
506
+{"return": {}}
507
+{"return": {}}
508
+{"return": {}}
509
+{"return": {}}
510
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
511
+
512
+image: TEST_DIR/t.IMGFMT
513
+file format: IMGFMT
514
+virtual size: 32M (33554432 bytes)
515
+cluster_size: 2097152
516
+Format specific information:
517
+ compat: 1.1
518
+ lazy refcounts: true
519
+ refcount bits: 1
520
+ corrupt: false
521
+
522
+=== Successful image creation (v2 non-default options) ===
523
+
524
+Testing:
525
+QMP_VERSION
526
+{"return": {}}
527
+{"return": {}}
528
+{"return": {}}
529
+{"return": {}}
530
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
531
+
532
+image: TEST_DIR/t.IMGFMT
533
+file format: IMGFMT
534
+virtual size: 32M (33554432 bytes)
535
+cluster_size: 512
536
+backing file: TEST_DIR/t.IMGFMT.base
537
+backing file format: IMGFMT
538
+Format specific information:
539
+ compat: 0.10
540
+ refcount bits: 16
541
+
542
+=== Successful image creation (encrypted) ===
543
+
544
+Testing: -object secret,id=keysec0,data=foo
545
+QMP_VERSION
546
+{"return": {}}
547
+{"return": {}}
548
+{"return": {}}
549
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
550
+
551
+image: TEST_DIR/t.IMGFMT
552
+file format: IMGFMT
553
+virtual size: 32M (33554432 bytes)
554
+Format specific information:
555
+ compat: 1.1
556
+ lazy refcounts: false
557
+ refcount bits: 16
558
+ encrypt:
559
+ ivgen alg: plain64
560
+ hash alg: sha1
561
+ cipher alg: twofish-128
562
+ uuid: 00000000-0000-0000-0000-000000000000
563
+ format: luks
564
+ cipher mode: ctr
565
+ slots:
566
+ [0]:
567
+ active: true
568
+ iters: 1024
569
+ key offset: 4096
570
+ stripes: 4000
571
+ [1]:
572
+ active: false
573
+ key offset: 69632
574
+ [2]:
575
+ active: false
576
+ key offset: 135168
577
+ [3]:
578
+ active: false
579
+ key offset: 200704
580
+ [4]:
581
+ active: false
582
+ key offset: 266240
583
+ [5]:
584
+ active: false
585
+ key offset: 331776
586
+ [6]:
587
+ active: false
588
+ key offset: 397312
589
+ [7]:
590
+ active: false
591
+ key offset: 462848
592
+ payload offset: 528384
593
+ master key iters: 1024
594
+ corrupt: false
595
+
596
+=== Invalid BlockdevRef ===
597
+
598
+Testing:
599
+QMP_VERSION
600
+{"return": {}}
601
+{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}}
602
+{"return": {}}
603
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
604
+
605
+
606
+=== Invalid sizes ===
607
+
608
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
609
+QMP_VERSION
610
+{"return": {}}
611
+{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}}
612
+{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}}
613
+{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}}
614
+{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}}
615
+{"return": {}}
616
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
617
+
618
+
619
+=== Invalid version ===
620
+
621
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
622
+QMP_VERSION
623
+{"return": {}}
624
+{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}}
625
+{"error": {"class": "GenericError", "desc": "Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)"}}
626
+{"error": {"class": "GenericError", "desc": "Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)"}}
627
+{"return": {}}
628
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
629
+
630
+
631
+=== Invalid backing file options ===
632
+
633
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
634
+QMP_VERSION
635
+{"return": {}}
636
+{"error": {"class": "GenericError", "desc": "Backing file and preallocation cannot be used at the same time"}}
637
+{"error": {"class": "GenericError", "desc": "Backing format cannot be used without backing file"}}
638
+{"return": {}}
639
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
640
+
641
+
642
+=== Invalid cluster size ===
643
+
644
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
645
+QMP_VERSION
646
+{"return": {}}
647
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
648
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
649
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
650
+{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}}
651
+{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}}
652
+{"return": {}}
653
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
654
+
655
+
656
+=== Invalid refcount width ===
657
+
658
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0
659
+QMP_VERSION
660
+{"return": {}}
661
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
662
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
663
+{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}}
664
+{"return": {}}
665
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
666
+
667
+*** done
668
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
669
index XXXXXXX..XXXXXXX 100644
670
--- a/tests/qemu-iotests/group
671
+++ b/tests/qemu-iotests/group
672
@@ -XXX,XX +XXX,XX @@
673
203 rw auto
674
204 rw auto quick
675
205 rw auto quick
676
+206 rw auto
677
--
678
2.13.6
679
680
diff view generated by jsdifflib
Deleted patch
1
From: Fam Zheng <famz@redhat.com>
2
1
3
Signed-off-by: Fam Zheng <famz@redhat.com>
4
Message-id: 20171225025107.23985-1-famz@redhat.com
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
---
8
tests/qemu-iotests/153 | 8 +++++---
9
tests/qemu-iotests/153.out | 7 ++++---
10
2 files changed, 9 insertions(+), 6 deletions(-)
11
12
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
13
index XXXXXXX..XXXXXXX 100755
14
--- a/tests/qemu-iotests/153
15
+++ b/tests/qemu-iotests/153
16
@@ -XXX,XX +XXX,XX @@ _cleanup()
17
{
18
_cleanup_test_img
19
rm -f "${TEST_IMG}.base"
20
+ rm -f "${TEST_IMG}.overlay"
21
rm -f "${TEST_IMG}.convert"
22
rm -f "${TEST_IMG}.a"
23
rm -f "${TEST_IMG}.b"
24
@@ -XXX,XX +XXX,XX @@ rm -f "${TEST_IMG}.lnk" &>/dev/null
25
ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link"
26
_run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}"
27
28
-echo
29
-echo "== Closing an image should unlock it =="
30
_launch_qemu
31
32
_send_qemu_cmd $QEMU_HANDLE \
33
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $QEMU_HANDLE \
34
35
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
36
37
-echo "Closing drive"
38
+echo "Creating overlay with qemu-img when the guest is running should be allowed"
39
+_run_cmd $QEMU_IMG create -f $IMGFMT -b "${TEST_IMG}" "${TEST_IMG}.overlay"
40
+
41
+echo "== Closing an image should unlock it =="
42
_send_qemu_cmd $QEMU_HANDLE \
43
"{ 'execute': 'human-monitor-command',
44
'arguments': { 'command-line': 'drive_del d0' } }" \
45
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
46
index XXXXXXX..XXXXXXX 100644
47
--- a/tests/qemu-iotests/153.out
48
+++ b/tests/qemu-iotests/153.out
49
@@ -XXX,XX +XXX,XX @@ Is another process using the image?
50
== Symbolic link ==
51
QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
52
Is another process using the image?
53
-
54
-== Closing an image should unlock it ==
55
{"return": {}}
56
Adding drive
57
58
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
59
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
60
Is another process using the image?
61
-Closing drive
62
+Creating overlay with qemu-img when the guest is running should be allowed
63
+
64
+_qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay
65
+== Closing an image should unlock it ==
66
67
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
68
Adding two and closing one
69
--
70
2.13.6
71
72
diff view generated by jsdifflib
Deleted patch
1
From: Alberto Garcia <berto@igalia.com>
2
1
3
This patch tweaks TestParallelOps in iotest 030 so it allocates data
4
in smaller regions (256KB/512KB instead of 512KB/1MB) and the
5
block-stream job in test_stream_commit() only needs to copy data that
6
is at the very end of the image.
7
8
This way when the block-stream job is awakened it will finish right
9
away without any chance of being stopped by block_job_sleep_ns(). This
10
triggers the bug that was fixed by 3d5d319e1221082974711af1d09d82f and
11
1a63a907507fbbcfaee3f622907ec24 and is therefore a more useful test
12
case for parallel block jobs.
13
14
After this patch the aforementiond bug can also be reproduced with the
15
test_stream_parallel() test case.
16
17
Since with this change the stream job in test_stream_commit() finishes
18
early, this patch introduces a similar test case where both jobs are
19
slowed down so they can actually run in parallel.
20
21
Signed-off-by: Alberto Garcia <berto@igalia.com>
22
Cc: John Snow <jsnow@redhat.com>
23
Message-id: 20180306130121.30243-1-berto@igalia.com
24
Signed-off-by: Max Reitz <mreitz@redhat.com>
25
---
26
tests/qemu-iotests/030 | 52 ++++++++++++++++++++++++++++++++++++++--------
27
tests/qemu-iotests/030.out | 4 ++--
28
2 files changed, 45 insertions(+), 11 deletions(-)
29
30
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
31
index XXXXXXX..XXXXXXX 100755
32
--- a/tests/qemu-iotests/030
33
+++ b/tests/qemu-iotests/030
34
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase):
35
class TestParallelOps(iotests.QMPTestCase):
36
num_ops = 4 # Number of parallel block-stream operations
37
num_imgs = num_ops * 2 + 1
38
- image_len = num_ops * 1024 * 1024
39
+ image_len = num_ops * 512 * 1024
40
imgs = []
41
42
def setUp(self):
43
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
44
'-o', 'backing_file=%s' % self.imgs[i-1], self.imgs[i])
45
46
# Put data into the images we are copying data from
47
- for i in range(self.num_imgs / 2):
48
- img_index = i * 2 + 1
49
- # Alternate between 512k and 1M.
50
+ odd_img_indexes = [x for x in reversed(range(self.num_imgs)) if x % 2 == 1]
51
+ for i in range(len(odd_img_indexes)):
52
+ # Alternate between 256KB and 512KB.
53
# This way jobs will not finish in the same order they were created
54
- num_kb = 512 + 512 * (i % 2)
55
+ num_kb = 256 + 256 * (i % 2)
56
qemu_io('-f', iotests.imgfmt,
57
- '-c', 'write -P %d %d %d' % (i, i*1024*1024, num_kb * 1024),
58
- self.imgs[img_index])
59
+ '-c', 'write -P 0xFF %dk %dk' % (i * 512, num_kb),
60
+ self.imgs[odd_img_indexes[i]])
61
62
# Attach the drive to the VM
63
self.vm = iotests.VM()
64
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
65
self.wait_until_completed(drive='commit-drive0')
66
67
# Test a block-stream and a block-commit job in parallel
68
- def test_stream_commit(self):
69
+ # Here the stream job is supposed to finish quickly in order to reproduce
70
+ # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507
71
+ def test_stream_commit_1(self):
72
self.assertLessEqual(8, self.num_imgs)
73
self.assert_no_active_block_jobs()
74
75
# Stream from node0 into node2
76
- result = self.vm.qmp('block-stream', device='node2', job_id='node2')
77
+ result = self.vm.qmp('block-stream', device='node2', base_node='node0', job_id='node2')
78
self.assert_qmp(result, 'return', {})
79
80
# Commit from the active layer into node3
81
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
82
83
self.assert_no_active_block_jobs()
84
85
+ # This is similar to test_stream_commit_1 but both jobs are slowed
86
+ # down so they can run in parallel for a little while.
87
+ def test_stream_commit_2(self):
88
+ self.assertLessEqual(8, self.num_imgs)
89
+ self.assert_no_active_block_jobs()
90
+
91
+ # Stream from node0 into node4
92
+ result = self.vm.qmp('block-stream', device='node4', base_node='node0', job_id='node4', speed=1024*1024)
93
+ self.assert_qmp(result, 'return', {})
94
+
95
+ # Commit from the active layer into node5
96
+ result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024)
97
+ self.assert_qmp(result, 'return', {})
98
+
99
+ # Wait for all jobs to be finished.
100
+ pending_jobs = ['node4', 'drive0']
101
+ while len(pending_jobs) > 0:
102
+ for event in self.vm.get_qmp_events(wait=True):
103
+ if event['event'] == 'BLOCK_JOB_COMPLETED':
104
+ node_name = self.dictpath(event, 'data/device')
105
+ self.assertTrue(node_name in pending_jobs)
106
+ self.assert_qmp_absent(event, 'data/error')
107
+ pending_jobs.remove(node_name)
108
+ if event['event'] == 'BLOCK_JOB_READY':
109
+ self.assert_qmp(event, 'data/device', 'drive0')
110
+ self.assert_qmp(event, 'data/type', 'commit')
111
+ self.assert_qmp_absent(event, 'data/error')
112
+ self.assertTrue('drive0' in pending_jobs)
113
+ self.vm.qmp('block-job-complete', device='drive0')
114
+
115
+ self.assert_no_active_block_jobs()
116
+
117
# Test the base_node parameter
118
def test_stream_base_node_name(self):
119
self.assert_no_active_block_jobs()
120
diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out
121
index XXXXXXX..XXXXXXX 100644
122
--- a/tests/qemu-iotests/030.out
123
+++ b/tests/qemu-iotests/030.out
124
@@ -XXX,XX +XXX,XX @@
125
-.......................
126
+........................
127
----------------------------------------------------------------------
128
-Ran 23 tests
129
+Ran 24 tests
130
131
OK
132
--
133
2.13.6
134
135
diff view generated by jsdifflib
Deleted patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
1
3
There is a race between the test's 'query-migrate' QMP command after the
4
QMP 'STOP' event and completing the migration:
5
6
The test case invokes 'query-migrate' upon receiving 'STOP'. At this
7
point the migration thread may still be in the process of completing.
8
Therefore 'query-migrate' can return 'status': 'active' for a brief
9
window of time instead of 'status': 'completed'. This results in
10
qemu-iotests 203 hanging.
11
12
Solve the race by enabling the 'events' migration capability, which
13
causes QEMU to emit migration-specific QMP events that do not suffer
14
from this race condition. Wait for the QMP 'MIGRATION' event with
15
'status': 'completed'.
16
17
Reported-by: Max Reitz <mreitz@redhat.com>
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Message-id: 20180305155926.25858-1-stefanha@redhat.com
20
Reviewed-by: Max Reitz <mreitz@redhat.com>
21
Signed-off-by: Max Reitz <mreitz@redhat.com>
22
---
23
tests/qemu-iotests/203 | 15 +++++++++++----
24
tests/qemu-iotests/203.out | 5 +++++
25
2 files changed, 16 insertions(+), 4 deletions(-)
26
27
diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203
28
index XXXXXXX..XXXXXXX 100755
29
--- a/tests/qemu-iotests/203
30
+++ b/tests/qemu-iotests/203
31
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('disk0.img') as disk0_img_path, \
32
node_name='drive1-node', iothread='iothread0',
33
force=True))
34
35
+ iotests.log('Enabling migration QMP events...')
36
+ iotests.log(vm.qmp('migrate-set-capabilities', capabilities=[
37
+ {
38
+ 'capability': 'events',
39
+ 'state': True
40
+ }
41
+ ]))
42
+
43
iotests.log('Starting migration...')
44
iotests.log(vm.qmp('migrate', uri='exec:cat >/dev/null'))
45
while True:
46
- vm.get_qmp_event(wait=60.0)
47
- result = vm.qmp('query-migrate')
48
- status = result.get('return', {}).get('status', None)
49
- if status == 'completed':
50
+ event = vm.event_wait('MIGRATION')
51
+ iotests.log(event, filters=[iotests.filter_qmp_event])
52
+ if event['data']['status'] == 'completed':
53
break
54
diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out
55
index XXXXXXX..XXXXXXX 100644
56
--- a/tests/qemu-iotests/203.out
57
+++ b/tests/qemu-iotests/203.out
58
@@ -XXX,XX +XXX,XX @@ Launching VM...
59
Setting IOThreads...
60
{u'return': {}}
61
{u'return': {}}
62
+Enabling migration QMP events...
63
+{u'return': {}}
64
Starting migration...
65
{u'return': {}}
66
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'}
67
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'}
68
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'}
69
--
70
2.13.6
71
72
diff view generated by jsdifflib