1
The following changes since commit 9514f2648ca05b38e852b490a12b8cd98d5808c1:
1
The following changes since commit 4c8c1cc544dbd5e2564868e61c5037258e393832:
2
2
3
Merge remote-tracking branch 'remotes/gkurz/tags/for-upstream' into staging (2017-02-28 17:39:49 +0000)
3
Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.10-pull-request' into staging (2017-06-22 19:01:58 +0100)
4
4
5
are available in the git repository at:
5
are available in the git repository at:
6
6
7
7
8
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
git://repo.or.cz/qemu/kevin.git tags/for-upstream
9
9
10
for you to fetch changes up to b2c2832c6140cfe3ddc0de2d77eeb0b77dea8fd3:
10
for you to fetch changes up to 1512008812410ca4054506a7c44343088abdd977:
11
11
12
block: Add Error parameter to bdrv_append() (2017-02-28 20:47:51 +0100)
12
Merge remote-tracking branch 'mreitz/tags/pull-block-2017-06-23' into queue-block (2017-06-23 14:09:12 +0200)
13
13
14
----------------------------------------------------------------
14
----------------------------------------------------------------
15
15
Block layer patches
16
Block layer patches
16
17
17
----------------------------------------------------------------
18
----------------------------------------------------------------
18
Kevin Wolf (44):
19
Alberto Garcia (9):
19
block: Add op blocker permission constants
20
throttle: Update throttle-groups.c documentation
20
block: Add Error argument to bdrv_attach_child()
21
qcow2: Remove unused Error variable in do_perform_cow()
21
block: Let callers request permissions when attaching a child node
22
qcow2: Use unsigned int for both members of Qcow2COWRegion
22
block: Involve block drivers in permission granting
23
qcow2: Make perform_cow() call do_perform_cow() twice
23
block: Default .bdrv_child_perm() for filter drivers
24
qcow2: Split do_perform_cow() into _read(), _encrypt() and _write()
24
block: Request child permissions in filter drivers
25
qcow2: Allow reading both COW regions with only one request
25
block: Default .bdrv_child_perm() for format drivers
26
qcow2: Pass a QEMUIOVector to do_perform_cow_{read,write}()
26
block: Request child permissions in format drivers
27
qcow2: Merge the writing of the COW regions with the guest data
27
vvfat: Implement .bdrv_child_perm()
28
qcow2: Use offset_into_cluster() and offset_to_l2_index()
28
block: Require .bdrv_child_perm() with child nodes
29
block: Request real permissions in bdrv_attach_child()
30
block: Add permissions to BlockBackend
31
block: Add permissions to blk_new()
32
block: Add error parameter to blk_insert_bs()
33
block: Add BDRV_O_RESIZE for blk_new_open()
34
block: Request real permissions in blk_new_open()
35
block: Allow error return in BlockDevOps.change_media_cb()
36
hw/block: Request permissions
37
hw/block: Introduce share-rw qdev property
38
blockjob: Add permissions to block_job_create()
39
block: Add BdrvChildRole.get_parent_desc()
40
block: Include details on permission errors in message
41
block: Add BdrvChildRole.stay_at_node
42
blockjob: Add permissions to block_job_add_bdrv()
43
commit: Use real permissions in commit block job
44
commit: Use real permissions for HMP 'commit'
45
backup: Use real permissions in backup block job
46
block: Fix pending requests check in bdrv_append()
47
block: BdrvChildRole.attach/detach() callbacks
48
block: Allow backing file links in change_parent_backing_link()
49
blockjob: Factor out block_job_remove_all_bdrv()
50
mirror: Use real permissions in mirror/active commit block job
51
stream: Use real permissions in streaming block job
52
mirror: Add filter-node-name to blockdev-mirror
53
commit: Add filter-node-name to block-commit
54
hmp: Request permissions in qemu-io
55
migration/block: Use real permissions
56
nbd/server: Use real permissions for NBD exports
57
tests: Remove FIXME comments
58
block: Pass BdrvChild to bdrv_aligned_preadv/pwritev and copy-on-read
59
block: Assertions for write permissions
60
block: Assertions for resize permission
61
block: Add Error parameter to bdrv_set_backing_hd()
62
block: Add Error parameter to bdrv_append()
63
29
64
Markus Armbruster (1):
30
Kevin Wolf (37):
65
option: Tweak invalid size error message and unbreak iotest 049
31
commit: Fix completion with extra reference
32
qemu-iotests: Allow starting new qemu after cleanup
33
qemu-iotests: Test exiting qemu with running job
34
doc: Document generic -blockdev options
35
doc: Document driver-specific -blockdev options
36
qed: Use bottom half to resume waiting requests
37
qed: Make qed_read_table() synchronous
38
qed: Remove callback from qed_read_table()
39
qed: Remove callback from qed_read_l2_table()
40
qed: Remove callback from qed_find_cluster()
41
qed: Make qed_read_backing_file() synchronous
42
qed: Make qed_copy_from_backing_file() synchronous
43
qed: Remove callback from qed_copy_from_backing_file()
44
qed: Make qed_write_header() synchronous
45
qed: Remove callback from qed_write_header()
46
qed: Make qed_write_table() synchronous
47
qed: Remove GenericCB
48
qed: Remove callback from qed_write_table()
49
qed: Make qed_aio_read_data() synchronous
50
qed: Make qed_aio_write_main() synchronous
51
qed: Inline qed_commit_l2_update()
52
qed: Add return value to qed_aio_write_l1_update()
53
qed: Add return value to qed_aio_write_l2_update()
54
qed: Add return value to qed_aio_write_main()
55
qed: Add return value to qed_aio_write_cow()
56
qed: Add return value to qed_aio_write_inplace/alloc()
57
qed: Add return value to qed_aio_read/write_data()
58
qed: Remove ret argument from qed_aio_next_io()
59
qed: Remove recursion in qed_aio_next_io()
60
qed: Implement .bdrv_co_readv/writev
61
qed: Use CoQueue for serialising allocations
62
qed: Simplify request handling
63
qed: Use a coroutine for need_check_timer
64
qed: Add coroutine_fn to I/O path functions
65
qed: Use bdrv_co_* for coroutine_fns
66
block: Remove bdrv_aio_readv/writev/flush()
67
Merge remote-tracking branch 'mreitz/tags/pull-block-2017-06-23' into queue-block
66
68
67
Peter Lieven (1):
69
Manos Pitsidianakis (1):
68
qemu-img: make convert async
70
block: change variable names in BlockDriverState
69
71
70
block.c | 583 ++++++++++++++++++++++++++++++++++-----
72
Max Reitz (3):
71
block/backup.c | 22 +-
73
blkdebug: Catch bs->exact_filename overflow
72
block/blkdebug.c | 2 +
74
blkverify: Catch bs->exact_filename overflow
73
block/blkreplay.c | 1 +
75
block: Do not strcmp() with NULL uri->scheme
74
block/blkverify.c | 1 +
75
block/block-backend.c | 116 +++++++-
76
block/bochs.c | 1 +
77
block/cloop.c | 1 +
78
block/commit.c | 176 ++++++++++--
79
block/crypto.c | 1 +
80
block/dmg.c | 1 +
81
block/io.c | 41 +--
82
block/mirror.c | 237 ++++++++++++++--
83
block/parallels.c | 4 +-
84
block/qcow.c | 4 +-
85
block/qcow2.c | 19 +-
86
block/qed.c | 4 +-
87
block/quorum.c | 11 +-
88
block/raw-format.c | 1 +
89
block/replication.c | 3 +-
90
block/sheepdog.c | 2 +-
91
block/stream.c | 47 +++-
92
block/vdi.c | 4 +-
93
block/vhdx.c | 4 +-
94
block/vmdk.c | 7 +-
95
block/vpc.c | 4 +-
96
block/vvfat.c | 24 +-
97
blockdev.c | 74 ++++-
98
blockjob.c | 62 ++++-
99
hmp.c | 33 ++-
100
hw/block/block.c | 24 +-
101
hw/block/fdc.c | 28 +-
102
hw/block/m25p80.c | 8 +
103
hw/block/nand.c | 7 +
104
hw/block/nvme.c | 8 +-
105
hw/block/onenand.c | 7 +
106
hw/block/pflash_cfi01.c | 18 +-
107
hw/block/pflash_cfi02.c | 19 +-
108
hw/block/virtio-blk.c | 8 +-
109
hw/core/qdev-properties-system.c | 9 +-
110
hw/ide/core.c | 2 +-
111
hw/ide/qdev.c | 9 +-
112
hw/nvram/spapr_nvram.c | 8 +
113
hw/scsi/scsi-disk.c | 12 +-
114
hw/sd/sd.c | 8 +-
115
hw/usb/dev-storage.c | 6 +-
116
include/block/block.h | 46 ++-
117
include/block/block_int.h | 126 ++++++++-
118
include/block/blockjob.h | 14 +-
119
include/block/blockjob_int.h | 4 +-
120
include/hw/block/block.h | 8 +-
121
include/qemu-io.h | 1 +
122
include/sysemu/block-backend.h | 9 +-
123
migration/block.c | 21 +-
124
nbd/server.c | 16 +-
125
qapi/block-core.json | 16 +-
126
qemu-img-cmds.hx | 4 +-
127
qemu-img.c | 334 +++++++++++++++-------
128
qemu-img.texi | 16 +-
129
qemu-io-cmds.c | 28 ++
130
tests/qemu-iotests/049.out | 14 +-
131
tests/qemu-iotests/051.pc.out | 6 +-
132
tests/qemu-iotests/055 | 11 +-
133
tests/qemu-iotests/085.out | 2 +-
134
tests/qemu-iotests/141 | 2 +-
135
tests/qemu-iotests/141.out | 4 +-
136
tests/qemu-iotests/172.out | 53 ++++
137
tests/test-blockjob-txn.c | 6 +-
138
tests/test-blockjob.c | 10 +-
139
tests/test-throttle.c | 7 +-
140
util/qemu-option.c | 2 +-
141
71 files changed, 2039 insertions(+), 392 deletions(-)
142
76
77
Stefan Hajnoczi (10):
78
block: count bdrv_co_rw_vmstate() requests
79
block: use BDRV_POLL_WHILE() in bdrv_rw_vmstate()
80
migration: avoid recursive AioContext locking in save_vmstate()
81
migration: use bdrv_drain_all_begin/end() instead bdrv_drain_all()
82
virtio-pci: use ioeventfd even when KVM is disabled
83
migration: hold AioContext lock for loadvm qemu_fclose()
84
qemu-iotests: 068: extract _qemu() function
85
qemu-iotests: 068: use -drive/-device instead of -hda
86
qemu-iotests: 068: test iothread mode
87
qemu-img: don't shadow opts variable in img_dd()
88
89
Stephen Bates (1):
90
nvme: Add support for Read Data and Write Data in CMBs.
91
92
sochin.jiang (1):
93
fix: avoid an infinite loop or a dangling pointer problem in img_commit
94
95
block/Makefile.objs | 2 +-
96
block/blkdebug.c | 46 +--
97
block/blkreplay.c | 8 +-
98
block/blkverify.c | 12 +-
99
block/block-backend.c | 22 +-
100
block/commit.c | 7 +
101
block/file-posix.c | 34 +-
102
block/io.c | 240 ++-----------
103
block/iscsi.c | 20 +-
104
block/mirror.c | 8 +-
105
block/nbd-client.c | 8 +-
106
block/nbd-client.h | 4 +-
107
block/nbd.c | 6 +-
108
block/nfs.c | 2 +-
109
block/qcow2-cluster.c | 201 ++++++++---
110
block/qcow2.c | 94 +++--
111
block/qcow2.h | 11 +-
112
block/qed-cluster.c | 124 +++----
113
block/qed-gencb.c | 33 --
114
block/qed-table.c | 261 +++++---------
115
block/qed.c | 779 ++++++++++++++++-------------------------
116
block/qed.h | 54 +--
117
block/raw-format.c | 8 +-
118
block/rbd.c | 4 +-
119
block/sheepdog.c | 12 +-
120
block/ssh.c | 2 +-
121
block/throttle-groups.c | 2 +-
122
block/trace-events | 3 -
123
blockjob.c | 4 +-
124
hw/block/nvme.c | 83 +++--
125
hw/block/nvme.h | 1 +
126
hw/virtio/virtio-pci.c | 2 +-
127
include/block/block.h | 16 +-
128
include/block/block_int.h | 6 +-
129
include/block/blockjob.h | 18 +
130
include/sysemu/block-backend.h | 20 +-
131
migration/savevm.c | 32 +-
132
qemu-img.c | 29 +-
133
qemu-io-cmds.c | 46 +--
134
qemu-options.hx | 221 ++++++++++--
135
tests/qemu-iotests/068 | 37 +-
136
tests/qemu-iotests/068.out | 11 +-
137
tests/qemu-iotests/185 | 206 +++++++++++
138
tests/qemu-iotests/185.out | 59 ++++
139
tests/qemu-iotests/common.qemu | 3 +
140
tests/qemu-iotests/group | 1 +
141
46 files changed, 1477 insertions(+), 1325 deletions(-)
142
delete mode 100644 block/qed-gencb.c
143
create mode 100755 tests/qemu-iotests/185
144
create mode 100644 tests/qemu-iotests/185.out
145
diff view generated by jsdifflib
1
This is probably one of the most interesting conversions to the new
1
commit_complete() can't assume that after its block_job_completed() the
2
op blocker system because a commit block job intentionally leaves some
2
job is actually immediately freed; someone else may still be holding
3
intermediate block nodes in the backing chain that aren't valid on their
3
references. In this case, the op blockers on the intermediate nodes make
4
own any more; only the whole chain together results in a valid view.
4
the graph reconfiguration in the completion code fail.
5
5
6
In order to provide the 'consistent read' permission to the parents of
6
Call block_job_remove_all_bdrv() manually so that we know for sure that
7
the 'top' node of the commit job, a new filter block driver is inserted
7
any blockers on intermediate nodes are given up.
8
above 'top' which doesn't require 'consistent read' on its backing
9
chain. Subsequently, the commit job can block 'consistent read' on all
10
intermediate nodes without causing a conflict.
11
8
9
Cc: qemu-stable@nongnu.org
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Acked-by: Fam Zheng <famz@redhat.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
---
13
---
16
block/commit.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++---------
14
block/commit.c | 7 +++++++
17
1 file changed, 95 insertions(+), 18 deletions(-)
15
1 file changed, 7 insertions(+)
18
16
19
diff --git a/block/commit.c b/block/commit.c
17
diff --git a/block/commit.c b/block/commit.c
20
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
21
--- a/block/commit.c
19
--- a/block/commit.c
22
+++ b/block/commit.c
20
+++ b/block/commit.c
23
@@ -XXX,XX +XXX,XX @@ typedef struct CommitBlockJob {
24
BlockJob common;
25
RateLimit limit;
26
BlockDriverState *active;
27
+ BlockDriverState *commit_top_bs;
28
BlockBackend *top;
29
BlockBackend *base;
30
BlockdevOnError on_error;
31
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
32
BlockDriverState *active = s->active;
33
BlockDriverState *top = blk_bs(s->top);
34
BlockDriverState *base = blk_bs(s->base);
35
- BlockDriverState *overlay_bs = bdrv_find_overlay(active, top);
36
+ BlockDriverState *overlay_bs = bdrv_find_overlay(active, s->commit_top_bs);
37
int ret = data->ret;
38
+ bool remove_commit_top_bs = false;
39
+
40
+ /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
41
+ * the normal backing chain can be restored. */
42
+ blk_unref(s->base);
43
44
if (!block_job_is_cancelled(&s->common) && ret == 0) {
45
/* success */
46
- ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
47
+ ret = bdrv_drop_intermediate(active, s->commit_top_bs, base,
48
+ s->backing_file_str);
49
+ } else if (overlay_bs) {
50
+ /* XXX Can (or should) we somehow keep 'consistent read' blocked even
51
+ * after the failed/cancelled commit job is gone? If we already wrote
52
+ * something to base, the intermediate images aren't valid any more. */
53
+ remove_commit_top_bs = true;
54
}
55
56
/* restore base open flags here if appropriate (e.g., change the base back
57
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
21
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
58
}
22
}
59
g_free(s->backing_file_str);
23
g_free(s->backing_file_str);
60
blk_unref(s->top);
24
blk_unref(s->top);
61
- blk_unref(s->base);
25
+
26
+ /* If there is more than one reference to the job (e.g. if called from
27
+ * block_job_finish_sync()), block_job_completed() won't free it and
28
+ * therefore the blockers on the intermediate nodes remain. This would
29
+ * cause bdrv_set_backing_hd() to fail. */
30
+ block_job_remove_all_bdrv(job);
31
+
62
block_job_completed(&s->common, ret);
32
block_job_completed(&s->common, ret);
63
g_free(data);
33
g_free(data);
64
+
65
+ /* If bdrv_drop_intermediate() didn't already do that, remove the commit
66
+ * filter driver from the backing chain. Do this as the final step so that
67
+ * the 'consistent read' permission can be granted. */
68
+ if (remove_commit_top_bs) {
69
+ bdrv_set_backing_hd(overlay_bs, top);
70
+ }
71
}
72
73
static void coroutine_fn commit_run(void *opaque)
74
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
75
.start = commit_run,
76
};
77
78
+static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
79
+ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
80
+{
81
+ return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
82
+}
83
+
84
+static void bdrv_commit_top_close(BlockDriverState *bs)
85
+{
86
+}
87
+
88
+static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
89
+ const BdrvChildRole *role,
90
+ uint64_t perm, uint64_t shared,
91
+ uint64_t *nperm, uint64_t *nshared)
92
+{
93
+ *nperm = 0;
94
+ *nshared = BLK_PERM_ALL;
95
+}
96
+
97
+/* Dummy node that provides consistent read to its users without requiring it
98
+ * from its backing file and that allows writes on the backing file chain. */
99
+static BlockDriver bdrv_commit_top = {
100
+ .format_name = "commit_top",
101
+ .bdrv_co_preadv = bdrv_commit_top_preadv,
102
+ .bdrv_close = bdrv_commit_top_close,
103
+ .bdrv_child_perm = bdrv_commit_top_child_perm,
104
+};
105
+
106
void commit_start(const char *job_id, BlockDriverState *bs,
107
BlockDriverState *base, BlockDriverState *top, int64_t speed,
108
BlockdevOnError on_error, const char *backing_file_str,
109
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
110
int orig_base_flags;
111
BlockDriverState *iter;
112
BlockDriverState *overlay_bs;
113
+ BlockDriverState *commit_top_bs = NULL;
114
Error *local_err = NULL;
115
int ret;
116
117
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
118
return;
119
}
120
121
- /* FIXME Use real permissions */
122
s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
123
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
124
if (!s) {
125
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
126
}
127
}
128
129
+ /* Insert commit_top block node above top, so we can block consistent read
130
+ * on the backing chain below it */
131
+ commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, 0, errp);
132
+ if (commit_top_bs == NULL) {
133
+ goto fail;
134
+ }
135
+
136
+ bdrv_set_backing_hd(commit_top_bs, top);
137
+ bdrv_set_backing_hd(overlay_bs, commit_top_bs);
138
+
139
+ s->commit_top_bs = commit_top_bs;
140
+ bdrv_unref(commit_top_bs);
141
142
/* Block all nodes between top and base, because they will
143
* disappear from the chain after this operation. */
144
assert(bdrv_chain_contains(top, base));
145
- for (iter = top; iter != backing_bs(base); iter = backing_bs(iter)) {
146
- /* FIXME Use real permissions */
147
- block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
148
- BLK_PERM_ALL, &error_abort);
149
+ for (iter = top; iter != base; iter = backing_bs(iter)) {
150
+ /* XXX BLK_PERM_WRITE needs to be allowed so we don't block ourselves
151
+ * at s->base (if writes are blocked for a node, they are also blocked
152
+ * for its backing file). The other options would be a second filter
153
+ * driver above s->base. */
154
+ ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
155
+ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE,
156
+ errp);
157
+ if (ret < 0) {
158
+ goto fail;
159
+ }
160
}
161
+
162
+ ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
163
+ if (ret < 0) {
164
+ goto fail;
165
+ }
166
+
167
/* overlay_bs must be blocked because it needs to be modified to
168
- * update the backing image string, but if it's the root node then
169
- * don't block it again */
170
- if (bs != overlay_bs) {
171
- /* FIXME Use real permissions */
172
- block_job_add_bdrv(&s->common, "overlay of top", overlay_bs, 0,
173
- BLK_PERM_ALL, &error_abort);
174
+ * update the backing image string. */
175
+ ret = block_job_add_bdrv(&s->common, "overlay of top", overlay_bs,
176
+ BLK_PERM_GRAPH_MOD, BLK_PERM_ALL, errp);
177
+ if (ret < 0) {
178
+ goto fail;
179
}
180
181
- /* FIXME Use real permissions */
182
- s->base = blk_new(0, BLK_PERM_ALL);
183
+ s->base = blk_new(BLK_PERM_CONSISTENT_READ
184
+ | BLK_PERM_WRITE
185
+ | BLK_PERM_RESIZE,
186
+ BLK_PERM_CONSISTENT_READ
187
+ | BLK_PERM_GRAPH_MOD
188
+ | BLK_PERM_WRITE_UNCHANGED);
189
ret = blk_insert_bs(s->base, base, errp);
190
if (ret < 0) {
191
goto fail;
192
}
193
194
- /* FIXME Use real permissions */
195
+ /* Required permissions are already taken with block_job_add_bdrv() */
196
s->top = blk_new(0, BLK_PERM_ALL);
197
- ret = blk_insert_bs(s->top, top, errp);
198
+ blk_insert_bs(s->top, top, errp);
199
if (ret < 0) {
200
goto fail;
201
}
202
@@ -XXX,XX +XXX,XX @@ fail:
203
if (s->top) {
204
blk_unref(s->top);
205
}
206
+ if (commit_top_bs) {
207
+ bdrv_set_backing_hd(overlay_bs, top);
208
+ }
209
block_job_unref(&s->common);
210
}
211
34
212
--
35
--
213
1.8.3.1
36
1.8.3.1
214
37
215
38
diff view generated by jsdifflib
1
This adds assertions that ensure that the necessary write permissions
1
After _cleanup_qemu(), test cases should be able to start the next qemu
2
have been granted before someone attempts to write to a node.
2
process and call _cleanup_qemu() for that one as well. For this to work
3
cleanly, we need to improve the cleanup so that the second invocation
4
doesn't try to kill the qemu instances from the first invocation a
5
second time (which would result in error messages).
3
6
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Acked-by: Fam Zheng <famz@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
10
---
8
block/io.c | 3 +++
11
tests/qemu-iotests/common.qemu | 3 +++
9
1 file changed, 3 insertions(+)
12
1 file changed, 3 insertions(+)
10
13
11
diff --git a/block/io.c b/block/io.c
14
diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu
12
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
13
--- a/block/io.c
16
--- a/tests/qemu-iotests/common.qemu
14
+++ b/block/io.c
17
+++ b/tests/qemu-iotests/common.qemu
15
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
18
@@ -XXX,XX +XXX,XX @@ function _cleanup_qemu()
16
size_t skip_bytes;
19
rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}"
17
int ret;
20
eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors
18
21
eval "exec ${QEMU_OUT[$i]}<&-"
19
+ assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE));
20
+
22
+
21
/* Cover entire cluster so no additional backing file I/O is required when
23
+ unset QEMU_IN[$i]
22
* allocating cluster in the image file.
24
+ unset QEMU_OUT[$i]
23
*/
25
done
24
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
26
}
25
assert(!waited || !req->serialising);
26
assert(req->overlap_offset <= offset);
27
assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
28
+ assert(child->perm & BLK_PERM_WRITE);
29
30
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
31
32
--
27
--
33
1.8.3.1
28
1.8.3.1
34
29
35
30
diff view generated by jsdifflib
1
This adds an assertion that ensures that the necessary resize permission
1
When qemu is exited, all running jobs should be cancelled successfully.
2
has been granted before bdrv_truncate() is called.
2
This adds a test for this for all types of block jobs that currently
3
exist in qemu.
3
4
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
7
---
7
---
8
block.c | 3 +++
8
tests/qemu-iotests/185 | 206 +++++++++++++++++++++++++++++++++++++++++++++
9
block/io.c | 1 +
9
tests/qemu-iotests/185.out | 59 +++++++++++++
10
2 files changed, 4 insertions(+)
10
tests/qemu-iotests/group | 1 +
11
3 files changed, 266 insertions(+)
12
create mode 100755 tests/qemu-iotests/185
13
create mode 100644 tests/qemu-iotests/185.out
11
14
12
diff --git a/block.c b/block.c
15
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
16
new file mode 100755
17
index XXXXXXX..XXXXXXX
18
--- /dev/null
19
+++ b/tests/qemu-iotests/185
20
@@ -XXX,XX +XXX,XX @@
21
+#!/bin/bash
22
+#
23
+# Test exiting qemu while jobs are still running
24
+#
25
+# Copyright (C) 2017 Red Hat, Inc.
26
+#
27
+# This program is free software; you can redistribute it and/or modify
28
+# it under the terms of the GNU General Public License as published by
29
+# the Free Software Foundation; either version 2 of the License, or
30
+# (at your option) any later version.
31
+#
32
+# This program is distributed in the hope that it will be useful,
33
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
34
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35
+# GNU General Public License for more details.
36
+#
37
+# You should have received a copy of the GNU General Public License
38
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
39
+#
40
+
41
+# creator
42
+owner=kwolf@redhat.com
43
+
44
+seq=`basename $0`
45
+echo "QA output created by $seq"
46
+
47
+here=`pwd`
48
+status=1 # failure is the default!
49
+
50
+MIG_SOCKET="${TEST_DIR}/migrate"
51
+
52
+_cleanup()
53
+{
54
+ rm -f "${TEST_IMG}.mid"
55
+ rm -f "${TEST_IMG}.copy"
56
+ _cleanup_test_img
57
+ _cleanup_qemu
58
+}
59
+trap "_cleanup; exit \$status" 0 1 2 3 15
60
+
61
+# get standard environment, filters and checks
62
+. ./common.rc
63
+. ./common.filter
64
+. ./common.qemu
65
+
66
+_supported_fmt qcow2
67
+_supported_proto file
68
+_supported_os Linux
69
+
70
+size=64M
71
+TEST_IMG="${TEST_IMG}.base" _make_test_img $size
72
+
73
+echo
74
+echo === Starting VM ===
75
+echo
76
+
77
+qemu_comm_method="qmp"
78
+
79
+_launch_qemu \
80
+ -drive file="${TEST_IMG}.base",cache=$CACHEMODE,driver=$IMGFMT,id=disk
81
+h=$QEMU_HANDLE
82
+_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return'
83
+
84
+echo
85
+echo === Creating backing chain ===
86
+echo
87
+
88
+_send_qemu_cmd $h \
89
+ "{ 'execute': 'blockdev-snapshot-sync',
90
+ 'arguments': { 'device': 'disk',
91
+ 'snapshot-file': '$TEST_IMG.mid',
92
+ 'format': '$IMGFMT',
93
+ 'mode': 'absolute-paths' } }" \
94
+ "return"
95
+
96
+_send_qemu_cmd $h \
97
+ "{ 'execute': 'human-monitor-command',
98
+ 'arguments': { 'command-line':
99
+ 'qemu-io disk \"write 0 4M\"' } }" \
100
+ "return"
101
+
102
+_send_qemu_cmd $h \
103
+ "{ 'execute': 'blockdev-snapshot-sync',
104
+ 'arguments': { 'device': 'disk',
105
+ 'snapshot-file': '$TEST_IMG',
106
+ 'format': '$IMGFMT',
107
+ 'mode': 'absolute-paths' } }" \
108
+ "return"
109
+
110
+echo
111
+echo === Start commit job and exit qemu ===
112
+echo
113
+
114
+# Note that the reference output intentionally includes the 'offset' field in
115
+# BLOCK_JOB_CANCELLED events for all of the following block jobs. They are
116
+# predictable and any change in the offsets would hint at a bug in the job
117
+# throttling code.
118
+#
119
+# In order to achieve these predictable offsets, all of the following tests
120
+# use speed=65536. Each job will perform exactly one iteration before it has
121
+# to sleep at least for a second, which is plenty of time for the 'quit' QMP
122
+# command to be received (after receiving the command, the rest runs
123
+# synchronously, so jobs can arbitrarily continue or complete).
124
+#
125
+# The buffer size for commit and streaming is 512k (waiting for 8 seconds after
126
+# the first request), for active commit and mirror it's large enough to cover
127
+# the full 4M, and for backup it's the qcow2 cluster size, which we know is
128
+# 64k. As all of these are at least as large as the speed, we are sure that the
129
+# offset doesn't advance after the first iteration before qemu exits.
130
+
131
+_send_qemu_cmd $h \
132
+ "{ 'execute': 'block-commit',
133
+ 'arguments': { 'device': 'disk',
134
+ 'base':'$TEST_IMG.base',
135
+ 'top': '$TEST_IMG.mid',
136
+ 'speed': 65536 } }" \
137
+ "return"
138
+
139
+_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
140
+wait=1 _cleanup_qemu
141
+
142
+echo
143
+echo === Start active commit job and exit qemu ===
144
+echo
145
+
146
+_launch_qemu \
147
+ -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk
148
+h=$QEMU_HANDLE
149
+_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return'
150
+
151
+_send_qemu_cmd $h \
152
+ "{ 'execute': 'block-commit',
153
+ 'arguments': { 'device': 'disk',
154
+ 'base':'$TEST_IMG.base',
155
+ 'speed': 65536 } }" \
156
+ "return"
157
+
158
+_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
159
+wait=1 _cleanup_qemu
160
+
161
+echo
162
+echo === Start mirror job and exit qemu ===
163
+echo
164
+
165
+_launch_qemu \
166
+ -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk
167
+h=$QEMU_HANDLE
168
+_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return'
169
+
170
+_send_qemu_cmd $h \
171
+ "{ 'execute': 'drive-mirror',
172
+ 'arguments': { 'device': 'disk',
173
+ 'target': '$TEST_IMG.copy',
174
+ 'format': '$IMGFMT',
175
+ 'sync': 'full',
176
+ 'speed': 65536 } }" \
177
+ "return"
178
+
179
+_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
180
+wait=1 _cleanup_qemu
181
+
182
+echo
183
+echo === Start backup job and exit qemu ===
184
+echo
185
+
186
+_launch_qemu \
187
+ -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk
188
+h=$QEMU_HANDLE
189
+_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return'
190
+
191
+_send_qemu_cmd $h \
192
+ "{ 'execute': 'drive-backup',
193
+ 'arguments': { 'device': 'disk',
194
+ 'target': '$TEST_IMG.copy',
195
+ 'format': '$IMGFMT',
196
+ 'sync': 'full',
197
+ 'speed': 65536 } }" \
198
+ "return"
199
+
200
+_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
201
+wait=1 _cleanup_qemu
202
+
203
+echo
204
+echo === Start streaming job and exit qemu ===
205
+echo
206
+
207
+_launch_qemu \
208
+ -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk
209
+h=$QEMU_HANDLE
210
+_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return'
211
+
212
+_send_qemu_cmd $h \
213
+ "{ 'execute': 'block-stream',
214
+ 'arguments': { 'device': 'disk',
215
+ 'speed': 65536 } }" \
216
+ "return"
217
+
218
+_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
219
+wait=1 _cleanup_qemu
220
+
221
+_check_test_img
222
+
223
+# success, all done
224
+echo "*** done"
225
+rm -f $seq.full
226
+status=0
227
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
228
new file mode 100644
229
index XXXXXXX..XXXXXXX
230
--- /dev/null
231
+++ b/tests/qemu-iotests/185.out
232
@@ -XXX,XX +XXX,XX @@
233
+QA output created by 185
234
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
235
+
236
+=== Starting VM ===
237
+
238
+{"return": {}}
239
+
240
+=== Creating backing chain ===
241
+
242
+Formatting 'TEST_DIR/t.qcow2.mid', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.qcow2.base backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
243
+{"return": {}}
244
+wrote 4194304/4194304 bytes at offset 0
245
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
246
+{"return": ""}
247
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.qcow2.mid backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
248
+{"return": {}}
249
+
250
+=== Start commit job and exit qemu ===
251
+
252
+{"return": {}}
253
+{"return": {}}
254
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
255
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}}
256
+
257
+=== Start active commit job and exit qemu ===
258
+
259
+{"return": {}}
260
+{"return": {}}
261
+{"return": {}}
262
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
263
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}}
264
+
265
+=== Start mirror job and exit qemu ===
266
+
267
+{"return": {}}
268
+Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
269
+{"return": {}}
270
+{"return": {}}
271
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
272
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}}
273
+
274
+=== Start backup job and exit qemu ===
275
+
276
+{"return": {}}
277
+Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
278
+{"return": {}}
279
+{"return": {}}
280
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
281
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}}
282
+
283
+=== Start streaming job and exit qemu ===
284
+
285
+{"return": {}}
286
+{"return": {}}
287
+{"return": {}}
288
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
289
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}}
290
+No errors were found on the image.
291
+*** done
292
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
13
index XXXXXXX..XXXXXXX 100644
293
index XXXXXXX..XXXXXXX 100644
14
--- a/block.c
294
--- a/tests/qemu-iotests/group
15
+++ b/block.c
295
+++ b/tests/qemu-iotests/group
16
@@ -XXX,XX +XXX,XX @@ int bdrv_truncate(BdrvChild *child, int64_t offset)
296
@@ -XXX,XX +XXX,XX @@
17
BlockDriverState *bs = child->bs;
297
181 rw auto migration
18
BlockDriver *drv = bs->drv;
298
182 rw auto quick
19
int ret;
299
183 rw auto migration
20
+
300
+185 rw auto
21
+ assert(child->perm & BLK_PERM_RESIZE);
22
+
23
if (!drv)
24
return -ENOMEDIUM;
25
if (!drv->bdrv_truncate)
26
diff --git a/block/io.c b/block/io.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/block/io.c
29
+++ b/block/io.c
30
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
31
assert(req->overlap_offset <= offset);
32
assert(offset + bytes <= req->overlap_offset + req->overlap_bytes);
33
assert(child->perm & BLK_PERM_WRITE);
34
+ assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE);
35
36
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
37
38
--
301
--
39
1.8.3.1
302
1.8.3.1
40
303
41
304
diff view generated by jsdifflib
1
This is where we want to check the permissions, so we need to have the
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
BdrvChild around where they are stored.
3
2
3
Calling aio_poll() directly may have been fine previously, but this is
4
the future, man! The difference between an aio_poll() loop and
5
BDRV_POLL_WHILE() is that BDRV_POLL_WHILE() releases the AioContext
6
around aio_poll().
7
8
This allows the IOThread to run fd handlers or BHs to complete the
9
request. Failure to release the AioContext causes deadlocks.
10
11
Using BDRV_POLL_WHILE() partially fixes a 'savevm' hang with -object
12
iothread.
13
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Reviewed-by: Eric Blake <eblake@redhat.com>
16
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Acked-by: Fam Zheng <famz@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
18
---
8
block/io.c | 37 +++++++++++++++++++++----------------
19
block/io.c | 4 +---
9
1 file changed, 21 insertions(+), 16 deletions(-)
20
1 file changed, 1 insertion(+), 3 deletions(-)
10
21
11
diff --git a/block/io.c b/block/io.c
22
diff --git a/block/io.c b/block/io.c
12
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
13
--- a/block/io.c
24
--- a/block/io.c
14
+++ b/block/io.c
25
+++ b/block/io.c
15
@@ -XXX,XX +XXX,XX @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
26
@@ -XXX,XX +XXX,XX @@ bdrv_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos,
16
return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov);
27
Coroutine *co = qemu_coroutine_create(bdrv_co_rw_vmstate_entry, &data);
28
29
bdrv_coroutine_enter(bs, co);
30
- while (data.ret == -EINPROGRESS) {
31
- aio_poll(bdrv_get_aio_context(bs), true);
32
- }
33
+ BDRV_POLL_WHILE(bs, data.ret == -EINPROGRESS);
34
return data.ret;
35
}
17
}
36
}
18
19
-static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
20
+static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
21
int64_t offset, unsigned int bytes, QEMUIOVector *qiov)
22
{
23
+ BlockDriverState *bs = child->bs;
24
+
25
/* Perform I/O through a temporary buffer so that users who scribble over
26
* their read buffer while the operation is in progress do not end up
27
* modifying the image file. This is critical for zero-copy guest I/O
28
@@ -XXX,XX +XXX,XX @@ err:
29
* handles copy on read, zeroing after EOF, and fragmentation of large
30
* reads; any other features must be implemented by the caller.
31
*/
32
-static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
33
+static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
34
BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
35
int64_t align, QEMUIOVector *qiov, int flags)
36
{
37
+ BlockDriverState *bs = child->bs;
38
int64_t total_bytes, max_bytes;
39
int ret = 0;
40
uint64_t bytes_remaining = bytes;
41
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
42
}
43
44
if (!ret || pnum != nb_sectors) {
45
- ret = bdrv_co_do_copy_on_readv(bs, offset, bytes, qiov);
46
+ ret = bdrv_co_do_copy_on_readv(child, offset, bytes, qiov);
47
goto out;
48
}
49
}
50
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
51
}
52
53
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ);
54
- ret = bdrv_aligned_preadv(bs, &req, offset, bytes, align,
55
+ ret = bdrv_aligned_preadv(child, &req, offset, bytes, align,
56
use_local_qiov ? &local_qiov : qiov,
57
flags);
58
tracked_request_end(&req);
59
@@ -XXX,XX +XXX,XX @@ fail:
60
* Forwards an already correctly aligned write request to the BlockDriver,
61
* after possibly fragmenting it.
62
*/
63
-static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
64
+static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
65
BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
66
int64_t align, QEMUIOVector *qiov, int flags)
67
{
68
+ BlockDriverState *bs = child->bs;
69
BlockDriver *drv = bs->drv;
70
bool waited;
71
int ret;
72
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
73
return ret;
74
}
75
76
-static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
77
+static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
78
int64_t offset,
79
unsigned int bytes,
80
BdrvRequestFlags flags,
81
BdrvTrackedRequest *req)
82
{
83
+ BlockDriverState *bs = child->bs;
84
uint8_t *buf = NULL;
85
QEMUIOVector local_qiov;
86
struct iovec iov;
87
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
88
mark_request_serialising(req, align);
89
wait_serialising_requests(req);
90
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
91
- ret = bdrv_aligned_preadv(bs, req, offset & ~(align - 1), align,
92
+ ret = bdrv_aligned_preadv(child, req, offset & ~(align - 1), align,
93
align, &local_qiov, 0);
94
if (ret < 0) {
95
goto fail;
96
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
97
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
98
99
memset(buf + head_padding_bytes, 0, zero_bytes);
100
- ret = bdrv_aligned_pwritev(bs, req, offset & ~(align - 1), align,
101
+ ret = bdrv_aligned_pwritev(child, req, offset & ~(align - 1), align,
102
align, &local_qiov,
103
flags & ~BDRV_REQ_ZERO_WRITE);
104
if (ret < 0) {
105
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
106
if (bytes >= align) {
107
/* Write the aligned part in the middle. */
108
uint64_t aligned_bytes = bytes & ~(align - 1);
109
- ret = bdrv_aligned_pwritev(bs, req, offset, aligned_bytes, align,
110
+ ret = bdrv_aligned_pwritev(child, req, offset, aligned_bytes, align,
111
NULL, flags);
112
if (ret < 0) {
113
goto fail;
114
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
115
mark_request_serialising(req, align);
116
wait_serialising_requests(req);
117
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
118
- ret = bdrv_aligned_preadv(bs, req, offset, align,
119
+ ret = bdrv_aligned_preadv(child, req, offset, align,
120
align, &local_qiov, 0);
121
if (ret < 0) {
122
goto fail;
123
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
124
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
125
126
memset(buf, 0, bytes);
127
- ret = bdrv_aligned_pwritev(bs, req, offset, align, align,
128
+ ret = bdrv_aligned_pwritev(child, req, offset, align, align,
129
&local_qiov, flags & ~BDRV_REQ_ZERO_WRITE);
130
}
131
fail:
132
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
133
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE);
134
135
if (!qiov) {
136
- ret = bdrv_co_do_zero_pwritev(bs, offset, bytes, flags, &req);
137
+ ret = bdrv_co_do_zero_pwritev(child, offset, bytes, flags, &req);
138
goto out;
139
}
140
141
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
142
qemu_iovec_init_external(&head_qiov, &head_iov, 1);
143
144
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
145
- ret = bdrv_aligned_preadv(bs, &req, offset & ~(align - 1), align,
146
+ ret = bdrv_aligned_preadv(child, &req, offset & ~(align - 1), align,
147
align, &head_qiov, 0);
148
if (ret < 0) {
149
goto fail;
150
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
151
qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
152
153
bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
154
- ret = bdrv_aligned_preadv(bs, &req, (offset + bytes) & ~(align - 1), align,
155
- align, &tail_qiov, 0);
156
+ ret = bdrv_aligned_preadv(child, &req, (offset + bytes) & ~(align - 1),
157
+ align, align, &tail_qiov, 0);
158
if (ret < 0) {
159
goto fail;
160
}
161
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
162
bytes = ROUND_UP(bytes, align);
163
}
164
165
- ret = bdrv_aligned_pwritev(bs, &req, offset, bytes, align,
166
+ ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align,
167
use_local_qiov ? &local_qiov : qiov,
168
flags);
169
170
--
37
--
171
1.8.3.1
38
1.8.3.1
172
39
173
40
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
Commit 75cdcd1 neglected to update tests/qemu-iotests/049.out, and
3
AioContext was designed to allow nested acquire/release calls. It uses
4
made the error message for negative size worse. Fix that.
4
a recursive mutex so callers don't need to worry about nesting...or so
5
we thought.
5
6
6
Reported-by: Thomas Huth <thuth@redhat.com>
7
BDRV_POLL_WHILE() is used to wait for block I/O requests. It releases
7
Signed-off-by: Markus Armbruster <armbru@redhat.com>
8
the AioContext temporarily around aio_poll(). This gives IOThreads a
9
chance to acquire the AioContext to process I/O completions.
10
11
It turns out that recursive locking and BDRV_POLL_WHILE() don't mix.
12
BDRV_POLL_WHILE() only releases the AioContext once, so the IOThread
13
will not be able to acquire the AioContext if it was acquired
14
multiple times.
15
16
Instead of trying to release AioContext n times in BDRV_POLL_WHILE(),
17
this patch simply avoids nested locking in save_vmstate(). It's the
18
simplest fix and we should step back to consider the big picture with
19
all the recent changes to block layer threading.
20
21
This patch is the final fix to solve 'savevm' hanging with -object
22
iothread.
23
24
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
25
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Thomas Huth <thuth@redhat.com>
26
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
10
Tested-by: Christian Borntraeger <borntraeger@de.ibm.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
27
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
28
---
13
tests/qemu-iotests/049.out | 14 +++++++++-----
29
migration/savevm.c | 12 +++++++++++-
14
util/qemu-option.c | 2 +-
30
1 file changed, 11 insertions(+), 1 deletion(-)
15
2 files changed, 10 insertions(+), 6 deletions(-)
16
31
17
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
32
diff --git a/migration/savevm.c b/migration/savevm.c
18
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
19
--- a/tests/qemu-iotests/049.out
34
--- a/migration/savevm.c
20
+++ b/tests/qemu-iotests/049.out
35
+++ b/migration/savevm.c
21
@@ -XXX,XX +XXX,XX @@ qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024
36
@@ -XXX,XX +XXX,XX @@ int save_snapshot(const char *name, Error **errp)
22
qemu-img: Image size must be less than 8 EiB!
37
goto the_end;
23
38
}
24
qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2
39
25
-qemu-img: Parameter 'size' expects a non-negative number below 2^64
40
+ /* The bdrv_all_create_snapshot() call that follows acquires the AioContext
26
+qemu-img: Value '-1024' is out of range for parameter 'size'
41
+ * for itself. BDRV_POLL_WHILE() does not support nested locking because
27
qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2'
42
+ * it only releases the lock once. Therefore synchronous I/O will deadlock
28
43
+ * unless we release the AioContext before bdrv_all_create_snapshot().
29
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k
44
+ */
30
qemu-img: Image size must be less than 8 EiB!
45
+ aio_context_release(aio_context);
31
46
+ aio_context = NULL;
32
qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2
47
+
33
-qemu-img: Parameter 'size' expects a non-negative number below 2^64
48
ret = bdrv_all_create_snapshot(sn, bs, vm_state_size, &bs);
34
+qemu-img: Value '-1k' is out of range for parameter 'size'
49
if (ret < 0) {
35
qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2'
50
error_setg(errp, "Error while creating snapshot on '%s'",
36
51
@@ -XXX,XX +XXX,XX @@ int save_snapshot(const char *name, Error **errp)
37
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
52
ret = 0;
38
@@ -XXX,XX +XXX,XX @@ qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes
53
39
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
54
the_end:
40
55
- aio_context_release(aio_context);
41
qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2
56
+ if (aio_context) {
42
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
57
+ aio_context_release(aio_context);
43
+qemu-img: Parameter 'size' expects a non-negative number below 2^64
58
+ }
44
+Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
59
if (saved_vm_running) {
45
+and exabytes, respectively.
60
vm_start();
46
+qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2'
47
48
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar
49
qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for
50
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
51
52
qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2
53
-qemu-img: Parameter 'size' expects a size
54
-You may use k, M, G or T suffixes for kilobytes, megabytes, gigabytes and terabytes.
55
+qemu-img: Parameter 'size' expects a non-negative number below 2^64
56
+Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
57
+and exabytes, respectively.
58
qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2'
59
60
== Check correct interpretation of suffixes for cluster size ==
61
diff --git a/util/qemu-option.c b/util/qemu-option.c
62
index XXXXXXX..XXXXXXX 100644
63
--- a/util/qemu-option.c
64
+++ b/util/qemu-option.c
65
@@ -XXX,XX +XXX,XX @@ void parse_option_size(const char *name, const char *value,
66
67
err = qemu_strtosz(value, NULL, &size);
68
if (err == -ERANGE) {
69
- error_setg(errp, "Value '%s' is too large for parameter '%s'",
70
+ error_setg(errp, "Value '%s' is out of range for parameter '%s'",
71
value, name);
72
return;
73
}
61
}
74
--
62
--
75
1.8.3.1
63
1.8.3.1
76
64
77
65
diff view generated by jsdifflib
1
Management tools need to be able to know about every node in the graph
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
and need a way to address them. Changing the graph structure was okay
3
because libvirt doesn't really manage the node level yet, but future
4
libvirt versions need to deal with both new and old version of qemu.
5
2
6
This new option to blockdev-mirror allows the client to set a node-name
3
blk/bdrv_drain_all() only takes effect for a single instant and then
7
for the automatically inserted filter driver, and at the same time
4
resumes block jobs, guest devices, and other external clients like the
8
serves as a witness for a future libvirt that this version of qemu does
5
NBD server. This can be handy when performing a synchronous drain
9
automatically insert a filter driver.
6
before terminating the program, for example.
10
7
8
Monitor commands usually need to quiesce I/O across an entire code
9
region so blk/bdrv_drain_all() is not suitable. They must use
10
bdrv_drain_all_begin/end() to mark the region. This prevents new I/O
11
requests from slipping in or worse - block jobs completing and modifying
12
the graph.
13
14
I audited other blk/bdrv_drain_all() callers but did not find anything
15
that needs a similar fix. This patch fixes the savevm/loadvm commands.
16
Although I haven't encountered a read world issue this makes the code
17
safer.
18
19
Suggested-by: Kevin Wolf <kwolf@redhat.com>
20
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Acked-by: Fam Zheng <famz@redhat.com>
14
---
23
---
15
block/mirror.c | 14 ++++++++------
24
migration/savevm.c | 18 +++++++++++++++---
16
blockdev.c | 12 +++++++++++-
25
1 file changed, 15 insertions(+), 3 deletions(-)
17
include/block/block_int.h | 5 ++++-
18
qapi/block-core.json | 8 +++++++-
19
4 files changed, 30 insertions(+), 9 deletions(-)
20
26
21
diff --git a/block/mirror.c b/block/mirror.c
27
diff --git a/migration/savevm.c b/migration/savevm.c
22
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
23
--- a/block/mirror.c
29
--- a/migration/savevm.c
24
+++ b/block/mirror.c
30
+++ b/migration/savevm.c
25
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
31
@@ -XXX,XX +XXX,XX @@ int save_snapshot(const char *name, Error **errp)
26
void *opaque, Error **errp,
27
const BlockJobDriver *driver,
28
bool is_none_mode, BlockDriverState *base,
29
- bool auto_complete)
30
+ bool auto_complete, const char *filter_node_name)
31
{
32
MirrorBlockJob *s;
33
BlockDriverState *mirror_top_bs;
34
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
35
/* In the case of active commit, add dummy driver to provide consistent
36
* reads on the top, while disabling it in the intermediate nodes, and make
37
* the backing chain writable. */
38
- mirror_top_bs = bdrv_new_open_driver(&bdrv_mirror_top, NULL, BDRV_O_RDWR,
39
- errp);
40
+ mirror_top_bs = bdrv_new_open_driver(&bdrv_mirror_top, filter_node_name,
41
+ BDRV_O_RDWR, errp);
42
if (mirror_top_bs == NULL) {
43
return;
44
}
32
}
45
@@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs,
33
vm_stop(RUN_STATE_SAVE_VM);
46
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
34
47
BlockdevOnError on_source_error,
35
+ bdrv_drain_all_begin();
48
BlockdevOnError on_target_error,
36
+
49
- bool unmap, Error **errp)
37
aio_context_acquire(aio_context);
50
+ bool unmap, const char *filter_node_name, Error **errp)
38
51
{
39
memset(sn, 0, sizeof(*sn));
52
bool is_none_mode;
40
@@ -XXX,XX +XXX,XX @@ int save_snapshot(const char *name, Error **errp)
53
BlockDriverState *base;
41
if (aio_context) {
54
@@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs,
42
aio_context_release(aio_context);
55
mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces,
43
}
56
speed, granularity, buf_size, backing_mode,
44
+
57
on_source_error, on_target_error, unmap, NULL, NULL, errp,
45
+ bdrv_drain_all_end();
58
- &mirror_job_driver, is_none_mode, base, false);
46
+
59
+ &mirror_job_driver, is_none_mode, base, false,
47
if (saved_vm_running) {
60
+ filter_node_name);
48
vm_start();
49
}
50
@@ -XXX,XX +XXX,XX @@ int load_snapshot(const char *name, Error **errp)
51
}
52
53
/* Flush all IO requests so they don't interfere with the new state. */
54
- bdrv_drain_all();
55
+ bdrv_drain_all_begin();
56
57
ret = bdrv_all_goto_snapshot(name, &bs);
58
if (ret < 0) {
59
error_setg(errp, "Error %d while activating snapshot '%s' on '%s'",
60
ret, name, bdrv_get_device_name(bs));
61
- return ret;
62
+ goto err_drain;
63
}
64
65
/* restore the VM state */
66
f = qemu_fopen_bdrv(bs_vm_state, 0);
67
if (!f) {
68
error_setg(errp, "Could not open VM state file");
69
- return -EINVAL;
70
+ ret = -EINVAL;
71
+ goto err_drain;
72
}
73
74
qemu_system_reset(SHUTDOWN_CAUSE_NONE);
75
@@ -XXX,XX +XXX,XX @@ int load_snapshot(const char *name, Error **errp)
76
ret = qemu_loadvm_state(f);
77
aio_context_release(aio_context);
78
79
+ bdrv_drain_all_end();
80
+
81
migration_incoming_state_destroy();
82
if (ret < 0) {
83
error_setg(errp, "Error %d while loading VM state", ret);
84
@@ -XXX,XX +XXX,XX @@ int load_snapshot(const char *name, Error **errp)
85
}
86
87
return 0;
88
+
89
+err_drain:
90
+ bdrv_drain_all_end();
91
+ return ret;
61
}
92
}
62
93
63
void commit_active_start(const char *job_id, BlockDriverState *bs,
94
void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev)
64
@@ -XXX,XX +XXX,XX @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
65
mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0,
66
MIRROR_LEAVE_BACKING_CHAIN,
67
on_error, on_error, true, cb, opaque, &local_err,
68
- &commit_active_job_driver, false, base, auto_complete);
69
+ &commit_active_job_driver, false, base, auto_complete,
70
+ NULL);
71
if (local_err) {
72
error_propagate(errp, local_err);
73
goto error_restore_flags;
74
diff --git a/blockdev.c b/blockdev.c
75
index XXXXXXX..XXXXXXX 100644
76
--- a/blockdev.c
77
+++ b/blockdev.c
78
@@ -XXX,XX +XXX,XX @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
79
bool has_on_target_error,
80
BlockdevOnError on_target_error,
81
bool has_unmap, bool unmap,
82
+ bool has_filter_node_name,
83
+ const char *filter_node_name,
84
Error **errp)
85
{
86
87
@@ -XXX,XX +XXX,XX @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
88
if (!has_unmap) {
89
unmap = true;
90
}
91
+ if (!has_filter_node_name) {
92
+ filter_node_name = NULL;
93
+ }
94
95
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
96
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
97
@@ -XXX,XX +XXX,XX @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
98
mirror_start(job_id, bs, target,
99
has_replaces ? replaces : NULL,
100
speed, granularity, buf_size, sync, backing_mode,
101
- on_source_error, on_target_error, unmap, errp);
102
+ on_source_error, on_target_error, unmap, filter_node_name,
103
+ errp);
104
}
105
106
void qmp_drive_mirror(DriveMirror *arg, Error **errp)
107
@@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
108
arg->has_on_source_error, arg->on_source_error,
109
arg->has_on_target_error, arg->on_target_error,
110
arg->has_unmap, arg->unmap,
111
+ false, NULL,
112
&local_err);
113
bdrv_unref(target_bs);
114
error_propagate(errp, local_err);
115
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
116
BlockdevOnError on_source_error,
117
bool has_on_target_error,
118
BlockdevOnError on_target_error,
119
+ bool has_filter_node_name,
120
+ const char *filter_node_name,
121
Error **errp)
122
{
123
BlockDriverState *bs;
124
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
125
has_on_source_error, on_source_error,
126
has_on_target_error, on_target_error,
127
true, true,
128
+ has_filter_node_name, filter_node_name,
129
&local_err);
130
error_propagate(errp, local_err);
131
132
diff --git a/include/block/block_int.h b/include/block/block_int.h
133
index XXXXXXX..XXXXXXX 100644
134
--- a/include/block/block_int.h
135
+++ b/include/block/block_int.h
136
@@ -XXX,XX +XXX,XX @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
137
* @on_source_error: The action to take upon error reading from the source.
138
* @on_target_error: The action to take upon error writing to the target.
139
* @unmap: Whether to unmap target where source sectors only contain zeroes.
140
+ * @filter_node_name: The node name that should be assigned to the filter
141
+ * driver that the mirror job inserts into the graph above @bs. NULL means that
142
+ * a node name should be autogenerated.
143
* @errp: Error object.
144
*
145
* Start a mirroring operation on @bs. Clusters that are allocated
146
@@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs,
147
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
148
BlockdevOnError on_source_error,
149
BlockdevOnError on_target_error,
150
- bool unmap, Error **errp);
151
+ bool unmap, const char *filter_node_name, Error **errp);
152
153
/*
154
* backup_job_create:
155
diff --git a/qapi/block-core.json b/qapi/block-core.json
156
index XXXXXXX..XXXXXXX 100644
157
--- a/qapi/block-core.json
158
+++ b/qapi/block-core.json
159
@@ -XXX,XX +XXX,XX @@
160
# default 'report' (no limitations, since this applies to
161
# a different block device than @device).
162
#
163
+# @filter-node-name: #optional the node name that should be assigned to the
164
+# filter driver that the mirror job inserts into the graph
165
+# above @device. If this option is not given, a node name is
166
+# autogenerated. (Since: 2.9)
167
+#
168
# Returns: nothing on success.
169
#
170
# Since: 2.6
171
@@ -XXX,XX +XXX,XX @@
172
'sync': 'MirrorSyncMode',
173
'*speed': 'int', '*granularity': 'uint32',
174
'*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
175
- '*on-target-error': 'BlockdevOnError' } }
176
+ '*on-target-error': 'BlockdevOnError',
177
+ '*filter-node-name': 'str' } }
178
179
##
180
# @block_set_io_throttle:
181
--
95
--
182
1.8.3.1
96
1.8.3.1
183
97
184
98
diff view generated by jsdifflib
1
Management tools need to be able to know about every node in the graph
1
This adds documentation for the -blockdev options that apply to all
2
and need a way to address them. Changing the graph structure was okay
2
nodes independent of the block driver used.
3
because libvirt doesn't really manage the node level yet, but future
4
libvirt versions need to deal with both new and old version of qemu.
5
3
6
This new option to blockdev-commit allows the client to set a node-name
4
All options that are shared by -blockdev and -drive are now explained in
7
for the automatically inserted filter driver, and at the same time
5
the section for -blockdev. The documentation of -drive mentions that all
8
serves as a witness for a future libvirt that this version of qemu does
6
-blockdev options are accepted as well.
9
automatically insert a filter driver.
10
7
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Acked-by: Fam Zheng <famz@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
---
11
---
15
block/commit.c | 5 +++--
12
qemu-options.hx | 108 +++++++++++++++++++++++++++++++++++++++++---------------
16
block/mirror.c | 3 ++-
13
1 file changed, 79 insertions(+), 29 deletions(-)
17
block/replication.c | 2 +-
18
blockdev.c | 10 +++++++---
19
include/block/block_int.h | 13 ++++++++++---
20
qapi/block-core.json | 8 +++++++-
21
qemu-img.c | 4 ++--
22
7 files changed, 32 insertions(+), 13 deletions(-)
23
14
24
diff --git a/block/commit.c b/block/commit.c
15
diff --git a/qemu-options.hx b/qemu-options.hx
25
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
26
--- a/block/commit.c
17
--- a/qemu-options.hx
27
+++ b/block/commit.c
18
+++ b/qemu-options.hx
28
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_commit_top = {
19
@@ -XXX,XX +XXX,XX @@ DEF("blockdev", HAS_ARG, QEMU_OPTION_blockdev,
29
void commit_start(const char *job_id, BlockDriverState *bs,
20
" [,read-only=on|off][,detect-zeroes=on|off|unmap]\n"
30
BlockDriverState *base, BlockDriverState *top, int64_t speed,
21
" [,driver specific parameters...]\n"
31
BlockdevOnError on_error, const char *backing_file_str,
22
" configure a block backend\n", QEMU_ARCH_ALL)
32
- Error **errp)
23
+STEXI
33
+ const char *filter_node_name, Error **errp)
24
+@item -blockdev @var{option}[,@var{option}[,@var{option}[,...]]]
34
{
25
+@findex -blockdev
35
CommitBlockJob *s;
26
+
36
BlockReopenQueue *reopen_queue = NULL;
27
+Define a new block driver node.
37
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
28
+
38
29
+@table @option
39
/* Insert commit_top block node above top, so we can block consistent read
30
+@item Valid options for any block driver node:
40
* on the backing chain below it */
31
+
41
- commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, 0, errp);
32
+@table @code
42
+ commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, filter_node_name, 0,
33
+@item driver
43
+ errp);
34
+Specifies the block driver to use for the given node.
44
if (commit_top_bs == NULL) {
35
+@item node-name
45
goto fail;
36
+This defines the name of the block driver node by which it will be referenced
46
}
37
+later. The name must be unique, i.e. it must not match the name of a different
47
diff --git a/block/mirror.c b/block/mirror.c
38
+block driver node, or (if you use @option{-drive} as well) the ID of a drive.
48
index XXXXXXX..XXXXXXX 100644
39
+
49
--- a/block/mirror.c
40
+If no node name is specified, it is automatically generated. The generated node
50
+++ b/block/mirror.c
41
+name is not intended to be predictable and changes between QEMU invocations.
51
@@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs,
42
+For the top level, an explicit node name must be specified.
52
void commit_active_start(const char *job_id, BlockDriverState *bs,
43
+@item read-only
53
BlockDriverState *base, int creation_flags,
44
+Open the node read-only. Guest write attempts will fail.
54
int64_t speed, BlockdevOnError on_error,
45
+@item cache.direct
55
+ const char *filter_node_name,
46
+The host page cache can be avoided with @option{cache.direct=on}. This will
56
BlockCompletionFunc *cb, void *opaque, Error **errp,
47
+attempt to do disk IO directly to the guest's memory. QEMU may still perform an
57
bool auto_complete)
48
+internal copy of the data.
58
{
49
+@item cache.no-flush
59
@@ -XXX,XX +XXX,XX @@ void commit_active_start(const char *job_id, BlockDriverState *bs,
50
+In case you don't care about data integrity over host failures, you can use
60
MIRROR_LEAVE_BACKING_CHAIN,
51
+@option{cache.no-flush=on}. This option tells QEMU that it never needs to write
61
on_error, on_error, true, cb, opaque, &local_err,
52
+any data to the disk but can instead keep things in cache. If anything goes
62
&commit_active_job_driver, false, base, auto_complete,
53
+wrong, like your host losing power, the disk storage getting disconnected
63
- NULL);
54
+accidentally, etc. your image will most probably be rendered unusable.
64
+ filter_node_name);
55
+@item discard=@var{discard}
65
if (local_err) {
56
+@var{discard} is one of "ignore" (or "off") or "unmap" (or "on") and controls
66
error_propagate(errp, local_err);
57
+whether @code{discard} (also known as @code{trim} or @code{unmap}) requests are
67
goto error_restore_flags;
58
+ignored or passed to the filesystem. Some machine types may not support
68
diff --git a/block/replication.c b/block/replication.c
59
+discard requests.
69
index XXXXXXX..XXXXXXX 100644
60
+@item detect-zeroes=@var{detect-zeroes}
70
--- a/block/replication.c
61
+@var{detect-zeroes} is "off", "on" or "unmap" and enables the automatic
71
+++ b/block/replication.c
62
+conversion of plain zero writes by the OS to driver specific optimized
72
@@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
63
+zero write commands. You may even choose "unmap" if @var{discard} is set
73
s->replication_state = BLOCK_REPLICATION_FAILOVER;
64
+to "unmap" to allow a zero write to be converted to an @code{unmap} operation.
74
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
65
+@end table
75
BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
66
+
76
- replication_done, bs, errp, true);
67
+@end table
77
+ NULL, replication_done, bs, errp, true);
68
+
78
break;
69
+ETEXI
79
default:
70
80
aio_context_release(aio_context);
71
DEF("drive", HAS_ARG, QEMU_OPTION_drive,
81
diff --git a/blockdev.c b/blockdev.c
72
"-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n"
82
index XXXXXXX..XXXXXXX 100644
73
@@ -XXX,XX +XXX,XX @@ STEXI
83
--- a/blockdev.c
74
@item -drive @var{option}[,@var{option}[,@var{option}[,...]]]
84
+++ b/blockdev.c
75
@findex -drive
85
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
76
86
bool has_top, const char *top,
77
-Define a new drive. Valid options are:
87
bool has_backing_file, const char *backing_file,
78
+Define a new drive. This includes creating a block driver node (the backend) as
88
bool has_speed, int64_t speed,
79
+well as a guest device, and is mostly a shortcut for defining the corresponding
89
+ bool has_filter_node_name, const char *filter_node_name,
80
+@option{-blockdev} and @option{-device} options.
90
Error **errp)
81
+
91
{
82
+@option{-drive} accepts all options that are accepted by @option{-blockdev}. In
92
BlockDriverState *bs;
83
+addition, it knows the following options:
93
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
84
94
if (!has_speed) {
85
@table @option
95
speed = 0;
86
@item file=@var{file}
96
}
87
@@ -XXX,XX +XXX,XX @@ These options have the same definition as they have in @option{-hdachs}.
97
+ if (!has_filter_node_name) {
88
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
98
+ filter_node_name = NULL;
89
(see @option{-snapshot}).
99
+ }
90
@item cache=@var{cache}
100
91
-@var{cache} is "none", "writeback", "unsafe", "directsync" or "writethrough" and controls how the host cache is used to access block data.
101
/* Important Note:
92
+@var{cache} is "none", "writeback", "unsafe", "directsync" or "writethrough"
102
* libvirt relies on the DeviceNotFound error class in order to probe for
93
+and controls how the host cache is used to access block data. This is a
103
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
94
+shortcut that sets the @option{cache.direct} and @option{cache.no-flush}
104
goto out;
95
+options (as in @option{-blockdev}), and additionally @option{cache.writeback},
105
}
96
+which provides a default for the @option{write-cache} option of block guest
106
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
97
+devices (as in @option{-device}). The modes correspond to the following
107
- BLOCK_JOB_DEFAULT, speed, on_error, NULL, NULL,
98
+settings:
108
- &local_err, false);
99
+
109
+ BLOCK_JOB_DEFAULT, speed, on_error,
100
+@c Our texi2pod.pl script doesn't support @multitable, so fall back to using
110
+ filter_node_name, NULL, NULL, &local_err, false);
101
+@c plain ASCII art (well, UTF-8 art really). This looks okay both in the manpage
111
} else {
102
+@c and the HTML output.
112
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
103
+@example
113
if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
104
+@ │ cache.writeback cache.direct cache.no-flush
114
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
105
+─────────────┼─────────────────────────────────────────────────
115
}
106
+writeback │ on off off
116
commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed,
107
+none │ on on off
117
on_error, has_backing_file ? backing_file : NULL,
108
+writethrough │ off off off
118
- &local_err);
109
+directsync │ off on off
119
+ filter_node_name, &local_err);
110
+unsafe │ on off on
120
}
111
+@end example
121
if (local_err != NULL) {
112
+
122
error_propagate(errp, local_err);
113
+The default mode is @option{cache=writeback}.
123
diff --git a/include/block/block_int.h b/include/block/block_int.h
114
+
124
index XXXXXXX..XXXXXXX 100644
115
@item aio=@var{aio}
125
--- a/include/block/block_int.h
116
@var{aio} is "threads", or "native" and selects between pthread based disk I/O and native Linux AIO.
126
+++ b/include/block/block_int.h
117
-@item discard=@var{discard}
127
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
118
-@var{discard} is one of "ignore" (or "off") or "unmap" (or "on") and controls whether @dfn{discard} (also known as @dfn{trim} or @dfn{unmap}) requests are ignored or passed to the filesystem. Some machine types may not support discard requests.
128
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
119
@item format=@var{format}
129
* @on_error: The action to take upon error.
120
Specify which disk @var{format} will be used rather than detecting
130
* @backing_file_str: String to use as the backing file in @top's overlay
121
the format. Can be used to specify format=raw to avoid interpreting
131
+ * @filter_node_name: The node name that should be assigned to the filter
122
@@ -XXX,XX +XXX,XX @@ Specify which @var{action} to take on write and read errors. Valid actions are:
132
+ * driver that the commit job inserts into the graph above @top. NULL means
123
"report" (report the error to the guest), "enospc" (pause QEMU only if the
133
+ * that a node name should be autogenerated.
124
host disk is full; report the error to the guest otherwise).
134
* @errp: Error object.
125
The default setting is @option{werror=enospc} and @option{rerror=report}.
135
*
126
-@item readonly
136
*/
127
-Open drive @option{file} as read-only. Guest write attempts will fail.
137
void commit_start(const char *job_id, BlockDriverState *bs,
128
@item copy-on-read=@var{copy-on-read}
138
BlockDriverState *base, BlockDriverState *top, int64_t speed,
129
@var{copy-on-read} is "on" or "off" and enables whether to copy read backing
139
BlockdevOnError on_error, const char *backing_file_str,
130
file sectors into the image file.
140
- Error **errp);
131
-@item detect-zeroes=@var{detect-zeroes}
141
+ const char *filter_node_name, Error **errp);
132
-@var{detect-zeroes} is "off", "on" or "unmap" and enables the automatic
142
/**
133
-conversion of plain zero writes by the OS to driver specific optimized
143
* commit_active_start:
134
-zero write commands. You may even choose "unmap" if @var{discard} is set
144
* @job_id: The id of the newly-created job, or %NULL to use the
135
-to "unmap" to allow a zero write to be converted to an UNMAP operation.
145
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
136
@item bps=@var{b},bps_rd=@var{r},bps_wr=@var{w}
146
* See @BlockJobCreateFlags
137
Specify bandwidth throttling limits in bytes per second, either for all request
147
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
138
types or for reads or writes only. Small values can lead to timeouts or hangs
148
* @on_error: The action to take upon error.
139
@@ -XXX,XX +XXX,XX @@ prevent guests from circumventing throttling limits by using many small disks
149
+ * @filter_node_name: The node name that should be assigned to the filter
140
instead of a single larger disk.
150
+ * driver that the commit job inserts into the graph above @bs. NULL means that
141
@end table
151
+ * a node name should be autogenerated.
142
152
* @cb: Completion function for the job.
143
-By default, the @option{cache=writeback} mode is used. It will report data
153
* @opaque: Opaque pointer value passed to @cb.
144
+By default, the @option{cache.writeback=on} mode is used. It will report data
154
* @errp: Error object.
145
writes as completed as soon as the data is present in the host page cache.
155
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
146
This is safe as long as your guest OS makes sure to correctly flush disk caches
156
void commit_active_start(const char *job_id, BlockDriverState *bs,
147
where needed. If your guest OS does not handle volatile disk write caches
157
BlockDriverState *base, int creation_flags,
148
correctly and your host crashes or loses power, then the guest may experience
158
int64_t speed, BlockdevOnError on_error,
149
data corruption.
159
- BlockCompletionFunc *cb,
150
160
- void *opaque, Error **errp, bool auto_complete);
151
-For such guests, you should consider using @option{cache=writethrough}. This
161
+ const char *filter_node_name,
152
+For such guests, you should consider using @option{cache.writeback=off}. This
162
+ BlockCompletionFunc *cb, void *opaque, Error **errp,
153
means that the host page cache will be used to read and write data, but write
163
+ bool auto_complete);
154
notification will be sent to the guest only after QEMU has made sure to flush
164
/*
155
each write to the disk. Be aware that this has a major impact on performance.
165
* mirror_start:
156
166
* @job_id: The id of the newly-created job, or %NULL to use the
157
-The host page cache can be avoided entirely with @option{cache=none}. This will
167
diff --git a/qapi/block-core.json b/qapi/block-core.json
158
-attempt to do disk IO directly to the guest's memory. QEMU may still perform
168
index XXXXXXX..XXXXXXX 100644
159
-an internal copy of the data. Note that this is considered a writeback mode and
169
--- a/qapi/block-core.json
160
-the guest OS must handle the disk write cache correctly in order to avoid data
170
+++ b/qapi/block-core.json
161
-corruption on host crashes.
171
@@ -XXX,XX +XXX,XX @@
162
-
172
#
163
-The host page cache can be avoided while only sending write notifications to
173
# @speed: #optional the maximum speed, in bytes per second
164
-the guest when the data has been flushed to the disk using
174
#
165
-@option{cache=directsync}.
175
+# @filter-node-name: #optional the node name that should be assigned to the
166
-
176
+# filter driver that the commit job inserts into the graph
167
-In case you don't care about data integrity over host failures, use
177
+# above @top. If this option is not given, a node name is
168
-@option{cache=unsafe}. This option tells QEMU that it never needs to write any
178
+# autogenerated. (Since: 2.9)
169
-data to the disk but can instead keep things in cache. If anything goes wrong,
179
+#
170
-like your host losing power, the disk storage getting disconnected accidentally,
180
# Returns: Nothing on success
171
-etc. your image will most probably be rendered unusable. When using
181
# If commit or stream is already active on this device, DeviceInUse
172
-the @option{-snapshot} option, unsafe caching is always used.
182
# If @device does not exist, DeviceNotFound
173
+When using the @option{-snapshot} option, unsafe caching is always used.
183
@@ -XXX,XX +XXX,XX @@
174
184
##
175
Copy-on-read avoids accessing the same backing file sectors repeatedly and is
185
{ 'command': 'block-commit',
176
useful when the backing file is over a slow network. By default copy-on-read
186
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', '*top': 'str',
187
- '*backing-file': 'str', '*speed': 'int' } }
188
+ '*backing-file': 'str', '*speed': 'int',
189
+ '*filter-node-name': 'str' } }
190
191
##
192
# @drive-backup:
193
diff --git a/qemu-img.c b/qemu-img.c
194
index XXXXXXX..XXXXXXX 100644
195
--- a/qemu-img.c
196
+++ b/qemu-img.c
197
@@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv)
198
aio_context = bdrv_get_aio_context(bs);
199
aio_context_acquire(aio_context);
200
commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0,
201
- BLOCKDEV_ON_ERROR_REPORT, common_block_job_cb, &cbi,
202
- &local_err, false);
203
+ BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb,
204
+ &cbi, &local_err, false);
205
aio_context_release(aio_context);
206
if (local_err) {
207
goto done;
208
--
177
--
209
1.8.3.1
178
1.8.3.1
210
179
211
180
diff view generated by jsdifflib
1
Now that all block drivers with children tell us what permissions they
1
This documents the driver-specific options for the raw, qcow2 and file
2
need from each of their children, bdrv_attach_child() can use this
2
block drivers for the man page. For everything else, we refer to the
3
information and make the right requirements while trying to attach new
3
QAPI documentation.
4
children.
5
4
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Acked-by: Fam Zheng <famz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
8
---
10
block.c | 10 ++++++++--
9
qemu-options.hx | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
11
1 file changed, 8 insertions(+), 2 deletions(-)
10
1 file changed, 114 insertions(+), 1 deletion(-)
12
11
13
diff --git a/block.c b/block.c
12
diff --git a/qemu-options.hx b/qemu-options.hx
14
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
14
--- a/qemu-options.hx
16
+++ b/block.c
15
+++ b/qemu-options.hx
17
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
16
@@ -XXX,XX +XXX,XX @@ STEXI
18
Error **errp)
17
@item -blockdev @var{option}[,@var{option}[,@var{option}[,...]]]
19
{
18
@findex -blockdev
20
BdrvChild *child;
19
21
+ uint64_t perm, shared_perm;
20
-Define a new block driver node.
21
+Define a new block driver node. Some of the options apply to all block drivers,
22
+other options are only accepted for a specific block driver. See below for a
23
+list of generic options and options for the most common block drivers.
22
+
24
+
23
+ bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
25
+Options that expect a reference to another node (e.g. @code{file}) can be
26
+given in two ways. Either you specify the node name of an already existing node
27
+(file=@var{node-name}), or you define a new node inline, adding options
28
+for the referenced node after a dot (file.filename=@var{path},file.aio=native).
24
+
29
+
25
+ assert(parent_bs->drv);
30
+A block driver node created with @option{-blockdev} can be used for a guest
26
+ parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role,
31
+device by specifying its node name for the @code{drive} property in a
27
+ perm, shared_perm, &perm, &shared_perm);
32
+@option{-device} argument that defines a block device.
28
33
29
- /* FIXME Use real permissions */
34
@table @option
30
child = bdrv_root_attach_child(child_bs, child_name, child_role,
35
@item Valid options for any block driver node:
31
- 0, BLK_PERM_ALL, parent_bs, errp);
36
@@ -XXX,XX +XXX,XX @@ zero write commands. You may even choose "unmap" if @var{discard} is set
32
+ perm, shared_perm, parent_bs, errp);
37
to "unmap" to allow a zero write to be converted to an @code{unmap} operation.
33
if (child == NULL) {
38
@end table
34
return NULL;
39
35
}
40
+@item Driver-specific options for @code{file}
41
+
42
+This is the protocol-level block driver for accessing regular files.
43
+
44
+@table @code
45
+@item filename
46
+The path to the image file in the local filesystem
47
+@item aio
48
+Specifies the AIO backend (threads/native, default: threads)
49
+@end table
50
+Example:
51
+@example
52
+-blockdev driver=file,node-name=disk,filename=disk.img
53
+@end example
54
+
55
+@item Driver-specific options for @code{raw}
56
+
57
+This is the image format block driver for raw images. It is usually
58
+stacked on top of a protocol level block driver such as @code{file}.
59
+
60
+@table @code
61
+@item file
62
+Reference to or definition of the data source block driver node
63
+(e.g. a @code{file} driver node)
64
+@end table
65
+Example 1:
66
+@example
67
+-blockdev driver=file,node-name=disk_file,filename=disk.img
68
+-blockdev driver=raw,node-name=disk,file=disk_file
69
+@end example
70
+Example 2:
71
+@example
72
+-blockdev driver=raw,node-name=disk,file.driver=file,file.filename=disk.img
73
+@end example
74
+
75
+@item Driver-specific options for @code{qcow2}
76
+
77
+This is the image format block driver for qcow2 images. It is usually
78
+stacked on top of a protocol level block driver such as @code{file}.
79
+
80
+@table @code
81
+@item file
82
+Reference to or definition of the data source block driver node
83
+(e.g. a @code{file} driver node)
84
+
85
+@item backing
86
+Reference to or definition of the backing file block device (default is taken
87
+from the image file). It is allowed to pass an empty string here in order to
88
+disable the default backing file.
89
+
90
+@item lazy-refcounts
91
+Whether to enable the lazy refcounts feature (on/off; default is taken from the
92
+image file)
93
+
94
+@item cache-size
95
+The maximum total size of the L2 table and refcount block caches in bytes
96
+(default: 1048576 bytes or 8 clusters, whichever is larger)
97
+
98
+@item l2-cache-size
99
+The maximum size of the L2 table cache in bytes
100
+(default: 4/5 of the total cache size)
101
+
102
+@item refcount-cache-size
103
+The maximum size of the refcount block cache in bytes
104
+(default: 1/5 of the total cache size)
105
+
106
+@item cache-clean-interval
107
+Clean unused entries in the L2 and refcount caches. The interval is in seconds.
108
+The default value is 0 and it disables this feature.
109
+
110
+@item pass-discard-request
111
+Whether discard requests to the qcow2 device should be forwarded to the data
112
+source (on/off; default: on if discard=unmap is specified, off otherwise)
113
+
114
+@item pass-discard-snapshot
115
+Whether discard requests for the data source should be issued when a snapshot
116
+operation (e.g. deleting a snapshot) frees clusters in the qcow2 file (on/off;
117
+default: on)
118
+
119
+@item pass-discard-other
120
+Whether discard requests for the data source should be issued on other
121
+occasions where a cluster gets freed (on/off; default: off)
122
+
123
+@item overlap-check
124
+Which overlap checks to perform for writes to the image
125
+(none/constant/cached/all; default: cached). For details or finer
126
+granularity control refer to the QAPI documentation of @code{blockdev-add}.
127
+@end table
128
+
129
+Example 1:
130
+@example
131
+-blockdev driver=file,node-name=my_file,filename=/tmp/disk.qcow2
132
+-blockdev driver=qcow2,node-name=hda,file=my_file,overlap-check=none,cache-size=16777216
133
+@end example
134
+Example 2:
135
+@example
136
+-blockdev driver=qcow2,node-name=disk,file.driver=http,file.filename=http://example.com/image.qcow2
137
+@end example
138
+
139
+@item Driver-specific options for other drivers
140
+Please refer to the QAPI documentation of the @code{blockdev-add} QMP command.
141
+
142
@end table
143
144
ETEXI
36
--
145
--
37
1.8.3.1
146
1.8.3.1
38
147
39
148
diff view generated by jsdifflib
1
Not requesting any permissions is actually correct for these test cases
1
From: Alberto Garcia <berto@igalia.com>
2
because no actual I/O or other operation covered by the permission
3
system is performed.
4
2
3
There used to be throttle_timers_{detach,attach}_aio_context() calls
4
in bdrv_set_aio_context(), but since 7ca7f0f6db1fedd28d490795d778cf239
5
they are now in blk_set_aio_context().
6
7
Signed-off-by: Alberto Garcia <berto@igalia.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
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
Acked-by: Fam Zheng <famz@redhat.com>
8
---
10
---
9
tests/test-blockjob.c | 2 +-
11
block/throttle-groups.c | 2 +-
10
tests/test-throttle.c | 2 +-
12
1 file changed, 1 insertion(+), 1 deletion(-)
11
2 files changed, 2 insertions(+), 2 deletions(-)
12
13
13
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
14
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
14
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
15
--- a/tests/test-blockjob.c
16
--- a/block/throttle-groups.c
16
+++ b/tests/test-blockjob.c
17
+++ b/block/throttle-groups.c
17
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
18
@@ -XXX,XX +XXX,XX @@
18
* BlockDriverState inserted. */
19
* Again, all this is handled internally and is mostly transparent to
19
static BlockBackend *create_blk(const char *name)
20
* the outside. The 'throttle_timers' field however has an additional
20
{
21
* constraint because it may be temporarily invalid (see for example
21
- /* FIXME Use real permissions */
22
- * bdrv_set_aio_context()). Therefore in this file a thread will
22
+ /* No I/O is performed on this device */
23
+ * blk_set_aio_context()). Therefore in this file a thread will
23
BlockBackend *blk = blk_new(0, BLK_PERM_ALL);
24
* access some other BlockBackend's timers only after verifying that
24
BlockDriverState *bs;
25
* that BlockBackend has throttled requests in the queue.
25
26
*/
26
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/tests/test-throttle.c
29
+++ b/tests/test-throttle.c
30
@@ -XXX,XX +XXX,XX @@ static void test_groups(void)
31
BlockBackend *blk1, *blk2, *blk3;
32
BlockBackendPublic *blkp1, *blkp2, *blkp3;
33
34
- /* FIXME Use real permissions */
35
+ /* No actual I/O is performed on these devices */
36
blk1 = blk_new(0, BLK_PERM_ALL);
37
blk2 = blk_new(0, BLK_PERM_ALL);
38
blk3 = blk_new(0, BLK_PERM_ALL);
39
--
27
--
40
1.8.3.1
28
1.8.3.1
41
29
42
30
diff view generated by jsdifflib
1
bdrv_append() cares about isolation of the node that it modifies, but
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
not about activity in some subtree below it. Instead of using the
3
recursive bdrv_requests_pending(), directly check bs->in_flight, which
4
considers only the node in question.
5
2
3
Old kvm.ko versions only supported a tiny number of ioeventfds so
4
virtio-pci avoids ioeventfds when kvm_has_many_ioeventfds() returns 0.
5
6
Do not check kvm_has_many_ioeventfds() when KVM is disabled since it
7
always returns 0. Since commit 8c56c1a592b5092d91da8d8943c17777d6462a6f
8
("memory: emulate ioeventfd") it has been possible to use ioeventfds in
9
qtest or TCG mode.
10
11
This patch makes -device virtio-blk-pci,iothread=iothread0 work even
12
when KVM is disabled.
13
14
I have tested that virtio-blk-pci works under TCG both with and without
15
iothread.
16
17
Cc: Michael S. Tsirkin <mst@redhat.com>
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Acked-by: Fam Zheng <famz@redhat.com>
9
---
21
---
10
block.c | 4 ++--
22
hw/virtio/virtio-pci.c | 2 +-
11
1 file changed, 2 insertions(+), 2 deletions(-)
23
1 file changed, 1 insertion(+), 1 deletion(-)
12
24
13
diff --git a/block.c b/block.c
25
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
14
index XXXXXXX..XXXXXXX 100644
26
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
27
--- a/hw/virtio/virtio-pci.c
16
+++ b/block.c
28
+++ b/hw/virtio/virtio-pci.c
17
@@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from,
29
@@ -XXX,XX +XXX,XX @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
18
*/
30
bool pcie_port = pci_bus_is_express(pci_dev->bus) &&
19
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
31
!pci_bus_is_root(pci_dev->bus);
20
{
32
21
- assert(!bdrv_requests_pending(bs_top));
33
- if (!kvm_has_many_ioeventfds()) {
22
- assert(!bdrv_requests_pending(bs_new));
34
+ if (kvm_enabled() && !kvm_has_many_ioeventfds()) {
23
+ assert(!atomic_read(&bs_top->in_flight));
35
proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
24
+ assert(!atomic_read(&bs_new->in_flight));
36
}
25
26
bdrv_ref(bs_top);
27
37
28
--
38
--
29
1.8.3.1
39
1.8.3.1
30
40
31
41
diff view generated by jsdifflib
1
NBD can't cope with device size changes, so resize must be forbidden,
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
but otherwise we can tolerate anything. Depending on whether the export
3
is writable or not, we only require consistent reads and writes.
4
2
3
migration_incoming_state_destroy() uses qemu_fclose() on the vmstate
4
file. Make sure to call it inside an AioContext acquire/release region.
5
6
This fixes an 'qemu: qemu_mutex_unlock: Operation not permitted' abort
7
in loadvm.
8
9
This patch closes the vmstate file before ending the drained region.
10
Previously we closed the vmstate file after ending the drained region.
11
The order does not matter.
12
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Acked-by: Fam Zheng <famz@redhat.com>
8
---
15
---
9
nbd/server.c | 11 +++++++++--
16
migration/savevm.c | 2 +-
10
1 file changed, 9 insertions(+), 2 deletions(-)
17
1 file changed, 1 insertion(+), 1 deletion(-)
11
18
12
diff --git a/nbd/server.c b/nbd/server.c
19
diff --git a/migration/savevm.c b/migration/savevm.c
13
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
14
--- a/nbd/server.c
21
--- a/migration/savevm.c
15
+++ b/nbd/server.c
22
+++ b/migration/savevm.c
16
@@ -XXX,XX +XXX,XX @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
23
@@ -XXX,XX +XXX,XX @@ int load_snapshot(const char *name, Error **errp)
17
{
24
18
BlockBackend *blk;
25
aio_context_acquire(aio_context);
19
NBDExport *exp = g_malloc0(sizeof(NBDExport));
26
ret = qemu_loadvm_state(f);
20
+ uint64_t perm;
27
+ migration_incoming_state_destroy();
21
int ret;
28
aio_context_release(aio_context);
22
29
23
- /* FIXME Use real permissions */
30
bdrv_drain_all_end();
24
- blk = blk_new(0, BLK_PERM_ALL);
31
25
+ /* Don't allow resize while the NBD server is running, otherwise we don't
32
- migration_incoming_state_destroy();
26
+ * care what happens with the node. */
27
+ perm = BLK_PERM_CONSISTENT_READ;
28
+ if ((nbdflags & NBD_FLAG_READ_ONLY) == 0) {
29
+ perm |= BLK_PERM_WRITE;
30
+ }
31
+ blk = blk_new(perm, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
32
+ BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD);
33
ret = blk_insert_bs(blk, bs, errp);
34
if (ret < 0) {
33
if (ret < 0) {
35
goto fail;
34
error_setg(errp, "Error %d while loading VM state", ret);
35
return ret;
36
--
36
--
37
1.8.3.1
37
1.8.3.1
38
38
39
39
diff view generated by jsdifflib
1
Almost all format drivers have the same characteristics as far as
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
permissions are concerned: They have one or more children for storing
3
their own data and, more importantly, metadata (can be written to and
4
grow even without external write requests, must be protected against
5
other writers and present consistent data) and optionally a backing file
6
(this is just data, so like for a filter, it only depends on what the
7
parent nodes need).
8
2
9
This provides a default implementation that can be shared by most of
3
Avoid duplicating the QEMU command-line.
10
our format drivers.
11
4
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Acked-by: Fam Zheng <famz@redhat.com>
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
---
7
---
16
block.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
8
tests/qemu-iotests/068 | 15 +++++++++------
17
include/block/block_int.h | 8 ++++++++
9
1 file changed, 9 insertions(+), 6 deletions(-)
18
2 files changed, 52 insertions(+)
19
10
20
diff --git a/block.c b/block.c
11
diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068
21
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100755
22
--- a/block.c
13
--- a/tests/qemu-iotests/068
23
+++ b/block.c
14
+++ b/tests/qemu-iotests/068
24
@@ -XXX,XX +XXX,XX @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
15
@@ -XXX,XX +XXX,XX @@ case "$QEMU_DEFAULT_MACHINE" in
25
(c->shared_perm & DEFAULT_PERM_UNCHANGED);
16
;;
26
}
17
esac
27
18
28
+void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
19
-# Give qemu some time to boot before saving the VM state
29
+ const BdrvChildRole *role,
20
-bash -c 'sleep 1; echo -e "savevm 0\nquit"' |\
30
+ uint64_t perm, uint64_t shared,
21
- $QEMU $platform_parm -nographic -monitor stdio -serial none -hda "$TEST_IMG" |\
31
+ uint64_t *nperm, uint64_t *nshared)
22
+_qemu()
32
+{
23
+{
33
+ bool backing = (role == &child_backing);
24
+ $QEMU $platform_parm -nographic -monitor stdio -serial none -hda "$TEST_IMG" \
34
+ assert(role == &child_backing || role == &child_file);
25
+ "$@" |\
35
+
26
_filter_qemu | _filter_hmp
36
+ if (!backing) {
37
+ /* Apart from the modifications below, the same permissions are
38
+ * forwarded and left alone as for filters */
39
+ bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
40
+
41
+ /* Format drivers may touch metadata even if the guest doesn't write */
42
+ if (!bdrv_is_read_only(bs)) {
43
+ perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
44
+ }
45
+
46
+ /* bs->file always needs to be consistent because of the metadata. We
47
+ * can never allow other users to resize or write to it. */
48
+ perm |= BLK_PERM_CONSISTENT_READ;
49
+ shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
50
+ } else {
51
+ /* We want consistent read from backing files if the parent needs it.
52
+ * No other operations are performed on backing files. */
53
+ perm &= BLK_PERM_CONSISTENT_READ;
54
+
55
+ /* If the parent can deal with changing data, we're okay with a
56
+ * writable and resizable backing file. */
57
+ /* TODO Require !(perm & BLK_PERM_CONSISTENT_READ), too? */
58
+ if (shared & BLK_PERM_WRITE) {
59
+ shared = BLK_PERM_WRITE | BLK_PERM_RESIZE;
60
+ } else {
61
+ shared = 0;
62
+ }
63
+
64
+ shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
65
+ BLK_PERM_WRITE_UNCHANGED;
66
+ }
67
+
68
+ *nperm = perm;
69
+ *nshared = shared;
70
+}
27
+}
71
+
28
+
72
static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
29
+# Give qemu some time to boot before saving the VM state
73
bool check_new_perm)
30
+bash -c 'sleep 1; echo -e "savevm 0\nquit"' | _qemu
74
{
31
# Now try to continue from that VM state (this should just work)
75
diff --git a/include/block/block_int.h b/include/block/block_int.h
32
-echo quit |\
76
index XXXXXXX..XXXXXXX 100644
33
- $QEMU $platform_parm -nographic -monitor stdio -serial none -hda "$TEST_IMG" -loadvm 0 |\
77
--- a/include/block/block_int.h
34
- _filter_qemu | _filter_hmp
78
+++ b/include/block/block_int.h
35
+echo quit | _qemu -loadvm 0
79
@@ -XXX,XX +XXX,XX @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
36
80
uint64_t perm, uint64_t shared,
37
# success, all done
81
uint64_t *nperm, uint64_t *nshared);
38
echo "*** done"
82
83
+/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by
84
+ * (non-raw) image formats: Like above for bs->backing, but for bs->file it
85
+ * requires WRITE | RESIZE for read-write images, always requires
86
+ * CONSISTENT_READ and doesn't share WRITE. */
87
+void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
88
+ const BdrvChildRole *role,
89
+ uint64_t perm, uint64_t shared,
90
+ uint64_t *nperm, uint64_t *nshared);
91
92
const char *bdrv_get_parent_name(const BlockDriverState *bs);
93
void blk_dev_change_media_cb(BlockBackend *blk, bool load);
94
--
39
--
95
1.8.3.1
40
1.8.3.1
96
41
97
42
diff view generated by jsdifflib
1
Now that the backing file child role implements .attach/.detach
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
callbacks, nothing prevents us from modifying the graph even if that
3
involves changing backing file links.
4
2
3
Perform the savevm/loadvm test with both iothread on and off. This
4
covers the recently found savevm/loadvm hang when iothread is enabled.
5
6
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Acked-by: Fam Zheng <famz@redhat.com>
8
---
8
---
9
block.c | 7 +++----
9
tests/qemu-iotests/068 | 23 ++++++++++++++---------
10
1 file changed, 3 insertions(+), 4 deletions(-)
10
tests/qemu-iotests/068.out | 11 ++++++++++-
11
2 files changed, 24 insertions(+), 10 deletions(-)
11
12
12
diff --git a/block.c b/block.c
13
diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068
14
index XXXXXXX..XXXXXXX 100755
15
--- a/tests/qemu-iotests/068
16
+++ b/tests/qemu-iotests/068
17
@@ -XXX,XX +XXX,XX @@ _supported_os Linux
18
IMGOPTS="compat=1.1"
19
IMG_SIZE=128K
20
21
-echo
22
-echo "=== Saving and reloading a VM state to/from a qcow2 image ==="
23
-echo
24
-_make_test_img $IMG_SIZE
25
-
26
case "$QEMU_DEFAULT_MACHINE" in
27
s390-ccw-virtio)
28
platform_parm="-no-shutdown"
29
@@ -XXX,XX +XXX,XX @@ _qemu()
30
_filter_qemu | _filter_hmp
31
}
32
33
-# Give qemu some time to boot before saving the VM state
34
-bash -c 'sleep 1; echo -e "savevm 0\nquit"' | _qemu
35
-# Now try to continue from that VM state (this should just work)
36
-echo quit | _qemu -loadvm 0
37
+for extra_args in \
38
+ "" \
39
+ "-object iothread,id=iothread0 -set device.hba0.iothread=iothread0"; do
40
+ echo
41
+ echo "=== Saving and reloading a VM state to/from a qcow2 image ($extra_args) ==="
42
+ echo
43
+
44
+ _make_test_img $IMG_SIZE
45
+
46
+ # Give qemu some time to boot before saving the VM state
47
+ bash -c 'sleep 1; echo -e "savevm 0\nquit"' | _qemu $extra_args
48
+ # Now try to continue from that VM state (this should just work)
49
+ echo quit | _qemu $extra_args -loadvm 0
50
+done
51
52
# success, all done
53
echo "*** done"
54
diff --git a/tests/qemu-iotests/068.out b/tests/qemu-iotests/068.out
13
index XXXXXXX..XXXXXXX 100644
55
index XXXXXXX..XXXXXXX 100644
14
--- a/block.c
56
--- a/tests/qemu-iotests/068.out
15
+++ b/block.c
57
+++ b/tests/qemu-iotests/068.out
16
@@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from,
58
@@ -XXX,XX +XXX,XX @@
17
continue;
59
QA output created by 068
18
}
60
19
if (c->role == &child_backing) {
61
-=== Saving and reloading a VM state to/from a qcow2 image ===
20
- /* @from is generally not allowed to be a backing file, except for
62
+=== Saving and reloading a VM state to/from a qcow2 image () ===
21
- * when @to is the overlay. In that case, @from may not be replaced
63
+
22
- * by @to as @to's backing node. */
64
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072
23
+ /* If @from is a backing file of @to, ignore the child to avoid
65
+QEMU X.Y.Z monitor - type 'help' for more information
24
+ * creating a loop. We only want to change the pointer of other
66
+(qemu) savevm 0
25
+ * parents. */
67
+(qemu) quit
26
QLIST_FOREACH(to_c, &to->children, next) {
68
+QEMU X.Y.Z monitor - type 'help' for more information
27
if (to_c == c) {
69
+(qemu) quit
28
break;
70
+
29
@@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from,
71
+=== Saving and reloading a VM state to/from a qcow2 image (-object iothread,id=iothread0 -set device.hba0.iothread=iothread0) ===
30
}
72
31
}
73
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072
32
74
QEMU X.Y.Z monitor - type 'help' for more information
33
- assert(c->role != &child_backing);
34
bdrv_ref(to);
35
/* FIXME Are we sure that bdrv_replace_child() can't run into
36
* &error_abort because of permissions? */
37
--
75
--
38
1.8.3.1
76
1.8.3.1
39
77
40
78
diff view generated by jsdifflib
1
Instead of just telling that there was some conflict, we can be specific
1
From: Stephen Bates <sbates@raithlin.com>
2
and tell which permissions were in conflict and which way the conflict
2
3
is.
3
Add the ability for the NVMe model to support both the RDS and WDS
4
4
modes in the Controller Memory Buffer.
5
6
Although not currently supported in the upstreamed Linux kernel a fork
7
with support exists [1] and user-space test programs that build on
8
this also exist [2].
9
10
Useful for testing CMB functionality in preperation for real CMB
11
enabled NVMe devices (coming soon).
12
13
[1] https://github.com/sbates130272/linux-p2pmem
14
[2] https://github.com/sbates130272/p2pmem-test
15
16
Signed-off-by: Stephen Bates <sbates@raithlin.com>
17
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
18
Reviewed-by: Keith Busch <keith.busch@intel.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
---
20
---
9
block.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
21
hw/block/nvme.c | 83 +++++++++++++++++++++++++++++++++++++++------------------
10
1 file changed, 56 insertions(+), 11 deletions(-)
22
hw/block/nvme.h | 1 +
11
23
2 files changed, 58 insertions(+), 26 deletions(-)
12
diff --git a/block.c b/block.c
24
25
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
13
index XXXXXXX..XXXXXXX 100644
26
index XXXXXXX..XXXXXXX 100644
14
--- a/block.c
27
--- a/hw/block/nvme.c
15
+++ b/block.c
28
+++ b/hw/block/nvme.c
16
@@ -XXX,XX +XXX,XX @@ static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
29
@@ -XXX,XX +XXX,XX @@
17
*shared_perm = cumulative_shared_perms;
30
* cmb_size_mb=<cmb_size_mb[optional]>
31
*
32
* Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at
33
- * offset 0 in BAR2 and supports SQS only for now.
34
+ * offset 0 in BAR2 and supports only WDS, RDS and SQS for now.
35
*/
36
37
#include "qemu/osdep.h"
38
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
39
}
18
}
40
}
19
41
20
+static char *bdrv_child_user_desc(BdrvChild *c)
42
-static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2,
21
+{
43
- uint32_t len, NvmeCtrl *n)
22
+ if (c->role->get_parent_desc) {
44
+static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
23
+ return c->role->get_parent_desc(c);
45
+ uint64_t prp2, uint32_t len, NvmeCtrl *n)
46
{
47
hwaddr trans_len = n->page_size - (prp1 % n->page_size);
48
trans_len = MIN(len, trans_len);
49
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2,
50
51
if (!prp1) {
52
return NVME_INVALID_FIELD | NVME_DNR;
53
+ } else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
54
+ prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
55
+ qsg->nsg = 0;
56
+ qemu_iovec_init(iov, num_prps);
57
+ qemu_iovec_add(iov, (void *)&n->cmbuf[prp1 - n->ctrl_mem.addr], trans_len);
58
+ } else {
59
+ pci_dma_sglist_init(qsg, &n->parent_obj, num_prps);
60
+ qemu_sglist_add(qsg, prp1, trans_len);
61
}
62
-
63
- pci_dma_sglist_init(qsg, &n->parent_obj, num_prps);
64
- qemu_sglist_add(qsg, prp1, trans_len);
65
len -= trans_len;
66
if (len) {
67
if (!prp2) {
68
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2,
69
70
nents = (len + n->page_size - 1) >> n->page_bits;
71
prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
72
- pci_dma_read(&n->parent_obj, prp2, (void *)prp_list, prp_trans);
73
+ nvme_addr_read(n, prp2, (void *)prp_list, prp_trans);
74
while (len != 0) {
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
76
77
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2,
78
i = 0;
79
nents = (len + n->page_size - 1) >> n->page_bits;
80
prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t);
81
- pci_dma_read(&n->parent_obj, prp_ent, (void *)prp_list,
82
+ nvme_addr_read(n, prp_ent, (void *)prp_list,
83
prp_trans);
84
prp_ent = le64_to_cpu(prp_list[i]);
85
}
86
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2,
87
}
88
89
trans_len = MIN(len, n->page_size);
90
- qemu_sglist_add(qsg, prp_ent, trans_len);
91
+ if (qsg->nsg){
92
+ qemu_sglist_add(qsg, prp_ent, trans_len);
93
+ } else {
94
+ qemu_iovec_add(iov, (void *)&n->cmbuf[prp_ent - n->ctrl_mem.addr], trans_len);
95
+ }
96
len -= trans_len;
97
i++;
98
}
99
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2,
100
if (prp2 & (n->page_size - 1)) {
101
goto unmap;
102
}
103
- qemu_sglist_add(qsg, prp2, len);
104
+ if (qsg->nsg) {
105
+ qemu_sglist_add(qsg, prp2, len);
106
+ } else {
107
+ qemu_iovec_add(iov, (void *)&n->cmbuf[prp2 - n->ctrl_mem.addr], trans_len);
108
+ }
109
}
110
}
111
return NVME_SUCCESS;
112
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
113
uint64_t prp1, uint64_t prp2)
114
{
115
QEMUSGList qsg;
116
+ QEMUIOVector iov;
117
+ uint16_t status = NVME_SUCCESS;
118
119
- if (nvme_map_prp(&qsg, prp1, prp2, len, n)) {
120
+ if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
121
return NVME_INVALID_FIELD | NVME_DNR;
122
}
123
- if (dma_buf_read(ptr, len, &qsg)) {
124
+ if (qsg.nsg > 0) {
125
+ if (dma_buf_read(ptr, len, &qsg)) {
126
+ status = NVME_INVALID_FIELD | NVME_DNR;
127
+ }
128
qemu_sglist_destroy(&qsg);
129
- return NVME_INVALID_FIELD | NVME_DNR;
130
+ } else {
131
+ if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
132
+ status = NVME_INVALID_FIELD | NVME_DNR;
133
+ }
134
+ qemu_iovec_destroy(&iov);
135
}
136
- qemu_sglist_destroy(&qsg);
137
- return NVME_SUCCESS;
138
+ return status;
139
}
140
141
static void nvme_post_cqes(void *opaque)
142
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
143
return NVME_LBA_RANGE | NVME_DNR;
144
}
145
146
- if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) {
147
+ if (nvme_map_prp(&req->qsg, &req->iov, prp1, prp2, data_size, n)) {
148
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
149
return NVME_INVALID_FIELD | NVME_DNR;
150
}
151
152
- assert((nlb << data_shift) == req->qsg.size);
153
-
154
- req->has_sg = true;
155
dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct);
156
- req->aiocb = is_write ?
157
- dma_blk_write(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE,
158
- nvme_rw_cb, req) :
159
- dma_blk_read(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE,
160
- nvme_rw_cb, req);
161
+ if (req->qsg.nsg > 0) {
162
+ req->has_sg = true;
163
+ req->aiocb = is_write ?
164
+ dma_blk_write(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE,
165
+ nvme_rw_cb, req) :
166
+ dma_blk_read(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE,
167
+ nvme_rw_cb, req);
168
+ } else {
169
+ req->has_sg = false;
170
+ req->aiocb = is_write ?
171
+ blk_aio_pwritev(n->conf.blk, data_offset, &req->iov, 0, nvme_rw_cb,
172
+ req) :
173
+ blk_aio_preadv(n->conf.blk, data_offset, &req->iov, 0, nvme_rw_cb,
174
+ req);
24
+ }
175
+ }
176
177
return NVME_NO_COMPLETE;
178
}
179
@@ -XXX,XX +XXX,XX @@ static int nvme_init(PCIDevice *pci_dev)
180
NVME_CMBSZ_SET_SQS(n->bar.cmbsz, 1);
181
NVME_CMBSZ_SET_CQS(n->bar.cmbsz, 0);
182
NVME_CMBSZ_SET_LISTS(n->bar.cmbsz, 0);
183
- NVME_CMBSZ_SET_RDS(n->bar.cmbsz, 0);
184
- NVME_CMBSZ_SET_WDS(n->bar.cmbsz, 0);
185
+ NVME_CMBSZ_SET_RDS(n->bar.cmbsz, 1);
186
+ NVME_CMBSZ_SET_WDS(n->bar.cmbsz, 1);
187
NVME_CMBSZ_SET_SZU(n->bar.cmbsz, 2); /* MBs */
188
NVME_CMBSZ_SET_SZ(n->bar.cmbsz, n->cmb_size_mb);
189
190
+ n->cmbloc = n->bar.cmbloc;
191
+ n->cmbsz = n->bar.cmbsz;
25
+
192
+
26
+ return g_strdup("another user");
193
n->cmbuf = g_malloc0(NVME_CMBSZ_GETSIZE(n->bar.cmbsz));
27
+}
194
memory_region_init_io(&n->ctrl_mem, OBJECT(n), &nvme_cmb_ops, n,
28
+
195
"nvme-cmb", NVME_CMBSZ_GETSIZE(n->bar.cmbsz));
29
+static char *bdrv_perm_names(uint64_t perm)
196
diff --git a/hw/block/nvme.h b/hw/block/nvme.h
30
+{
197
index XXXXXXX..XXXXXXX 100644
31
+ struct perm_name {
198
--- a/hw/block/nvme.h
32
+ uint64_t perm;
199
+++ b/hw/block/nvme.h
33
+ const char *name;
200
@@ -XXX,XX +XXX,XX @@ typedef struct NvmeRequest {
34
+ } permissions[] = {
201
NvmeCqe cqe;
35
+ { BLK_PERM_CONSISTENT_READ, "consistent read" },
202
BlockAcctCookie acct;
36
+ { BLK_PERM_WRITE, "write" },
203
QEMUSGList qsg;
37
+ { BLK_PERM_WRITE_UNCHANGED, "write unchanged" },
204
+ QEMUIOVector iov;
38
+ { BLK_PERM_RESIZE, "resize" },
205
QTAILQ_ENTRY(NvmeRequest)entry;
39
+ { BLK_PERM_GRAPH_MOD, "change children" },
206
} NvmeRequest;
40
+ { 0, NULL }
41
+ };
42
+
43
+ char *result = g_strdup("");
44
+ struct perm_name *p;
45
+
46
+ for (p = permissions; p->name; p++) {
47
+ if (perm & p->perm) {
48
+ char *old = result;
49
+ result = g_strdup_printf("%s%s%s", old, *old ? ", " : "", p->name);
50
+ g_free(old);
51
+ }
52
+ }
53
+
54
+ return result;
55
+}
56
+
57
/*
58
* Checks whether a new reference to @bs can be added if the new user requires
59
* @new_used_perm/@new_shared_perm as its permissions. If @ignore_child is set,
60
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
61
continue;
62
}
63
64
- if ((new_used_perm & c->shared_perm) != new_used_perm ||
65
- (c->perm & new_shared_perm) != c->perm)
66
- {
67
- const char *user = NULL;
68
- if (c->role->get_name) {
69
- user = c->role->get_name(c);
70
- if (user && !*user) {
71
- user = NULL;
72
- }
73
- }
74
- error_setg(errp, "Conflicts with %s", user ?: "another operation");
75
+ if ((new_used_perm & c->shared_perm) != new_used_perm) {
76
+ char *user = bdrv_child_user_desc(c);
77
+ char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
78
+ error_setg(errp, "Conflicts with use by %s as '%s', which does not "
79
+ "allow '%s' on %s",
80
+ user, c->name, perm_names, bdrv_get_node_name(c->bs));
81
+ g_free(user);
82
+ g_free(perm_names);
83
+ return -EPERM;
84
+ }
85
+
86
+ if ((c->perm & new_shared_perm) != c->perm) {
87
+ char *user = bdrv_child_user_desc(c);
88
+ char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm);
89
+ error_setg(errp, "Conflicts with use by %s as '%s', which uses "
90
+ "'%s' on %s",
91
+ user, c->name, perm_names, bdrv_get_node_name(c->bs));
92
+ g_free(user);
93
+ g_free(perm_names);
94
return -EPERM;
95
}
96
207
97
--
208
--
98
1.8.3.1
209
1.8.3.1
99
210
100
211
diff view generated by jsdifflib
1
When the parents' child links are updated in bdrv_append() or
1
From: Alberto Garcia <berto@igalia.com>
2
bdrv_replace_in_backing_chain(), this should affect all child links of
3
BlockBackends or other nodes, but not on child links held for other
4
purposes (like for setting permissions). This patch allows to control
5
the behaviour per BdrvChildRole.
6
2
3
Qcow2COWRegion has two attributes:
4
5
- The offset of the COW region from the start of the first cluster
6
touched by the I/O request. Since it's always going to be positive
7
and the maximum request size is at most INT_MAX, we can use a
8
regular unsigned int to store this offset.
9
10
- The size of the COW region in bytes. This is guaranteed to be >= 0,
11
so we should use an unsigned type instead.
12
13
In x86_64 this reduces the size of Qcow2COWRegion from 16 to 8 bytes.
14
It will also help keep some assertions simpler now that we know that
15
there are no negative numbers.
16
17
The prototype of do_perform_cow() is also updated to reflect these
18
changes.
19
20
Signed-off-by: Alberto Garcia <berto@igalia.com>
21
Reviewed-by: Eric Blake <eblake@redhat.com>
22
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Acked-by: Fam Zheng <famz@redhat.com>
10
---
24
---
11
block.c | 3 +++
25
block/qcow2-cluster.c | 4 ++--
12
include/block/block_int.h | 4 ++++
26
block/qcow2.h | 4 ++--
13
2 files changed, 7 insertions(+)
27
2 files changed, 4 insertions(+), 4 deletions(-)
14
28
15
diff --git a/block.c b/block.c
29
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
16
index XXXXXXX..XXXXXXX 100644
30
index XXXXXXX..XXXXXXX 100644
17
--- a/block.c
31
--- a/block/qcow2-cluster.c
18
+++ b/block.c
32
+++ b/block/qcow2-cluster.c
19
@@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from,
33
@@ -XXX,XX +XXX,XX @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
20
BdrvChild *c, *next, *to_c;
34
static int coroutine_fn do_perform_cow(BlockDriverState *bs,
21
35
uint64_t src_cluster_offset,
22
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
36
uint64_t cluster_offset,
23
+ if (c->role->stay_at_node) {
37
- int offset_in_cluster,
24
+ continue;
38
- int bytes)
25
+ }
39
+ unsigned offset_in_cluster,
26
if (c->role == &child_backing) {
40
+ unsigned bytes)
27
/* @from is generally not allowed to be a backing file, except for
41
{
28
* when @to is the overlay. In that case, @from may not be replaced
42
BDRVQcow2State *s = bs->opaque;
29
diff --git a/include/block/block_int.h b/include/block/block_int.h
43
QEMUIOVector qiov;
44
diff --git a/block/qcow2.h b/block/qcow2.h
30
index XXXXXXX..XXXXXXX 100644
45
index XXXXXXX..XXXXXXX 100644
31
--- a/include/block/block_int.h
46
--- a/block/qcow2.h
32
+++ b/include/block/block_int.h
47
+++ b/block/qcow2.h
33
@@ -XXX,XX +XXX,XX @@ typedef struct BdrvAioNotifier {
48
@@ -XXX,XX +XXX,XX @@ typedef struct Qcow2COWRegion {
34
} BdrvAioNotifier;
49
* Offset of the COW region in bytes from the start of the first cluster
35
50
* touched by the request.
36
struct BdrvChildRole {
51
*/
37
+ /* If true, bdrv_replace_in_backing_chain() doesn't change the node this
52
- uint64_t offset;
38
+ * BdrvChild points to. */
53
+ unsigned offset;
39
+ bool stay_at_node;
54
40
+
55
/** Number of bytes to copy */
41
void (*inherit_options)(int *child_flags, QDict *child_options,
56
- int nb_bytes;
42
int parent_flags, QDict *parent_options);
57
+ unsigned nb_bytes;
43
58
} Qcow2COWRegion;
59
60
/**
44
--
61
--
45
1.8.3.1
62
1.8.3.1
46
63
47
64
diff view generated by jsdifflib
1
It will have to return an error soon, so prepare the callers for it.
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
Instead of calling perform_cow() twice with a different COW region
4
each time, call it just once and make perform_cow() handle both
5
regions.
6
7
This patch simply moves code around. The next one will do the actual
8
reordering of the COW operations.
9
10
Signed-off-by: Alberto Garcia <berto@igalia.com>
11
Reviewed-by: Eric Blake <eblake@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Acked-by: Fam Zheng <famz@redhat.com>
6
---
14
---
7
block.c | 16 +++++++++++++---
15
block/qcow2-cluster.c | 36 ++++++++++++++++++++++--------------
8
block/quorum.c | 9 ++++++++-
16
1 file changed, 22 insertions(+), 14 deletions(-)
9
include/block/block.h | 3 ++-
10
3 files changed, 23 insertions(+), 5 deletions(-)
11
17
12
diff --git a/block.c b/block.c
18
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
13
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
14
--- a/block.c
20
--- a/block/qcow2-cluster.c
15
+++ b/block.c
21
+++ b/block/qcow2-cluster.c
16
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
22
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn do_perform_cow(BlockDriverState *bs,
17
BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
23
struct iovec iov;
18
BlockDriverState *child_bs,
24
int ret;
19
const char *child_name,
25
20
- const BdrvChildRole *child_role)
26
+ if (bytes == 0) {
21
+ const BdrvChildRole *child_role,
27
+ return 0;
22
+ Error **errp)
23
{
24
BdrvChild *child = bdrv_root_attach_child(child_bs, child_name, child_role,
25
parent_bs);
26
@@ -XXX,XX +XXX,XX @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
27
bs->backing = NULL;
28
goto out;
29
}
30
- bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
31
+ /* FIXME Error handling */
32
+ bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
33
+ &error_abort);
34
bs->open_flags &= ~BDRV_O_NO_BACKING;
35
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
36
pstrcpy(bs->backing_format, sizeof(bs->backing_format),
37
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename,
38
const BdrvChildRole *child_role,
39
bool allow_none, Error **errp)
40
{
41
+ BdrvChild *c;
42
BlockDriverState *bs;
43
44
bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_role,
45
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename,
46
return NULL;
47
}
48
49
- return bdrv_attach_child(parent, bs, bdref_key, child_role);
50
+ c = bdrv_attach_child(parent, bs, bdref_key, child_role, errp);
51
+ if (!c) {
52
+ bdrv_unref(bs);
53
+ return NULL;
54
+ }
28
+ }
55
+
29
+
56
+ return c;
30
iov.iov_len = bytes;
31
iov.iov_base = qemu_try_blockalign(bs, iov.iov_len);
32
if (iov.iov_base == NULL) {
33
@@ -XXX,XX +XXX,XX @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
34
return cluster_offset;
57
}
35
}
58
36
59
static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
37
-static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
60
diff --git a/block/quorum.c b/block/quorum.c
38
+static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
61
index XXXXXXX..XXXXXXX 100644
39
{
62
--- a/block/quorum.c
40
BDRVQcow2State *s = bs->opaque;
63
+++ b/block/quorum.c
41
+ Qcow2COWRegion *start = &m->cow_start;
64
@@ -XXX,XX +XXX,XX @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs,
42
+ Qcow2COWRegion *end = &m->cow_end;
65
43
int ret;
66
/* We can safely add the child now */
44
67
bdrv_ref(child_bs);
45
- if (r->nb_bytes == 0) {
68
- child = bdrv_attach_child(bs, child_bs, indexstr, &child_format);
46
+ if (start->nb_bytes == 0 && end->nb_bytes == 0) {
47
return 0;
48
}
49
50
qemu_co_mutex_unlock(&s->lock);
51
- ret = do_perform_cow(bs, m->offset, m->alloc_offset, r->offset, r->nb_bytes);
52
- qemu_co_mutex_lock(&s->lock);
53
-
54
+ ret = do_perform_cow(bs, m->offset, m->alloc_offset,
55
+ start->offset, start->nb_bytes);
56
if (ret < 0) {
57
- return ret;
58
+ goto fail;
59
}
60
61
+ ret = do_perform_cow(bs, m->offset, m->alloc_offset,
62
+ end->offset, end->nb_bytes);
69
+
63
+
70
+ child = bdrv_attach_child(bs, child_bs, indexstr, &child_format, errp);
64
+fail:
71
+ if (child == NULL) {
65
+ qemu_co_mutex_lock(&s->lock);
72
+ s->next_child_index--;
66
+
73
+ bdrv_unref(child_bs);
67
/*
74
+ goto out;
68
* Before we update the L2 table to actually point to the new cluster, we
69
* need to be sure that the refcounts have been increased and COW was
70
* handled.
71
*/
72
- qcow2_cache_depends_on_flush(s->l2_table_cache);
73
+ if (ret == 0) {
74
+ qcow2_cache_depends_on_flush(s->l2_table_cache);
75
+ }
75
+ }
76
s->children = g_renew(BdrvChild *, s->children, s->num_children + 1);
76
77
s->children[s->num_children++] = child;
77
- return 0;
78
78
+ return ret;
79
+out:
80
bdrv_drained_end(bs);
81
}
79
}
82
80
83
diff --git a/include/block/block.h b/include/block/block.h
81
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
84
index XXXXXXX..XXXXXXX 100644
82
@@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
85
--- a/include/block/block.h
83
}
86
+++ b/include/block/block.h
84
87
@@ -XXX,XX +XXX,XX @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child);
85
/* copy content of unmodified sectors */
88
BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
86
- ret = perform_cow(bs, m, &m->cow_start);
89
BlockDriverState *child_bs,
87
- if (ret < 0) {
90
const char *child_name,
88
- goto err;
91
- const BdrvChildRole *child_role);
89
- }
92
+ const BdrvChildRole *child_role,
90
-
93
+ Error **errp);
91
- ret = perform_cow(bs, m, &m->cow_end);
94
92
+ ret = perform_cow(bs, m);
95
bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp);
93
if (ret < 0) {
96
void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason);
94
goto err;
95
}
97
--
96
--
98
1.8.3.1
97
1.8.3.1
99
98
100
99
diff view generated by jsdifflib
1
The HMP command 'qemu-io' is a bit tricky because it wants to work on
1
From: Alberto Garcia <berto@igalia.com>
2
the original BlockBackend, but additional permissions could be required.
2
3
The details are explained in a comment in the code, but in summary, just
3
This patch splits do_perform_cow() into three separate functions to
4
request whatever permissions the current qemu-io command needs.
4
read, encrypt and write the COW regions.
5
5
6
perform_cow() can now read both regions first, then encrypt them and
7
finally write them to disk. The memory allocation is also done in
8
this function now, using one single buffer large enough to hold both
9
regions.
10
11
Signed-off-by: Alberto Garcia <berto@igalia.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Acked-by: Fam Zheng <famz@redhat.com>
9
---
14
---
10
block/block-backend.c | 6 ++++++
15
block/qcow2-cluster.c | 117 +++++++++++++++++++++++++++++++++++++-------------
11
hmp.c | 26 +++++++++++++++++++++++++-
16
1 file changed, 87 insertions(+), 30 deletions(-)
12
include/qemu-io.h | 1 +
17
13
include/sysemu/block-backend.h | 1 +
18
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
14
qemu-io-cmds.c | 28 ++++++++++++++++++++++++++++
15
5 files changed, 61 insertions(+), 1 deletion(-)
16
17
diff --git a/block/block-backend.c b/block/block-backend.c
18
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
19
--- a/block/block-backend.c
20
--- a/block/qcow2-cluster.c
20
+++ b/block/block-backend.c
21
+++ b/block/qcow2-cluster.c
21
@@ -XXX,XX +XXX,XX @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
22
@@ -XXX,XX +XXX,XX @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
22
return 0;
23
return 0;
23
}
24
}
24
25
25
+void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm)
26
-static int coroutine_fn do_perform_cow(BlockDriverState *bs,
27
- uint64_t src_cluster_offset,
28
- uint64_t cluster_offset,
29
- unsigned offset_in_cluster,
30
- unsigned bytes)
31
+static int coroutine_fn do_perform_cow_read(BlockDriverState *bs,
32
+ uint64_t src_cluster_offset,
33
+ unsigned offset_in_cluster,
34
+ uint8_t *buffer,
35
+ unsigned bytes)
36
{
37
- BDRVQcow2State *s = bs->opaque;
38
QEMUIOVector qiov;
39
- struct iovec iov;
40
+ struct iovec iov = { .iov_base = buffer, .iov_len = bytes };
41
int ret;
42
43
if (bytes == 0) {
44
return 0;
45
}
46
47
- iov.iov_len = bytes;
48
- iov.iov_base = qemu_try_blockalign(bs, iov.iov_len);
49
- if (iov.iov_base == NULL) {
50
- return -ENOMEM;
51
- }
52
-
53
qemu_iovec_init_external(&qiov, &iov, 1);
54
55
BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
56
57
if (!bs->drv) {
58
- ret = -ENOMEDIUM;
59
- goto out;
60
+ return -ENOMEDIUM;
61
}
62
63
/* Call .bdrv_co_readv() directly instead of using the public block-layer
64
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn do_perform_cow(BlockDriverState *bs,
65
ret = bs->drv->bdrv_co_preadv(bs, src_cluster_offset + offset_in_cluster,
66
bytes, &qiov, 0);
67
if (ret < 0) {
68
- goto out;
69
+ return ret;
70
}
71
72
- if (bs->encrypted) {
73
+ return 0;
74
+}
75
+
76
+static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
77
+ uint64_t src_cluster_offset,
78
+ unsigned offset_in_cluster,
79
+ uint8_t *buffer,
80
+ unsigned bytes)
26
+{
81
+{
27
+ *perm = blk->perm;
82
+ if (bytes && bs->encrypted) {
28
+ *shared_perm = blk->shared_perm;
83
+ BDRVQcow2State *s = bs->opaque;
84
int64_t sector = (src_cluster_offset + offset_in_cluster)
85
>> BDRV_SECTOR_BITS;
86
assert(s->cipher);
87
assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
88
assert((bytes & ~BDRV_SECTOR_MASK) == 0);
89
- if (qcow2_encrypt_sectors(s, sector, iov.iov_base, iov.iov_base,
90
+ if (qcow2_encrypt_sectors(s, sector, buffer, buffer,
91
bytes >> BDRV_SECTOR_BITS, true, NULL) < 0) {
92
- ret = -EIO;
93
- goto out;
94
+ return false;
95
}
96
}
97
+ return true;
29
+}
98
+}
30
+
99
+
31
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
100
+static int coroutine_fn do_perform_cow_write(BlockDriverState *bs,
32
{
101
+ uint64_t cluster_offset,
33
if (blk->dev) {
102
+ unsigned offset_in_cluster,
34
diff --git a/hmp.c b/hmp.c
103
+ uint8_t *buffer,
35
index XXXXXXX..XXXXXXX 100644
104
+ unsigned bytes)
36
--- a/hmp.c
105
+{
37
+++ b/hmp.c
106
+ QEMUIOVector qiov;
38
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
107
+ struct iovec iov = { .iov_base = buffer, .iov_len = bytes };
39
if (!blk) {
108
+ int ret;
40
BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err);
109
+
41
if (bs) {
110
+ if (bytes == 0) {
42
- /* FIXME Use real permissions */
111
+ return 0;
43
blk = local_blk = blk_new(0, BLK_PERM_ALL);
112
+ }
44
ret = blk_insert_bs(blk, bs, &err);
113
+
45
if (ret < 0) {
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
46
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
115
47
aio_context = blk_get_aio_context(blk);
116
ret = qcow2_pre_write_overlap_check(bs, 0,
48
aio_context_acquire(aio_context);
117
cluster_offset + offset_in_cluster, bytes);
49
118
if (ret < 0) {
50
+ /*
119
- goto out;
51
+ * Notably absent: Proper permission management. This is sad, but it seems
120
+ return ret;
52
+ * almost impossible to achieve without changing the semantics and thereby
121
}
53
+ * limiting the use cases of the qemu-io HMP command.
122
54
+ *
123
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
55
+ * In an ideal world we would unconditionally create a new BlockBackend for
124
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
56
+ * qemuio_command(), but we have commands like 'reopen' and want them to
125
bytes, &qiov, 0);
57
+ * take effect on the exact BlockBackend whose name the user passed instead
126
if (ret < 0) {
58
+ * of just on a temporary copy of it.
127
- goto out;
59
+ *
128
+ return ret;
60
+ * Another problem is that deleting the temporary BlockBackend involves
129
}
61
+ * draining all requests on it first, but some qemu-iotests cases want to
130
62
+ * issue multiple aio_read/write requests and expect them to complete in
131
- ret = 0;
63
+ * the background while the monitor has already returned.
132
-out:
64
+ *
133
- qemu_vfree(iov.iov_base);
65
+ * This is also what prevents us from saving the original permissions and
134
- return ret;
66
+ * restoring them later: We can't revoke permissions until all requests
135
+ return 0;
67
+ * have completed, and we don't know when that is nor can we really let
136
}
68
+ * anything else run before we have revoken them to avoid race conditions.
137
69
+ *
138
70
+ * What happens now is that command() in qemu-io-cmds.c can extend the
139
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
71
+ * permissions if necessary for the qemu-io command. And they simply stay
140
BDRVQcow2State *s = bs->opaque;
72
+ * extended, possibly resulting in a read-only guest device keeping write
141
Qcow2COWRegion *start = &m->cow_start;
73
+ * permissions. Ugly, but it appears to be the lesser evil.
142
Qcow2COWRegion *end = &m->cow_end;
74
+ */
143
+ unsigned buffer_size;
75
qemuio_command(blk, command);
144
+ uint8_t *start_buffer, *end_buffer;
76
145
int ret;
77
aio_context_release(aio_context);
146
78
diff --git a/include/qemu-io.h b/include/qemu-io.h
147
+ assert(start->nb_bytes <= UINT_MAX - end->nb_bytes);
79
index XXXXXXX..XXXXXXX 100644
148
+
80
--- a/include/qemu-io.h
149
if (start->nb_bytes == 0 && end->nb_bytes == 0) {
81
+++ b/include/qemu-io.h
82
@@ -XXX,XX +XXX,XX @@ typedef struct cmdinfo {
83
const char *args;
84
const char *oneline;
85
helpfunc_t help;
86
+ uint64_t perm;
87
} cmdinfo_t;
88
89
extern bool qemuio_misalign;
90
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
91
index XXXXXXX..XXXXXXX 100644
92
--- a/include/sysemu/block-backend.h
93
+++ b/include/sysemu/block-backend.h
94
@@ -XXX,XX +XXX,XX @@ bool bdrv_has_blk(BlockDriverState *bs);
95
bool bdrv_is_root_node(BlockDriverState *bs);
96
int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
97
Error **errp);
98
+void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm);
99
100
void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow);
101
void blk_iostatus_enable(BlockBackend *blk);
102
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
103
index XXXXXXX..XXXXXXX 100644
104
--- a/qemu-io-cmds.c
105
+++ b/qemu-io-cmds.c
106
@@ -XXX,XX +XXX,XX @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc,
107
}
108
return 0;
150
return 0;
109
}
151
}
110
+
152
111
+ /* Request additional permissions if necessary for this command. The caller
153
+ /* Reserve a buffer large enough to store the data from both the
112
+ * is responsible for restoring the original permissions afterwards if this
154
+ * start and end COW regions. Add some padding in the middle if
113
+ * is what it wants. */
155
+ * necessary to make sure that the end region is optimally aligned */
114
+ if (ct->perm && blk_is_available(blk)) {
156
+ buffer_size = QEMU_ALIGN_UP(start->nb_bytes, bdrv_opt_mem_align(bs)) +
115
+ uint64_t orig_perm, orig_shared_perm;
157
+ end->nb_bytes;
116
+ blk_get_perm(blk, &orig_perm, &orig_shared_perm);
158
+ start_buffer = qemu_try_blockalign(bs, buffer_size);
117
+
159
+ if (start_buffer == NULL) {
118
+ if (ct->perm & ~orig_perm) {
160
+ return -ENOMEM;
119
+ uint64_t new_perm;
161
+ }
120
+ Error *local_err = NULL;
162
+ /* The part of the buffer where the end region is located */
121
+ int ret;
163
+ end_buffer = start_buffer + buffer_size - end->nb_bytes;
122
+
164
+
123
+ new_perm = orig_perm | ct->perm;
165
qemu_co_mutex_unlock(&s->lock);
124
+
166
- ret = do_perform_cow(bs, m->offset, m->alloc_offset,
125
+ ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err);
167
- start->offset, start->nb_bytes);
126
+ if (ret < 0) {
168
+ /* First we read the existing data from both COW regions */
127
+ error_report_err(local_err);
169
+ ret = do_perform_cow_read(bs, m->offset, start->offset,
128
+ return 0;
170
+ start_buffer, start->nb_bytes);
129
+ }
171
if (ret < 0) {
172
goto fail;
173
}
174
175
- ret = do_perform_cow(bs, m->offset, m->alloc_offset,
176
- end->offset, end->nb_bytes);
177
+ ret = do_perform_cow_read(bs, m->offset, end->offset,
178
+ end_buffer, end->nb_bytes);
179
+ if (ret < 0) {
180
+ goto fail;
181
+ }
182
+
183
+ /* Encrypt the data if necessary before writing it */
184
+ if (bs->encrypted) {
185
+ if (!do_perform_cow_encrypt(bs, m->offset, start->offset,
186
+ start_buffer, start->nb_bytes) ||
187
+ !do_perform_cow_encrypt(bs, m->offset, end->offset,
188
+ end_buffer, end->nb_bytes)) {
189
+ ret = -EIO;
190
+ goto fail;
130
+ }
191
+ }
131
+ }
192
+ }
132
+
193
+
133
optind = 0;
194
+ /* And now we can write everything */
134
return ct->cfunc(blk, argc, argv);
195
+ ret = do_perform_cow_write(bs, m->alloc_offset, start->offset,
196
+ start_buffer, start->nb_bytes);
197
+ if (ret < 0) {
198
+ goto fail;
199
+ }
200
201
+ ret = do_perform_cow_write(bs, m->alloc_offset, end->offset,
202
+ end_buffer, end->nb_bytes);
203
fail:
204
qemu_co_mutex_lock(&s->lock);
205
206
@@ -XXX,XX +XXX,XX @@ fail:
207
qcow2_cache_depends_on_flush(s->l2_table_cache);
208
}
209
210
+ qemu_vfree(start_buffer);
211
return ret;
135
}
212
}
136
@@ -XXX,XX +XXX,XX @@ static const cmdinfo_t write_cmd = {
213
137
.name = "write",
138
.altname = "w",
139
.cfunc = write_f,
140
+ .perm = BLK_PERM_WRITE,
141
.argmin = 2,
142
.argmax = -1,
143
.args = "[-bcCfquz] [-P pattern] off len",
144
@@ -XXX,XX +XXX,XX @@ static int writev_f(BlockBackend *blk, int argc, char **argv);
145
static const cmdinfo_t writev_cmd = {
146
.name = "writev",
147
.cfunc = writev_f,
148
+ .perm = BLK_PERM_WRITE,
149
.argmin = 2,
150
.argmax = -1,
151
.args = "[-Cfq] [-P pattern] off len [len..]",
152
@@ -XXX,XX +XXX,XX @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv);
153
static const cmdinfo_t aio_write_cmd = {
154
.name = "aio_write",
155
.cfunc = aio_write_f,
156
+ .perm = BLK_PERM_WRITE,
157
.argmin = 2,
158
.argmax = -1,
159
.args = "[-Cfiquz] [-P pattern] off len [len..]",
160
@@ -XXX,XX +XXX,XX @@ static const cmdinfo_t truncate_cmd = {
161
.name = "truncate",
162
.altname = "t",
163
.cfunc = truncate_f,
164
+ .perm = BLK_PERM_WRITE | BLK_PERM_RESIZE,
165
.argmin = 1,
166
.argmax = 1,
167
.args = "off",
168
@@ -XXX,XX +XXX,XX @@ static const cmdinfo_t discard_cmd = {
169
.name = "discard",
170
.altname = "d",
171
.cfunc = discard_f,
172
+ .perm = BLK_PERM_WRITE,
173
.argmin = 2,
174
.argmax = -1,
175
.args = "[-Cq] off len",
176
--
214
--
177
1.8.3.1
215
1.8.3.1
178
216
179
217
diff view generated by jsdifflib
1
blk_new_open() is a convenience function that processes flags rather
1
From: Alberto Garcia <berto@igalia.com>
2
than QDict options as a simple way to just open an image file.
3
2
4
In order to keep it convenient in the future, it must automatically
3
Instead of passing a single buffer pointer to do_perform_cow_write(),
5
request the necessary permissions. This can easily be inferred from the
4
pass a QEMUIOVector. This will allow us to merge the write requests
6
flags for read and write, but we need another flag that tells us whether
5
for the COW regions and the actual data into a single one.
7
to get the resize permission.
8
6
9
We can't just always request it because that means that no block jobs
7
Although do_perform_cow_read() does not strictly need to change its
10
can run on the resulting BlockBackend (which is something that e.g.
8
API, we're doing it here as well for consistency.
11
qemu-img commit wants to do), but we also can't request it never because
12
most of the .bdrv_create() implementations call blk_truncate().
13
9
14
The solution is to introduce another flag that is passed by all users
10
Signed-off-by: Alberto Garcia <berto@igalia.com>
15
that want to resize the image.
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
block/qcow2-cluster.c | 51 ++++++++++++++++++++++++---------------------------
15
1 file changed, 24 insertions(+), 27 deletions(-)
16
16
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
18
Acked-by: Fam Zheng <famz@redhat.com>
19
Reviewed-by: Max Reitz <mreitz@redhat.com>
20
---
21
block/parallels.c | 3 ++-
22
block/qcow.c | 3 ++-
23
block/qcow2.c | 6 ++++--
24
block/qed.c | 3 ++-
25
block/sheepdog.c | 2 +-
26
block/vdi.c | 3 ++-
27
block/vhdx.c | 3 ++-
28
block/vmdk.c | 6 ++++--
29
block/vpc.c | 3 ++-
30
include/block/block.h | 1 +
31
qemu-img.c | 2 +-
32
11 files changed, 23 insertions(+), 12 deletions(-)
33
34
diff --git a/block/parallels.c b/block/parallels.c
35
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
36
--- a/block/parallels.c
19
--- a/block/qcow2-cluster.c
37
+++ b/block/parallels.c
20
+++ b/block/qcow2-cluster.c
38
@@ -XXX,XX +XXX,XX @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
21
@@ -XXX,XX +XXX,XX @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
22
static int coroutine_fn do_perform_cow_read(BlockDriverState *bs,
23
uint64_t src_cluster_offset,
24
unsigned offset_in_cluster,
25
- uint8_t *buffer,
26
- unsigned bytes)
27
+ QEMUIOVector *qiov)
28
{
29
- QEMUIOVector qiov;
30
- struct iovec iov = { .iov_base = buffer, .iov_len = bytes };
31
int ret;
32
33
- if (bytes == 0) {
34
+ if (qiov->size == 0) {
35
return 0;
39
}
36
}
40
37
41
file = blk_new_open(filename, NULL, NULL,
38
- qemu_iovec_init_external(&qiov, &iov, 1);
42
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
39
-
43
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
40
BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
44
+ &local_err);
41
45
if (file == NULL) {
42
if (!bs->drv) {
46
error_propagate(errp, local_err);
43
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn do_perform_cow_read(BlockDriverState *bs,
47
return -EIO;
44
* which can lead to deadlock when block layer copy-on-read is enabled.
48
diff --git a/block/qcow.c b/block/qcow.c
45
*/
49
index XXXXXXX..XXXXXXX 100644
46
ret = bs->drv->bdrv_co_preadv(bs, src_cluster_offset + offset_in_cluster,
50
--- a/block/qcow.c
47
- bytes, &qiov, 0);
51
+++ b/block/qcow.c
48
+ qiov->size, qiov, 0);
52
@@ -XXX,XX +XXX,XX @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
49
if (ret < 0) {
50
return ret;
53
}
51
}
54
52
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
55
qcow_blk = blk_new_open(filename, NULL, NULL,
53
static int coroutine_fn do_perform_cow_write(BlockDriverState *bs,
56
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
54
uint64_t cluster_offset,
57
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
55
unsigned offset_in_cluster,
58
+ &local_err);
56
- uint8_t *buffer,
59
if (qcow_blk == NULL) {
57
- unsigned bytes)
60
error_propagate(errp, local_err);
58
+ QEMUIOVector *qiov)
61
ret = -EIO;
59
{
62
diff --git a/block/qcow2.c b/block/qcow2.c
60
- QEMUIOVector qiov;
63
index XXXXXXX..XXXXXXX 100644
61
- struct iovec iov = { .iov_base = buffer, .iov_len = bytes };
64
--- a/block/qcow2.c
62
int ret;
65
+++ b/block/qcow2.c
63
66
@@ -XXX,XX +XXX,XX @@ static int qcow2_create2(const char *filename, int64_t total_size,
64
- if (bytes == 0) {
65
+ if (qiov->size == 0) {
66
return 0;
67
}
67
}
68
68
69
blk = blk_new_open(filename, NULL, NULL,
69
- qemu_iovec_init_external(&qiov, &iov, 1);
70
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
70
-
71
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
71
ret = qcow2_pre_write_overlap_check(bs, 0,
72
+ &local_err);
72
- cluster_offset + offset_in_cluster, bytes);
73
if (blk == NULL) {
73
+ cluster_offset + offset_in_cluster, qiov->size);
74
error_propagate(errp, local_err);
74
if (ret < 0) {
75
return -EIO;
75
return ret;
76
@@ -XXX,XX +XXX,XX @@ static int qcow2_create2(const char *filename, int64_t total_size,
77
options = qdict_new();
78
qdict_put(options, "driver", qstring_from_str("qcow2"));
79
blk = blk_new_open(filename, NULL, options,
80
- BDRV_O_RDWR | BDRV_O_NO_FLUSH, &local_err);
81
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
82
+ &local_err);
83
if (blk == NULL) {
84
error_propagate(errp, local_err);
85
ret = -EIO;
86
diff --git a/block/qed.c b/block/qed.c
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/qed.c
89
+++ b/block/qed.c
90
@@ -XXX,XX +XXX,XX @@ static int qed_create(const char *filename, uint32_t cluster_size,
91
}
76
}
92
77
93
blk = blk_new_open(filename, NULL, NULL,
78
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
94
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
79
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
95
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
80
- bytes, &qiov, 0);
96
+ &local_err);
81
+ qiov->size, qiov, 0);
97
if (blk == NULL) {
82
if (ret < 0) {
98
error_propagate(errp, local_err);
83
return ret;
99
return -EIO;
84
}
100
diff --git a/block/sheepdog.c b/block/sheepdog.c
85
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
101
index XXXXXXX..XXXXXXX 100644
86
unsigned data_bytes = end->offset - (start->offset + start->nb_bytes);
102
--- a/block/sheepdog.c
87
bool merge_reads;
103
+++ b/block/sheepdog.c
88
uint8_t *start_buffer, *end_buffer;
104
@@ -XXX,XX +XXX,XX @@ static int sd_prealloc(const char *filename, Error **errp)
89
+ QEMUIOVector qiov;
105
int ret;
90
int ret;
106
91
107
blk = blk_new_open(filename, NULL, NULL,
92
assert(start->nb_bytes <= UINT_MAX - end->nb_bytes);
108
- BDRV_O_RDWR | BDRV_O_PROTOCOL, errp);
93
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
109
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
94
/* The part of the buffer where the end region is located */
110
if (blk == NULL) {
95
end_buffer = start_buffer + buffer_size - end->nb_bytes;
111
ret = -EIO;
96
112
goto out_with_err_set;
97
+ qemu_iovec_init(&qiov, 1);
113
diff --git a/block/vdi.c b/block/vdi.c
98
+
114
index XXXXXXX..XXXXXXX 100644
99
qemu_co_mutex_unlock(&s->lock);
115
--- a/block/vdi.c
100
/* First we read the existing data from both COW regions. We
116
+++ b/block/vdi.c
101
* either read the whole region in one go, or the start and end
117
@@ -XXX,XX +XXX,XX @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
102
* regions separately. */
103
if (merge_reads) {
104
- ret = do_perform_cow_read(bs, m->offset, start->offset,
105
- start_buffer, buffer_size);
106
+ qemu_iovec_add(&qiov, start_buffer, buffer_size);
107
+ ret = do_perform_cow_read(bs, m->offset, start->offset, &qiov);
108
} else {
109
- ret = do_perform_cow_read(bs, m->offset, start->offset,
110
- start_buffer, start->nb_bytes);
111
+ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes);
112
+ ret = do_perform_cow_read(bs, m->offset, start->offset, &qiov);
113
if (ret < 0) {
114
goto fail;
115
}
116
117
- ret = do_perform_cow_read(bs, m->offset, end->offset,
118
- end_buffer, end->nb_bytes);
119
+ qemu_iovec_reset(&qiov);
120
+ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes);
121
+ ret = do_perform_cow_read(bs, m->offset, end->offset, &qiov);
118
}
122
}
119
123
if (ret < 0) {
120
blk = blk_new_open(filename, NULL, NULL,
124
goto fail;
121
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
125
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
122
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
123
+ &local_err);
124
if (blk == NULL) {
125
error_propagate(errp, local_err);
126
ret = -EIO;
127
diff --git a/block/vhdx.c b/block/vhdx.c
128
index XXXXXXX..XXXXXXX 100644
129
--- a/block/vhdx.c
130
+++ b/block/vhdx.c
131
@@ -XXX,XX +XXX,XX @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
132
}
126
}
133
127
134
blk = blk_new_open(filename, NULL, NULL,
128
/* And now we can write everything */
135
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
129
- ret = do_perform_cow_write(bs, m->alloc_offset, start->offset,
136
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
130
- start_buffer, start->nb_bytes);
137
+ &local_err);
131
+ qemu_iovec_reset(&qiov);
138
if (blk == NULL) {
132
+ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes);
139
error_propagate(errp, local_err);
133
+ ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov);
140
ret = -EIO;
134
if (ret < 0) {
141
diff --git a/block/vmdk.c b/block/vmdk.c
135
goto fail;
142
index XXXXXXX..XXXXXXX 100644
143
--- a/block/vmdk.c
144
+++ b/block/vmdk.c
145
@@ -XXX,XX +XXX,XX @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
146
}
136
}
147
137
148
blk = blk_new_open(filename, NULL, NULL,
138
- ret = do_perform_cow_write(bs, m->alloc_offset, end->offset,
149
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
139
- end_buffer, end->nb_bytes);
150
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
140
+ qemu_iovec_reset(&qiov);
151
+ &local_err);
141
+ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes);
152
if (blk == NULL) {
142
+ ret = do_perform_cow_write(bs, m->alloc_offset, end->offset, &qiov);
153
error_propagate(errp, local_err);
143
fail:
154
ret = -EIO;
144
qemu_co_mutex_lock(&s->lock);
155
@@ -XXX,XX +XXX,XX @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
145
146
@@ -XXX,XX +XXX,XX @@ fail:
156
}
147
}
157
148
158
new_blk = blk_new_open(filename, NULL, NULL,
149
qemu_vfree(start_buffer);
159
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
150
+ qemu_iovec_destroy(&qiov);
160
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
151
return ret;
161
+ &local_err);
152
}
162
if (new_blk == NULL) {
153
163
error_propagate(errp, local_err);
164
ret = -EIO;
165
diff --git a/block/vpc.c b/block/vpc.c
166
index XXXXXXX..XXXXXXX 100644
167
--- a/block/vpc.c
168
+++ b/block/vpc.c
169
@@ -XXX,XX +XXX,XX @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
170
}
171
172
blk = blk_new_open(filename, NULL, NULL,
173
- BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
174
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
175
+ &local_err);
176
if (blk == NULL) {
177
error_propagate(errp, local_err);
178
ret = -EIO;
179
diff --git a/include/block/block.h b/include/block/block.h
180
index XXXXXXX..XXXXXXX 100644
181
--- a/include/block/block.h
182
+++ b/include/block/block.h
183
@@ -XXX,XX +XXX,XX @@ typedef struct HDGeometry {
184
} HDGeometry;
185
186
#define BDRV_O_RDWR 0x0002
187
+#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */
188
#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */
189
#define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */
190
#define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */
191
diff --git a/qemu-img.c b/qemu-img.c
192
index XXXXXXX..XXXXXXX 100644
193
--- a/qemu-img.c
194
+++ b/qemu-img.c
195
@@ -XXX,XX +XXX,XX @@ static int img_resize(int argc, char **argv)
196
qemu_opts_del(param);
197
198
blk = img_open(image_opts, filename, fmt,
199
- BDRV_O_RDWR, false, quiet);
200
+ BDRV_O_RDWR | BDRV_O_RESIZE, false, quiet);
201
if (!blk) {
202
ret = -1;
203
goto out;
204
--
154
--
205
1.8.3.1
155
1.8.3.1
206
156
207
157
diff view generated by jsdifflib
1
Now that blk_insert_bs() requests the BlockBackend permissions for the
1
From: Alberto Garcia <berto@igalia.com>
2
node it attaches to, it can fail. Instead of aborting, pass the errors
3
to the callers.
4
2
3
If the guest tries to write data that results on the allocation of a
4
new cluster, instead of writing the guest data first and then the data
5
from the COW regions, write everything together using one single I/O
6
operation.
7
8
This can improve the write performance by 25% or more, depending on
9
several factors such as the media type, the cluster size and the I/O
10
request size.
11
12
Signed-off-by: Alberto Garcia <berto@igalia.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Acked-by: Fam Zheng <famz@redhat.com>
8
---
15
---
9
block.c | 5 ++++-
16
block/qcow2-cluster.c | 40 ++++++++++++++++++++++++--------
10
block/backup.c | 5 ++++-
17
block/qcow2.c | 64 +++++++++++++++++++++++++++++++++++++++++++--------
11
block/block-backend.c | 13 ++++++++-----
18
block/qcow2.h | 7 ++++++
12
block/commit.c | 38 ++++++++++++++++++++++++++++++--------
19
3 files changed, 91 insertions(+), 20 deletions(-)
13
block/mirror.c | 15 ++++++++++++---
14
block/qcow2.c | 10 ++++++++--
15
blockdev.c | 11 +++++++++--
16
blockjob.c | 7 ++++++-
17
hmp.c | 6 +++++-
18
hw/core/qdev-properties-system.c | 7 ++++++-
19
include/sysemu/block-backend.h | 2 +-
20
migration/block.c | 2 +-
21
nbd/server.c | 6 +++++-
22
tests/test-blockjob.c | 2 +-
23
14 files changed, 100 insertions(+), 29 deletions(-)
24
20
25
diff --git a/block.c b/block.c
21
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
26
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
27
--- a/block.c
23
--- a/block/qcow2-cluster.c
28
+++ b/block.c
24
+++ b/block/qcow2-cluster.c
29
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
25
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
30
}
26
assert(start->nb_bytes <= UINT_MAX - end->nb_bytes);
31
if (file_bs != NULL) {
27
assert(start->nb_bytes + end->nb_bytes <= UINT_MAX - data_bytes);
32
file = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
28
assert(start->offset + start->nb_bytes <= end->offset);
33
- blk_insert_bs(file, file_bs);
29
+ assert(!m->data_qiov || m->data_qiov->size == data_bytes);
34
+ blk_insert_bs(file, file_bs, &local_err);
30
35
bdrv_unref(file_bs);
31
if (start->nb_bytes == 0 && end->nb_bytes == 0) {
36
+ if (local_err) {
32
return 0;
37
+ goto fail;
33
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
38
+ }
34
/* The part of the buffer where the end region is located */
39
35
end_buffer = start_buffer + buffer_size - end->nb_bytes;
40
qdict_put(options, "file",
36
41
qstring_from_str(bdrv_get_node_name(file_bs)));
37
- qemu_iovec_init(&qiov, 1);
42
diff --git a/block/backup.c b/block/backup.c
38
+ qemu_iovec_init(&qiov, 2 + (m->data_qiov ? m->data_qiov->niov : 0));
43
index XXXXXXX..XXXXXXX 100644
39
44
--- a/block/backup.c
40
qemu_co_mutex_unlock(&s->lock);
45
+++ b/block/backup.c
41
/* First we read the existing data from both COW regions. We
46
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
42
@@ -XXX,XX +XXX,XX @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
47
48
/* FIXME Use real permissions */
49
job->target = blk_new(0, BLK_PERM_ALL);
50
- blk_insert_bs(job->target, target);
51
+ ret = blk_insert_bs(job->target, target, errp);
52
+ if (ret < 0) {
53
+ goto error;
54
+ }
55
56
job->on_source_error = on_source_error;
57
job->on_target_error = on_target_error;
58
diff --git a/block/block-backend.c b/block/block-backend.c
59
index XXXXXXX..XXXXXXX 100644
60
--- a/block/block-backend.c
61
+++ b/block/block-backend.c
62
@@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk)
63
/*
64
* Associates a new BlockDriverState with @blk.
65
*/
66
-void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
67
+int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
68
{
69
- bdrv_ref(bs);
70
- /* FIXME Error handling */
71
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
72
- blk->perm, blk->shared_perm, blk,
73
- &error_abort);
74
+ blk->perm, blk->shared_perm, blk, errp);
75
+ if (blk->root == NULL) {
76
+ return -EPERM;
77
+ }
78
+ bdrv_ref(bs);
79
80
notifier_list_notify(&blk->insert_bs_notifiers, blk);
81
if (blk->public.throttle_state) {
82
throttle_timers_attach_aio_context(
83
&blk->public.throttle_timers, bdrv_get_aio_context(bs));
84
}
85
+
86
+ return 0;
87
}
88
89
/*
90
diff --git a/block/commit.c b/block/commit.c
91
index XXXXXXX..XXXXXXX 100644
92
--- a/block/commit.c
93
+++ b/block/commit.c
94
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
95
BlockDriverState *iter;
96
BlockDriverState *overlay_bs;
97
Error *local_err = NULL;
98
+ int ret;
99
100
assert(top != bs);
101
if (top == base) {
102
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
103
bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
104
if (local_err != NULL) {
105
error_propagate(errp, local_err);
106
- block_job_unref(&s->common);
107
- return;
108
+ goto fail;
109
}
43
}
110
}
44
}
111
45
112
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
46
- /* And now we can write everything */
113
47
- qemu_iovec_reset(&qiov);
114
/* FIXME Use real permissions */
48
- qemu_iovec_add(&qiov, start_buffer, start->nb_bytes);
115
s->base = blk_new(0, BLK_PERM_ALL);
49
- ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov);
116
- blk_insert_bs(s->base, base);
50
- if (ret < 0) {
117
+ ret = blk_insert_bs(s->base, base, errp);
51
- goto fail;
118
+ if (ret < 0) {
52
+ /* And now we can write everything. If we have the guest data we
119
+ goto fail;
53
+ * can write everything in one single operation */
120
+ }
54
+ if (m->data_qiov) {
121
55
+ qemu_iovec_reset(&qiov);
122
/* FIXME Use real permissions */
56
+ if (start->nb_bytes) {
123
s->top = blk_new(0, BLK_PERM_ALL);
57
+ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes);
124
- blk_insert_bs(s->top, top);
58
+ }
125
+ ret = blk_insert_bs(s->top, top, errp);
59
+ qemu_iovec_concat(&qiov, m->data_qiov, 0, data_bytes);
126
+ if (ret < 0) {
60
+ if (end->nb_bytes) {
127
+ goto fail;
61
+ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes);
128
+ }
62
+ }
129
63
+ /* NOTE: we have a write_aio blkdebug event here followed by
130
s->active = bs;
64
+ * a cow_write one in do_perform_cow_write(), but there's only
131
65
+ * one single I/O operation */
132
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
66
+ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
133
67
+ ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov);
134
trace_commit_start(bs, base, top, s);
68
+ } else {
135
block_job_start(&s->common);
69
+ /* If there's no guest data then write both COW regions separately */
136
+ return;
70
+ qemu_iovec_reset(&qiov);
71
+ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes);
72
+ ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov);
73
+ if (ret < 0) {
74
+ goto fail;
75
+ }
137
+
76
+
138
+fail:
77
+ qemu_iovec_reset(&qiov);
139
+ if (s->base) {
78
+ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes);
140
+ blk_unref(s->base);
79
+ ret = do_perform_cow_write(bs, m->alloc_offset, end->offset, &qiov);
141
+ }
142
+ if (s->top) {
143
+ blk_unref(s->top);
144
+ }
145
+ block_job_unref(&s->common);
146
}
147
148
149
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
150
151
/* FIXME Use real permissions */
152
src = blk_new(0, BLK_PERM_ALL);
153
- blk_insert_bs(src, bs);
154
-
155
- /* FIXME Use real permissions */
156
backing = blk_new(0, BLK_PERM_ALL);
157
- blk_insert_bs(backing, bs->backing->bs);
158
+
159
+ ret = blk_insert_bs(src, bs, NULL);
160
+ if (ret < 0) {
161
+ goto ro_cleanup;
162
+ }
163
+
164
+ ret = blk_insert_bs(backing, bs->backing->bs, NULL);
165
+ if (ret < 0) {
166
+ goto ro_cleanup;
167
+ }
168
169
length = blk_getlength(src);
170
if (length < 0) {
171
diff --git a/block/mirror.c b/block/mirror.c
172
index XXXXXXX..XXXXXXX 100644
173
--- a/block/mirror.c
174
+++ b/block/mirror.c
175
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
176
bdrv_replace_in_backing_chain(to_replace, target_bs);
177
bdrv_drained_end(target_bs);
178
179
- /* We just changed the BDS the job BB refers to */
180
+ /* We just changed the BDS the job BB refers to, so switch the BB back
181
+ * so the cleanup does the right thing. We don't need any permissions
182
+ * any more now. */
183
blk_remove_bs(job->blk);
184
- blk_insert_bs(job->blk, src);
185
+ blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
186
+ blk_insert_bs(job->blk, src, &error_abort);
187
}
80
}
188
if (s->to_replace) {
81
189
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
82
- qemu_iovec_reset(&qiov);
190
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
83
- qemu_iovec_add(&qiov, end_buffer, end->nb_bytes);
191
bool auto_complete)
84
- ret = do_perform_cow_write(bs, m->alloc_offset, end->offset, &qiov);
192
{
85
fail:
193
MirrorBlockJob *s;
86
qemu_co_mutex_lock(&s->lock);
194
+ int ret;
87
195
196
if (granularity == 0) {
197
granularity = bdrv_get_default_bitmap_granularity(target);
198
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
199
200
/* FIXME Use real permissions */
201
s->target = blk_new(0, BLK_PERM_ALL);
202
- blk_insert_bs(s->target, target);
203
+ ret = blk_insert_bs(s->target, target, errp);
204
+ if (ret < 0) {
205
+ blk_unref(s->target);
206
+ block_job_unref(&s->common);
207
+ return;
208
+ }
209
210
s->replaces = g_strdup(replaces);
211
s->on_source_error = on_source_error;
212
diff --git a/block/qcow2.c b/block/qcow2.c
88
diff --git a/block/qcow2.c b/block/qcow2.c
213
index XXXXXXX..XXXXXXX 100644
89
index XXXXXXX..XXXXXXX 100644
214
--- a/block/qcow2.c
90
--- a/block/qcow2.c
215
+++ b/block/qcow2.c
91
+++ b/block/qcow2.c
216
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
92
@@ -XXX,XX +XXX,XX @@ fail:
217
uint64_t cluster_size = s->cluster_size;
93
return ret;
218
bool encrypt;
94
}
219
int refcount_bits = s->refcount_bits;
95
220
+ Error *local_err = NULL;
96
+/* Check if it's possible to merge a write request with the writing of
221
int ret;
97
+ * the data from the COW regions */
222
QemuOptDesc *desc = opts->list->desc;
98
+static bool merge_cow(uint64_t offset, unsigned bytes,
223
Qcow2AmendHelperCBInfo helper_cb_info;
99
+ QEMUIOVector *hd_qiov, QCowL2Meta *l2meta)
224
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
100
+{
225
101
+ QCowL2Meta *m;
226
if (new_size) {
102
+
227
BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
103
+ for (m = l2meta; m != NULL; m = m->next) {
228
- blk_insert_bs(blk, bs);
104
+ /* If both COW regions are empty then there's nothing to merge */
229
+ ret = blk_insert_bs(blk, bs, &local_err);
105
+ if (m->cow_start.nb_bytes == 0 && m->cow_end.nb_bytes == 0) {
230
+ if (ret < 0) {
106
+ continue;
231
+ error_report_err(local_err);
232
+ blk_unref(blk);
233
+ return ret;
234
+ }
107
+ }
235
+
108
+
236
ret = blk_truncate(blk, new_size);
109
+ /* The data (middle) region must be immediately after the
237
blk_unref(blk);
110
+ * start region */
238
-
111
+ if (l2meta_cow_start(m) + m->cow_start.nb_bytes != offset) {
239
if (ret < 0) {
112
+ continue;
240
return ret;
113
+ }
241
}
114
+
242
diff --git a/blockdev.c b/blockdev.c
115
+ /* The end region must be immediately after the data (middle)
243
index XXXXXXX..XXXXXXX 100644
116
+ * region */
244
--- a/blockdev.c
117
+ if (m->offset + m->cow_end.offset != offset + bytes) {
245
+++ b/blockdev.c
118
+ continue;
246
@@ -XXX,XX +XXX,XX @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
119
+ }
247
BlockDriverState *bs, Error **errp)
120
+
248
{
121
+ /* Make sure that adding both COW regions to the QEMUIOVector
249
bool has_device;
122
+ * does not exceed IOV_MAX */
250
+ int ret;
123
+ if (hd_qiov->niov > IOV_MAX - 2) {
251
124
+ continue;
252
/* For BBs without a device, we can exchange the BDS tree at will */
125
+ }
253
has_device = blk_get_attached_dev(blk);
126
+
254
@@ -XXX,XX +XXX,XX @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
127
+ m->data_qiov = hd_qiov;
255
return;
128
+ return true;
256
}
257
258
- blk_insert_bs(blk, bs);
259
+ ret = blk_insert_bs(blk, bs, errp);
260
+ if (ret < 0) {
261
+ return;
262
+ }
129
+ }
263
130
+
264
if (!blk_dev_has_tray(blk)) {
131
+ return false;
265
/* For tray-less devices, blockdev-close-tray is a no-op (or may not be
132
+}
266
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
133
+
267
}
134
static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
268
135
uint64_t bytes, QEMUIOVector *qiov,
269
blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
136
int flags)
270
- blk_insert_bs(blk, bs);
137
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
271
+ ret = blk_insert_bs(blk, bs, errp);
272
+ if (ret < 0) {
273
+ goto out;
274
+ }
275
276
/* complete all in-flight operations before resizing the device */
277
bdrv_drain_all();
278
diff --git a/blockjob.c b/blockjob.c
279
index XXXXXXX..XXXXXXX 100644
280
--- a/blockjob.c
281
+++ b/blockjob.c
282
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
283
{
284
BlockBackend *blk;
285
BlockJob *job;
286
+ int ret;
287
288
if (bs->job) {
289
error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
290
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
291
292
/* FIXME Use real permissions */
293
blk = blk_new(0, BLK_PERM_ALL);
294
- blk_insert_bs(blk, bs);
295
+ ret = blk_insert_bs(blk, bs, errp);
296
+ if (ret < 0) {
297
+ blk_unref(blk);
298
+ return NULL;
299
+ }
300
301
job = g_malloc0(driver->instance_size);
302
error_setg(&job->blocker, "block device is in use by block job: %s",
303
diff --git a/hmp.c b/hmp.c
304
index XXXXXXX..XXXXXXX 100644
305
--- a/hmp.c
306
+++ b/hmp.c
307
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
308
const char* device = qdict_get_str(qdict, "device");
309
const char* command = qdict_get_str(qdict, "command");
310
Error *err = NULL;
311
+ int ret;
312
313
blk = blk_by_name(device);
314
if (!blk) {
315
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
316
if (bs) {
317
/* FIXME Use real permissions */
318
blk = local_blk = blk_new(0, BLK_PERM_ALL);
319
- blk_insert_bs(blk, bs);
320
+ ret = blk_insert_bs(blk, bs, &err);
321
+ if (ret < 0) {
322
+ goto fail;
323
+ }
324
} else {
325
goto fail;
138
goto fail;
326
}
139
}
327
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
140
328
index XXXXXXX..XXXXXXX 100644
141
- qemu_co_mutex_unlock(&s->lock);
329
--- a/hw/core/qdev-properties-system.c
142
- BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
330
+++ b/hw/core/qdev-properties-system.c
143
- trace_qcow2_writev_data(qemu_coroutine_self(),
331
@@ -XXX,XX +XXX,XX @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
144
- cluster_offset + offset_in_cluster);
332
{
145
- ret = bdrv_co_pwritev(bs->file,
333
BlockBackend *blk;
146
- cluster_offset + offset_in_cluster,
334
bool blk_created = false;
147
- cur_bytes, &hd_qiov, 0);
335
+ int ret;
148
- qemu_co_mutex_lock(&s->lock);
336
149
- if (ret < 0) {
337
blk = blk_by_name(str);
150
- goto fail;
338
if (!blk) {
151
+ /* If we need to do COW, check if it's possible to merge the
339
@@ -XXX,XX +XXX,XX @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
152
+ * writing of the guest data together with that of the COW regions.
340
if (bs) {
153
+ * If it's not possible (or not necessary) then write the
341
/* FIXME Use real permissions */
154
+ * guest data now. */
342
blk = blk_new(0, BLK_PERM_ALL);
155
+ if (!merge_cow(offset, cur_bytes, &hd_qiov, l2meta)) {
343
- blk_insert_bs(blk, bs);
156
+ qemu_co_mutex_unlock(&s->lock);
344
blk_created = true;
157
+ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
345
+
158
+ trace_qcow2_writev_data(qemu_coroutine_self(),
346
+ ret = blk_insert_bs(blk, bs, errp);
159
+ cluster_offset + offset_in_cluster);
160
+ ret = bdrv_co_pwritev(bs->file,
161
+ cluster_offset + offset_in_cluster,
162
+ cur_bytes, &hd_qiov, 0);
163
+ qemu_co_mutex_lock(&s->lock);
347
+ if (ret < 0) {
164
+ if (ret < 0) {
348
+ goto fail;
165
+ goto fail;
349
+ }
166
+ }
350
}
167
}
351
}
168
352
if (!blk) {
169
while (l2meta != NULL) {
353
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
170
diff --git a/block/qcow2.h b/block/qcow2.h
354
index XXXXXXX..XXXXXXX 100644
171
index XXXXXXX..XXXXXXX 100644
355
--- a/include/sysemu/block-backend.h
172
--- a/block/qcow2.h
356
+++ b/include/sysemu/block-backend.h
173
+++ b/block/qcow2.h
357
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_by_public(BlockBackendPublic *public);
174
@@ -XXX,XX +XXX,XX @@ typedef struct QCowL2Meta
358
175
*/
359
BlockDriverState *blk_bs(BlockBackend *blk);
176
Qcow2COWRegion cow_end;
360
void blk_remove_bs(BlockBackend *blk);
177
361
-void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs);
178
+ /**
362
+int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp);
179
+ * The I/O vector with the data from the actual guest write request.
363
bool bdrv_has_blk(BlockDriverState *bs);
180
+ * If non-NULL, this is meant to be merged together with the data
364
bool bdrv_is_root_node(BlockDriverState *bs);
181
+ * from @cow_start and @cow_end into one single write operation.
365
int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
182
+ */
366
diff --git a/migration/block.c b/migration/block.c
183
+ QEMUIOVector *data_qiov;
367
index XXXXXXX..XXXXXXX 100644
184
+
368
--- a/migration/block.c
185
/** Pointer to next L2Meta of the same write request */
369
+++ b/migration/block.c
186
struct QCowL2Meta *next;
370
@@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f)
187
371
BlockDriverState *bs = bmds_bs[i].bs;
372
373
if (bmds) {
374
- blk_insert_bs(bmds->blk, bs);
375
+ blk_insert_bs(bmds->blk, bs, &error_abort);
376
377
alloc_aio_bitmap(bmds);
378
error_setg(&bmds->blocker, "block device is in use by migration");
379
diff --git a/nbd/server.c b/nbd/server.c
380
index XXXXXXX..XXXXXXX 100644
381
--- a/nbd/server.c
382
+++ b/nbd/server.c
383
@@ -XXX,XX +XXX,XX @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
384
{
385
BlockBackend *blk;
386
NBDExport *exp = g_malloc0(sizeof(NBDExport));
387
+ int ret;
388
389
/* FIXME Use real permissions */
390
blk = blk_new(0, BLK_PERM_ALL);
391
- blk_insert_bs(blk, bs);
392
+ ret = blk_insert_bs(blk, bs, errp);
393
+ if (ret < 0) {
394
+ goto fail;
395
+ }
396
blk_set_enable_write_cache(blk, !writethrough);
397
398
exp->refcount = 1;
399
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
400
index XXXXXXX..XXXXXXX 100644
401
--- a/tests/test-blockjob.c
402
+++ b/tests/test-blockjob.c
403
@@ -XXX,XX +XXX,XX @@ static BlockBackend *create_blk(const char *name)
404
bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort);
405
g_assert_nonnull(bs);
406
407
- blk_insert_bs(blk, bs);
408
+ blk_insert_bs(blk, bs, &error_abort);
409
bdrv_unref(bs);
410
411
if (name) {
412
--
188
--
413
1.8.3.1
189
1.8.3.1
414
190
415
191
diff view generated by jsdifflib
1
We want every user to be specific about the permissions it needs, so
1
From: Alberto Garcia <berto@igalia.com>
2
we'll pass the initial permissions as parameters to blk_new(). A user
3
only needs to call blk_set_perm() if it wants to change the permissions
4
after the fact.
5
2
6
The permissions are stored in the BlockBackend and applied whenever a
3
We already have functions for doing these calculations, so let's use
7
BlockDriverState should be attached in blk_insert_bs().
4
them instead of doing everything by hand. This makes the code a bit
5
more readable.
8
6
9
This does not include actually choosing the right set of permissions
7
Signed-off-by: Alberto Garcia <berto@igalia.com>
10
everywhere yet. Instead, the usual FIXME comment is added to each place
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
and will be addressed in individual patches.
9
---
10
block/qcow2-cluster.c | 4 ++--
11
block/qcow2.c | 2 +-
12
2 files changed, 3 insertions(+), 3 deletions(-)
12
13
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Acked-by: Fam Zheng <famz@redhat.com>
16
---
17
block.c | 2 +-
18
block/backup.c | 3 ++-
19
block/block-backend.c | 21 ++++++++++++++-------
20
block/commit.c | 12 ++++++++----
21
block/mirror.c | 3 ++-
22
block/qcow2.c | 2 +-
23
blockdev.c | 4 ++--
24
blockjob.c | 3 ++-
25
hmp.c | 3 ++-
26
hw/block/fdc.c | 3 ++-
27
hw/core/qdev-properties-system.c | 3 ++-
28
hw/ide/qdev.c | 3 ++-
29
hw/scsi/scsi-disk.c | 3 ++-
30
include/sysemu/block-backend.h | 2 +-
31
migration/block.c | 3 ++-
32
nbd/server.c | 3 ++-
33
tests/test-blockjob.c | 3 ++-
34
tests/test-throttle.c | 7 ++++---
35
18 files changed, 53 insertions(+), 30 deletions(-)
36
37
diff --git a/block.c b/block.c
38
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
39
--- a/block.c
16
--- a/block/qcow2-cluster.c
40
+++ b/block.c
17
+++ b/block/qcow2-cluster.c
41
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
18
@@ -XXX,XX +XXX,XX @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
42
goto fail;
19
43
}
20
/* find the cluster offset for the given disk offset */
44
if (file_bs != NULL) {
21
45
- file = blk_new();
22
- l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
46
+ file = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
23
+ l2_index = offset_to_l2_index(s, offset);
47
blk_insert_bs(file, file_bs);
24
*cluster_offset = be64_to_cpu(l2_table[l2_index]);
48
bdrv_unref(file_bs);
25
49
26
nb_clusters = size_to_clusters(s, bytes_needed);
50
diff --git a/block/backup.c b/block/backup.c
27
@@ -XXX,XX +XXX,XX @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
51
index XXXXXXX..XXXXXXX 100644
28
52
--- a/block/backup.c
29
/* find the cluster offset for the given disk offset */
53
+++ b/block/backup.c
30
54
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
31
- l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
55
goto error;
32
+ l2_index = offset_to_l2_index(s, offset);
56
}
33
57
34
*new_l2_table = l2_table;
58
- job->target = blk_new();
35
*new_l2_index = l2_index;
59
+ /* FIXME Use real permissions */
60
+ job->target = blk_new(0, BLK_PERM_ALL);
61
blk_insert_bs(job->target, target);
62
63
job->on_source_error = on_source_error;
64
diff --git a/block/block-backend.c b/block/block-backend.c
65
index XXXXXXX..XXXXXXX 100644
66
--- a/block/block-backend.c
67
+++ b/block/block-backend.c
68
@@ -XXX,XX +XXX,XX @@ static const BdrvChildRole child_root = {
69
70
/*
71
* Create a new BlockBackend with a reference count of one.
72
- * Store an error through @errp on failure, unless it's null.
73
+ *
74
+ * @perm is a bitmasks of BLK_PERM_* constants which describes the permissions
75
+ * to request for a block driver node that is attached to this BlockBackend.
76
+ * @shared_perm is a bitmask which describes which permissions may be granted
77
+ * to other users of the attached node.
78
+ * Both sets of permissions can be changed later using blk_set_perm().
79
+ *
80
* Return the new BlockBackend on success, null on failure.
81
*/
82
-BlockBackend *blk_new(void)
83
+BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
84
{
85
BlockBackend *blk;
86
87
blk = g_new0(BlockBackend, 1);
88
blk->refcnt = 1;
89
- blk->perm = 0;
90
- blk->shared_perm = BLK_PERM_ALL;
91
+ blk->perm = perm;
92
+ blk->shared_perm = shared_perm;
93
blk_set_enable_write_cache(blk, true);
94
95
qemu_co_queue_init(&blk->public.throttled_reqs[0]);
96
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
97
BlockBackend *blk;
98
BlockDriverState *bs;
99
100
- blk = blk_new();
101
+ blk = blk_new(0, BLK_PERM_ALL);
102
bs = bdrv_open(filename, reference, options, flags, errp);
103
if (!bs) {
104
blk_unref(blk);
105
@@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk)
106
void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
107
{
108
bdrv_ref(bs);
109
- /* FIXME Use real permissions */
110
+ /* FIXME Error handling */
111
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
112
- 0, BLK_PERM_ALL, blk, &error_abort);
113
+ blk->perm, blk->shared_perm, blk,
114
+ &error_abort);
115
116
notifier_list_notify(&blk->insert_bs_notifiers, blk);
117
if (blk->public.throttle_state) {
118
diff --git a/block/commit.c b/block/commit.c
119
index XXXXXXX..XXXXXXX 100644
120
--- a/block/commit.c
121
+++ b/block/commit.c
122
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
123
block_job_add_bdrv(&s->common, overlay_bs);
124
}
125
126
- s->base = blk_new();
127
+ /* FIXME Use real permissions */
128
+ s->base = blk_new(0, BLK_PERM_ALL);
129
blk_insert_bs(s->base, base);
130
131
- s->top = blk_new();
132
+ /* FIXME Use real permissions */
133
+ s->top = blk_new(0, BLK_PERM_ALL);
134
blk_insert_bs(s->top, top);
135
136
s->active = bs;
137
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
138
}
139
}
140
141
- src = blk_new();
142
+ /* FIXME Use real permissions */
143
+ src = blk_new(0, BLK_PERM_ALL);
144
blk_insert_bs(src, bs);
145
146
- backing = blk_new();
147
+ /* FIXME Use real permissions */
148
+ backing = blk_new(0, BLK_PERM_ALL);
149
blk_insert_bs(backing, bs->backing->bs);
150
151
length = blk_getlength(src);
152
diff --git a/block/mirror.c b/block/mirror.c
153
index XXXXXXX..XXXXXXX 100644
154
--- a/block/mirror.c
155
+++ b/block/mirror.c
156
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
157
return;
158
}
159
160
- s->target = blk_new();
161
+ /* FIXME Use real permissions */
162
+ s->target = blk_new(0, BLK_PERM_ALL);
163
blk_insert_bs(s->target, target);
164
165
s->replaces = g_strdup(replaces);
166
diff --git a/block/qcow2.c b/block/qcow2.c
36
diff --git a/block/qcow2.c b/block/qcow2.c
167
index XXXXXXX..XXXXXXX 100644
37
index XXXXXXX..XXXXXXX 100644
168
--- a/block/qcow2.c
38
--- a/block/qcow2.c
169
+++ b/block/qcow2.c
39
+++ b/block/qcow2.c
170
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
40
@@ -XXX,XX +XXX,XX @@ static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
171
}
41
}
172
42
173
if (new_size) {
43
/* Tables must be cluster aligned */
174
- BlockBackend *blk = blk_new();
44
- if (offset & (s->cluster_size - 1)) {
175
+ BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
45
+ if (offset_into_cluster(s, offset) != 0) {
176
blk_insert_bs(blk, bs);
46
return -EINVAL;
177
ret = blk_truncate(blk, new_size);
178
blk_unref(blk);
179
diff --git a/blockdev.c b/blockdev.c
180
index XXXXXXX..XXXXXXX 100644
181
--- a/blockdev.c
182
+++ b/blockdev.c
183
@@ -XXX,XX +XXX,XX @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
184
if ((!file || !*file) && !qdict_size(bs_opts)) {
185
BlockBackendRootState *blk_rs;
186
187
- blk = blk_new();
188
+ blk = blk_new(0, BLK_PERM_ALL);
189
blk_rs = blk_get_root_state(blk);
190
blk_rs->open_flags = bdrv_flags;
191
blk_rs->read_only = read_only;
192
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
193
goto out;
194
}
47
}
195
48
196
- blk = blk_new();
197
+ blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL);
198
blk_insert_bs(blk, bs);
199
200
/* complete all in-flight operations before resizing the device */
201
diff --git a/blockjob.c b/blockjob.c
202
index XXXXXXX..XXXXXXX 100644
203
--- a/blockjob.c
204
+++ b/blockjob.c
205
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
206
}
207
}
208
209
- blk = blk_new();
210
+ /* FIXME Use real permissions */
211
+ blk = blk_new(0, BLK_PERM_ALL);
212
blk_insert_bs(blk, bs);
213
214
job = g_malloc0(driver->instance_size);
215
diff --git a/hmp.c b/hmp.c
216
index XXXXXXX..XXXXXXX 100644
217
--- a/hmp.c
218
+++ b/hmp.c
219
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
220
if (!blk) {
221
BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err);
222
if (bs) {
223
- blk = local_blk = blk_new();
224
+ /* FIXME Use real permissions */
225
+ blk = local_blk = blk_new(0, BLK_PERM_ALL);
226
blk_insert_bs(blk, bs);
227
} else {
228
goto fail;
229
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
230
index XXXXXXX..XXXXXXX 100644
231
--- a/hw/block/fdc.c
232
+++ b/hw/block/fdc.c
233
@@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev)
234
235
if (!dev->conf.blk) {
236
/* Anonymous BlockBackend for an empty drive */
237
- dev->conf.blk = blk_new();
238
+ /* FIXME Use real permissions */
239
+ dev->conf.blk = blk_new(0, BLK_PERM_ALL);
240
ret = blk_attach_dev(dev->conf.blk, qdev);
241
assert(ret == 0);
242
}
243
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
244
index XXXXXXX..XXXXXXX 100644
245
--- a/hw/core/qdev-properties-system.c
246
+++ b/hw/core/qdev-properties-system.c
247
@@ -XXX,XX +XXX,XX @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
248
if (!blk) {
249
BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
250
if (bs) {
251
- blk = blk_new();
252
+ /* FIXME Use real permissions */
253
+ blk = blk_new(0, BLK_PERM_ALL);
254
blk_insert_bs(blk, bs);
255
blk_created = true;
256
}
257
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
258
index XXXXXXX..XXXXXXX 100644
259
--- a/hw/ide/qdev.c
260
+++ b/hw/ide/qdev.c
261
@@ -XXX,XX +XXX,XX @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
262
return -1;
263
} else {
264
/* Anonymous BlockBackend for an empty drive */
265
- dev->conf.blk = blk_new();
266
+ /* FIXME Use real permissions */
267
+ dev->conf.blk = blk_new(0, BLK_PERM_ALL);
268
}
269
}
270
271
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
272
index XXXXXXX..XXXXXXX 100644
273
--- a/hw/scsi/scsi-disk.c
274
+++ b/hw/scsi/scsi-disk.c
275
@@ -XXX,XX +XXX,XX @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
276
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
277
278
if (!dev->conf.blk) {
279
- dev->conf.blk = blk_new();
280
+ /* FIXME Use real permissions */
281
+ dev->conf.blk = blk_new(0, BLK_PERM_ALL);
282
}
283
284
s->qdev.blocksize = 2048;
285
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
286
index XXXXXXX..XXXXXXX 100644
287
--- a/include/sysemu/block-backend.h
288
+++ b/include/sysemu/block-backend.h
289
@@ -XXX,XX +XXX,XX @@ typedef struct BlockBackendPublic {
290
QLIST_ENTRY(BlockBackendPublic) round_robin;
291
} BlockBackendPublic;
292
293
-BlockBackend *blk_new(void);
294
+BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm);
295
BlockBackend *blk_new_open(const char *filename, const char *reference,
296
QDict *options, int flags, Error **errp);
297
int blk_get_refcnt(BlockBackend *blk);
298
diff --git a/migration/block.c b/migration/block.c
299
index XXXXXXX..XXXXXXX 100644
300
--- a/migration/block.c
301
+++ b/migration/block.c
302
@@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f)
303
}
304
305
bmds = g_new0(BlkMigDevState, 1);
306
- bmds->blk = blk_new();
307
+ /* FIXME Use real permissions */
308
+ bmds->blk = blk_new(0, BLK_PERM_ALL);
309
bmds->blk_name = g_strdup(bdrv_get_device_name(bs));
310
bmds->bulk_completed = 0;
311
bmds->total_sectors = sectors;
312
diff --git a/nbd/server.c b/nbd/server.c
313
index XXXXXXX..XXXXXXX 100644
314
--- a/nbd/server.c
315
+++ b/nbd/server.c
316
@@ -XXX,XX +XXX,XX @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size,
317
BlockBackend *blk;
318
NBDExport *exp = g_malloc0(sizeof(NBDExport));
319
320
- blk = blk_new();
321
+ /* FIXME Use real permissions */
322
+ blk = blk_new(0, BLK_PERM_ALL);
323
blk_insert_bs(blk, bs);
324
blk_set_enable_write_cache(blk, !writethrough);
325
326
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
327
index XXXXXXX..XXXXXXX 100644
328
--- a/tests/test-blockjob.c
329
+++ b/tests/test-blockjob.c
330
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
331
* BlockDriverState inserted. */
332
static BlockBackend *create_blk(const char *name)
333
{
334
- BlockBackend *blk = blk_new();
335
+ /* FIXME Use real permissions */
336
+ BlockBackend *blk = blk_new(0, BLK_PERM_ALL);
337
BlockDriverState *bs;
338
339
bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort);
340
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
341
index XXXXXXX..XXXXXXX 100644
342
--- a/tests/test-throttle.c
343
+++ b/tests/test-throttle.c
344
@@ -XXX,XX +XXX,XX @@ static void test_groups(void)
345
BlockBackend *blk1, *blk2, *blk3;
346
BlockBackendPublic *blkp1, *blkp2, *blkp3;
347
348
- blk1 = blk_new();
349
- blk2 = blk_new();
350
- blk3 = blk_new();
351
+ /* FIXME Use real permissions */
352
+ blk1 = blk_new(0, BLK_PERM_ALL);
353
+ blk2 = blk_new(0, BLK_PERM_ALL);
354
+ blk3 = blk_new(0, BLK_PERM_ALL);
355
356
blkp1 = blk_get_public(blk1);
357
blkp2 = blk_get_public(blk2);
358
--
49
--
359
1.8.3.1
50
1.8.3.1
360
51
361
52
diff view generated by jsdifflib
1
When attaching a node as a child to a new parent, the required and
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
shared permissions for this parent are checked against all other parents
2
Reviewed-by: Eric Blake <eblake@redhat.com>
3
of the node now, and an error is returned if there is a conflict.
3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
---
5
block/qed-cluster.c | 94 ++++++++++++++++++-----------------------------------
6
block/qed-table.c | 15 +++------
7
block/qed.h | 3 +-
8
3 files changed, 36 insertions(+), 76 deletions(-)
4
9
5
This allows error returns to a function that previously always
10
diff --git a/block/qed-cluster.c b/block/qed-cluster.c
6
succeeded, and the same is true for quite a few callers and their
7
callers. Converting all of them within the same patch would be too much,
8
so for now everyone tells that they don't need any permissions and allow
9
everyone else to do anything. This way we can use &error_abort initially
10
and convert caller by caller to pass actual permission requirements and
11
implement error handling.
12
13
All these places are marked with FIXME comments and it will be the job
14
of the next patches to clean them up again.
15
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Reviewed-by: Max Reitz <mreitz@redhat.com>
18
Acked-by: Fam Zheng <famz@redhat.com>
19
---
20
block.c | 66 +++++++++++++++++++++++++++++++++++++++++------
21
block/block-backend.c | 8 ++++--
22
include/block/block_int.h | 15 ++++++++++-
23
3 files changed, 78 insertions(+), 11 deletions(-)
24
25
diff --git a/block.c b/block.c
26
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
27
--- a/block.c
12
--- a/block/qed-cluster.c
28
+++ b/block.c
13
+++ b/block/qed-cluster.c
29
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
14
@@ -XXX,XX +XXX,XX @@ static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s,
30
return 0;
15
return i - index;
31
}
16
}
32
17
33
+static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
18
-typedef struct {
34
+ uint64_t new_shared_perm,
19
- BDRVQEDState *s;
35
+ BdrvChild *ignore_child, Error **errp)
20
- uint64_t pos;
36
+{
21
- size_t len;
37
+ BdrvChild *c;
22
-
38
+
23
- QEDRequest *request;
39
+ /* There is no reason why anyone couldn't tolerate write_unchanged */
24
-
40
+ assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
25
- /* User callback */
41
+
26
- QEDFindClusterFunc *cb;
42
+ QLIST_FOREACH(c, &bs->parents, next_parent) {
27
- void *opaque;
43
+ if (c == ignore_child) {
28
-} QEDFindClusterCB;
44
+ continue;
29
-
45
+ }
30
-static void qed_find_cluster_cb(void *opaque, int ret)
46
+
31
-{
47
+ if ((new_used_perm & c->shared_perm) != new_used_perm ||
32
- QEDFindClusterCB *find_cluster_cb = opaque;
48
+ (c->perm & new_shared_perm) != c->perm)
33
- BDRVQEDState *s = find_cluster_cb->s;
49
+ {
34
- QEDRequest *request = find_cluster_cb->request;
50
+ const char *user = NULL;
35
- uint64_t offset = 0;
51
+ if (c->role->get_name) {
36
- size_t len = 0;
52
+ user = c->role->get_name(c);
37
- unsigned int index;
53
+ if (user && !*user) {
38
- unsigned int n;
54
+ user = NULL;
39
-
55
+ }
40
- qed_acquire(s);
56
+ }
41
- if (ret) {
57
+ error_setg(errp, "Conflicts with %s", user ?: "another operation");
42
- goto out;
58
+ return -EPERM;
43
- }
59
+ }
44
-
45
- index = qed_l2_index(s, find_cluster_cb->pos);
46
- n = qed_bytes_to_clusters(s,
47
- qed_offset_into_cluster(s, find_cluster_cb->pos) +
48
- find_cluster_cb->len);
49
- n = qed_count_contiguous_clusters(s, request->l2_table->table,
50
- index, n, &offset);
51
-
52
- if (qed_offset_is_unalloc_cluster(offset)) {
53
- ret = QED_CLUSTER_L2;
54
- } else if (qed_offset_is_zero_cluster(offset)) {
55
- ret = QED_CLUSTER_ZERO;
56
- } else if (qed_check_cluster_offset(s, offset)) {
57
- ret = QED_CLUSTER_FOUND;
58
- } else {
59
- ret = -EINVAL;
60
- }
61
-
62
- len = MIN(find_cluster_cb->len, n * s->header.cluster_size -
63
- qed_offset_into_cluster(s, find_cluster_cb->pos));
64
-
65
-out:
66
- find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len);
67
- qed_release(s);
68
- g_free(find_cluster_cb);
69
-}
70
-
71
/**
72
* Find the offset of a data cluster
73
*
74
@@ -XXX,XX +XXX,XX @@ out:
75
void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
76
size_t len, QEDFindClusterFunc *cb, void *opaque)
77
{
78
- QEDFindClusterCB *find_cluster_cb;
79
uint64_t l2_offset;
80
+ uint64_t offset = 0;
81
+ unsigned int index;
82
+ unsigned int n;
83
+ int ret;
84
85
/* Limit length to L2 boundary. Requests are broken up at the L2 boundary
86
* so that a request acts on one L2 table at a time.
87
@@ -XXX,XX +XXX,XX @@ void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
88
return;
89
}
90
91
- find_cluster_cb = g_malloc(sizeof(*find_cluster_cb));
92
- find_cluster_cb->s = s;
93
- find_cluster_cb->pos = pos;
94
- find_cluster_cb->len = len;
95
- find_cluster_cb->cb = cb;
96
- find_cluster_cb->opaque = opaque;
97
- find_cluster_cb->request = request;
98
+ ret = qed_read_l2_table(s, request, l2_offset);
99
+ qed_acquire(s);
100
+ if (ret) {
101
+ goto out;
60
+ }
102
+ }
61
+
103
+
62
+ return 0;
104
+ index = qed_l2_index(s, pos);
63
+}
105
+ n = qed_bytes_to_clusters(s,
106
+ qed_offset_into_cluster(s, pos) + len);
107
+ n = qed_count_contiguous_clusters(s, request->l2_table->table,
108
+ index, n, &offset);
64
+
109
+
65
static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
110
+ if (qed_offset_is_unalloc_cluster(offset)) {
66
{
111
+ ret = QED_CLUSTER_L2;
67
BlockDriverState *old_bs = child->bs;
112
+ } else if (qed_offset_is_zero_cluster(offset)) {
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
113
+ ret = QED_CLUSTER_ZERO;
69
BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
114
+ } else if (qed_check_cluster_offset(s, offset)) {
70
const char *child_name,
115
+ ret = QED_CLUSTER_FOUND;
71
const BdrvChildRole *child_role,
116
+ } else {
72
- void *opaque)
117
+ ret = -EINVAL;
73
+ uint64_t perm, uint64_t shared_perm,
74
+ void *opaque, Error **errp)
75
{
76
- BdrvChild *child = g_new(BdrvChild, 1);
77
+ BdrvChild *child;
78
+ int ret;
79
+
80
+ ret = bdrv_check_update_perm(child_bs, perm, shared_perm, NULL, errp);
81
+ if (ret < 0) {
82
+ return NULL;
83
+ }
118
+ }
84
+
119
+
85
+ child = g_new(BdrvChild, 1);
120
+ len = MIN(len,
86
*child = (BdrvChild) {
121
+ n * s->header.cluster_size - qed_offset_into_cluster(s, pos));
87
- .bs = NULL,
122
88
- .name = g_strdup(child_name),
123
- qed_read_l2_table(s, request, l2_offset,
89
- .role = child_role,
124
- qed_find_cluster_cb, find_cluster_cb);
90
- .opaque = opaque,
125
+out:
91
+ .bs = NULL,
126
+ cb(opaque, ret, offset, len);
92
+ .name = g_strdup(child_name),
127
+ qed_release(s);
93
+ .role = child_role,
128
}
94
+ .perm = perm,
129
diff --git a/block/qed-table.c b/block/qed-table.c
95
+ .shared_perm = shared_perm,
130
index XXXXXXX..XXXXXXX 100644
96
+ .opaque = opaque,
131
--- a/block/qed-table.c
97
};
132
+++ b/block/qed-table.c
98
133
@@ -XXX,XX +XXX,XX @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
99
bdrv_replace_child(child, child_bs);
134
return ret;
100
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
135
}
101
const BdrvChildRole *child_role,
136
102
Error **errp)
137
-void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
138
- BlockCompletionFunc *cb, void *opaque)
139
+int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
103
{
140
{
104
- BdrvChild *child = bdrv_root_attach_child(child_bs, child_name, child_role,
141
int ret;
105
- parent_bs);
142
106
+ BdrvChild *child;
143
@@ -XXX,XX +XXX,XX @@ void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
107
+
144
/* Check for cached L2 entry */
108
+ /* FIXME Use real permissions */
145
request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset);
109
+ child = bdrv_root_attach_child(child_bs, child_name, child_role,
146
if (request->l2_table) {
110
+ 0, BLK_PERM_ALL, parent_bs, errp);
147
- cb(opaque, 0);
111
+ if (child == NULL) {
148
- return;
112
+ return NULL;
149
+ return 0;
113
+ }
150
}
114
+
151
115
QLIST_INSERT_HEAD(&parent_bs->children, child, next);
152
request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache);
116
return child;
153
@@ -XXX,XX +XXX,XX @@ void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
154
}
155
qed_release(s);
156
157
- cb(opaque, ret);
158
+ return ret;
117
}
159
}
118
diff --git a/block/block-backend.c b/block/block-backend.c
160
161
int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
162
{
163
- int ret = -EINPROGRESS;
164
-
165
- qed_read_l2_table(s, request, offset, qed_sync_cb, &ret);
166
- BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS);
167
-
168
- return ret;
169
+ return qed_read_l2_table(s, request, offset);
170
}
171
172
void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
173
diff --git a/block/qed.h b/block/qed.h
119
index XXXXXXX..XXXXXXX 100644
174
index XXXXXXX..XXXXXXX 100644
120
--- a/block/block-backend.c
175
--- a/block/qed.h
121
+++ b/block/block-backend.c
176
+++ b/block/qed.h
122
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
177
@@ -XXX,XX +XXX,XX @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
123
return NULL;
178
unsigned int n);
124
}
179
int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
125
180
uint64_t offset);
126
- blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk);
181
-void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
127
+ /* FIXME Use real permissions */
182
- BlockCompletionFunc *cb, void *opaque);
128
+ blk->root = bdrv_root_attach_child(bs, "root", &child_root,
183
+int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset);
129
+ 0, BLK_PERM_ALL, blk, &error_abort);
184
void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
130
185
unsigned int index, unsigned int n, bool flush,
131
return blk;
186
BlockCompletionFunc *cb, void *opaque);
132
}
133
@@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk)
134
void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
135
{
136
bdrv_ref(bs);
137
- blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk);
138
+ /* FIXME Use real permissions */
139
+ blk->root = bdrv_root_attach_child(bs, "root", &child_root,
140
+ 0, BLK_PERM_ALL, blk, &error_abort);
141
142
notifier_list_notify(&blk->insert_bs_notifiers, blk);
143
if (blk->public.throttle_state) {
144
diff --git a/include/block/block_int.h b/include/block/block_int.h
145
index XXXXXXX..XXXXXXX 100644
146
--- a/include/block/block_int.h
147
+++ b/include/block/block_int.h
148
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
149
char *name;
150
const BdrvChildRole *role;
151
void *opaque;
152
+
153
+ /**
154
+ * Granted permissions for operating on this BdrvChild (BLK_PERM_* bitmask)
155
+ */
156
+ uint64_t perm;
157
+
158
+ /**
159
+ * Permissions that can still be granted to other users of @bs while this
160
+ * BdrvChild is still attached to it. (BLK_PERM_* bitmask)
161
+ */
162
+ uint64_t shared_perm;
163
+
164
QLIST_ENTRY(BdrvChild) next;
165
QLIST_ENTRY(BdrvChild) next_parent;
166
};
167
@@ -XXX,XX +XXX,XX @@ void hmp_drive_add_node(Monitor *mon, const char *optstr);
168
BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
169
const char *child_name,
170
const BdrvChildRole *child_role,
171
- void *opaque);
172
+ uint64_t perm, uint64_t shared_perm,
173
+ void *opaque, Error **errp);
174
void bdrv_root_unref_child(BdrvChild *child);
175
176
const char *bdrv_get_parent_name(const BlockDriverState *bs);
177
--
187
--
178
1.8.3.1
188
1.8.3.1
179
189
180
190
diff view generated by jsdifflib
1
All block drivers that can have child nodes implement .bdrv_child_perm()
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
now. Make this officially a requirement by asserting that only drivers
2
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
3
without children can omit .bdrv_child_perm().
3
---
4
block/qed-cluster.c | 39 ++++++++++++++++++++++-----------------
5
block/qed.c | 24 +++++++++++-------------
6
block/qed.h | 4 ++--
7
3 files changed, 35 insertions(+), 32 deletions(-)
4
8
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
diff --git a/block/qed-cluster.c b/block/qed-cluster.c
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Acked-by: Fam Zheng <famz@redhat.com>
8
---
9
block.c | 6 ++++--
10
1 file changed, 4 insertions(+), 2 deletions(-)
11
12
diff --git a/block.c b/block.c
13
index XXXXXXX..XXXXXXX 100644
10
index XXXXXXX..XXXXXXX 100644
14
--- a/block.c
11
--- a/block/qed-cluster.c
15
+++ b/block.c
12
+++ b/block/qed-cluster.c
16
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
13
@@ -XXX,XX +XXX,XX @@ static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s,
17
cumulative_shared_perms, errp);
14
* @s: QED state
15
* @request: L2 cache entry
16
* @pos: Byte position in device
17
- * @len: Number of bytes
18
- * @cb: Completion function
19
- * @opaque: User data for completion function
20
+ * @len: Number of bytes (may be shortened on return)
21
+ * @img_offset: Contains offset in the image file on success
22
*
23
* This function translates a position in the block device to an offset in the
24
- * image file. It invokes the cb completion callback to report back the
25
- * translated offset or unallocated range in the image file.
26
+ * image file. The translated offset or unallocated range in the image file is
27
+ * reported back in *img_offset and *len.
28
*
29
* If the L2 table exists, request->l2_table points to the L2 table cache entry
30
* and the caller must free the reference when they are finished. The cache
31
* entry is exposed in this way to avoid callers having to read the L2 table
32
* again later during request processing. If request->l2_table is non-NULL it
33
* will be unreferenced before taking on the new cache entry.
34
+ *
35
+ * On success QED_CLUSTER_FOUND is returned and img_offset/len are a contiguous
36
+ * range in the image file.
37
+ *
38
+ * On failure QED_CLUSTER_L2 or QED_CLUSTER_L1 is returned for missing L2 or L1
39
+ * table offset, respectively. len is number of contiguous unallocated bytes.
40
*/
41
-void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
42
- size_t len, QEDFindClusterFunc *cb, void *opaque)
43
+int qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
44
+ size_t *len, uint64_t *img_offset)
45
{
46
uint64_t l2_offset;
47
uint64_t offset = 0;
48
@@ -XXX,XX +XXX,XX @@ void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
49
/* Limit length to L2 boundary. Requests are broken up at the L2 boundary
50
* so that a request acts on one L2 table at a time.
51
*/
52
- len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos);
53
+ *len = MIN(*len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos);
54
55
l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)];
56
if (qed_offset_is_unalloc_cluster(l2_offset)) {
57
- cb(opaque, QED_CLUSTER_L1, 0, len);
58
- return;
59
+ *img_offset = 0;
60
+ return QED_CLUSTER_L1;
18
}
61
}
19
62
if (!qed_check_table_offset(s, l2_offset)) {
20
- /* Drivers may not have .bdrv_child_perm() */
63
- cb(opaque, -EINVAL, 0, 0);
21
+ /* Drivers that never have children can omit .bdrv_child_perm() */
64
- return;
22
if (!drv->bdrv_child_perm) {
65
+ *img_offset = *len = 0;
23
+ assert(QLIST_EMPTY(&bs->children));
66
+ return -EINVAL;
24
return 0;
25
}
67
}
26
68
27
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
69
ret = qed_read_l2_table(s, request, l2_offset);
28
drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
70
@@ -XXX,XX +XXX,XX @@ void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
29
}
71
}
30
72
31
- /* Drivers may not have .bdrv_child_perm() */
73
index = qed_l2_index(s, pos);
32
+ /* Drivers that never have children can omit .bdrv_child_perm() */
74
- n = qed_bytes_to_clusters(s,
33
if (!drv->bdrv_child_perm) {
75
- qed_offset_into_cluster(s, pos) + len);
34
+ assert(QLIST_EMPTY(&bs->children));
76
+ n = qed_bytes_to_clusters(s, qed_offset_into_cluster(s, pos) + *len);
35
return;
77
n = qed_count_contiguous_clusters(s, request->l2_table->table,
78
index, n, &offset);
79
80
@@ -XXX,XX +XXX,XX @@ void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
81
ret = -EINVAL;
36
}
82
}
37
83
84
- len = MIN(len,
85
- n * s->header.cluster_size - qed_offset_into_cluster(s, pos));
86
+ *len = MIN(*len,
87
+ n * s->header.cluster_size - qed_offset_into_cluster(s, pos));
88
89
out:
90
- cb(opaque, ret, offset, len);
91
+ *img_offset = offset;
92
qed_release(s);
93
+ return ret;
94
}
95
diff --git a/block/qed.c b/block/qed.c
96
index XXXXXXX..XXXXXXX 100644
97
--- a/block/qed.c
98
+++ b/block/qed.c
99
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
100
.file = file,
101
};
102
QEDRequest request = { .l2_table = NULL };
103
+ uint64_t offset;
104
+ int ret;
105
106
- qed_find_cluster(s, &request, cb.pos, len, qed_is_allocated_cb, &cb);
107
+ ret = qed_find_cluster(s, &request, cb.pos, &len, &offset);
108
+ qed_is_allocated_cb(&cb, ret, offset, len);
109
110
- /* Now sleep if the callback wasn't invoked immediately */
111
- while (cb.status == BDRV_BLOCK_OFFSET_MASK) {
112
- cb.co = qemu_coroutine_self();
113
- qemu_coroutine_yield();
114
- }
115
+ /* The callback was invoked immediately */
116
+ assert(cb.status != BDRV_BLOCK_OFFSET_MASK);
117
118
qed_unref_l2_cache_entry(request.l2_table);
119
120
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
121
* or -errno
122
* @offset: Cluster offset in bytes
123
* @len: Length in bytes
124
- *
125
- * Callback from qed_find_cluster().
126
*/
127
static void qed_aio_write_data(void *opaque, int ret,
128
uint64_t offset, size_t len)
129
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_data(void *opaque, int ret,
130
* or -errno
131
* @offset: Cluster offset in bytes
132
* @len: Length in bytes
133
- *
134
- * Callback from qed_find_cluster().
135
*/
136
static void qed_aio_read_data(void *opaque, int ret,
137
uint64_t offset, size_t len)
138
@@ -XXX,XX +XXX,XX @@ static void qed_aio_next_io(QEDAIOCB *acb, int ret)
139
BDRVQEDState *s = acb_to_s(acb);
140
QEDFindClusterFunc *io_fn = (acb->flags & QED_AIOCB_WRITE) ?
141
qed_aio_write_data : qed_aio_read_data;
142
+ uint64_t offset;
143
+ size_t len;
144
145
trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size);
146
147
@@ -XXX,XX +XXX,XX @@ static void qed_aio_next_io(QEDAIOCB *acb, int ret)
148
}
149
150
/* Find next cluster and start I/O */
151
- qed_find_cluster(s, &acb->request,
152
- acb->cur_pos, acb->end_pos - acb->cur_pos,
153
- io_fn, acb);
154
+ len = acb->end_pos - acb->cur_pos;
155
+ ret = qed_find_cluster(s, &acb->request, acb->cur_pos, &len, &offset);
156
+ io_fn(acb, ret, offset, len);
157
}
158
159
static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
160
diff --git a/block/qed.h b/block/qed.h
161
index XXXXXXX..XXXXXXX 100644
162
--- a/block/qed.h
163
+++ b/block/qed.h
164
@@ -XXX,XX +XXX,XX @@ int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
165
/**
166
* Cluster functions
167
*/
168
-void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
169
- size_t len, QEDFindClusterFunc *cb, void *opaque);
170
+int qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
171
+ size_t *len, uint64_t *img_offset);
172
173
/**
174
* Consistency check
38
--
175
--
39
1.8.3.1
176
1.8.3.1
40
177
41
178
diff view generated by jsdifflib
1
This is a little simpler than the commit block job because it's
1
With this change, qed_aio_write_prefill() and qed_aio_write_postfill()
2
synchronous and only commits into the immediate backing file, but
2
collapse into a single function. This is reflected by a rename of the
3
otherwise doing more or less the same.
3
combined function to qed_aio_write_cow().
4
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Acked-by: Fam Zheng <famz@redhat.com>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
8
---
9
block/commit.c | 33 +++++++++++++++++++++++++++------
9
block/qed.c | 57 +++++++++++++++++++++++----------------------------------
10
1 file changed, 27 insertions(+), 6 deletions(-)
10
1 file changed, 23 insertions(+), 34 deletions(-)
11
11
12
diff --git a/block/commit.c b/block/commit.c
12
diff --git a/block/qed.c b/block/qed.c
13
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
14
--- a/block/commit.c
14
--- a/block/qed.c
15
+++ b/block/commit.c
15
+++ b/block/qed.c
16
@@ -XXX,XX +XXX,XX @@ fail:
16
@@ -XXX,XX +XXX,XX @@ static int qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
17
int bdrv_commit(BlockDriverState *bs)
17
* @pos: Byte position in device
18
* @len: Number of bytes
19
* @offset: Byte offset in image file
20
- * @cb: Completion function
21
- * @opaque: User data for completion function
22
*/
23
-static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
24
- uint64_t len, uint64_t offset,
25
- BlockCompletionFunc *cb,
26
- void *opaque)
27
+static int qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
28
+ uint64_t len, uint64_t offset)
18
{
29
{
19
BlockBackend *src, *backing;
30
QEMUIOVector qiov;
20
+ BlockDriverState *backing_file_bs = NULL;
31
QEMUIOVector *backing_qiov = NULL;
21
+ BlockDriverState *commit_top_bs = NULL;
32
@@ -XXX,XX +XXX,XX @@ static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
22
BlockDriver *drv = bs->drv;
33
23
int64_t sector, total_sectors, length, backing_length;
34
/* Skip copy entirely if there is no work to do */
24
int n, ro, open_flags;
35
if (len == 0) {
25
int ret = 0;
36
- cb(opaque, 0);
26
uint8_t *buf = NULL;
37
- return;
27
+ Error *local_err = NULL;
38
+ return 0;
28
29
if (!drv)
30
return -ENOMEDIUM;
31
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
32
}
33
}
39
}
34
40
35
- /* FIXME Use real permissions */
41
iov = (struct iovec) {
36
- src = blk_new(0, BLK_PERM_ALL);
42
@@ -XXX,XX +XXX,XX @@ static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
37
- backing = blk_new(0, BLK_PERM_ALL);
43
ret = 0;
38
+ src = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
44
out:
39
+ backing = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
45
qemu_vfree(iov.iov_base);
40
46
- cb(opaque, ret);
41
- ret = blk_insert_bs(src, bs, NULL);
47
+ return ret;
42
+ ret = blk_insert_bs(src, bs, &local_err);
48
}
43
if (ret < 0) {
49
44
+ error_report_err(local_err);
50
/**
45
+ goto ro_cleanup;
51
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_main(void *opaque, int ret)
46
+ }
52
}
53
54
/**
55
- * Populate back untouched region of new data cluster
56
+ * Populate untouched regions of new data cluster
57
*/
58
-static void qed_aio_write_postfill(void *opaque, int ret)
59
+static void qed_aio_write_cow(void *opaque, int ret)
60
{
61
QEDAIOCB *acb = opaque;
62
BDRVQEDState *s = acb_to_s(acb);
63
- uint64_t start = acb->cur_pos + acb->cur_qiov.size;
64
- uint64_t len =
65
- qed_start_of_cluster(s, start + s->header.cluster_size - 1) - start;
66
- uint64_t offset = acb->cur_cluster +
67
- qed_offset_into_cluster(s, acb->cur_pos) +
68
- acb->cur_qiov.size;
69
+ uint64_t start, len, offset;
47
+
70
+
48
+ /* Insert commit_top block node above backing, so we can write to it */
71
+ /* Populate front untouched region of new data cluster */
49
+ backing_file_bs = backing_bs(bs);
72
+ start = qed_start_of_cluster(s, acb->cur_pos);
50
+
73
+ len = qed_offset_into_cluster(s, acb->cur_pos);
51
+ commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, BDRV_O_RDWR,
74
52
+ &local_err);
75
+ trace_qed_aio_write_prefill(s, acb, start, len, acb->cur_cluster);
53
+ if (commit_top_bs == NULL) {
76
+ ret = qed_copy_from_backing_file(s, start, len, acb->cur_cluster);
54
+ error_report_err(local_err);
77
if (ret) {
55
goto ro_cleanup;
78
qed_aio_complete(acb, ret);
79
return;
56
}
80
}
57
81
58
- ret = blk_insert_bs(backing, bs->backing->bs, NULL);
82
- trace_qed_aio_write_postfill(s, acb, start, len, offset);
59
+ bdrv_set_backing_hd(commit_top_bs, backing_file_bs);
83
- qed_copy_from_backing_file(s, start, len, offset,
60
+ bdrv_set_backing_hd(bs, commit_top_bs);
84
- qed_aio_write_main, acb);
61
+
85
-}
62
+ ret = blk_insert_bs(backing, backing_file_bs, &local_err);
86
+ /* Populate back untouched region of new data cluster */
63
if (ret < 0) {
87
+ start = acb->cur_pos + acb->cur_qiov.size;
64
+ error_report_err(local_err);
88
+ len = qed_start_of_cluster(s, start + s->header.cluster_size - 1) - start;
65
goto ro_cleanup;
89
+ offset = acb->cur_cluster +
90
+ qed_offset_into_cluster(s, acb->cur_pos) +
91
+ acb->cur_qiov.size;
92
93
-/**
94
- * Populate front untouched region of new data cluster
95
- */
96
-static void qed_aio_write_prefill(void *opaque, int ret)
97
-{
98
- QEDAIOCB *acb = opaque;
99
- BDRVQEDState *s = acb_to_s(acb);
100
- uint64_t start = qed_start_of_cluster(s, acb->cur_pos);
101
- uint64_t len = qed_offset_into_cluster(s, acb->cur_pos);
102
+ trace_qed_aio_write_postfill(s, acb, start, len, offset);
103
+ ret = qed_copy_from_backing_file(s, start, len, offset);
104
105
- trace_qed_aio_write_prefill(s, acb, start, len, acb->cur_cluster);
106
- qed_copy_from_backing_file(s, start, len, acb->cur_cluster,
107
- qed_aio_write_postfill, acb);
108
+ qed_aio_write_main(acb, ret);
109
}
110
111
/**
112
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
113
114
cb = qed_aio_write_zero_cluster;
115
} else {
116
- cb = qed_aio_write_prefill;
117
+ cb = qed_aio_write_cow;
118
acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters);
66
}
119
}
67
120
68
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
69
ro_cleanup:
70
qemu_vfree(buf);
71
72
- blk_unref(src);
73
blk_unref(backing);
74
+ if (backing_file_bs) {
75
+ bdrv_set_backing_hd(bs, backing_file_bs);
76
+ }
77
+ bdrv_unref(commit_top_bs);
78
+ blk_unref(src);
79
80
if (ro) {
81
/* ignoring error return here */
82
--
121
--
83
1.8.3.1
122
1.8.3.1
84
123
85
124
diff view generated by jsdifflib
1
Aborting on error in bdrv_append() isn't correct. This patch fixes it
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
and lets the callers handle failures.
2
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
3
---
4
block/qed.c | 32 ++++++++++++--------------------
5
1 file changed, 12 insertions(+), 20 deletions(-)
3
6
4
Test case 085 needs a reference output update. This is caused by the
7
diff --git a/block/qed.c b/block/qed.c
5
reversed order of bdrv_set_backing_hd() and change_parent_backing_link()
6
in bdrv_append(): When the backing file of the new node is set, the
7
parent nodes are still pointing to the old top, so the backing blocker
8
is now initialised with the node name rather than the BlockBackend name.
9
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Acked-by: Fam Zheng <famz@redhat.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
---
14
block.c | 23 +++++++++++++++++------
15
block/mirror.c | 9 ++++++++-
16
blockdev.c | 18 +++++++++++++++---
17
include/block/block.h | 3 ++-
18
tests/qemu-iotests/085.out | 2 +-
19
5 files changed, 43 insertions(+), 12 deletions(-)
20
21
diff --git a/block.c b/block.c
22
index XXXXXXX..XXXXXXX 100644
8
index XXXXXXX..XXXXXXX 100644
23
--- a/block.c
9
--- a/block/qed.c
24
+++ b/block.c
10
+++ b/block/qed.c
25
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
11
@@ -XXX,XX +XXX,XX @@ int qed_write_header_sync(BDRVQEDState *s)
26
int64_t total_size;
12
* This function only updates known header fields in-place and does not affect
27
QemuOpts *opts = NULL;
13
* extra data after the QED header.
28
BlockDriverState *bs_snapshot;
29
+ Error *local_err = NULL;
30
int ret;
31
32
/* if snapshot, we create a temporary backing file and open it
33
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
34
* call bdrv_unref() on it), so in order to be able to return one, we have
35
* to increase bs_snapshot's refcount here */
36
bdrv_ref(bs_snapshot);
37
- bdrv_append(bs_snapshot, bs);
38
+ bdrv_append(bs_snapshot, bs, &local_err);
39
+ if (local_err) {
40
+ error_propagate(errp, local_err);
41
+ ret = -EINVAL;
42
+ goto out;
43
+ }
44
45
g_free(tmp_filename);
46
return bs_snapshot;
47
@@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from,
48
* parents of bs_top after bdrv_append() returns. If the caller needs to keep a
49
* reference of its own, it must call bdrv_ref().
50
*/
14
*/
51
-void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
15
-static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb,
52
+void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
16
- void *opaque)
53
+ Error **errp)
17
+static int qed_write_header(BDRVQEDState *s)
54
{
18
{
55
+ Error *local_err = NULL;
19
/* We must write full sectors for O_DIRECT but cannot necessarily generate
56
+
20
* the data following the header if an unrecognized compat feature is
57
assert(!atomic_read(&bs_top->in_flight));
21
@@ -XXX,XX +XXX,XX @@ static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb,
58
assert(!atomic_read(&bs_new->in_flight));
22
ret = 0;
59
23
out:
60
- bdrv_ref(bs_top);
24
qemu_vfree(buf);
61
+ bdrv_set_backing_hd(bs_new, bs_top, &local_err);
25
- cb(opaque, ret);
62
+ if (local_err) {
26
+ return ret;
63
+ error_propagate(errp, local_err);
64
+ goto out;
65
+ }
66
67
change_parent_backing_link(bs_top, bs_new);
68
- /* FIXME Error handling */
69
- bdrv_set_backing_hd(bs_new, bs_top, &error_abort);
70
- bdrv_unref(bs_top);
71
72
/* bs_new is now referenced by its new parents, we don't need the
73
* additional reference any more. */
74
+out:
75
bdrv_unref(bs_new);
76
}
27
}
77
28
78
diff --git a/block/mirror.c b/block/mirror.c
29
static uint64_t qed_max_image_size(uint32_t cluster_size, uint32_t table_size)
79
index XXXXXXX..XXXXXXX 100644
30
@@ -XXX,XX +XXX,XX @@ static void qed_unplug_allocating_write_reqs(BDRVQEDState *s)
80
--- a/block/mirror.c
81
+++ b/block/mirror.c
82
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
83
BlockDriverState *mirror_top_bs;
84
bool target_graph_mod;
85
bool target_is_backing;
86
+ Error *local_err = NULL;
87
int ret;
88
89
if (granularity == 0) {
90
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
91
* it alive until block_job_create() even if bs has no parent. */
92
bdrv_ref(mirror_top_bs);
93
bdrv_drained_begin(bs);
94
- bdrv_append(mirror_top_bs, bs);
95
+ bdrv_append(mirror_top_bs, bs, &local_err);
96
bdrv_drained_end(bs);
97
98
+ if (local_err) {
99
+ bdrv_unref(mirror_top_bs);
100
+ error_propagate(errp, local_err);
101
+ return;
102
+ }
103
+
104
/* Make sure that the source is not resized while the job is running */
105
s = block_job_create(job_id, driver, mirror_top_bs,
106
BLK_PERM_CONSISTENT_READ,
107
diff --git a/blockdev.c b/blockdev.c
108
index XXXXXXX..XXXXXXX 100644
109
--- a/blockdev.c
110
+++ b/blockdev.c
111
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
112
113
if (!state->new_bs->drv->supports_backing) {
114
error_setg(errp, "The snapshot does not support backing images");
115
+ return;
116
+ }
117
+
118
+ /* This removes our old bs and adds the new bs. This is an operation that
119
+ * can fail, so we need to do it in .prepare; undoing it for abort is
120
+ * always possible. */
121
+ bdrv_ref(state->new_bs);
122
+ bdrv_append(state->new_bs, state->old_bs, &local_err);
123
+ if (local_err) {
124
+ error_propagate(errp, local_err);
125
+ return;
126
}
31
}
127
}
32
}
128
33
129
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_commit(BlkActionState *common)
34
-static void qed_finish_clear_need_check(void *opaque, int ret)
130
35
-{
131
bdrv_set_aio_context(state->new_bs, state->aio_context);
36
- /* Do nothing */
132
37
-}
133
- /* This removes our old bs and adds the new bs */
38
-
134
- bdrv_append(state->new_bs, state->old_bs);
39
-static void qed_flush_after_clear_need_check(void *opaque, int ret)
135
/* We don't need (or want) to use the transactional
40
-{
136
* bdrv_reopen_multiple() across all the entries at once, because we
41
- BDRVQEDState *s = opaque;
137
* don't want to abort all of them if one of them fails the reopen */
42
-
138
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(BlkActionState *common)
43
- bdrv_aio_flush(s->bs, qed_finish_clear_need_check, s);
139
ExternalSnapshotState *state =
44
-
140
DO_UPCAST(ExternalSnapshotState, common, common);
45
- /* No need to wait until flush completes */
141
if (state->new_bs) {
46
- qed_unplug_allocating_write_reqs(s);
142
- bdrv_unref(state->new_bs);
47
-}
143
+ if (state->new_bs->backing) {
48
-
144
+ bdrv_replace_in_backing_chain(state->new_bs, state->old_bs);
49
static void qed_clear_need_check(void *opaque, int ret)
145
+ }
50
{
51
BDRVQEDState *s = opaque;
52
@@ -XXX,XX +XXX,XX @@ static void qed_clear_need_check(void *opaque, int ret)
146
}
53
}
54
55
s->header.features &= ~QED_F_NEED_CHECK;
56
- qed_write_header(s, qed_flush_after_clear_need_check, s);
57
+ ret = qed_write_header(s);
58
+ (void) ret;
59
+
60
+ qed_unplug_allocating_write_reqs(s);
61
+
62
+ ret = bdrv_flush(s->bs);
63
+ (void) ret;
147
}
64
}
148
65
149
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_clean(BlkActionState *common)
66
static void qed_need_check_timer_cb(void *opaque)
150
if (state->aio_context) {
67
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
151
bdrv_drained_end(state->old_bs);
68
{
152
aio_context_release(state->aio_context);
69
BDRVQEDState *s = acb_to_s(acb);
153
+ bdrv_unref(state->new_bs);
70
BlockCompletionFunc *cb;
71
+ int ret;
72
73
/* Cancel timer when the first allocating request comes in */
74
if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) {
75
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
76
77
if (qed_should_set_need_check(s)) {
78
s->header.features |= QED_F_NEED_CHECK;
79
- qed_write_header(s, cb, acb);
80
+ ret = qed_write_header(s);
81
+ cb(acb, ret);
82
} else {
83
cb(acb, 0);
154
}
84
}
155
}
156
157
diff --git a/include/block/block.h b/include/block/block.h
158
index XXXXXXX..XXXXXXX 100644
159
--- a/include/block/block.h
160
+++ b/include/block/block.h
161
@@ -XXX,XX +XXX,XX @@ int bdrv_create(BlockDriver *drv, const char* filename,
162
QemuOpts *opts, Error **errp);
163
int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp);
164
BlockDriverState *bdrv_new(void);
165
-void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
166
+void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
167
+ Error **errp);
168
void bdrv_replace_in_backing_chain(BlockDriverState *old,
169
BlockDriverState *new);
170
171
diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out
172
index XXXXXXX..XXXXXXX 100644
173
--- a/tests/qemu-iotests/085.out
174
+++ b/tests/qemu-iotests/085.out
175
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
176
177
=== Invalid command - snapshot node used as backing hd ===
178
179
-{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'virtio0'"}}
180
+{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'snap_12'"}}
181
182
=== Invalid command - snapshot node has a backing image ===
183
184
--
85
--
185
1.8.3.1
86
1.8.3.1
186
87
187
88
diff view generated by jsdifflib
1
For meaningful error messages in the permission system, we need to get
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
some human-readable description of the parent of a BdrvChild.
2
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
3
---
4
block/qed-table.c | 47 ++++++++++++-----------------------------------
5
block/qed.c | 12 +++++++-----
6
block/qed.h | 8 +++-----
7
3 files changed, 22 insertions(+), 45 deletions(-)
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
diff --git a/block/qed-table.c b/block/qed-table.c
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
7
---
8
block.c | 9 +++++++++
9
block/block-backend.c | 21 +++++++++++++++++++++
10
include/block/block_int.h | 6 ++++++
11
3 files changed, 36 insertions(+)
12
13
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
10
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
11
--- a/block/qed-table.c
16
+++ b/block.c
12
+++ b/block/qed-table.c
17
@@ -XXX,XX +XXX,XX @@ int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough)
13
@@ -XXX,XX +XXX,XX @@ out:
18
return 0;
14
* @index: Index of first element
15
* @n: Number of elements
16
* @flush: Whether or not to sync to disk
17
- * @cb: Completion function
18
- * @opaque: Argument for completion function
19
*/
20
-static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
21
- unsigned int index, unsigned int n, bool flush,
22
- BlockCompletionFunc *cb, void *opaque)
23
+static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
24
+ unsigned int index, unsigned int n, bool flush)
25
{
26
unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
27
unsigned int start, end, i;
28
@@ -XXX,XX +XXX,XX @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
29
ret = 0;
30
out:
31
qemu_vfree(new_table);
32
- cb(opaque, ret);
33
-}
34
-
35
-/**
36
- * Propagate return value from async callback
37
- */
38
-static void qed_sync_cb(void *opaque, int ret)
39
-{
40
- *(int *)opaque = ret;
41
+ return ret;
19
}
42
}
20
43
21
+static char *bdrv_child_get_parent_desc(BdrvChild *c)
44
int qed_read_l1_table_sync(BDRVQEDState *s)
22
+{
45
@@ -XXX,XX +XXX,XX @@ int qed_read_l1_table_sync(BDRVQEDState *s)
23
+ BlockDriverState *parent = c->opaque;
46
return qed_read_table(s, s->header.l1_table_offset, s->l1_table);
24
+ return g_strdup(bdrv_get_device_or_node_name(parent));
47
}
25
+}
48
26
+
49
-void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
27
static void bdrv_child_cb_drained_begin(BdrvChild *child)
50
- BlockCompletionFunc *cb, void *opaque)
51
+int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n)
28
{
52
{
29
BlockDriverState *bs = child->opaque;
53
BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
30
@@ -XXX,XX +XXX,XX @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options,
54
- qed_write_table(s, s->header.l1_table_offset,
55
- s->l1_table, index, n, false, cb, opaque);
56
+ return qed_write_table(s, s->header.l1_table_offset,
57
+ s->l1_table, index, n, false);
31
}
58
}
32
59
33
const BdrvChildRole child_file = {
60
int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
34
+ .get_parent_desc = bdrv_child_get_parent_desc,
61
unsigned int n)
35
.inherit_options = bdrv_inherited_options,
62
{
36
.drained_begin = bdrv_child_cb_drained_begin,
63
- int ret = -EINPROGRESS;
37
.drained_end = bdrv_child_cb_drained_end,
64
-
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options,
65
- qed_write_l1_table(s, index, n, qed_sync_cb, &ret);
66
- BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS);
67
-
68
- return ret;
69
+ return qed_write_l1_table(s, index, n);
39
}
70
}
40
71
41
const BdrvChildRole child_format = {
72
int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
42
+ .get_parent_desc = bdrv_child_get_parent_desc,
73
@@ -XXX,XX +XXX,XX @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
43
.inherit_options = bdrv_inherited_fmt_options,
74
return qed_read_l2_table(s, request, offset);
44
.drained_begin = bdrv_child_cb_drained_begin,
45
.drained_end = bdrv_child_cb_drained_end,
46
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
47
}
75
}
48
76
49
const BdrvChildRole child_backing = {
77
-void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
50
+ .get_parent_desc = bdrv_child_get_parent_desc,
78
- unsigned int index, unsigned int n, bool flush,
51
.inherit_options = bdrv_backing_options,
79
- BlockCompletionFunc *cb, void *opaque)
52
.drained_begin = bdrv_child_cb_drained_begin,
80
+int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
53
.drained_end = bdrv_child_cb_drained_end,
81
+ unsigned int index, unsigned int n, bool flush)
54
diff --git a/block/block-backend.c b/block/block-backend.c
82
{
83
BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE);
84
- qed_write_table(s, request->l2_table->offset,
85
- request->l2_table->table, index, n, flush, cb, opaque);
86
+ return qed_write_table(s, request->l2_table->offset,
87
+ request->l2_table->table, index, n, flush);
88
}
89
90
int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
91
unsigned int index, unsigned int n, bool flush)
92
{
93
- int ret = -EINPROGRESS;
94
-
95
- qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret);
96
- BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS);
97
-
98
- return ret;
99
+ return qed_write_l2_table(s, request, index, n, flush);
100
}
101
diff --git a/block/qed.c b/block/qed.c
55
index XXXXXXX..XXXXXXX 100644
102
index XXXXXXX..XXXXXXX 100644
56
--- a/block/block-backend.c
103
--- a/block/qed.c
57
+++ b/block/block-backend.c
104
+++ b/block/qed.c
58
@@ -XXX,XX +XXX,XX @@ static const AIOCBInfo block_backend_aiocb_info = {
105
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_l1_update(void *opaque, int ret)
59
106
index = qed_l1_index(s, acb->cur_pos);
60
static void drive_info_del(DriveInfo *dinfo);
107
s->l1_table->offsets[index] = acb->request.l2_table->offset;
61
static BlockBackend *bdrv_first_blk(BlockDriverState *bs);
108
62
+static char *blk_get_attached_dev_id(BlockBackend *blk);
109
- qed_write_l1_table(s, index, 1, qed_commit_l2_update, acb);
63
110
+ ret = qed_write_l1_table(s, index, 1);
64
/* All BlockBackends */
111
+ qed_commit_l2_update(acb, ret);
65
static QTAILQ_HEAD(, BlockBackend) block_backends =
112
}
66
@@ -XXX,XX +XXX,XX @@ static void blk_root_drained_end(BdrvChild *child);
113
67
static void blk_root_change_media(BdrvChild *child, bool load);
114
/**
68
static void blk_root_resize(BdrvChild *child);
115
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
69
116
70
+static char *blk_root_get_parent_desc(BdrvChild *child)
117
if (need_alloc) {
71
+{
118
/* Write out the whole new L2 table */
72
+ BlockBackend *blk = child->opaque;
119
- qed_write_l2_table(s, &acb->request, 0, s->table_nelems, true,
73
+ char *dev_id;
120
- qed_aio_write_l1_update, acb);
74
+
121
+ ret = qed_write_l2_table(s, &acb->request, 0, s->table_nelems, true);
75
+ if (blk->name) {
122
+ qed_aio_write_l1_update(acb, ret);
76
+ return g_strdup(blk->name);
123
} else {
77
+ }
124
/* Write out only the updated part of the L2 table */
78
+
125
- qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters, false,
79
+ dev_id = blk_get_attached_dev_id(blk);
126
- qed_aio_next_io_cb, acb);
80
+ if (*dev_id) {
127
+ ret = qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters,
81
+ return dev_id;
128
+ false);
82
+ } else {
129
+ qed_aio_next_io(acb, ret);
83
+ /* TODO Callback into the BB owner for something more detailed */
130
}
84
+ g_free(dev_id);
131
return;
85
+ return g_strdup("a block device");
132
86
+ }
133
diff --git a/block/qed.h b/block/qed.h
87
+}
88
+
89
static const char *blk_root_get_name(BdrvChild *child)
90
{
91
return blk_name(child->opaque);
92
@@ -XXX,XX +XXX,XX @@ static const BdrvChildRole child_root = {
93
.change_media = blk_root_change_media,
94
.resize = blk_root_resize,
95
.get_name = blk_root_get_name,
96
+ .get_parent_desc = blk_root_get_parent_desc,
97
98
.drained_begin = blk_root_drained_begin,
99
.drained_end = blk_root_drained_end,
100
diff --git a/include/block/block_int.h b/include/block/block_int.h
101
index XXXXXXX..XXXXXXX 100644
134
index XXXXXXX..XXXXXXX 100644
102
--- a/include/block/block_int.h
135
--- a/block/qed.h
103
+++ b/include/block/block_int.h
136
+++ b/block/qed.h
104
@@ -XXX,XX +XXX,XX @@ struct BdrvChildRole {
137
@@ -XXX,XX +XXX,XX @@ void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table);
105
* name), or NULL if the parent can't provide a better name. */
138
* Table I/O functions
106
const char* (*get_name)(BdrvChild *child);
139
*/
107
140
int qed_read_l1_table_sync(BDRVQEDState *s);
108
+ /* Returns a malloced string that describes the parent of the child for a
141
-void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
109
+ * human reader. This could be a node-name, BlockBackend name, qdev ID or
142
- BlockCompletionFunc *cb, void *opaque);
110
+ * QOM path of the device owning the BlockBackend, job type and ID etc. The
143
+int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n);
111
+ * caller is responsible for freeing the memory. */
144
int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
112
+ char* (*get_parent_desc)(BdrvChild *child);
145
unsigned int n);
113
+
146
int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
114
/*
147
uint64_t offset);
115
* If this pair of functions is implemented, the parent doesn't issue new
148
int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset);
116
* requests after returning from .drained_begin() until .drained_end() is
149
-void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
150
- unsigned int index, unsigned int n, bool flush,
151
- BlockCompletionFunc *cb, void *opaque);
152
+int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
153
+ unsigned int index, unsigned int n, bool flush);
154
int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
155
unsigned int index, unsigned int n, bool flush);
156
117
--
157
--
118
1.8.3.1
158
1.8.3.1
119
159
120
160
diff view generated by jsdifflib
1
The mirror block job is mainly used for two different scenarios:
1
Note that this code is generally not running in coroutine context, so
2
Mirroring to an otherwise unused, independent target node, or for active
2
this is an actual blocking synchronous operation. We'll fix this in a
3
commit where the target node is part of the backing chain of the source.
3
moment.
4
5
Similarly to the commit block job patch, we need to insert a new filter
6
node to keep the permissions correct during active commit.
7
8
Note that one change this implies is that job->blk points to
9
mirror_top_bs as its root now, and mirror_top_bs (rather than the actual
10
source node) contains the bs->job pointer. This requires qemu-img commit
11
to get the job by name now rather than just taking bs->job.
12
4
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Acked-by: Fam Zheng <famz@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Acked-by: Max Reitz <mreitz@redhat.com>
16
---
7
---
17
block/mirror.c | 216 ++++++++++++++++++++++++++++++++++++++-------
8
block/qed.c | 61 +++++++++++++++++++------------------------------------------
18
qemu-img.c | 6 +-
9
1 file changed, 19 insertions(+), 42 deletions(-)
19
tests/qemu-iotests/141 | 2 +-
20
tests/qemu-iotests/141.out | 4 +-
21
4 files changed, 190 insertions(+), 38 deletions(-)
22
10
23
diff --git a/block/mirror.c b/block/mirror.c
11
diff --git a/block/qed.c b/block/qed.c
24
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
25
--- a/block/mirror.c
13
--- a/block/qed.c
26
+++ b/block/mirror.c
14
+++ b/block/qed.c
27
@@ -XXX,XX +XXX,XX @@ typedef struct MirrorBlockJob {
15
@@ -XXX,XX +XXX,XX @@ static void qed_aio_start_io(QEDAIOCB *acb)
28
BlockJob common;
16
qed_aio_next_io(acb, 0);
29
RateLimit limit;
17
}
30
BlockBackend *target;
18
31
+ BlockDriverState *mirror_top_bs;
19
-static void qed_aio_next_io_cb(void *opaque, int ret)
32
+ BlockDriverState *source;
20
-{
33
BlockDriverState *base;
21
- QEDAIOCB *acb = opaque;
34
+
22
-
35
/* The name of the graph node to replace */
23
- qed_aio_next_io(acb, ret);
36
char *replaces;
24
-}
37
/* The BDS to replace */
25
-
38
@@ -XXX,XX +XXX,XX @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s,
26
static void qed_plug_allocating_write_reqs(BDRVQEDState *s)
39
40
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
41
{
27
{
42
- BlockDriverState *source = blk_bs(s->common.blk);
28
assert(!s->allocating_write_reqs_plugged);
43
+ BlockDriverState *source = s->source;
29
@@ -XXX,XX +XXX,XX @@ err:
44
int64_t sector_num, first_chunk;
30
qed_aio_complete(acb, ret);
45
uint64_t delay_ns = 0;
31
}
46
/* At least the first dirty chunk is mirrored in one iteration. */
32
47
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
33
-static void qed_aio_write_l2_update_cb(void *opaque, int ret)
48
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
34
-{
49
MirrorExitData *data = opaque;
35
- QEDAIOCB *acb = opaque;
50
AioContext *replace_aio_context = NULL;
36
- qed_aio_write_l2_update(acb, ret, acb->cur_cluster);
51
- BlockDriverState *src = blk_bs(s->common.blk);
37
-}
52
+ BlockDriverState *src = s->source;
53
BlockDriverState *target_bs = blk_bs(s->target);
54
+ BlockDriverState *mirror_top_bs = s->mirror_top_bs;
55
56
/* Make sure that the source BDS doesn't go away before we called
57
* block_job_completed(). */
58
bdrv_ref(src);
59
+ bdrv_ref(mirror_top_bs);
60
+
61
+ /* We don't access the source any more. Dropping any WRITE/RESIZE is
62
+ * required before it could become a backing file of target_bs. */
63
+ bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
64
+ &error_abort);
65
+ if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
66
+ BlockDriverState *backing = s->is_none_mode ? src : s->base;
67
+ if (backing_bs(target_bs) != backing) {
68
+ bdrv_set_backing_hd(target_bs, backing);
69
+ }
70
+ }
71
72
if (s->to_replace) {
73
replace_aio_context = bdrv_get_aio_context(s->to_replace);
74
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
75
bdrv_drained_begin(target_bs);
76
bdrv_replace_in_backing_chain(to_replace, target_bs);
77
bdrv_drained_end(target_bs);
78
-
38
-
79
- /* We just changed the BDS the job BB refers to, so switch the BB back
39
-/**
80
- * so the cleanup does the right thing. We don't need any permissions
40
- * Flush new data clusters before updating the L2 table
81
- * any more now. */
41
- *
82
- blk_remove_bs(job->blk);
42
- * This flush is necessary when a backing file is in use. A crash during an
83
- blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
43
- * allocating write could result in empty clusters in the image. If the write
84
- blk_insert_bs(job->blk, src, &error_abort);
44
- * only touched a subregion of the cluster, then backing image sectors have
45
- * been lost in the untouched region. The solution is to flush after writing a
46
- * new data cluster and before updating the L2 table.
47
- */
48
-static void qed_aio_write_flush_before_l2_update(void *opaque, int ret)
49
-{
50
- QEDAIOCB *acb = opaque;
51
- BDRVQEDState *s = acb_to_s(acb);
52
-
53
- if (!bdrv_aio_flush(s->bs->file->bs, qed_aio_write_l2_update_cb, opaque)) {
54
- qed_aio_complete(acb, -EIO);
55
- }
56
-}
57
-
58
/**
59
* Write data to the image file
60
*/
61
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_main(void *opaque, int ret)
62
BDRVQEDState *s = acb_to_s(acb);
63
uint64_t offset = acb->cur_cluster +
64
qed_offset_into_cluster(s, acb->cur_pos);
65
- BlockCompletionFunc *next_fn;
66
67
trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size);
68
69
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_main(void *opaque, int ret)
70
return;
85
}
71
}
86
if (s->to_replace) {
72
87
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
73
+ BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO);
88
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
74
+ ret = bdrv_pwritev(s->bs->file, offset, &acb->cur_qiov);
89
g_free(s->replaces);
75
+ if (ret >= 0) {
90
blk_unref(s->target);
76
+ ret = 0;
91
s->target = NULL;
92
+
93
+ /* Remove the mirror filter driver from the graph. Before this, get rid of
94
+ * the blockers on the intermediate nodes so that the resulting state is
95
+ * valid. */
96
+ block_job_remove_all_bdrv(job);
97
+ bdrv_replace_in_backing_chain(mirror_top_bs, backing_bs(mirror_top_bs));
98
+
99
+ /* We just changed the BDS the job BB refers to (with either or both of the
100
+ * bdrv_replace_in_backing_chain() calls), so switch the BB back so the
101
+ * cleanup does the right thing. We don't need any permissions any more
102
+ * now. */
103
+ blk_remove_bs(job->blk);
104
+ blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
105
+ blk_insert_bs(job->blk, mirror_top_bs, &error_abort);
106
+
107
block_job_completed(&s->common, data->ret);
108
+
109
g_free(data);
110
bdrv_drained_end(src);
111
+ bdrv_unref(mirror_top_bs);
112
bdrv_unref(src);
113
}
114
115
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
116
{
117
int64_t sector_num, end;
118
BlockDriverState *base = s->base;
119
- BlockDriverState *bs = blk_bs(s->common.blk);
120
+ BlockDriverState *bs = s->source;
121
BlockDriverState *target_bs = blk_bs(s->target);
122
int ret, n;
123
124
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
125
{
126
MirrorBlockJob *s = opaque;
127
MirrorExitData *data;
128
- BlockDriverState *bs = blk_bs(s->common.blk);
129
+ BlockDriverState *bs = s->source;
130
BlockDriverState *target_bs = blk_bs(s->target);
131
bool need_drain = true;
132
int64_t length;
133
@@ -XXX,XX +XXX,XX @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
134
static void mirror_complete(BlockJob *job, Error **errp)
135
{
136
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
137
- BlockDriverState *src, *target;
138
+ BlockDriverState *target;
139
140
- src = blk_bs(job->blk);
141
target = blk_bs(s->target);
142
143
if (!s->synced) {
144
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp)
145
replace_aio_context = bdrv_get_aio_context(s->to_replace);
146
aio_context_acquire(replace_aio_context);
147
148
+ /* TODO Translate this into permission system. Current definition of
149
+ * GRAPH_MOD would require to request it for the parents; they might
150
+ * not even be BlockDriverStates, however, so a BdrvChild can't address
151
+ * them. May need redefinition of GRAPH_MOD. */
152
error_setg(&s->replace_blocker,
153
"block device is in use by block-job-complete");
154
bdrv_op_block_all(s->to_replace, s->replace_blocker);
155
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp)
156
aio_context_release(replace_aio_context);
157
}
158
159
- if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
160
- BlockDriverState *backing = s->is_none_mode ? src : s->base;
161
- if (backing_bs(target) != backing) {
162
- bdrv_set_backing_hd(target, backing);
163
- }
164
- }
165
-
166
s->should_complete = true;
167
block_job_enter(&s->common);
168
}
169
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
170
.drain = mirror_drain,
171
};
172
173
+static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs,
174
+ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
175
+{
176
+ return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
177
+}
178
+
179
+static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs,
180
+ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
181
+{
182
+ return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
183
+}
184
+
185
+static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs)
186
+{
187
+ return bdrv_co_flush(bs->backing->bs);
188
+}
189
+
190
+static int64_t coroutine_fn bdrv_mirror_top_get_block_status(
191
+ BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
192
+ BlockDriverState **file)
193
+{
194
+ *pnum = nb_sectors;
195
+ *file = bs->backing->bs;
196
+ return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
197
+ (sector_num << BDRV_SECTOR_BITS);
198
+}
199
+
200
+static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs,
201
+ int64_t offset, int count, BdrvRequestFlags flags)
202
+{
203
+ return bdrv_co_pwrite_zeroes(bs->backing, offset, count, flags);
204
+}
205
+
206
+static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs,
207
+ int64_t offset, int count)
208
+{
209
+ return bdrv_co_pdiscard(bs->backing->bs, offset, count);
210
+}
211
+
212
+static void bdrv_mirror_top_close(BlockDriverState *bs)
213
+{
214
+}
215
+
216
+static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c,
217
+ const BdrvChildRole *role,
218
+ uint64_t perm, uint64_t shared,
219
+ uint64_t *nperm, uint64_t *nshared)
220
+{
221
+ /* Must be able to forward guest writes to the real image */
222
+ *nperm = 0;
223
+ if (perm & BLK_PERM_WRITE) {
224
+ *nperm |= BLK_PERM_WRITE;
225
+ }
77
+ }
226
+
78
+
227
+ *nshared = BLK_PERM_ALL;
79
if (acb->find_cluster_ret == QED_CLUSTER_FOUND) {
228
+}
80
- next_fn = qed_aio_next_io_cb;
229
+
81
+ qed_aio_next_io(acb, ret);
230
+/* Dummy node that provides consistent read to its users without requiring it
82
} else {
231
+ * from its backing file and that allows writes on the backing file chain. */
83
if (s->bs->backing) {
232
+static BlockDriver bdrv_mirror_top = {
84
- next_fn = qed_aio_write_flush_before_l2_update;
233
+ .format_name = "mirror_top",
85
- } else {
234
+ .bdrv_co_preadv = bdrv_mirror_top_preadv,
86
- next_fn = qed_aio_write_l2_update_cb;
235
+ .bdrv_co_pwritev = bdrv_mirror_top_pwritev,
87
+ /*
236
+ .bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes,
88
+ * Flush new data clusters before updating the L2 table
237
+ .bdrv_co_pdiscard = bdrv_mirror_top_pdiscard,
89
+ *
238
+ .bdrv_co_flush = bdrv_mirror_top_flush,
90
+ * This flush is necessary when a backing file is in use. A crash
239
+ .bdrv_co_get_block_status = bdrv_mirror_top_get_block_status,
91
+ * during an allocating write could result in empty clusters in the
240
+ .bdrv_close = bdrv_mirror_top_close,
92
+ * image. If the write only touched a subregion of the cluster,
241
+ .bdrv_child_perm = bdrv_mirror_top_child_perm,
93
+ * then backing image sectors have been lost in the untouched
242
+};
94
+ * region. The solution is to flush after writing a new data
243
+
95
+ * cluster and before updating the L2 table.
244
static void mirror_start_job(const char *job_id, BlockDriverState *bs,
96
+ */
245
int creation_flags, BlockDriverState *target,
97
+ ret = bdrv_flush(s->bs->file->bs);
246
const char *replaces, int64_t speed,
98
}
247
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
99
+ qed_aio_write_l2_update(acb, ret, acb->cur_cluster);
248
bool auto_complete)
249
{
250
MirrorBlockJob *s;
251
+ BlockDriverState *mirror_top_bs;
252
+ bool target_graph_mod;
253
+ bool target_is_backing;
254
int ret;
255
256
if (granularity == 0) {
257
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
258
buf_size = DEFAULT_MIRROR_BUF_SIZE;
259
}
260
261
- /* FIXME Use real permissions */
262
- s = block_job_create(job_id, driver, bs, 0, BLK_PERM_ALL, speed,
263
+ /* In the case of active commit, add dummy driver to provide consistent
264
+ * reads on the top, while disabling it in the intermediate nodes, and make
265
+ * the backing chain writable. */
266
+ mirror_top_bs = bdrv_new_open_driver(&bdrv_mirror_top, NULL, BDRV_O_RDWR,
267
+ errp);
268
+ if (mirror_top_bs == NULL) {
269
+ return;
270
+ }
271
+ mirror_top_bs->total_sectors = bs->total_sectors;
272
+
273
+ /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep
274
+ * it alive until block_job_create() even if bs has no parent. */
275
+ bdrv_ref(mirror_top_bs);
276
+ bdrv_drained_begin(bs);
277
+ bdrv_append(mirror_top_bs, bs);
278
+ bdrv_drained_end(bs);
279
+
280
+ /* Make sure that the source is not resized while the job is running */
281
+ s = block_job_create(job_id, driver, mirror_top_bs,
282
+ BLK_PERM_CONSISTENT_READ,
283
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
284
+ BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed,
285
creation_flags, cb, opaque, errp);
286
+ bdrv_unref(mirror_top_bs);
287
if (!s) {
288
- return;
289
+ goto fail;
290
}
100
}
291
-
101
-
292
- /* FIXME Use real permissions */
102
- BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO);
293
- s->target = blk_new(0, BLK_PERM_ALL);
103
- bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
294
+ s->source = bs;
104
- &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE,
295
+ s->mirror_top_bs = mirror_top_bs;
105
- next_fn, acb);
296
+
297
+ /* No resize for the target either; while the mirror is still running, a
298
+ * consistent read isn't necessarily possible. We could possibly allow
299
+ * writes and graph modifications, though it would likely defeat the
300
+ * purpose of a mirror, so leave them blocked for now.
301
+ *
302
+ * In the case of active commit, things look a bit different, though,
303
+ * because the target is an already populated backing file in active use.
304
+ * We can allow anything except resize there.*/
305
+ target_is_backing = bdrv_chain_contains(bs, target);
306
+ target_graph_mod = (backing_mode != MIRROR_LEAVE_BACKING_CHAIN);
307
+ s->target = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE |
308
+ (target_graph_mod ? BLK_PERM_GRAPH_MOD : 0),
309
+ BLK_PERM_WRITE_UNCHANGED |
310
+ (target_is_backing ? BLK_PERM_CONSISTENT_READ |
311
+ BLK_PERM_WRITE |
312
+ BLK_PERM_GRAPH_MOD : 0));
313
ret = blk_insert_bs(s->target, target, errp);
314
if (ret < 0) {
315
- blk_unref(s->target);
316
- block_job_unref(&s->common);
317
- return;
318
+ goto fail;
319
}
320
321
s->replaces = g_strdup(replaces);
322
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
323
return;
324
}
325
326
- /* FIXME Use real permissions */
327
+ /* Required permissions are already taken with blk_new() */
328
block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL,
329
&error_abort);
330
331
/* In commit_active_start() all intermediate nodes disappear, so
332
* any jobs in them must be blocked */
333
- if (bdrv_chain_contains(bs, target)) {
334
+ if (target_is_backing) {
335
BlockDriverState *iter;
336
for (iter = backing_bs(bs); iter != target; iter = backing_bs(iter)) {
337
- /* FIXME Use real permissions */
338
- block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
339
- BLK_PERM_ALL, &error_abort);
340
+ /* XXX BLK_PERM_WRITE needs to be allowed so we don't block
341
+ * ourselves at s->base (if writes are blocked for a node, they are
342
+ * also blocked for its backing file). The other options would be a
343
+ * second filter driver above s->base (== target). */
344
+ ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
345
+ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE,
346
+ errp);
347
+ if (ret < 0) {
348
+ goto fail;
349
+ }
350
}
351
}
352
353
trace_mirror_start(bs, s, opaque);
354
block_job_start(&s->common);
355
+ return;
356
+
357
+fail:
358
+ if (s) {
359
+ g_free(s->replaces);
360
+ blk_unref(s->target);
361
+ block_job_unref(&s->common);
362
+ }
363
+
364
+ bdrv_replace_in_backing_chain(mirror_top_bs, backing_bs(mirror_top_bs));
365
}
106
}
366
107
367
void mirror_start(const char *job_id, BlockDriverState *bs,
108
/**
368
diff --git a/qemu-img.c b/qemu-img.c
369
index XXXXXXX..XXXXXXX 100644
370
--- a/qemu-img.c
371
+++ b/qemu-img.c
372
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
373
{
374
AioContext *aio_context = blk_get_aio_context(job->blk);
375
376
+ /* FIXME In error cases, the job simply goes away and we access a dangling
377
+ * pointer below. */
378
aio_context_acquire(aio_context);
379
do {
380
aio_poll(aio_context, true);
381
@@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv)
382
const char *filename, *fmt, *cache, *base;
383
BlockBackend *blk;
384
BlockDriverState *bs, *base_bs;
385
+ BlockJob *job;
386
bool progress = false, quiet = false, drop = false;
387
bool writethrough;
388
Error *local_err = NULL;
389
@@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv)
390
bdrv_ref(bs);
391
}
392
393
- run_block_job(bs->job, &local_err);
394
+ job = block_job_get("commit");
395
+ run_block_job(job, &local_err);
396
if (local_err) {
397
goto unref_backing;
398
}
399
diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141
400
index XXXXXXX..XXXXXXX 100755
401
--- a/tests/qemu-iotests/141
402
+++ b/tests/qemu-iotests/141
403
@@ -XXX,XX +XXX,XX @@ test_blockjob()
404
_send_qemu_cmd $QEMU_HANDLE \
405
"{'execute': 'x-blockdev-del',
406
'arguments': {'node-name': 'drv0'}}" \
407
- 'error'
408
+ 'error' | _filter_generated_node_ids
409
410
_send_qemu_cmd $QEMU_HANDLE \
411
"{'execute': 'block-job-cancel',
412
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
413
index XXXXXXX..XXXXXXX 100644
414
--- a/tests/qemu-iotests/141.out
415
+++ b/tests/qemu-iotests/141.out
416
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
417
Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
418
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
419
{"return": {}}
420
-{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
421
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
422
{"return": {}}
423
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
424
{"return": {}}
425
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
426
{"return": {}}
427
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
428
{"return": {}}
429
-{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
430
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
431
{"return": {}}
432
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
433
{"return": {}}
434
--
109
--
435
1.8.3.1
110
1.8.3.1
436
111
437
112
diff view generated by jsdifflib
1
This makes all device emulations with a qdev drive property request
1
qed_commit_l2_update() is unconditionally called at the end of
2
permissions on their BlockBackend. The only thing we block at this point
2
qed_aio_write_l1_update(). Inline it.
3
is resizing images for some devices that can't support it.
4
3
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
---
6
---
9
hw/block/block.c | 22 +++++++++++++++++++++-
7
block/qed.c | 36 ++++++++++++++----------------------
10
hw/block/fdc.c | 25 +++++++++++++++++++++++--
8
1 file changed, 14 insertions(+), 22 deletions(-)
11
hw/block/m25p80.c | 8 ++++++++
12
hw/block/nand.c | 7 +++++++
13
hw/block/nvme.c | 8 +++++++-
14
hw/block/onenand.c | 7 +++++++
15
hw/block/pflash_cfi01.c | 18 ++++++++++++------
16
hw/block/pflash_cfi02.c | 19 +++++++++++++------
17
hw/block/virtio-blk.c | 8 +++++++-
18
hw/core/qdev-properties-system.c | 1 -
19
hw/ide/qdev.c | 8 ++++++--
20
hw/nvram/spapr_nvram.c | 8 ++++++++
21
hw/scsi/scsi-disk.c | 9 +++++++--
22
hw/sd/sd.c | 6 ++++++
23
hw/usb/dev-storage.c | 6 +++++-
24
include/hw/block/block.h | 3 ++-
25
tests/qemu-iotests/051.pc.out | 6 +++---
26
17 files changed, 142 insertions(+), 27 deletions(-)
27
9
28
diff --git a/hw/block/block.c b/hw/block/block.c
10
diff --git a/block/qed.c b/block/qed.c
29
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
30
--- a/hw/block/block.c
12
--- a/block/qed.c
31
+++ b/hw/block/block.c
13
+++ b/block/qed.c
32
@@ -XXX,XX +XXX,XX @@ void blkconf_blocksizes(BlockConf *conf)
14
@@ -XXX,XX +XXX,XX @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
33
}
34
}
15
}
35
16
36
-void blkconf_apply_backend_options(BlockConf *conf)
17
/**
37
+void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
18
- * Commit the current L2 table to the cache
38
+ bool resizable, Error **errp)
19
+ * Update L1 table with new L2 table offset and write it out
20
*/
21
-static void qed_commit_l2_update(void *opaque, int ret)
22
+static void qed_aio_write_l1_update(void *opaque, int ret)
39
{
23
{
40
BlockBackend *blk = conf->blk;
24
QEDAIOCB *acb = opaque;
41
BlockdevOnError rerror, werror;
25
BDRVQEDState *s = acb_to_s(acb);
42
+ uint64_t perm, shared_perm;
26
CachedL2Table *l2_table = acb->request.l2_table;
43
bool wce;
27
uint64_t l2_offset = l2_table->offset;
44
+ int ret;
28
+ int index;
45
+
29
+
46
+ perm = BLK_PERM_CONSISTENT_READ;
30
+ if (ret) {
47
+ if (!readonly) {
31
+ qed_aio_complete(acb, ret);
48
+ perm |= BLK_PERM_WRITE;
49
+ }
50
+
51
+ /* TODO Remove BLK_PERM_WRITE unless explicitly configured so */
52
+ shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
53
+ BLK_PERM_GRAPH_MOD | BLK_PERM_WRITE;
54
+ if (resizable) {
55
+ shared_perm |= BLK_PERM_RESIZE;
56
+ }
57
+
58
+ ret = blk_set_perm(blk, perm, shared_perm, errp);
59
+ if (ret < 0) {
60
+ return;
32
+ return;
61
+ }
33
+ }
62
34
63
switch (conf->wce) {
35
+ index = qed_l1_index(s, acb->cur_pos);
64
case ON_OFF_AUTO_ON: wce = true; break;
36
+ s->l1_table->offsets[index] = l2_table->offset;
65
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
66
index XXXXXXX..XXXXXXX 100644
67
--- a/hw/block/fdc.c
68
+++ b/hw/block/fdc.c
69
@@ -XXX,XX +XXX,XX @@ typedef enum FDiskFlags {
70
struct FDrive {
71
FDCtrl *fdctrl;
72
BlockBackend *blk;
73
+ BlockConf *conf;
74
/* Drive status */
75
FloppyDriveType drive; /* CMOS drive type */
76
uint8_t perpendicular; /* 2.88 MB access mode */
77
@@ -XXX,XX +XXX,XX @@ static void fd_revalidate(FDrive *drv)
78
static void fd_change_cb(void *opaque, bool load, Error **errp)
79
{
80
FDrive *drive = opaque;
81
+ Error *local_err = NULL;
82
+
37
+
83
+ if (!load) {
38
+ ret = qed_write_l1_table(s, index, 1);
84
+ blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
85
+ } else {
86
+ blkconf_apply_backend_options(drive->conf,
87
+ blk_is_read_only(drive->blk), false,
88
+ &local_err);
89
+ if (local_err) {
90
+ error_propagate(errp, local_err);
91
+ return;
92
+ }
93
+ }
94
95
drive->media_changed = 1;
96
drive->media_validated = false;
97
@@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev)
98
FloppyDrive *dev = FLOPPY_DRIVE(qdev);
99
FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus);
100
FDrive *drive;
101
+ Error *local_err = NULL;
102
int ret;
103
104
if (dev->unit == -1) {
105
@@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev)
106
107
if (!dev->conf.blk) {
108
/* Anonymous BlockBackend for an empty drive */
109
- /* FIXME Use real permissions */
110
dev->conf.blk = blk_new(0, BLK_PERM_ALL);
111
ret = blk_attach_dev(dev->conf.blk, qdev);
112
assert(ret == 0);
113
@@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev)
114
* blkconf_apply_backend_options(). */
115
dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
116
dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
117
- blkconf_apply_backend_options(&dev->conf);
118
+
39
+
119
+ blkconf_apply_backend_options(&dev->conf, blk_is_read_only(dev->conf.blk),
40
+ /* Commit the current L2 table to the cache */
120
+ false, &local_err);
41
qed_commit_l2_cache_entry(&s->l2_cache, l2_table);
121
+ if (local_err) {
42
122
+ error_report_err(local_err);
43
/* This is guaranteed to succeed because we just committed the entry to the
123
+ return -1;
44
@@ -XXX,XX +XXX,XX @@ static void qed_commit_l2_update(void *opaque, int ret)
124
+ }
45
qed_aio_next_io(acb, ret);
125
46
}
126
/* 'enospc' is the default for -drive, 'report' is what blk_new() gives us
47
127
* for empty drives. */
48
-/**
128
@@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev)
49
- * Update L1 table with new L2 table offset and write it out
129
return -1;
50
- */
130
}
51
-static void qed_aio_write_l1_update(void *opaque, int ret)
131
52
-{
132
+ drive->conf = &dev->conf;
53
- QEDAIOCB *acb = opaque;
133
drive->blk = dev->conf.blk;
54
- BDRVQEDState *s = acb_to_s(acb);
134
drive->fdctrl = bus->fdc;
55
- int index;
135
56
-
136
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
57
- if (ret) {
137
index XXXXXXX..XXXXXXX 100644
58
- qed_aio_complete(acb, ret);
138
--- a/hw/block/m25p80.c
59
- return;
139
+++ b/hw/block/m25p80.c
140
@@ -XXX,XX +XXX,XX @@ static void m25p80_realize(SSISlave *ss, Error **errp)
141
{
142
Flash *s = M25P80(ss);
143
M25P80Class *mc = M25P80_GET_CLASS(s);
144
+ int ret;
145
146
s->pi = mc->pi;
147
148
@@ -XXX,XX +XXX,XX @@ static void m25p80_realize(SSISlave *ss, Error **errp)
149
s->dirty_page = -1;
150
151
if (s->blk) {
152
+ uint64_t perm = BLK_PERM_CONSISTENT_READ |
153
+ (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
154
+ ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
155
+ if (ret < 0) {
156
+ return;
157
+ }
158
+
159
DB_PRINT_L(0, "Binding to IF_MTD drive\n");
160
s->storage = blk_blockalign(s->blk, s->size);
161
162
diff --git a/hw/block/nand.c b/hw/block/nand.c
163
index XXXXXXX..XXXXXXX 100644
164
--- a/hw/block/nand.c
165
+++ b/hw/block/nand.c
166
@@ -XXX,XX +XXX,XX @@ static void nand_realize(DeviceState *dev, Error **errp)
167
{
168
int pagesize;
169
NANDFlashState *s = NAND(dev);
170
+ int ret;
171
+
172
173
s->buswidth = nand_flash_ids[s->chip_id].width >> 3;
174
s->size = nand_flash_ids[s->chip_id].size << 20;
175
@@ -XXX,XX +XXX,XX @@ static void nand_realize(DeviceState *dev, Error **errp)
176
error_setg(errp, "Can't use a read-only drive");
177
return;
178
}
179
+ ret = blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
180
+ BLK_PERM_ALL, errp);
181
+ if (ret < 0) {
182
+ return;
183
+ }
184
if (blk_getlength(s->blk) >=
185
(s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
186
pagesize = 0;
187
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
188
index XXXXXXX..XXXXXXX 100644
189
--- a/hw/block/nvme.c
190
+++ b/hw/block/nvme.c
191
@@ -XXX,XX +XXX,XX @@ static int nvme_init(PCIDevice *pci_dev)
192
int i;
193
int64_t bs_size;
194
uint8_t *pci_conf;
195
+ Error *local_err = NULL;
196
197
if (!n->conf.blk) {
198
return -1;
199
@@ -XXX,XX +XXX,XX @@ static int nvme_init(PCIDevice *pci_dev)
200
return -1;
201
}
202
blkconf_blocksizes(&n->conf);
203
- blkconf_apply_backend_options(&n->conf);
204
+ blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
205
+ false, &local_err);
206
+ if (local_err) {
207
+ error_report_err(local_err);
208
+ return -1;
209
+ }
210
211
pci_conf = pci_dev->config;
212
pci_conf[PCI_INTERRUPT_PIN] = 1;
213
diff --git a/hw/block/onenand.c b/hw/block/onenand.c
214
index XXXXXXX..XXXXXXX 100644
215
--- a/hw/block/onenand.c
216
+++ b/hw/block/onenand.c
217
@@ -XXX,XX +XXX,XX @@ static int onenand_initfn(SysBusDevice *sbd)
218
OneNANDState *s = ONE_NAND(dev);
219
uint32_t size = 1 << (24 + ((s->id.dev >> 4) & 7));
220
void *ram;
221
+ Error *local_err = NULL;
222
223
s->base = (hwaddr)-1;
224
s->rdy = NULL;
225
@@ -XXX,XX +XXX,XX @@ static int onenand_initfn(SysBusDevice *sbd)
226
error_report("Can't use a read-only drive");
227
return -1;
228
}
229
+ blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
230
+ BLK_PERM_ALL, &local_err);
231
+ if (local_err) {
232
+ error_report_err(local_err);
233
+ return -1;
234
+ }
235
s->blk_cur = s->blk;
236
}
237
s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT),
238
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
239
index XXXXXXX..XXXXXXX 100644
240
--- a/hw/block/pflash_cfi01.c
241
+++ b/hw/block/pflash_cfi01.c
242
@@ -XXX,XX +XXX,XX @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
243
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
244
245
if (pfl->blk) {
246
+ uint64_t perm;
247
+ pfl->ro = blk_is_read_only(pfl->blk);
248
+ perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
249
+ ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
250
+ if (ret < 0) {
251
+ return;
252
+ }
253
+ } else {
254
+ pfl->ro = 0;
255
+ }
256
+
257
+ if (pfl->blk) {
258
/* read the initial flash content */
259
ret = blk_pread(pfl->blk, 0, pfl->storage, total_len);
260
261
@@ -XXX,XX +XXX,XX @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
262
}
263
}
264
265
- if (pfl->blk) {
266
- pfl->ro = blk_is_read_only(pfl->blk);
267
- } else {
268
- pfl->ro = 0;
269
- }
60
- }
270
-
61
-
271
/* Default to devices being used at their maximum device width. This was
62
- index = qed_l1_index(s, acb->cur_pos);
272
* assumed before the device_width support was added.
63
- s->l1_table->offsets[index] = acb->request.l2_table->offset;
273
*/
274
diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
275
index XXXXXXX..XXXXXXX 100644
276
--- a/hw/block/pflash_cfi02.c
277
+++ b/hw/block/pflash_cfi02.c
278
@@ -XXX,XX +XXX,XX @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
279
vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl));
280
pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
281
pfl->chip_len = chip_len;
282
+
283
+ if (pfl->blk) {
284
+ uint64_t perm;
285
+ pfl->ro = blk_is_read_only(pfl->blk);
286
+ perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
287
+ ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
288
+ if (ret < 0) {
289
+ return;
290
+ }
291
+ } else {
292
+ pfl->ro = 0;
293
+ }
294
+
295
if (pfl->blk) {
296
/* read the initial flash content */
297
ret = blk_pread(pfl->blk, 0, pfl->storage, chip_len);
298
@@ -XXX,XX +XXX,XX @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
299
pfl->rom_mode = 1;
300
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
301
302
- if (pfl->blk) {
303
- pfl->ro = blk_is_read_only(pfl->blk);
304
- } else {
305
- pfl->ro = 0;
306
- }
307
-
64
-
308
pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
65
- ret = qed_write_l1_table(s, index, 1);
309
pfl->wcycle = 0;
66
- qed_commit_l2_update(acb, ret);
310
pfl->cmd = 0;
67
-}
311
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
68
312
index XXXXXXX..XXXXXXX 100644
69
/**
313
--- a/hw/block/virtio-blk.c
70
* Update L2 table with new cluster offsets and write them out
314
+++ b/hw/block/virtio-blk.c
315
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
316
}
317
318
blkconf_serial(&conf->conf, &conf->serial);
319
- blkconf_apply_backend_options(&conf->conf);
320
+ blkconf_apply_backend_options(&conf->conf,
321
+ blk_is_read_only(conf->conf.blk), true,
322
+ &err);
323
+ if (err) {
324
+ error_propagate(errp, err);
325
+ return;
326
+ }
327
s->original_wce = blk_enable_write_cache(conf->conf.blk);
328
blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err);
329
if (err) {
330
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
331
index XXXXXXX..XXXXXXX 100644
332
--- a/hw/core/qdev-properties-system.c
333
+++ b/hw/core/qdev-properties-system.c
334
@@ -XXX,XX +XXX,XX @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
335
if (!blk) {
336
BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
337
if (bs) {
338
- /* FIXME Use real permissions */
339
blk = blk_new(0, BLK_PERM_ALL);
340
blk_created = true;
341
342
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
343
index XXXXXXX..XXXXXXX 100644
344
--- a/hw/ide/qdev.c
345
+++ b/hw/ide/qdev.c
346
@@ -XXX,XX +XXX,XX @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
347
return -1;
348
} else {
349
/* Anonymous BlockBackend for an empty drive */
350
- /* FIXME Use real permissions */
351
dev->conf.blk = blk_new(0, BLK_PERM_ALL);
352
}
353
}
354
@@ -XXX,XX +XXX,XX @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
355
return -1;
356
}
357
}
358
- blkconf_apply_backend_options(&dev->conf);
359
+ blkconf_apply_backend_options(&dev->conf, kind == IDE_CD, kind != IDE_CD,
360
+ &err);
361
+ if (err) {
362
+ error_report_err(err);
363
+ return -1;
364
+ }
365
366
if (ide_init_drive(s, dev->conf.blk, kind,
367
dev->version, dev->serial, dev->model, dev->wwn,
368
diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c
369
index XXXXXXX..XXXXXXX 100644
370
--- a/hw/nvram/spapr_nvram.c
371
+++ b/hw/nvram/spapr_nvram.c
372
@@ -XXX,XX +XXX,XX @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPRMachineState *spapr,
373
static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp)
374
{
375
sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev);
376
+ int ret;
377
378
if (nvram->blk) {
379
nvram->size = blk_getlength(nvram->blk);
380
+
381
+ ret = blk_set_perm(nvram->blk,
382
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
383
+ BLK_PERM_ALL, errp);
384
+ if (ret < 0) {
385
+ return;
386
+ }
387
} else {
388
nvram->size = DEFAULT_NVRAM_SIZE;
389
}
390
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
391
index XXXXXXX..XXXXXXX 100644
392
--- a/hw/scsi/scsi-disk.c
393
+++ b/hw/scsi/scsi-disk.c
394
@@ -XXX,XX +XXX,XX @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
395
return;
396
}
397
}
398
- blkconf_apply_backend_options(&dev->conf);
399
+ blkconf_apply_backend_options(&dev->conf,
400
+ blk_is_read_only(s->qdev.conf.blk),
401
+ dev->type == TYPE_DISK, &err);
402
+ if (err) {
403
+ error_propagate(errp, err);
404
+ return;
405
+ }
406
407
if (s->qdev.conf.discard_granularity == -1) {
408
s->qdev.conf.discard_granularity =
409
@@ -XXX,XX +XXX,XX @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
410
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
411
412
if (!dev->conf.blk) {
413
- /* FIXME Use real permissions */
414
dev->conf.blk = blk_new(0, BLK_PERM_ALL);
415
}
416
417
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
418
index XXXXXXX..XXXXXXX 100644
419
--- a/hw/sd/sd.c
420
+++ b/hw/sd/sd.c
421
@@ -XXX,XX +XXX,XX @@ static void sd_instance_finalize(Object *obj)
422
static void sd_realize(DeviceState *dev, Error **errp)
423
{
424
SDState *sd = SD_CARD(dev);
425
+ int ret;
426
427
if (sd->blk && blk_is_read_only(sd->blk)) {
428
error_setg(errp, "Cannot use read-only drive as SD card");
429
@@ -XXX,XX +XXX,XX @@ static void sd_realize(DeviceState *dev, Error **errp)
430
}
431
432
if (sd->blk) {
433
+ ret = blk_set_perm(sd->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
434
+ BLK_PERM_ALL, errp);
435
+ if (ret < 0) {
436
+ return;
437
+ }
438
blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
439
}
440
}
441
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
442
index XXXXXXX..XXXXXXX 100644
443
--- a/hw/usb/dev-storage.c
444
+++ b/hw/usb/dev-storage.c
445
@@ -XXX,XX +XXX,XX @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
446
447
blkconf_serial(&s->conf, &dev->serial);
448
blkconf_blocksizes(&s->conf);
449
- blkconf_apply_backend_options(&s->conf);
450
+ blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true, &err);
451
+ if (err) {
452
+ error_propagate(errp, err);
453
+ return;
454
+ }
455
456
/*
457
* Hack alert: this pretends to be a block device, but it's really
458
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
459
index XXXXXXX..XXXXXXX 100644
460
--- a/include/hw/block/block.h
461
+++ b/include/hw/block/block.h
462
@@ -XXX,XX +XXX,XX @@ void blkconf_geometry(BlockConf *conf, int *trans,
463
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
464
Error **errp);
465
void blkconf_blocksizes(BlockConf *conf);
466
-void blkconf_apply_backend_options(BlockConf *conf);
467
+void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
468
+ bool resizable, Error **errp);
469
470
/* Hard disk geometry */
471
472
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
473
index XXXXXXX..XXXXXXX 100644
474
--- a/tests/qemu-iotests/051.pc.out
475
+++ b/tests/qemu-iotests/051.pc.out
476
@@ -XXX,XX +XXX,XX @@ qququiquit
477
478
Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on
479
QEMU X.Y.Z monitor - type 'help' for more information
480
-(qemu) QEMU_PROG: Can't use a read-only drive
481
+(qemu) QEMU_PROG: Block node is read-only
482
QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed.
483
484
Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on
485
@@ -XXX,XX +XXX,XX @@ QEMU X.Y.Z monitor - type 'help' for more information
486
487
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk
488
QEMU X.Y.Z monitor - type 'help' for more information
489
-(qemu) QEMU_PROG: -device ide-drive,drive=disk: Can't use a read-only drive
490
+(qemu) QEMU_PROG: -device ide-drive,drive=disk: Block node is read-only
491
QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed.
492
493
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk
494
QEMU X.Y.Z monitor - type 'help' for more information
495
-(qemu) QEMU_PROG: -device ide-hd,drive=disk: Can't use a read-only drive
496
+(qemu) QEMU_PROG: -device ide-hd,drive=disk: Block node is read-only
497
QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed.
498
499
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk
500
--
71
--
501
1.8.3.1
72
1.8.3.1
502
73
503
74
diff view generated by jsdifflib
1
This makes use of the .bdrv_child_perm() implementation for formats that
1
Don't recurse into qed_aio_next_io() and qed_aio_complete() here, but
2
we just added. All format drivers expose the permissions they actually
2
just return an error code and let the caller handle it.
3
need nows, so that they can be set accordingly and updated when parents
4
are attached or detached.
5
6
The only format not included here is raw, which was already converted
7
with the other filter drivers.
8
3
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Acked-by: Fam Zheng <famz@redhat.com>
12
---
6
---
13
block/bochs.c | 1 +
7
block/qed.c | 19 +++++++++----------
14
block/cloop.c | 1 +
8
1 file changed, 9 insertions(+), 10 deletions(-)
15
block/crypto.c | 1 +
16
block/dmg.c | 1 +
17
block/parallels.c | 1 +
18
block/qcow.c | 1 +
19
block/qcow2.c | 1 +
20
block/qed.c | 1 +
21
block/vdi.c | 1 +
22
block/vhdx.c | 1 +
23
block/vmdk.c | 1 +
24
block/vpc.c | 1 +
25
12 files changed, 12 insertions(+)
26
9
27
diff --git a/block/bochs.c b/block/bochs.c
28
index XXXXXXX..XXXXXXX 100644
29
--- a/block/bochs.c
30
+++ b/block/bochs.c
31
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_bochs = {
32
.instance_size    = sizeof(BDRVBochsState),
33
.bdrv_probe        = bochs_probe,
34
.bdrv_open        = bochs_open,
35
+ .bdrv_child_perm = bdrv_format_default_perms,
36
.bdrv_refresh_limits = bochs_refresh_limits,
37
.bdrv_co_preadv = bochs_co_preadv,
38
.bdrv_close        = bochs_close,
39
diff --git a/block/cloop.c b/block/cloop.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/block/cloop.c
42
+++ b/block/cloop.c
43
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_cloop = {
44
.instance_size = sizeof(BDRVCloopState),
45
.bdrv_probe = cloop_probe,
46
.bdrv_open = cloop_open,
47
+ .bdrv_child_perm = bdrv_format_default_perms,
48
.bdrv_refresh_limits = cloop_refresh_limits,
49
.bdrv_co_preadv = cloop_co_preadv,
50
.bdrv_close = cloop_close,
51
diff --git a/block/crypto.c b/block/crypto.c
52
index XXXXXXX..XXXXXXX 100644
53
--- a/block/crypto.c
54
+++ b/block/crypto.c
55
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_crypto_luks = {
56
.bdrv_probe = block_crypto_probe_luks,
57
.bdrv_open = block_crypto_open_luks,
58
.bdrv_close = block_crypto_close,
59
+ .bdrv_child_perm = bdrv_format_default_perms,
60
.bdrv_create = block_crypto_create_luks,
61
.bdrv_truncate = block_crypto_truncate,
62
.create_opts = &block_crypto_create_opts_luks,
63
diff --git a/block/dmg.c b/block/dmg.c
64
index XXXXXXX..XXXXXXX 100644
65
--- a/block/dmg.c
66
+++ b/block/dmg.c
67
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_dmg = {
68
.bdrv_probe = dmg_probe,
69
.bdrv_open = dmg_open,
70
.bdrv_refresh_limits = dmg_refresh_limits,
71
+ .bdrv_child_perm = bdrv_format_default_perms,
72
.bdrv_co_preadv = dmg_co_preadv,
73
.bdrv_close = dmg_close,
74
};
75
diff --git a/block/parallels.c b/block/parallels.c
76
index XXXXXXX..XXXXXXX 100644
77
--- a/block/parallels.c
78
+++ b/block/parallels.c
79
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_parallels = {
80
.bdrv_probe        = parallels_probe,
81
.bdrv_open        = parallels_open,
82
.bdrv_close        = parallels_close,
83
+ .bdrv_child_perm = bdrv_format_default_perms,
84
.bdrv_co_get_block_status = parallels_co_get_block_status,
85
.bdrv_has_zero_init = bdrv_has_zero_init_1,
86
.bdrv_co_flush_to_os = parallels_co_flush_to_os,
87
diff --git a/block/qcow.c b/block/qcow.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/block/qcow.c
90
+++ b/block/qcow.c
91
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qcow = {
92
.bdrv_probe        = qcow_probe,
93
.bdrv_open        = qcow_open,
94
.bdrv_close        = qcow_close,
95
+ .bdrv_child_perm = bdrv_format_default_perms,
96
.bdrv_reopen_prepare = qcow_reopen_prepare,
97
.bdrv_create = qcow_create,
98
.bdrv_has_zero_init = bdrv_has_zero_init_1,
99
diff --git a/block/qcow2.c b/block/qcow2.c
100
index XXXXXXX..XXXXXXX 100644
101
--- a/block/qcow2.c
102
+++ b/block/qcow2.c
103
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
104
.bdrv_reopen_commit = qcow2_reopen_commit,
105
.bdrv_reopen_abort = qcow2_reopen_abort,
106
.bdrv_join_options = qcow2_join_options,
107
+ .bdrv_child_perm = bdrv_format_default_perms,
108
.bdrv_create = qcow2_create,
109
.bdrv_has_zero_init = bdrv_has_zero_init_1,
110
.bdrv_co_get_block_status = qcow2_co_get_block_status,
111
diff --git a/block/qed.c b/block/qed.c
10
diff --git a/block/qed.c b/block/qed.c
112
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
113
--- a/block/qed.c
12
--- a/block/qed.c
114
+++ b/block/qed.c
13
+++ b/block/qed.c
115
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = {
14
@@ -XXX,XX +XXX,XX @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
116
.bdrv_open = bdrv_qed_open,
15
/**
117
.bdrv_close = bdrv_qed_close,
16
* Update L1 table with new L2 table offset and write it out
118
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
17
*/
119
+ .bdrv_child_perm = bdrv_format_default_perms,
18
-static void qed_aio_write_l1_update(void *opaque, int ret)
120
.bdrv_create = bdrv_qed_create,
19
+static int qed_aio_write_l1_update(QEDAIOCB *acb)
121
.bdrv_has_zero_init = bdrv_has_zero_init_1,
20
{
122
.bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
21
- QEDAIOCB *acb = opaque;
123
diff --git a/block/vdi.c b/block/vdi.c
22
BDRVQEDState *s = acb_to_s(acb);
124
index XXXXXXX..XXXXXXX 100644
23
CachedL2Table *l2_table = acb->request.l2_table;
125
--- a/block/vdi.c
24
uint64_t l2_offset = l2_table->offset;
126
+++ b/block/vdi.c
25
- int index;
127
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vdi = {
26
-
128
.bdrv_open = vdi_open,
27
- if (ret) {
129
.bdrv_close = vdi_close,
28
- qed_aio_complete(acb, ret);
130
.bdrv_reopen_prepare = vdi_reopen_prepare,
29
- return;
131
+ .bdrv_child_perm = bdrv_format_default_perms,
30
- }
132
.bdrv_create = vdi_create,
31
+ int index, ret;
133
.bdrv_has_zero_init = bdrv_has_zero_init_1,
32
134
.bdrv_co_get_block_status = vdi_co_get_block_status,
33
index = qed_l1_index(s, acb->cur_pos);
135
diff --git a/block/vhdx.c b/block/vhdx.c
34
s->l1_table->offsets[index] = l2_table->offset;
136
index XXXXXXX..XXXXXXX 100644
35
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_l1_update(void *opaque, int ret)
137
--- a/block/vhdx.c
36
acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset);
138
+++ b/block/vhdx.c
37
assert(acb->request.l2_table != NULL);
139
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vhdx = {
38
140
.bdrv_open = vhdx_open,
39
- qed_aio_next_io(acb, ret);
141
.bdrv_close = vhdx_close,
40
+ return ret;
142
.bdrv_reopen_prepare = vhdx_reopen_prepare,
41
}
143
+ .bdrv_child_perm = bdrv_format_default_perms,
42
144
.bdrv_co_readv = vhdx_co_readv,
43
145
.bdrv_co_writev = vhdx_co_writev,
44
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
146
.bdrv_create = vhdx_create,
45
if (need_alloc) {
147
diff --git a/block/vmdk.c b/block/vmdk.c
46
/* Write out the whole new L2 table */
148
index XXXXXXX..XXXXXXX 100644
47
ret = qed_write_l2_table(s, &acb->request, 0, s->table_nelems, true);
149
--- a/block/vmdk.c
48
- qed_aio_write_l1_update(acb, ret);
150
+++ b/block/vmdk.c
49
+ if (ret) {
151
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vmdk = {
50
+ goto err;
152
.bdrv_open = vmdk_open,
51
+ }
153
.bdrv_check = vmdk_check,
52
+ ret = qed_aio_write_l1_update(acb);
154
.bdrv_reopen_prepare = vmdk_reopen_prepare,
53
+ qed_aio_next_io(acb, ret);
155
+ .bdrv_child_perm = bdrv_format_default_perms,
54
+
156
.bdrv_co_preadv = vmdk_co_preadv,
55
} else {
157
.bdrv_co_pwritev = vmdk_co_pwritev,
56
/* Write out only the updated part of the L2 table */
158
.bdrv_co_pwritev_compressed = vmdk_co_pwritev_compressed,
57
ret = qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters,
159
diff --git a/block/vpc.c b/block/vpc.c
160
index XXXXXXX..XXXXXXX 100644
161
--- a/block/vpc.c
162
+++ b/block/vpc.c
163
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vpc = {
164
.bdrv_open = vpc_open,
165
.bdrv_close = vpc_close,
166
.bdrv_reopen_prepare = vpc_reopen_prepare,
167
+ .bdrv_child_perm = bdrv_format_default_perms,
168
.bdrv_create = vpc_create,
169
170
.bdrv_co_preadv = vpc_co_preadv,
171
--
58
--
172
1.8.3.1
59
1.8.3.1
173
60
174
61
diff view generated by jsdifflib
1
Some devices allow a media change between read-only and read-write
1
Don't recurse into qed_aio_next_io() and qed_aio_complete() here, but
2
media. They need to adapt the permissions in their .change_media_cb()
2
just return an error code and let the caller handle it.
3
implementation, which can fail. So add an Error parameter to the
4
function.
5
3
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Acked-by: Fam Zheng <famz@redhat.com>
9
---
6
---
10
block/block-backend.c | 20 +++++++++++++++-----
7
block/qed.c | 43 ++++++++++++++++++++++++++-----------------
11
blockdev.c | 19 +++++++++++++++----
8
1 file changed, 26 insertions(+), 17 deletions(-)
12
hw/block/fdc.c | 2 +-
13
hw/ide/core.c | 2 +-
14
hw/scsi/scsi-disk.c | 2 +-
15
hw/sd/sd.c | 2 +-
16
include/block/block_int.h | 2 +-
17
include/sysemu/block-backend.h | 2 +-
18
8 files changed, 36 insertions(+), 15 deletions(-)
19
9
20
diff --git a/block/block-backend.c b/block/block-backend.c
10
diff --git a/block/qed.c b/block/qed.c
21
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
22
--- a/block/block-backend.c
12
--- a/block/qed.c
23
+++ b/block/block-backend.c
13
+++ b/block/qed.c
24
@@ -XXX,XX +XXX,XX @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
14
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_l1_update(QEDAIOCB *acb)
25
15
/**
26
/*
16
* Update L2 table with new cluster offsets and write them out
27
* Notify @blk's attached device model of media change.
28
- * If @load is true, notify of media load.
29
- * Else, notify of media eject.
30
+ *
31
+ * If @load is true, notify of media load. This action can fail, meaning that
32
+ * the medium cannot be loaded. @errp is set then.
33
+ *
34
+ * If @load is false, notify of media eject. This can never fail.
35
+ *
36
* Also send DEVICE_TRAY_MOVED events as appropriate.
37
*/
17
*/
38
-void blk_dev_change_media_cb(BlockBackend *blk, bool load)
18
-static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
39
+void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
19
+static int qed_aio_write_l2_update(QEDAIOCB *acb, uint64_t offset)
40
{
20
{
41
if (blk->dev_ops && blk->dev_ops->change_media_cb) {
21
BDRVQEDState *s = acb_to_s(acb);
42
bool tray_was_open, tray_is_open;
22
bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1;
43
+ Error *local_err = NULL;
23
- int index;
44
24
-
45
assert(!blk->legacy_dev);
25
- if (ret) {
46
26
- goto err;
47
tray_was_open = blk_dev_is_tray_open(blk);
27
- }
48
- blk->dev_ops->change_media_cb(blk->dev_opaque, load);
28
+ int index, ret;
49
+ blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
29
50
+ if (local_err) {
30
if (need_alloc) {
51
+ assert(load == true);
31
qed_unref_l2_cache_entry(acb->request.l2_table);
52
+ error_propagate(errp, local_err);
32
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
53
+ return;
33
/* Write out the whole new L2 table */
34
ret = qed_write_l2_table(s, &acb->request, 0, s->table_nelems, true);
35
if (ret) {
36
- goto err;
37
+ return ret;
38
}
39
- ret = qed_aio_write_l1_update(acb);
40
- qed_aio_next_io(acb, ret);
41
-
42
+ return qed_aio_write_l1_update(acb);
43
} else {
44
/* Write out only the updated part of the L2 table */
45
ret = qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters,
46
false);
47
- qed_aio_next_io(acb, ret);
48
+ if (ret) {
49
+ return ret;
54
+ }
50
+ }
55
tray_is_open = blk_dev_is_tray_open(blk);
51
}
56
52
- return;
57
if (tray_was_open != tray_is_open) {
53
-
58
@@ -XXX,XX +XXX,XX @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load)
54
-err:
59
55
- qed_aio_complete(acb, ret);
60
static void blk_root_change_media(BdrvChild *child, bool load)
56
+ return 0;
61
{
62
- blk_dev_change_media_cb(child->opaque, load);
63
+ blk_dev_change_media_cb(child->opaque, load, NULL);
64
}
57
}
65
58
66
/*
59
/**
67
diff --git a/blockdev.c b/blockdev.c
60
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_main(void *opaque, int ret)
68
index XXXXXXX..XXXXXXX 100644
61
*/
69
--- a/blockdev.c
62
ret = bdrv_flush(s->bs->file->bs);
70
+++ b/blockdev.c
63
}
71
@@ -XXX,XX +XXX,XX @@ static int do_open_tray(const char *blk_name, const char *qdev_id,
64
- qed_aio_write_l2_update(acb, ret, acb->cur_cluster);
65
+ if (ret) {
66
+ goto err;
67
+ }
68
+ ret = qed_aio_write_l2_update(acb, acb->cur_cluster);
69
+ if (ret) {
70
+ goto err;
71
+ }
72
+ qed_aio_next_io(acb, 0);
72
}
73
}
73
74
+ return;
74
if (!locked || force) {
75
+
75
- blk_dev_change_media_cb(blk, false);
76
+err:
76
+ blk_dev_change_media_cb(blk, false, &error_abort);
77
+ qed_aio_complete(acb, ret);
77
}
78
}
78
79
79
if (locked && !force) {
80
/**
80
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_close_tray(bool has_device, const char *device,
81
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_zero_cluster(void *opaque, int ret)
81
Error **errp)
82
{
83
BlockBackend *blk;
84
+ Error *local_err = NULL;
85
86
device = has_device ? device : NULL;
87
id = has_id ? id : NULL;
88
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_close_tray(bool has_device, const char *device,
89
return;
82
return;
90
}
83
}
91
84
92
- blk_dev_change_media_cb(blk, true);
85
- qed_aio_write_l2_update(acb, 0, 1);
93
+ blk_dev_change_media_cb(blk, true, &local_err);
86
+ ret = qed_aio_write_l2_update(acb, 1);
94
+ if (local_err) {
87
+ if (ret < 0) {
95
+ error_propagate(errp, local_err);
88
+ qed_aio_complete(acb, ret);
96
+ return;
89
+ return;
97
+ }
90
+ }
91
+ qed_aio_next_io(acb, 0);
98
}
92
}
99
93
100
void qmp_x_blockdev_remove_medium(bool has_device, const char *device,
94
/**
101
@@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_remove_medium(bool has_device, const char *device,
102
* called at all); therefore, the medium needs to be ejected here.
103
* Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
104
* value passed here (i.e. false). */
105
- blk_dev_change_media_cb(blk, false);
106
+ blk_dev_change_media_cb(blk, false, &error_abort);
107
}
108
109
out:
110
@@ -XXX,XX +XXX,XX @@ out:
111
static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
112
BlockDriverState *bs, Error **errp)
113
{
114
+ Error *local_err = NULL;
115
bool has_device;
116
int ret;
117
118
@@ -XXX,XX +XXX,XX @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
119
* slot here.
120
* Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
121
* value passed here (i.e. true). */
122
- blk_dev_change_media_cb(blk, true);
123
+ blk_dev_change_media_cb(blk, true, &local_err);
124
+ if (local_err) {
125
+ error_propagate(errp, local_err);
126
+ blk_remove_bs(blk);
127
+ return;
128
+ }
129
}
130
}
131
132
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
133
index XXXXXXX..XXXXXXX 100644
134
--- a/hw/block/fdc.c
135
+++ b/hw/block/fdc.c
136
@@ -XXX,XX +XXX,XX @@ static void fd_revalidate(FDrive *drv)
137
}
138
}
139
140
-static void fd_change_cb(void *opaque, bool load)
141
+static void fd_change_cb(void *opaque, bool load, Error **errp)
142
{
143
FDrive *drive = opaque;
144
145
diff --git a/hw/ide/core.c b/hw/ide/core.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/hw/ide/core.c
148
+++ b/hw/ide/core.c
149
@@ -XXX,XX +XXX,XX @@ static void ide_cfata_metadata_write(IDEState *s)
150
}
151
152
/* called when the inserted state of the media has changed */
153
-static void ide_cd_change_cb(void *opaque, bool load)
154
+static void ide_cd_change_cb(void *opaque, bool load, Error **errp)
155
{
156
IDEState *s = opaque;
157
uint64_t nb_sectors;
158
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
159
index XXXXXXX..XXXXXXX 100644
160
--- a/hw/scsi/scsi-disk.c
161
+++ b/hw/scsi/scsi-disk.c
162
@@ -XXX,XX +XXX,XX @@ static void scsi_disk_resize_cb(void *opaque)
163
}
164
}
165
166
-static void scsi_cd_change_media_cb(void *opaque, bool load)
167
+static void scsi_cd_change_media_cb(void *opaque, bool load, Error **errp)
168
{
169
SCSIDiskState *s = opaque;
170
171
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
172
index XXXXXXX..XXXXXXX 100644
173
--- a/hw/sd/sd.c
174
+++ b/hw/sd/sd.c
175
@@ -XXX,XX +XXX,XX @@ static bool sd_get_readonly(SDState *sd)
176
return sd->wp_switch;
177
}
178
179
-static void sd_cardchange(void *opaque, bool load)
180
+static void sd_cardchange(void *opaque, bool load, Error **errp)
181
{
182
SDState *sd = opaque;
183
DeviceState *dev = DEVICE(sd);
184
diff --git a/include/block/block_int.h b/include/block/block_int.h
185
index XXXXXXX..XXXXXXX 100644
186
--- a/include/block/block_int.h
187
+++ b/include/block/block_int.h
188
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
189
uint64_t *nperm, uint64_t *nshared);
190
191
const char *bdrv_get_parent_name(const BlockDriverState *bs);
192
-void blk_dev_change_media_cb(BlockBackend *blk, bool load);
193
+void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp);
194
bool blk_dev_has_removable_media(BlockBackend *blk);
195
bool blk_dev_has_tray(BlockBackend *blk);
196
void blk_dev_eject_request(BlockBackend *blk, bool force);
197
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
198
index XXXXXXX..XXXXXXX 100644
199
--- a/include/sysemu/block-backend.h
200
+++ b/include/sysemu/block-backend.h
201
@@ -XXX,XX +XXX,XX @@ typedef struct BlockDevOps {
202
* changes. Sure would be useful if it did.
203
* Device models with removable media must implement this callback.
204
*/
205
- void (*change_media_cb)(void *opaque, bool load);
206
+ void (*change_media_cb)(void *opaque, bool load, Error **errp);
207
/*
208
* Runs when an eject request is issued from the monitor, the tray
209
* is closed, and the medium is locked.
210
--
95
--
211
1.8.3.1
96
1.8.3.1
212
97
213
98
diff view generated by jsdifflib
1
The correct permissions are relatively obvious here (and explained in
1
Don't recurse into qed_aio_next_io() and qed_aio_complete() here, but
2
code comments). For intermediate streaming, we need to reopen the top
2
just return an error code and let the caller handle it.
3
node read-write before creating the job now because the permissions
3
4
system catches attempts to get the BLK_PERM_WRITE_UNCHANGED permission
4
While refactoring qed_aio_write_alloc() to accomodate the change,
5
on a read-only node.
5
qed_aio_write_zero_cluster() ended up with a single line, so I chose to
6
inline that line and remove the function completely.
6
7
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Acked-by: Fam Zheng <famz@redhat.com>
10
---
10
---
11
block/stream.c | 39 +++++++++++++++++++++++++++------------
11
block/qed.c | 58 +++++++++++++++++++++-------------------------------------
12
1 file changed, 27 insertions(+), 12 deletions(-)
12
1 file changed, 21 insertions(+), 37 deletions(-)
13
13
14
diff --git a/block/stream.c b/block/stream.c
14
diff --git a/block/qed.c b/block/qed.c
15
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/stream.c
16
--- a/block/qed.c
17
+++ b/block/stream.c
17
+++ b/block/qed.c
18
@@ -XXX,XX +XXX,XX @@ static void stream_complete(BlockJob *job, void *opaque)
18
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_main(QEDAIOCB *acb)
19
19
/**
20
/* Reopen the image back in read-only mode if necessary */
20
* Populate untouched regions of new data cluster
21
if (s->bs_flags != bdrv_get_flags(bs)) {
21
*/
22
+ /* Give up write permissions before making it read-only */
22
-static void qed_aio_write_cow(void *opaque, int ret)
23
+ blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
23
+static int qed_aio_write_cow(QEDAIOCB *acb)
24
bdrv_reopen(bs, s->bs_flags, NULL);
24
{
25
- QEDAIOCB *acb = opaque;
26
BDRVQEDState *s = acb_to_s(acb);
27
uint64_t start, len, offset;
28
+ int ret;
29
30
/* Populate front untouched region of new data cluster */
31
start = qed_start_of_cluster(s, acb->cur_pos);
32
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_cow(void *opaque, int ret)
33
34
trace_qed_aio_write_prefill(s, acb, start, len, acb->cur_cluster);
35
ret = qed_copy_from_backing_file(s, start, len, acb->cur_cluster);
36
- if (ret) {
37
- qed_aio_complete(acb, ret);
38
- return;
39
+ if (ret < 0) {
40
+ return ret;
25
}
41
}
26
42
27
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
43
/* Populate back untouched region of new data cluster */
28
BlockDriverState *iter;
44
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_cow(void *opaque, int ret)
29
int orig_bs_flags;
45
30
46
trace_qed_aio_write_postfill(s, acb, start, len, offset);
31
- /* FIXME Use real permissions */
47
ret = qed_copy_from_backing_file(s, start, len, offset);
32
- s = block_job_create(job_id, &stream_job_driver, bs, 0, BLK_PERM_ALL,
48
- if (ret) {
33
- speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
49
- qed_aio_complete(acb, ret);
34
- if (!s) {
35
- return;
50
- return;
36
- }
51
- }
37
-
52
-
38
/* Make sure that the image is opened in read-write mode */
53
- ret = qed_aio_write_main(acb);
39
orig_bs_flags = bdrv_get_flags(bs);
54
if (ret < 0) {
40
if (!(orig_bs_flags & BDRV_O_RDWR)) {
55
- qed_aio_complete(acb, ret);
41
if (bdrv_reopen(bs, orig_bs_flags | BDRV_O_RDWR, errp) != 0) {
56
- return;
42
- block_job_unref(&s->common);
57
+ return ret;
58
}
59
- qed_aio_next_io(acb, 0);
60
+
61
+ return qed_aio_write_main(acb);
62
}
63
64
/**
65
@@ -XXX,XX +XXX,XX @@ static bool qed_should_set_need_check(BDRVQEDState *s)
66
return !(s->header.features & QED_F_NEED_CHECK);
67
}
68
69
-static void qed_aio_write_zero_cluster(void *opaque, int ret)
70
-{
71
- QEDAIOCB *acb = opaque;
72
-
73
- if (ret) {
74
- qed_aio_complete(acb, ret);
75
- return;
76
- }
77
-
78
- ret = qed_aio_write_l2_update(acb, 1);
79
- if (ret < 0) {
80
- qed_aio_complete(acb, ret);
81
- return;
82
- }
83
- qed_aio_next_io(acb, 0);
84
-}
85
-
86
/**
87
* Write new data cluster
88
*
89
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_zero_cluster(void *opaque, int ret)
90
static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
91
{
92
BDRVQEDState *s = acb_to_s(acb);
93
- BlockCompletionFunc *cb;
94
int ret;
95
96
/* Cancel timer when the first allocating request comes in */
97
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
98
qed_aio_start_io(acb);
43
return;
99
return;
44
}
100
}
101
-
102
- cb = qed_aio_write_zero_cluster;
103
} else {
104
- cb = qed_aio_write_cow;
105
acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters);
45
}
106
}
46
107
47
- /* Block all intermediate nodes between bs and base, because they
108
if (qed_should_set_need_check(s)) {
48
- * will disappear from the chain after this operation */
109
s->header.features |= QED_F_NEED_CHECK;
49
+ /* Prevent concurrent jobs trying to modify the graph structure here, we
110
ret = qed_write_header(s);
50
+ * already have our own plans. Also don't allow resize as the image size is
111
- cb(acb, ret);
51
+ * queried only at the job start and then cached. */
112
+ if (ret < 0) {
52
+ s = block_job_create(job_id, &stream_job_driver, bs,
113
+ qed_aio_complete(acb, ret);
53
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
114
+ return;
54
+ BLK_PERM_GRAPH_MOD,
115
+ }
55
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
56
+ BLK_PERM_WRITE,
57
+ speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
58
+ if (!s) {
59
+ goto fail;
60
+ }
116
+ }
61
+
117
+
62
+ /* Block all intermediate nodes between bs and base, because they will
118
+ if (acb->flags & QED_AIOCB_ZERO) {
63
+ * disappear from the chain after this operation. The streaming job reads
119
+ ret = qed_aio_write_l2_update(acb, 1);
64
+ * every block only once, assuming that it doesn't change, so block writes
120
} else {
65
+ * and resizes. */
121
- cb(acb, 0);
66
for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) {
122
+ ret = qed_aio_write_cow(acb);
67
- /* FIXME Use real permissions */
68
block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
69
- BLK_PERM_ALL, &error_abort);
70
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED,
71
+ &error_abort);
72
}
123
}
73
124
+ if (ret < 0) {
74
s->base = base;
125
+ qed_aio_complete(acb, ret);
75
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
126
+ return;
76
s->on_error = on_error;
77
trace_stream_start(bs, base, s);
78
block_job_start(&s->common);
79
+ return;
80
+
81
+fail:
82
+ if (orig_bs_flags != bdrv_get_flags(bs)) {
83
+ bdrv_reopen(bs, s->bs_flags, NULL);
84
+ }
127
+ }
128
+ qed_aio_next_io(acb, 0);
85
}
129
}
130
131
/**
86
--
132
--
87
1.8.3.1
133
1.8.3.1
88
134
89
135
diff view generated by jsdifflib
1
Block jobs don't actually do I/O through the the reference they create
1
Don't recurse into qed_aio_next_io() and qed_aio_complete() here, but
2
with block_job_add_bdrv(), but they might want to use the permisssion
2
just return an error code and let the caller handle it.
3
system to express what the block job does to intermediate nodes. This
4
adds permissions to block_job_add_bdrv() to provide the means to request
5
permissions.
6
3
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Acked-by: Fam Zheng <famz@redhat.com>
10
---
6
---
11
block/backup.c | 4 +++-
7
block/qed.c | 43 ++++++++++++++++++++-----------------------
12
block/commit.c | 8 ++++++--
8
1 file changed, 20 insertions(+), 23 deletions(-)
13
block/mirror.c | 9 +++++++--
14
block/stream.c | 4 +++-
15
blockjob.c | 36 ++++++++++++++++++++++++++++++------
16
include/block/blockjob.h | 5 ++++-
17
6 files changed, 53 insertions(+), 13 deletions(-)
18
9
19
diff --git a/block/backup.c b/block/backup.c
10
diff --git a/block/qed.c b/block/qed.c
20
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
21
--- a/block/backup.c
12
--- a/block/qed.c
22
+++ b/block/backup.c
13
+++ b/block/qed.c
23
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
14
@@ -XXX,XX +XXX,XX @@ static bool qed_should_set_need_check(BDRVQEDState *s)
24
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
15
*
16
* This path is taken when writing to previously unallocated clusters.
17
*/
18
-static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
19
+static int qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
20
{
21
BDRVQEDState *s = acb_to_s(acb);
22
int ret;
23
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
25
}
24
}
26
25
if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs) ||
27
- block_job_add_bdrv(&job->common, target);
26
s->allocating_write_reqs_plugged) {
28
+ /* FIXME Use real permissions */
27
- return; /* wait for existing request to finish */
29
+ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
28
+ return -EINPROGRESS; /* wait for existing request to finish */
30
+ &error_abort);
31
job->common.len = len;
32
block_job_txn_add_job(txn, &job->common);
33
34
diff --git a/block/commit.c b/block/commit.c
35
index XXXXXXX..XXXXXXX 100644
36
--- a/block/commit.c
37
+++ b/block/commit.c
38
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
39
* disappear from the chain after this operation. */
40
assert(bdrv_chain_contains(top, base));
41
for (iter = top; iter != backing_bs(base); iter = backing_bs(iter)) {
42
- block_job_add_bdrv(&s->common, iter);
43
+ /* FIXME Use real permissions */
44
+ block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
45
+ BLK_PERM_ALL, &error_abort);
46
}
29
}
47
/* overlay_bs must be blocked because it needs to be modified to
30
48
* update the backing image string, but if it's the root node then
31
acb->cur_nclusters = qed_bytes_to_clusters(s,
49
* don't block it again */
32
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
50
if (bs != overlay_bs) {
33
if (acb->flags & QED_AIOCB_ZERO) {
51
- block_job_add_bdrv(&s->common, overlay_bs);
34
/* Skip ahead if the clusters are already zero */
52
+ /* FIXME Use real permissions */
35
if (acb->find_cluster_ret == QED_CLUSTER_ZERO) {
53
+ block_job_add_bdrv(&s->common, "overlay of top", overlay_bs, 0,
36
- qed_aio_start_io(acb);
54
+ BLK_PERM_ALL, &error_abort);
37
- return;
55
}
38
+ return 0;
56
39
}
57
/* FIXME Use real permissions */
40
} else {
58
diff --git a/block/mirror.c b/block/mirror.c
41
acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters);
59
index XXXXXXX..XXXXXXX 100644
42
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
60
--- a/block/mirror.c
43
s->header.features |= QED_F_NEED_CHECK;
61
+++ b/block/mirror.c
44
ret = qed_write_header(s);
62
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
45
if (ret < 0) {
63
return;
46
- qed_aio_complete(acb, ret);
64
}
47
- return;
65
48
+ return ret;
66
- block_job_add_bdrv(&s->common, target);
67
+ /* FIXME Use real permissions */
68
+ block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL,
69
+ &error_abort);
70
+
71
/* In commit_active_start() all intermediate nodes disappear, so
72
* any jobs in them must be blocked */
73
if (bdrv_chain_contains(bs, target)) {
74
BlockDriverState *iter;
75
for (iter = backing_bs(bs); iter != target; iter = backing_bs(iter)) {
76
- block_job_add_bdrv(&s->common, iter);
77
+ /* FIXME Use real permissions */
78
+ block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
79
+ BLK_PERM_ALL, &error_abort);
80
}
49
}
81
}
50
}
82
51
83
diff --git a/block/stream.c b/block/stream.c
52
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
84
index XXXXXXX..XXXXXXX 100644
53
ret = qed_aio_write_cow(acb);
85
--- a/block/stream.c
86
+++ b/block/stream.c
87
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
88
/* Block all intermediate nodes between bs and base, because they
89
* will disappear from the chain after this operation */
90
for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) {
91
- block_job_add_bdrv(&s->common, iter);
92
+ /* FIXME Use real permissions */
93
+ block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
94
+ BLK_PERM_ALL, &error_abort);
95
}
54
}
96
55
if (ret < 0) {
97
s->base = base;
56
- qed_aio_complete(acb, ret);
98
diff --git a/blockjob.c b/blockjob.c
57
- return;
99
index XXXXXXX..XXXXXXX 100644
58
+ return ret;
100
--- a/blockjob.c
59
}
101
+++ b/blockjob.c
60
- qed_aio_next_io(acb, 0);
102
@@ -XXX,XX +XXX,XX @@ struct BlockJobTxn {
103
104
static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
105
106
+static char *child_job_get_parent_desc(BdrvChild *c)
107
+{
108
+ BlockJob *job = c->opaque;
109
+ return g_strdup_printf("%s job '%s'",
110
+ BlockJobType_lookup[job->driver->job_type],
111
+ job->id);
112
+}
113
+
114
+static const BdrvChildRole child_job = {
115
+ .get_parent_desc = child_job_get_parent_desc,
116
+ .stay_at_node = true,
117
+};
118
+
119
BlockJob *block_job_next(BlockJob *job)
120
{
121
if (!job) {
122
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
123
block_job_unref(job);
124
}
125
126
-void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs)
127
+int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
128
+ uint64_t perm, uint64_t shared_perm, Error **errp)
129
{
130
- job->nodes = g_slist_prepend(job->nodes, bs);
131
+ BdrvChild *c;
132
+
133
+ c = bdrv_root_attach_child(bs, name, &child_job, perm, shared_perm,
134
+ job, errp);
135
+ if (c == NULL) {
136
+ return -EPERM;
137
+ }
138
+
139
+ job->nodes = g_slist_prepend(job->nodes, c);
140
bdrv_ref(bs);
141
bdrv_op_block_all(bs, job->blocker);
142
+
143
+ return 0;
61
+ return 0;
144
}
62
}
145
63
146
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
64
/**
147
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
65
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
148
job = g_malloc0(driver->instance_size);
66
*
149
error_setg(&job->blocker, "block device is in use by block job: %s",
67
* This path is taken when writing to already allocated clusters.
150
BlockJobType_lookup[driver->job_type]);
68
*/
151
- block_job_add_bdrv(job, bs);
69
-static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
152
+ block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
70
+static int qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
153
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
71
{
154
72
- int ret;
155
job->driver = driver;
73
-
156
@@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job)
74
/* Allocate buffer for zero writes */
157
BlockDriverState *bs = blk_bs(job->blk);
75
if (acb->flags & QED_AIOCB_ZERO) {
158
bs->job = NULL;
76
struct iovec *iov = acb->qiov->iov;
159
for (l = job->nodes; l; l = l->next) {
77
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
160
- bs = l->data;
78
if (!iov->iov_base) {
161
- bdrv_op_unblock_all(bs, job->blocker);
79
iov->iov_base = qemu_try_blockalign(acb->common.bs, iov->iov_len);
162
- bdrv_unref(bs);
80
if (iov->iov_base == NULL) {
163
+ BdrvChild *c = l->data;
81
- qed_aio_complete(acb, -ENOMEM);
164
+ bdrv_op_unblock_all(c->bs, job->blocker);
82
- return;
165
+ bdrv_root_unref_child(c);
83
+ return -ENOMEM;
84
}
85
memset(iov->iov_base, 0, iov->iov_len);
166
}
86
}
167
g_slist_free(job->nodes);
87
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
168
blk_remove_aio_context_notifier(job->blk,
88
qemu_iovec_concat(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
169
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
89
170
index XXXXXXX..XXXXXXX 100644
90
/* Do the actual write */
171
--- a/include/block/blockjob.h
91
- ret = qed_aio_write_main(acb);
172
+++ b/include/block/blockjob.h
92
- if (ret < 0) {
173
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id);
93
- qed_aio_complete(acb, ret);
94
- return;
95
- }
96
- qed_aio_next_io(acb, 0);
97
+ return qed_aio_write_main(acb);
98
}
99
174
/**
100
/**
175
* block_job_add_bdrv:
101
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_data(void *opaque, int ret,
176
* @job: A block job
102
177
+ * @name: The name to assign to the new BdrvChild
103
switch (ret) {
178
* @bs: A BlockDriverState that is involved in @job
104
case QED_CLUSTER_FOUND:
179
+ * @perm, @shared_perm: Permissions to request on the node
105
- qed_aio_write_inplace(acb, offset, len);
180
*
106
+ ret = qed_aio_write_inplace(acb, offset, len);
181
* Add @bs to the list of BlockDriverState that are involved in
107
break;
182
* @job. This means that all operations will be blocked on @bs while
108
183
* @job exists.
109
case QED_CLUSTER_L2:
184
*/
110
case QED_CLUSTER_L1:
185
-void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs);
111
case QED_CLUSTER_ZERO:
186
+int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
112
- qed_aio_write_alloc(acb, len);
187
+ uint64_t perm, uint64_t shared_perm, Error **errp);
113
+ ret = qed_aio_write_alloc(acb, len);
114
break;
115
116
default:
117
- qed_aio_complete(acb, ret);
118
+ assert(ret < 0);
119
break;
120
}
121
+
122
+ if (ret < 0) {
123
+ if (ret != -EINPROGRESS) {
124
+ qed_aio_complete(acb, ret);
125
+ }
126
+ return;
127
+ }
128
+ qed_aio_next_io(acb, 0);
129
}
188
130
189
/**
131
/**
190
* block_job_set_speed:
191
--
132
--
192
1.8.3.1
133
1.8.3.1
193
134
194
135
diff view generated by jsdifflib
1
This functions creates a BlockBackend internally, so the block jobs need
1
All callers pass ret = 0, so we can just remove it.
2
to tell it what they want to do with the BB.
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>
4
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
7
---
5
---
8
block/backup.c | 5 +++--
6
block/qed.c | 17 ++++++-----------
9
block/commit.c | 5 +++--
7
1 file changed, 6 insertions(+), 11 deletions(-)
10
block/mirror.c | 5 +++--
11
block/stream.c | 5 +++--
12
blockjob.c | 6 +++---
13
include/block/blockjob_int.h | 4 +++-
14
tests/test-blockjob-txn.c | 6 +++---
15
tests/test-blockjob.c | 5 +++--
16
8 files changed, 24 insertions(+), 17 deletions(-)
17
8
18
diff --git a/block/backup.c b/block/backup.c
9
diff --git a/block/qed.c b/block/qed.c
19
index XXXXXXX..XXXXXXX 100644
10
index XXXXXXX..XXXXXXX 100644
20
--- a/block/backup.c
11
--- a/block/qed.c
21
+++ b/block/backup.c
12
+++ b/block/qed.c
22
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
13
@@ -XXX,XX +XXX,XX @@ static CachedL2Table *qed_new_l2_table(BDRVQEDState *s)
23
goto error;
14
return l2_table;
15
}
16
17
-static void qed_aio_next_io(QEDAIOCB *acb, int ret);
18
+static void qed_aio_next_io(QEDAIOCB *acb);
19
20
static void qed_aio_start_io(QEDAIOCB *acb)
21
{
22
- qed_aio_next_io(acb, 0);
23
+ qed_aio_next_io(acb);
24
}
25
26
static void qed_plug_allocating_write_reqs(BDRVQEDState *s)
27
@@ -XXX,XX +XXX,XX @@ static int qed_aio_read_data(void *opaque, int ret, uint64_t offset, size_t len)
28
/**
29
* Begin next I/O or complete the request
30
*/
31
-static void qed_aio_next_io(QEDAIOCB *acb, int ret)
32
+static void qed_aio_next_io(QEDAIOCB *acb)
33
{
34
BDRVQEDState *s = acb_to_s(acb);
35
uint64_t offset;
36
size_t len;
37
+ int ret;
38
39
- trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size);
40
+ trace_qed_aio_next_io(s, acb, 0, acb->cur_pos + acb->cur_qiov.size);
41
42
if (acb->backing_qiov) {
43
qemu_iovec_destroy(acb->backing_qiov);
44
@@ -XXX,XX +XXX,XX @@ static void qed_aio_next_io(QEDAIOCB *acb, int ret)
45
acb->backing_qiov = NULL;
24
}
46
}
25
47
26
- job = block_job_create(job_id, &backup_job_driver, bs, speed,
48
- /* Handle I/O error */
27
- creation_flags, cb, opaque, errp);
49
- if (ret) {
28
+ /* FIXME Use real permissions */
50
- qed_aio_complete(acb, ret);
29
+ job = block_job_create(job_id, &backup_job_driver, bs, 0, BLK_PERM_ALL,
51
- return;
30
+ speed, creation_flags, cb, opaque, errp);
52
- }
31
if (!job) {
53
-
32
goto error;
54
acb->qiov_offset += acb->cur_qiov.size;
33
}
55
acb->cur_pos += acb->cur_qiov.size;
34
diff --git a/block/commit.c b/block/commit.c
56
qemu_iovec_reset(&acb->cur_qiov);
35
index XXXXXXX..XXXXXXX 100644
57
@@ -XXX,XX +XXX,XX @@ static void qed_aio_next_io(QEDAIOCB *acb, int ret)
36
--- a/block/commit.c
58
}
37
+++ b/block/commit.c
38
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
39
return;
59
return;
40
}
60
}
41
61
- qed_aio_next_io(acb, 0);
42
- s = block_job_create(job_id, &commit_job_driver, bs, speed,
62
+ qed_aio_next_io(acb);
43
- BLOCK_JOB_DEFAULT, NULL, NULL, errp);
44
+ /* FIXME Use real permissions */
45
+ s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
46
+ speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
47
if (!s) {
48
return;
49
}
50
diff --git a/block/mirror.c b/block/mirror.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/block/mirror.c
53
+++ b/block/mirror.c
54
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
55
buf_size = DEFAULT_MIRROR_BUF_SIZE;
56
}
57
58
- s = block_job_create(job_id, driver, bs, speed, creation_flags,
59
- cb, opaque, errp);
60
+ /* FIXME Use real permissions */
61
+ s = block_job_create(job_id, driver, bs, 0, BLK_PERM_ALL, speed,
62
+ creation_flags, cb, opaque, errp);
63
if (!s) {
64
return;
65
}
66
diff --git a/block/stream.c b/block/stream.c
67
index XXXXXXX..XXXXXXX 100644
68
--- a/block/stream.c
69
+++ b/block/stream.c
70
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
71
BlockDriverState *iter;
72
int orig_bs_flags;
73
74
- s = block_job_create(job_id, &stream_job_driver, bs, speed,
75
- BLOCK_JOB_DEFAULT, NULL, NULL, errp);
76
+ /* FIXME Use real permissions */
77
+ s = block_job_create(job_id, &stream_job_driver, bs, 0, BLK_PERM_ALL,
78
+ speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
79
if (!s) {
80
return;
81
}
82
diff --git a/blockjob.c b/blockjob.c
83
index XXXXXXX..XXXXXXX 100644
84
--- a/blockjob.c
85
+++ b/blockjob.c
86
@@ -XXX,XX +XXX,XX @@ void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs)
87
}
63
}
88
64
89
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
65
static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
90
- BlockDriverState *bs, int64_t speed, int flags,
91
+ BlockDriverState *bs, uint64_t perm,
92
+ uint64_t shared_perm, int64_t speed, int flags,
93
BlockCompletionFunc *cb, void *opaque, Error **errp)
94
{
95
BlockBackend *blk;
96
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
97
}
98
}
99
100
- /* FIXME Use real permissions */
101
- blk = blk_new(0, BLK_PERM_ALL);
102
+ blk = blk_new(perm, shared_perm);
103
ret = blk_insert_bs(blk, bs, errp);
104
if (ret < 0) {
105
blk_unref(blk);
106
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
107
index XXXXXXX..XXXXXXX 100644
108
--- a/include/block/blockjob_int.h
109
+++ b/include/block/blockjob_int.h
110
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
111
* generated automatically.
112
* @job_type: The class object for the newly-created job.
113
* @bs: The block
114
+ * @perm, @shared_perm: Permissions to request for @bs
115
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
116
* @cb: Completion function for the job.
117
* @opaque: Opaque pointer value passed to @cb.
118
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
119
* called from a wrapper that is specific to the job type.
120
*/
121
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
122
- BlockDriverState *bs, int64_t speed, int flags,
123
+ BlockDriverState *bs, uint64_t perm,
124
+ uint64_t shared_perm, int64_t speed, int flags,
125
BlockCompletionFunc *cb, void *opaque, Error **errp);
126
127
/**
128
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/tests/test-blockjob-txn.c
131
+++ b/tests/test-blockjob-txn.c
132
@@ -XXX,XX +XXX,XX @@ static BlockJob *test_block_job_start(unsigned int iterations,
133
g_assert_nonnull(bs);
134
135
snprintf(job_id, sizeof(job_id), "job%u", counter++);
136
- s = block_job_create(job_id, &test_block_job_driver, bs, 0,
137
- BLOCK_JOB_DEFAULT, test_block_job_cb,
138
- data, &error_abort);
139
+ s = block_job_create(job_id, &test_block_job_driver, bs,
140
+ 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT,
141
+ test_block_job_cb, data, &error_abort);
142
s->iterations = iterations;
143
s->use_timer = use_timer;
144
s->rc = rc;
145
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/tests/test-blockjob.c
148
+++ b/tests/test-blockjob.c
149
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
150
BlockJob *job;
151
Error *errp = NULL;
152
153
- job = block_job_create(id, &test_block_job_driver, blk_bs(blk), 0,
154
- BLOCK_JOB_DEFAULT, block_job_cb, NULL, &errp);
155
+ job = block_job_create(id, &test_block_job_driver, blk_bs(blk),
156
+ 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, block_job_cb,
157
+ NULL, &errp);
158
if (should_succeed) {
159
g_assert_null(errp);
160
g_assert_nonnull(job);
161
--
66
--
162
1.8.3.1
67
1.8.3.1
163
68
164
69
diff view generated by jsdifflib
1
The BlockBackend can now store the permissions that its user requires.
1
Most of the qed code is now synchronous and matches the coroutine model.
2
This is necessary because nodes can be ejected from or inserted into a
2
One notable exception is the serialisation between requests which can
3
BlockBackend and all of these operations must make sure that the user
3
still schedule a callback. Before we can replace this with coroutine
4
still gets what it requested initially.
4
locks, let's convert the driver's external interfaces to the coroutine
5
versions.
6
7
We need to be careful to handle both requests that call the completion
8
callback directly from the calling coroutine (i.e. fully synchronous
9
code) and requests that involve some callback, so that we need to yield
10
and wait for the completion callback coming from outside the coroutine.
5
11
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
8
Acked-by: Fam Zheng <famz@redhat.com>
14
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
15
---
10
block/block-backend.c | 27 +++++++++++++++++++++++++++
16
block/qed.c | 97 ++++++++++++++++++++++++++-----------------------------------
11
include/sysemu/block-backend.h | 2 ++
17
1 file changed, 42 insertions(+), 55 deletions(-)
12
2 files changed, 29 insertions(+)
13
18
14
diff --git a/block/block-backend.c b/block/block-backend.c
19
diff --git a/block/qed.c b/block/qed.c
15
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
16
--- a/block/block-backend.c
21
--- a/block/qed.c
17
+++ b/block/block-backend.c
22
+++ b/block/qed.c
18
@@ -XXX,XX +XXX,XX @@ struct BlockBackend {
23
@@ -XXX,XX +XXX,XX @@ static void qed_aio_next_io(QEDAIOCB *acb)
19
bool iostatus_enabled;
20
BlockDeviceIoStatus iostatus;
21
22
+ uint64_t perm;
23
+ uint64_t shared_perm;
24
+
25
bool allow_write_beyond_eof;
26
27
NotifierList remove_bs_notifiers, insert_bs_notifiers;
28
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(void)
29
30
blk = g_new0(BlockBackend, 1);
31
blk->refcnt = 1;
32
+ blk->perm = 0;
33
+ blk->shared_perm = BLK_PERM_ALL;
34
blk_set_enable_write_cache(blk, true);
35
36
qemu_co_queue_init(&blk->public.throttled_reqs[0]);
37
@@ -XXX,XX +XXX,XX @@ void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs)
38
}
24
}
39
}
25
}
40
26
41
+/*
27
-static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
42
+ * Sets the permission bitmasks that the user of the BlockBackend needs.
28
- int64_t sector_num,
43
+ */
29
- QEMUIOVector *qiov, int nb_sectors,
44
+int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
30
- BlockCompletionFunc *cb,
45
+ Error **errp)
31
- void *opaque, int flags)
32
+typedef struct QEDRequestCo {
33
+ Coroutine *co;
34
+ bool done;
35
+ int ret;
36
+} QEDRequestCo;
37
+
38
+static void qed_co_request_cb(void *opaque, int ret)
39
{
40
- QEDAIOCB *acb = qemu_aio_get(&qed_aiocb_info, bs, cb, opaque);
41
+ QEDRequestCo *co = opaque;
42
43
- trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors,
44
- opaque, flags);
45
+ co->done = true;
46
+ co->ret = ret;
47
+ qemu_coroutine_enter_if_inactive(co->co);
48
+}
49
+
50
+static int coroutine_fn qed_co_request(BlockDriverState *bs, int64_t sector_num,
51
+ QEMUIOVector *qiov, int nb_sectors,
52
+ int flags)
46
+{
53
+{
47
+ int ret;
54
+ QEDRequestCo co = {
55
+ .co = qemu_coroutine_self(),
56
+ .done = false,
57
+ };
58
+ QEDAIOCB *acb = qemu_aio_get(&qed_aiocb_info, bs, qed_co_request_cb, &co);
48
+
59
+
49
+ if (blk->root) {
60
+ trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors, &co, flags);
50
+ ret = bdrv_child_try_set_perm(blk->root, perm, shared_perm, errp);
61
51
+ if (ret < 0) {
62
acb->flags = flags;
52
+ return ret;
63
acb->qiov = qiov;
53
+ }
64
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
65
66
/* Start request */
67
qed_aio_start_io(acb);
68
- return &acb->common;
69
-}
70
71
-static BlockAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs,
72
- int64_t sector_num,
73
- QEMUIOVector *qiov, int nb_sectors,
74
- BlockCompletionFunc *cb,
75
- void *opaque)
76
-{
77
- return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
78
+ if (!co.done) {
79
+ qemu_coroutine_yield();
54
+ }
80
+ }
55
+
81
+
56
+ blk->perm = perm;
82
+ return co.ret;
57
+ blk->shared_perm = shared_perm;
83
}
58
+
84
59
+ return 0;
85
-static BlockAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
60
+}
86
- int64_t sector_num,
61
+
87
- QEMUIOVector *qiov, int nb_sectors,
62
static int blk_do_attach_dev(BlockBackend *blk, void *dev)
88
- BlockCompletionFunc *cb,
89
- void *opaque)
90
+static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs,
91
+ int64_t sector_num, int nb_sectors,
92
+ QEMUIOVector *qiov)
63
{
93
{
64
if (blk->dev) {
94
- return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb,
65
@@ -XXX,XX +XXX,XX @@ void blk_detach_dev(BlockBackend *blk, void *dev)
95
- opaque, QED_AIOCB_WRITE);
66
blk->dev_ops = NULL;
96
+ return qed_co_request(bs, sector_num, qiov, nb_sectors, 0);
67
blk->dev_opaque = NULL;
68
blk->guest_block_size = 512;
69
+ blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);
70
blk_unref(blk);
71
}
97
}
72
98
73
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
99
-typedef struct {
74
index XXXXXXX..XXXXXXX 100644
100
- Coroutine *co;
75
--- a/include/sysemu/block-backend.h
101
- int ret;
76
+++ b/include/sysemu/block-backend.h
102
- bool done;
77
@@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk);
103
-} QEDWriteZeroesCB;
78
void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs);
104
-
79
bool bdrv_has_blk(BlockDriverState *bs);
105
-static void coroutine_fn qed_co_pwrite_zeroes_cb(void *opaque, int ret)
80
bool bdrv_is_root_node(BlockDriverState *bs);
106
+static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs,
81
+int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm,
107
+ int64_t sector_num, int nb_sectors,
82
+ Error **errp);
108
+ QEMUIOVector *qiov)
83
109
{
84
void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow);
110
- QEDWriteZeroesCB *cb = opaque;
85
void blk_iostatus_enable(BlockBackend *blk);
111
-
112
- cb->done = true;
113
- cb->ret = ret;
114
- if (cb->co) {
115
- aio_co_wake(cb->co);
116
- }
117
+ return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE);
118
}
119
120
static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
121
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
122
int count,
123
BdrvRequestFlags flags)
124
{
125
- BlockAIOCB *blockacb;
126
BDRVQEDState *s = bs->opaque;
127
- QEDWriteZeroesCB cb = { .done = false };
128
QEMUIOVector qiov;
129
struct iovec iov;
130
131
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
132
iov.iov_len = count;
133
134
qemu_iovec_init_external(&qiov, &iov, 1);
135
- blockacb = qed_aio_setup(bs, offset >> BDRV_SECTOR_BITS, &qiov,
136
- count >> BDRV_SECTOR_BITS,
137
- qed_co_pwrite_zeroes_cb, &cb,
138
- QED_AIOCB_WRITE | QED_AIOCB_ZERO);
139
- if (!blockacb) {
140
- return -EIO;
141
- }
142
- if (!cb.done) {
143
- cb.co = qemu_coroutine_self();
144
- qemu_coroutine_yield();
145
- }
146
- assert(cb.done);
147
- return cb.ret;
148
+ return qed_co_request(bs, offset >> BDRV_SECTOR_BITS, &qiov,
149
+ count >> BDRV_SECTOR_BITS,
150
+ QED_AIOCB_WRITE | QED_AIOCB_ZERO);
151
}
152
153
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
154
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = {
155
.bdrv_create = bdrv_qed_create,
156
.bdrv_has_zero_init = bdrv_has_zero_init_1,
157
.bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
158
- .bdrv_aio_readv = bdrv_qed_aio_readv,
159
- .bdrv_aio_writev = bdrv_qed_aio_writev,
160
+ .bdrv_co_readv = bdrv_qed_co_readv,
161
+ .bdrv_co_writev = bdrv_qed_co_writev,
162
.bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes,
163
.bdrv_truncate = bdrv_qed_truncate,
164
.bdrv_getlength = bdrv_qed_getlength,
86
--
165
--
87
1.8.3.1
166
1.8.3.1
88
167
89
168
diff view generated by jsdifflib
1
The backup block job doesn't have very complicated requirements: It
1
Now that we're running in coroutine context, the ad-hoc serialisation
2
needs to read from the source and write to the target, but it's fine
2
code (which drops a request that has to wait out of coroutine context)
3
with either side being changed. The only restriction is that we can't
3
can be replaced by a CoQueue.
4
resize the image because the job uses a cached value.
5
4
6
qemu-iotests 055 needs to be changed because it used a target which was
5
This means that when we resume a serialised request, it is running in
7
already attached to a virtio-blk device. The permission system correctly
6
coroutine context again and its I/O isn't blocking any more.
8
forbids this (virtio-blk can't accept another writer with its default
9
share-rw=off).
10
7
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Acked-by: Fam Zheng <famz@redhat.com>
14
---
10
---
15
block/backup.c | 15 ++++++++++-----
11
block/qed.c | 49 +++++++++++++++++--------------------------------
16
tests/qemu-iotests/055 | 11 +++++++----
12
block/qed.h | 3 ++-
17
2 files changed, 17 insertions(+), 9 deletions(-)
13
2 files changed, 19 insertions(+), 33 deletions(-)
18
14
19
diff --git a/block/backup.c b/block/backup.c
15
diff --git a/block/qed.c b/block/qed.c
20
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
21
--- a/block/backup.c
17
--- a/block/qed.c
22
+++ b/block/backup.c
18
+++ b/block/qed.c
23
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
19
@@ -XXX,XX +XXX,XX @@ static void qed_plug_allocating_write_reqs(BDRVQEDState *s)
24
goto error;
20
21
static void qed_unplug_allocating_write_reqs(BDRVQEDState *s)
22
{
23
- QEDAIOCB *acb;
24
-
25
assert(s->allocating_write_reqs_plugged);
26
27
s->allocating_write_reqs_plugged = false;
28
-
29
- acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs);
30
- if (acb) {
31
- qed_aio_start_io(acb);
32
- }
33
+ qemu_co_enter_next(&s->allocating_write_reqs);
34
}
35
36
static void qed_clear_need_check(void *opaque, int ret)
37
@@ -XXX,XX +XXX,XX @@ static void qed_need_check_timer_cb(void *opaque)
38
BDRVQEDState *s = opaque;
39
40
/* The timer should only fire when allocating writes have drained */
41
- assert(!QSIMPLEQ_FIRST(&s->allocating_write_reqs));
42
+ assert(!s->allocating_acb);
43
44
trace_qed_need_check_timer_cb(s);
45
46
@@ -XXX,XX +XXX,XX @@ static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags,
47
int ret;
48
49
s->bs = bs;
50
- QSIMPLEQ_INIT(&s->allocating_write_reqs);
51
+ qemu_co_queue_init(&s->allocating_write_reqs);
52
53
ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header));
54
if (ret < 0) {
55
@@ -XXX,XX +XXX,XX @@ static void qed_aio_complete_bh(void *opaque)
56
qed_release(s);
57
}
58
59
-static void qed_resume_alloc_bh(void *opaque)
60
-{
61
- qed_aio_start_io(opaque);
62
-}
63
-
64
static void qed_aio_complete(QEDAIOCB *acb, int ret)
65
{
66
BDRVQEDState *s = acb_to_s(acb);
67
@@ -XXX,XX +XXX,XX @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
68
* next request in the queue. This ensures that we don't cycle through
69
* requests multiple times but rather finish one at a time completely.
70
*/
71
- if (acb == QSIMPLEQ_FIRST(&s->allocating_write_reqs)) {
72
- QEDAIOCB *next_acb;
73
- QSIMPLEQ_REMOVE_HEAD(&s->allocating_write_reqs, next);
74
- next_acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs);
75
- if (next_acb) {
76
- aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
77
- qed_resume_alloc_bh, next_acb);
78
+ if (acb == s->allocating_acb) {
79
+ s->allocating_acb = NULL;
80
+ if (!qemu_co_queue_empty(&s->allocating_write_reqs)) {
81
+ qemu_co_enter_next(&s->allocating_write_reqs);
82
} else if (s->header.features & QED_F_NEED_CHECK) {
83
qed_start_need_check_timer(s);
84
}
85
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
86
int ret;
87
88
/* Cancel timer when the first allocating request comes in */
89
- if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) {
90
+ if (s->allocating_acb == NULL) {
91
qed_cancel_need_check_timer(s);
25
}
92
}
26
93
27
- /* FIXME Use real permissions */
94
/* Freeze this request if another allocating write is in progress */
28
- job = block_job_create(job_id, &backup_job_driver, bs, 0, BLK_PERM_ALL,
95
- if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs)) {
29
+ /* job->common.len is fixed, so we can't allow resize */
96
- QSIMPLEQ_INSERT_TAIL(&s->allocating_write_reqs, acb, next);
30
+ job = block_job_create(job_id, &backup_job_driver, bs,
97
- }
31
+ BLK_PERM_CONSISTENT_READ,
98
- if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs) ||
32
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
99
- s->allocating_write_reqs_plugged) {
33
+ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
100
- return -EINPROGRESS; /* wait for existing request to finish */
34
speed, creation_flags, cb, opaque, errp);
101
+ if (s->allocating_acb != acb || s->allocating_write_reqs_plugged) {
35
if (!job) {
102
+ if (s->allocating_acb != NULL) {
36
goto error;
103
+ qemu_co_queue_wait(&s->allocating_write_reqs, NULL);
104
+ assert(s->allocating_acb == NULL);
105
+ }
106
+ s->allocating_acb = acb;
107
+ return -EAGAIN; /* start over with looking up table entries */
37
}
108
}
38
109
39
- /* FIXME Use real permissions */
110
acb->cur_nclusters = qed_bytes_to_clusters(s,
40
- job->target = blk_new(0, BLK_PERM_ALL);
111
@@ -XXX,XX +XXX,XX @@ static void qed_aio_next_io(QEDAIOCB *acb)
41
+ /* The target must match the source in size, so no resize here either */
112
ret = qed_aio_read_data(acb, ret, offset, len);
42
+ job->target = blk_new(BLK_PERM_WRITE,
113
}
43
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
114
44
+ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
115
- if (ret < 0) {
45
ret = blk_insert_bs(job->target, target, errp);
116
- if (ret != -EINPROGRESS) {
46
if (ret < 0) {
117
- qed_aio_complete(acb, ret);
47
goto error;
118
- }
48
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
119
+ if (ret < 0 && ret != -EAGAIN) {
49
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
120
+ qed_aio_complete(acb, ret);
121
return;
122
}
50
}
123
}
51
124
diff --git a/block/qed.h b/block/qed.h
52
- /* FIXME Use real permissions */
125
index XXXXXXX..XXXXXXX 100644
53
+ /* Required permissions are already taken with target's blk_new() */
126
--- a/block/qed.h
54
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
127
+++ b/block/qed.h
55
&error_abort);
128
@@ -XXX,XX +XXX,XX @@ typedef struct {
56
job->common.len = len;
129
uint32_t l2_mask;
57
diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055
130
58
index XXXXXXX..XXXXXXX 100755
131
/* Allocating write request queue */
59
--- a/tests/qemu-iotests/055
132
- QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs;
60
+++ b/tests/qemu-iotests/055
133
+ QEDAIOCB *allocating_acb;
61
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase):
134
+ CoQueue allocating_write_reqs;
62
def setUp(self):
135
bool allocating_write_reqs_plugged;
63
qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
136
64
137
/* Periodic flush and clear need check flag */
65
- self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
66
+ self.vm = iotests.VM().add_drive(test_img)
67
+ self.vm.add_drive(blockdev_target_img, interface="none")
68
if iotests.qemu_default_machine == 'pc':
69
self.vm.add_drive(None, 'media=cdrom', 'ide')
70
self.vm.launch()
71
@@ -XXX,XX +XXX,XX @@ class TestSetSpeed(iotests.QMPTestCase):
72
def setUp(self):
73
qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
74
75
- self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
76
+ self.vm = iotests.VM().add_drive(test_img)
77
+ self.vm.add_drive(blockdev_target_img, interface="none")
78
self.vm.launch()
79
80
def tearDown(self):
81
@@ -XXX,XX +XXX,XX @@ class TestSingleTransaction(iotests.QMPTestCase):
82
def setUp(self):
83
qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
84
85
- self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
86
+ self.vm = iotests.VM().add_drive(test_img)
87
+ self.vm.add_drive(blockdev_target_img, interface="none")
88
if iotests.qemu_default_machine == 'pc':
89
self.vm.add_drive(None, 'media=cdrom', 'ide')
90
self.vm.launch()
91
@@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase):
92
93
qemu_img('create', '-f', fmt, blockdev_target_img,
94
str(TestDriveCompression.image_len), *args)
95
- self.vm.add_drive(blockdev_target_img, format=fmt)
96
+ self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
97
98
self.vm.launch()
99
100
--
138
--
101
1.8.3.1
139
1.8.3.1
102
140
103
141
diff view generated by jsdifflib
1
Request BLK_PERM_CONSISTENT_READ for the source of block migration, and
1
Now that we process a request in the same coroutine from beginning to
2
handle potential permission errors as good as we can in this place
2
end and don't drop out of it any more, we can look like a proper
3
(which is not very good, but it matches the other failure cases).
3
coroutine-based driver and simply call qed_aio_next_io() and get a
4
return value from it instead of spawning an additional coroutine that
5
reenters the parent when it's done.
4
6
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
---
9
---
9
migration/block.c | 22 +++++++++++++++++-----
10
block/qed.c | 101 +++++++++++++-----------------------------------------------
10
1 file changed, 17 insertions(+), 5 deletions(-)
11
block/qed.h | 3 +-
11
12
2 files changed, 22 insertions(+), 82 deletions(-)
12
diff --git a/migration/block.c b/migration/block.c
13
14
diff --git a/block/qed.c b/block/qed.c
13
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
14
--- a/migration/block.c
16
--- a/block/qed.c
15
+++ b/migration/block.c
17
+++ b/block/qed.c
16
@@ -XXX,XX +XXX,XX @@ static void unset_dirty_tracking(void)
18
@@ -XXX,XX +XXX,XX @@
19
#include "qapi/qmp/qerror.h"
20
#include "sysemu/block-backend.h"
21
22
-static const AIOCBInfo qed_aiocb_info = {
23
- .aiocb_size = sizeof(QEDAIOCB),
24
-};
25
-
26
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
27
const char *filename)
28
{
29
@@ -XXX,XX +XXX,XX @@ static CachedL2Table *qed_new_l2_table(BDRVQEDState *s)
30
return l2_table;
31
}
32
33
-static void qed_aio_next_io(QEDAIOCB *acb);
34
-
35
-static void qed_aio_start_io(QEDAIOCB *acb)
36
-{
37
- qed_aio_next_io(acb);
38
-}
39
-
40
static void qed_plug_allocating_write_reqs(BDRVQEDState *s)
41
{
42
assert(!s->allocating_write_reqs_plugged);
43
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
44
45
static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
46
{
47
- return acb->common.bs->opaque;
48
+ return acb->bs->opaque;
49
}
50
51
/**
52
@@ -XXX,XX +XXX,XX @@ static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index,
17
}
53
}
18
}
54
}
19
55
20
-static void init_blk_migration(QEMUFile *f)
56
-static void qed_aio_complete_bh(void *opaque)
21
+static int init_blk_migration(QEMUFile *f)
57
-{
22
{
58
- QEDAIOCB *acb = opaque;
23
BlockDriverState *bs;
59
- BDRVQEDState *s = acb_to_s(acb);
24
BlkMigDevState *bmds;
60
- BlockCompletionFunc *cb = acb->common.cb;
25
@@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f)
61
- void *user_opaque = acb->common.opaque;
26
BlkMigDevState *bmds;
62
- int ret = acb->bh_ret;
27
BlockDriverState *bs;
63
-
28
} *bmds_bs;
64
- qemu_aio_unref(acb);
29
+ Error *local_err = NULL;
65
-
30
+ int ret;
66
- /* Invoke callback */
31
67
- qed_acquire(s);
32
block_mig_state.submitted = 0;
68
- cb(user_opaque, ret);
33
block_mig_state.read_done = 0;
69
- qed_release(s);
34
@@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f)
70
-}
35
71
-
36
sectors = bdrv_nb_sectors(bs);
72
-static void qed_aio_complete(QEDAIOCB *acb, int ret)
37
if (sectors <= 0) {
73
+static void qed_aio_complete(QEDAIOCB *acb)
38
+ ret = sectors;
74
{
39
goto out;
75
BDRVQEDState *s = acb_to_s(acb);
40
}
76
41
77
- trace_qed_aio_complete(s, acb, ret);
42
bmds = g_new0(BlkMigDevState, 1);
78
-
43
- /* FIXME Use real permissions */
79
/* Free resources */
44
- bmds->blk = blk_new(0, BLK_PERM_ALL);
80
qemu_iovec_destroy(&acb->cur_qiov);
45
+ bmds->blk = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
81
qed_unref_l2_cache_entry(acb->request.l2_table);
46
bmds->blk_name = g_strdup(bdrv_get_device_name(bs));
82
@@ -XXX,XX +XXX,XX @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
47
bmds->bulk_completed = 0;
83
acb->qiov->iov[0].iov_base = NULL;
48
bmds->total_sectors = sectors;
49
@@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f)
50
BlockDriverState *bs = bmds_bs[i].bs;
51
52
if (bmds) {
53
- blk_insert_bs(bmds->blk, bs, &error_abort);
54
+ ret = blk_insert_bs(bmds->blk, bs, &local_err);
55
+ if (ret < 0) {
56
+ error_report_err(local_err);
57
+ goto out;
58
+ }
59
60
alloc_aio_bitmap(bmds);
61
error_setg(&bmds->blocker, "block device is in use by migration");
62
@@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f)
63
}
64
}
84
}
65
85
66
+ ret = 0;
86
- /* Arrange for a bh to invoke the completion function */
67
out:
87
- acb->bh_ret = ret;
68
g_free(bmds_bs);
88
- aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
89
- qed_aio_complete_bh, acb);
90
-
91
/* Start next allocating write request waiting behind this one. Note that
92
* requests enqueue themselves when they first hit an unallocated cluster
93
* but they wait until the entire request is finished before waking up the
94
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
95
struct iovec *iov = acb->qiov->iov;
96
97
if (!iov->iov_base) {
98
- iov->iov_base = qemu_try_blockalign(acb->common.bs, iov->iov_len);
99
+ iov->iov_base = qemu_try_blockalign(acb->bs, iov->iov_len);
100
if (iov->iov_base == NULL) {
101
return -ENOMEM;
102
}
103
@@ -XXX,XX +XXX,XX @@ static int qed_aio_read_data(void *opaque, int ret, uint64_t offset, size_t len)
104
{
105
QEDAIOCB *acb = opaque;
106
BDRVQEDState *s = acb_to_s(acb);
107
- BlockDriverState *bs = acb->common.bs;
108
+ BlockDriverState *bs = acb->bs;
109
110
/* Adjust offset into cluster */
111
offset += qed_offset_into_cluster(s, acb->cur_pos);
112
@@ -XXX,XX +XXX,XX @@ static int qed_aio_read_data(void *opaque, int ret, uint64_t offset, size_t len)
113
/**
114
* Begin next I/O or complete the request
115
*/
116
-static void qed_aio_next_io(QEDAIOCB *acb)
117
+static int qed_aio_next_io(QEDAIOCB *acb)
118
{
119
BDRVQEDState *s = acb_to_s(acb);
120
uint64_t offset;
121
@@ -XXX,XX +XXX,XX @@ static void qed_aio_next_io(QEDAIOCB *acb)
122
123
/* Complete request */
124
if (acb->cur_pos >= acb->end_pos) {
125
- qed_aio_complete(acb, 0);
126
- return;
127
+ ret = 0;
128
+ break;
129
}
130
131
/* Find next cluster and start I/O */
132
len = acb->end_pos - acb->cur_pos;
133
ret = qed_find_cluster(s, &acb->request, acb->cur_pos, &len, &offset);
134
if (ret < 0) {
135
- qed_aio_complete(acb, ret);
136
- return;
137
+ break;
138
}
139
140
if (acb->flags & QED_AIOCB_WRITE) {
141
@@ -XXX,XX +XXX,XX @@ static void qed_aio_next_io(QEDAIOCB *acb)
142
}
143
144
if (ret < 0 && ret != -EAGAIN) {
145
- qed_aio_complete(acb, ret);
146
- return;
147
+ break;
148
}
149
}
150
-}
151
152
-typedef struct QEDRequestCo {
153
- Coroutine *co;
154
- bool done;
155
- int ret;
156
-} QEDRequestCo;
157
-
158
-static void qed_co_request_cb(void *opaque, int ret)
159
-{
160
- QEDRequestCo *co = opaque;
161
-
162
- co->done = true;
163
- co->ret = ret;
164
- qemu_coroutine_enter_if_inactive(co->co);
165
+ trace_qed_aio_complete(s, acb, ret);
166
+ qed_aio_complete(acb);
69
+ return ret;
167
+ return ret;
70
}
168
}
71
169
72
/* Called with no lock taken. */
170
static int coroutine_fn qed_co_request(BlockDriverState *bs, int64_t sector_num,
73
@@ -XXX,XX +XXX,XX @@ static int block_save_setup(QEMUFile *f, void *opaque)
171
QEMUIOVector *qiov, int nb_sectors,
74
block_mig_state.submitted, block_mig_state.transferred);
172
int flags)
75
173
{
76
qemu_mutex_lock_iothread();
174
- QEDRequestCo co = {
77
- init_blk_migration(f);
175
- .co = qemu_coroutine_self(),
78
+ ret = init_blk_migration(f);
176
- .done = false,
79
+ if (ret < 0) {
177
+ QEDAIOCB acb = {
80
+ qemu_mutex_unlock_iothread();
178
+ .bs = bs,
81
+ return ret;
179
+ .cur_pos = (uint64_t) sector_num * BDRV_SECTOR_SIZE,
82
+ }
180
+ .end_pos = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE,
83
181
+ .qiov = qiov,
84
/* start track dirty blocks */
182
+ .flags = flags,
85
ret = set_dirty_tracking();
183
};
184
- QEDAIOCB *acb = qemu_aio_get(&qed_aiocb_info, bs, qed_co_request_cb, &co);
185
-
186
- trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors, &co, flags);
187
+ qemu_iovec_init(&acb.cur_qiov, qiov->niov);
188
189
- acb->flags = flags;
190
- acb->qiov = qiov;
191
- acb->qiov_offset = 0;
192
- acb->cur_pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE;
193
- acb->end_pos = acb->cur_pos + nb_sectors * BDRV_SECTOR_SIZE;
194
- acb->backing_qiov = NULL;
195
- acb->request.l2_table = NULL;
196
- qemu_iovec_init(&acb->cur_qiov, qiov->niov);
197
+ trace_qed_aio_setup(bs->opaque, &acb, sector_num, nb_sectors, NULL, flags);
198
199
/* Start request */
200
- qed_aio_start_io(acb);
201
-
202
- if (!co.done) {
203
- qemu_coroutine_yield();
204
- }
205
-
206
- return co.ret;
207
+ return qed_aio_next_io(&acb);
208
}
209
210
static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs,
211
diff --git a/block/qed.h b/block/qed.h
212
index XXXXXXX..XXXXXXX 100644
213
--- a/block/qed.h
214
+++ b/block/qed.h
215
@@ -XXX,XX +XXX,XX @@ enum {
216
};
217
218
typedef struct QEDAIOCB {
219
- BlockAIOCB common;
220
- int bh_ret; /* final return status for completion bh */
221
+ BlockDriverState *bs;
222
QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */
223
int flags; /* QED_AIOCB_* bits ORed together */
224
uint64_t end_pos; /* request end on block device, in bytes */
86
--
225
--
87
1.8.3.1
226
1.8.3.1
88
227
89
228
diff view generated by jsdifflib
1
vvfat is the last remaining driver that can have children, but doesn't
1
This fixes the last place where we degraded from AIO to actual blocking
2
implement .bdrv_child_perm() yet. The default handlers aren't suitable
2
synchronous I/O requests. Putting it into a coroutine means that instead
3
here, so let's implement a very simple driver-specific one that protects
3
of blocking, the coroutine simply yields while doing I/O.
4
the internal child from being used by other users as good as our
5
permissions permit.
6
4
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Acked-by: Fam Zheng <famz@redhat.com>
10
---
7
---
11
block.c | 2 +-
8
block/qed.c | 33 +++++++++++++++++----------------
12
block/vvfat.c | 22 ++++++++++++++++++++++
9
1 file changed, 17 insertions(+), 16 deletions(-)
13
include/block/block_int.h | 1 +
14
3 files changed, 24 insertions(+), 1 deletion(-)
15
10
16
diff --git a/block.c b/block.c
11
diff --git a/block/qed.c b/block/qed.c
17
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
13
--- a/block/qed.c
19
+++ b/block.c
14
+++ b/block/qed.c
20
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
15
@@ -XXX,XX +XXX,XX @@ static void qed_unplug_allocating_write_reqs(BDRVQEDState *s)
21
*child_flags = flags;
16
qemu_co_enter_next(&s->allocating_write_reqs);
22
}
17
}
23
18
24
-static const BdrvChildRole child_backing = {
19
-static void qed_clear_need_check(void *opaque, int ret)
25
+const BdrvChildRole child_backing = {
20
+static void qed_need_check_timer_entry(void *opaque)
26
.inherit_options = bdrv_backing_options,
21
{
27
.drained_begin = bdrv_child_cb_drained_begin,
22
BDRVQEDState *s = opaque;
28
.drained_end = bdrv_child_cb_drained_end,
23
+ int ret;
29
diff --git a/block/vvfat.c b/block/vvfat.c
24
30
index XXXXXXX..XXXXXXX 100644
25
- if (ret) {
31
--- a/block/vvfat.c
26
+ /* The timer should only fire when allocating writes have drained */
32
+++ b/block/vvfat.c
27
+ assert(!s->allocating_acb);
33
@@ -XXX,XX +XXX,XX @@ err:
28
+
34
return ret;
29
+ trace_qed_need_check_timer_cb(s);
30
+
31
+ qed_acquire(s);
32
+ qed_plug_allocating_write_reqs(s);
33
+
34
+ /* Ensure writes are on disk before clearing flag */
35
+ ret = bdrv_co_flush(s->bs->file->bs);
36
+ qed_release(s);
37
+ if (ret < 0) {
38
qed_unplug_allocating_write_reqs(s);
39
return;
40
}
41
@@ -XXX,XX +XXX,XX @@ static void qed_clear_need_check(void *opaque, int ret)
42
43
qed_unplug_allocating_write_reqs(s);
44
45
- ret = bdrv_flush(s->bs);
46
+ ret = bdrv_co_flush(s->bs);
47
(void) ret;
35
}
48
}
36
49
37
+static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
50
static void qed_need_check_timer_cb(void *opaque)
38
+ const BdrvChildRole *role,
39
+ uint64_t perm, uint64_t shared,
40
+ uint64_t *nperm, uint64_t *nshared)
41
+{
42
+ BDRVVVFATState *s = bs->opaque;
43
+
44
+ assert(c == s->qcow || role == &child_backing);
45
+
46
+ if (c == s->qcow) {
47
+ /* This is a private node, nobody should try to attach to it */
48
+ *nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
49
+ *nshared = BLK_PERM_WRITE_UNCHANGED;
50
+ } else {
51
+ /* The backing file is there so 'commit' can use it. vvfat doesn't
52
+ * access it in any way. */
53
+ *nperm = 0;
54
+ *nshared = BLK_PERM_ALL;
55
+ }
56
+}
57
+
58
static void vvfat_close(BlockDriverState *bs)
59
{
51
{
60
BDRVVVFATState *s = bs->opaque;
52
- BDRVQEDState *s = opaque;
61
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vvfat = {
53
-
62
.bdrv_file_open = vvfat_open,
54
- /* The timer should only fire when allocating writes have drained */
63
.bdrv_refresh_limits = vvfat_refresh_limits,
55
- assert(!s->allocating_acb);
64
.bdrv_close = vvfat_close,
56
-
65
+ .bdrv_child_perm = vvfat_child_perm,
57
- trace_qed_need_check_timer_cb(s);
66
58
-
67
.bdrv_co_preadv = vvfat_co_preadv,
59
- qed_acquire(s);
68
.bdrv_co_pwritev = vvfat_co_pwritev,
60
- qed_plug_allocating_write_reqs(s);
69
diff --git a/include/block/block_int.h b/include/block/block_int.h
61
-
70
index XXXXXXX..XXXXXXX 100644
62
- /* Ensure writes are on disk before clearing flag */
71
--- a/include/block/block_int.h
63
- bdrv_aio_flush(s->bs->file->bs, qed_clear_need_check, s);
72
+++ b/include/block/block_int.h
64
- qed_release(s);
73
@@ -XXX,XX +XXX,XX @@ struct BdrvChildRole {
65
+ Coroutine *co = qemu_coroutine_create(qed_need_check_timer_entry, opaque);
74
66
+ qemu_coroutine_enter(co);
75
extern const BdrvChildRole child_file;
67
}
76
extern const BdrvChildRole child_format;
68
77
+extern const BdrvChildRole child_backing;
69
void qed_acquire(BDRVQEDState *s)
78
79
struct BdrvChild {
80
BlockDriverState *bs;
81
--
70
--
82
1.8.3.1
71
1.8.3.1
83
72
84
73
diff view generated by jsdifflib
1
In many cases, the required permissions of one node on its children
1
Now that we stay in coroutine context for the whole request when doing
2
depend on what its parents require from it. For example, the raw format
2
reads or writes, we can add coroutine_fn annotations to many functions
3
or most filter drivers only need to request consistent reads if that's
3
that can do I/O or yield directly.
4
something that one of their parents wants.
5
6
In order to achieve this, this patch introduces two new BlockDriver
7
callbacks. The first one lets drivers first check (recursively) whether
8
the requested permissions can be set; the second one actually sets the
9
new permission bitmask.
10
11
Also add helper functions that drivers can use in their implementation
12
of the callbacks to update their permissions on a specific child.
13
4
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Acked-by: Fam Zheng <famz@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
16
Reviewed-by: Max Reitz <mreitz@redhat.com>
17
---
7
---
18
block.c | 206 +++++++++++++++++++++++++++++++++++++++++++++-
8
block/qed-cluster.c | 5 +++--
19
include/block/block_int.h | 61 ++++++++++++++
9
block/qed.c | 44 ++++++++++++++++++++++++--------------------
20
2 files changed, 263 insertions(+), 4 deletions(-)
10
block/qed.h | 5 +++--
21
11
3 files changed, 30 insertions(+), 24 deletions(-)
22
diff --git a/block.c b/block.c
12
13
diff --git a/block/qed-cluster.c b/block/qed-cluster.c
23
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
24
--- a/block.c
15
--- a/block/qed-cluster.c
25
+++ b/block.c
16
+++ b/block/qed-cluster.c
26
@@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename,
17
@@ -XXX,XX +XXX,XX @@ static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s,
27
return 0;
18
* On failure QED_CLUSTER_L2 or QED_CLUSTER_L1 is returned for missing L2 or L1
19
* table offset, respectively. len is number of contiguous unallocated bytes.
20
*/
21
-int qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
22
- size_t *len, uint64_t *img_offset)
23
+int coroutine_fn qed_find_cluster(BDRVQEDState *s, QEDRequest *request,
24
+ uint64_t pos, size_t *len,
25
+ uint64_t *img_offset)
26
{
27
uint64_t l2_offset;
28
uint64_t offset = 0;
29
diff --git a/block/qed.c b/block/qed.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/block/qed.c
32
+++ b/block/qed.c
33
@@ -XXX,XX +XXX,XX @@ int qed_write_header_sync(BDRVQEDState *s)
34
* This function only updates known header fields in-place and does not affect
35
* extra data after the QED header.
36
*/
37
-static int qed_write_header(BDRVQEDState *s)
38
+static int coroutine_fn qed_write_header(BDRVQEDState *s)
39
{
40
/* We must write full sectors for O_DIRECT but cannot necessarily generate
41
* the data following the header if an unrecognized compat feature is
42
@@ -XXX,XX +XXX,XX @@ static void qed_unplug_allocating_write_reqs(BDRVQEDState *s)
43
qemu_co_enter_next(&s->allocating_write_reqs);
28
}
44
}
29
45
30
+/*
46
-static void qed_need_check_timer_entry(void *opaque)
31
+ * Check whether permissions on this node can be changed in a way that
47
+static void coroutine_fn qed_need_check_timer_entry(void *opaque)
32
+ * @cumulative_perms and @cumulative_shared_perms are the new cumulative
48
{
33
+ * permissions of all its parents. This involves checking whether all necessary
49
BDRVQEDState *s = opaque;
34
+ * permission changes to child nodes can be performed.
50
int ret;
35
+ *
51
@@ -XXX,XX +XXX,XX @@ static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
36
+ * A call to this function must always be followed by a call to bdrv_set_perm()
52
* This function reads qiov->size bytes starting at pos from the backing file.
37
+ * or bdrv_abort_perm_update().
53
* If there is no backing file then zeroes are read.
38
+ */
54
*/
39
+static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms,
55
-static int qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
40
+ uint64_t cumulative_shared_perms, Error **errp)
56
- QEMUIOVector *qiov,
41
+{
57
- QEMUIOVector **backing_qiov)
42
+ BlockDriver *drv = bs->drv;
58
+static int coroutine_fn qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
43
+ BdrvChild *c;
59
+ QEMUIOVector *qiov,
44
+ int ret;
60
+ QEMUIOVector **backing_qiov)
45
+
61
{
46
+ /* Write permissions never work with read-only images */
62
uint64_t backing_length = 0;
47
+ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
63
size_t size;
48
+ bdrv_is_read_only(bs))
64
@@ -XXX,XX +XXX,XX @@ static int qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
49
+ {
65
* @len: Number of bytes
50
+ error_setg(errp, "Block node is read-only");
66
* @offset: Byte offset in image file
51
+ return -EPERM;
67
*/
52
+ }
68
-static int qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
53
+
69
- uint64_t len, uint64_t offset)
54
+ /* Check this node */
70
+static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
55
+ if (!drv) {
71
+ uint64_t pos, uint64_t len,
56
+ return 0;
72
+ uint64_t offset)
57
+ }
73
{
58
+
74
QEMUIOVector qiov;
59
+ if (drv->bdrv_check_perm) {
75
QEMUIOVector *backing_qiov = NULL;
60
+ return drv->bdrv_check_perm(bs, cumulative_perms,
76
@@ -XXX,XX +XXX,XX @@ out:
61
+ cumulative_shared_perms, errp);
77
* The cluster offset may be an allocated byte offset in the image file, the
62
+ }
78
* zero cluster marker, or the unallocated cluster marker.
63
+
79
*/
64
+ /* Drivers may not have .bdrv_child_perm() */
80
-static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index,
65
+ if (!drv->bdrv_child_perm) {
81
- unsigned int n, uint64_t cluster)
66
+ return 0;
82
+static void coroutine_fn qed_update_l2_table(BDRVQEDState *s, QEDTable *table,
67
+ }
83
+ int index, unsigned int n,
68
+
84
+ uint64_t cluster)
69
+ /* Check all children */
85
{
70
+ QLIST_FOREACH(c, &bs->children, next) {
86
int i;
71
+ uint64_t cur_perm, cur_shared;
87
for (i = index; i < index + n; i++) {
72
+ drv->bdrv_child_perm(bs, c, c->role,
88
@@ -XXX,XX +XXX,XX @@ static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index,
73
+ cumulative_perms, cumulative_shared_perms,
74
+ &cur_perm, &cur_shared);
75
+ ret = bdrv_child_check_perm(c, cur_perm, cur_shared, errp);
76
+ if (ret < 0) {
77
+ return ret;
78
+ }
79
+ }
80
+
81
+ return 0;
82
+}
83
+
84
+/*
85
+ * Notifies drivers that after a previous bdrv_check_perm() call, the
86
+ * permission update is not performed and any preparations made for it (e.g.
87
+ * taken file locks) need to be undone.
88
+ *
89
+ * This function recursively notifies all child nodes.
90
+ */
91
+static void bdrv_abort_perm_update(BlockDriverState *bs)
92
+{
93
+ BlockDriver *drv = bs->drv;
94
+ BdrvChild *c;
95
+
96
+ if (!drv) {
97
+ return;
98
+ }
99
+
100
+ if (drv->bdrv_abort_perm_update) {
101
+ drv->bdrv_abort_perm_update(bs);
102
+ }
103
+
104
+ QLIST_FOREACH(c, &bs->children, next) {
105
+ bdrv_child_abort_perm_update(c);
106
+ }
107
+}
108
+
109
+static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
110
+ uint64_t cumulative_shared_perms)
111
+{
112
+ BlockDriver *drv = bs->drv;
113
+ BdrvChild *c;
114
+
115
+ if (!drv) {
116
+ return;
117
+ }
118
+
119
+ /* Update this node */
120
+ if (drv->bdrv_set_perm) {
121
+ drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
122
+ }
123
+
124
+ /* Drivers may not have .bdrv_child_perm() */
125
+ if (!drv->bdrv_child_perm) {
126
+ return;
127
+ }
128
+
129
+ /* Update all children */
130
+ QLIST_FOREACH(c, &bs->children, next) {
131
+ uint64_t cur_perm, cur_shared;
132
+ drv->bdrv_child_perm(bs, c, c->role,
133
+ cumulative_perms, cumulative_shared_perms,
134
+ &cur_perm, &cur_shared);
135
+ bdrv_child_set_perm(c, cur_perm, cur_shared);
136
+ }
137
+}
138
+
139
+static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
140
+ uint64_t *shared_perm)
141
+{
142
+ BdrvChild *c;
143
+ uint64_t cumulative_perms = 0;
144
+ uint64_t cumulative_shared_perms = BLK_PERM_ALL;
145
+
146
+ QLIST_FOREACH(c, &bs->parents, next_parent) {
147
+ cumulative_perms |= c->perm;
148
+ cumulative_shared_perms &= c->shared_perm;
149
+ }
150
+
151
+ *perm = cumulative_perms;
152
+ *shared_perm = cumulative_shared_perms;
153
+}
154
+
155
+/*
156
+ * Checks whether a new reference to @bs can be added if the new user requires
157
+ * @new_used_perm/@new_shared_perm as its permissions. If @ignore_child is set,
158
+ * this old reference is ignored in the calculations; this allows checking
159
+ * permission updates for an existing reference.
160
+ *
161
+ * Needs to be followed by a call to either bdrv_set_perm() or
162
+ * bdrv_abort_perm_update(). */
163
static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
164
uint64_t new_shared_perm,
165
BdrvChild *ignore_child, Error **errp)
166
{
167
BdrvChild *c;
168
+ uint64_t cumulative_perms = new_used_perm;
169
+ uint64_t cumulative_shared_perms = new_shared_perm;
170
171
/* There is no reason why anyone couldn't tolerate write_unchanged */
172
assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
173
@@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm,
174
error_setg(errp, "Conflicts with %s", user ?: "another operation");
175
return -EPERM;
176
}
177
+
178
+ cumulative_perms |= c->perm;
179
+ cumulative_shared_perms &= c->shared_perm;
180
}
181
182
+ return bdrv_check_perm(bs, cumulative_perms, cumulative_shared_perms, errp);
183
+}
184
+
185
+/* Needs to be followed by a call to either bdrv_child_set_perm() or
186
+ * bdrv_child_abort_perm_update(). */
187
+int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
188
+ Error **errp)
189
+{
190
+ return bdrv_check_update_perm(c->bs, perm, shared, c, errp);
191
+}
192
+
193
+void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
194
+{
195
+ uint64_t cumulative_perms, cumulative_shared_perms;
196
+
197
+ c->perm = perm;
198
+ c->shared_perm = shared;
199
+
200
+ bdrv_get_cumulative_perm(c->bs, &cumulative_perms,
201
+ &cumulative_shared_perms);
202
+ bdrv_set_perm(c->bs, cumulative_perms, cumulative_shared_perms);
203
+}
204
+
205
+void bdrv_child_abort_perm_update(BdrvChild *c)
206
+{
207
+ bdrv_abort_perm_update(c->bs);
208
+}
209
+
210
+int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
211
+ Error **errp)
212
+{
213
+ int ret;
214
+
215
+ ret = bdrv_child_check_perm(c, perm, shared, errp);
216
+ if (ret < 0) {
217
+ bdrv_child_abort_perm_update(c);
218
+ return ret;
219
+ }
220
+
221
+ bdrv_child_set_perm(c, perm, shared);
222
+
223
return 0;
224
}
225
226
-static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
227
+static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
228
+ bool check_new_perm)
229
{
230
BlockDriverState *old_bs = child->bs;
231
+ uint64_t perm, shared_perm;
232
233
if (old_bs) {
234
if (old_bs->quiesce_counter && child->role->drained_end) {
235
child->role->drained_end(child);
236
}
237
QLIST_REMOVE(child, next_parent);
238
+
239
+ /* Update permissions for old node. This is guaranteed to succeed
240
+ * because we're just taking a parent away, so we're loosening
241
+ * restrictions. */
242
+ bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm);
243
+ bdrv_check_perm(old_bs, perm, shared_perm, &error_abort);
244
+ bdrv_set_perm(old_bs, perm, shared_perm);
245
}
246
247
child->bs = new_bs;
248
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
249
if (new_bs->quiesce_counter && child->role->drained_begin) {
250
child->role->drained_begin(child);
251
}
252
+
253
+ bdrv_get_cumulative_perm(new_bs, &perm, &shared_perm);
254
+ if (check_new_perm) {
255
+ bdrv_check_perm(new_bs, perm, shared_perm, &error_abort);
256
+ }
257
+ bdrv_set_perm(new_bs, perm, shared_perm);
258
}
89
}
259
}
90
}
260
91
261
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
92
-static void qed_aio_complete(QEDAIOCB *acb)
262
93
+static void coroutine_fn qed_aio_complete(QEDAIOCB *acb)
263
ret = bdrv_check_update_perm(child_bs, perm, shared_perm, NULL, errp);
94
{
264
if (ret < 0) {
95
BDRVQEDState *s = acb_to_s(acb);
265
+ bdrv_abort_perm_update(child_bs);
96
266
return NULL;
97
@@ -XXX,XX +XXX,XX @@ static void qed_aio_complete(QEDAIOCB *acb)
267
}
98
/**
268
99
* Update L1 table with new L2 table offset and write it out
269
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
100
*/
270
.opaque = opaque,
101
-static int qed_aio_write_l1_update(QEDAIOCB *acb)
271
};
102
+static int coroutine_fn qed_aio_write_l1_update(QEDAIOCB *acb)
272
103
{
273
- bdrv_replace_child(child, child_bs);
104
BDRVQEDState *s = acb_to_s(acb);
274
+ /* This performs the matching bdrv_set_perm() for the above check. */
105
CachedL2Table *l2_table = acb->request.l2_table;
275
+ bdrv_replace_child(child, child_bs, false);
106
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_l1_update(QEDAIOCB *acb)
276
107
/**
277
return child;
108
* Update L2 table with new cluster offsets and write them out
278
}
109
*/
279
@@ -XXX,XX +XXX,XX @@ static void bdrv_detach_child(BdrvChild *child)
110
-static int qed_aio_write_l2_update(QEDAIOCB *acb, uint64_t offset)
280
child->next.le_prev = NULL;
111
+static int coroutine_fn qed_aio_write_l2_update(QEDAIOCB *acb, uint64_t offset)
281
}
112
{
282
113
BDRVQEDState *s = acb_to_s(acb);
283
- bdrv_replace_child(child, NULL);
114
bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1;
284
+ bdrv_replace_child(child, NULL, false);
115
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_l2_update(QEDAIOCB *acb, uint64_t offset)
285
116
/**
286
g_free(child->name);
117
* Write data to the image file
287
g_free(child);
118
*/
288
@@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from,
119
-static int qed_aio_write_main(QEDAIOCB *acb)
289
120
+static int coroutine_fn qed_aio_write_main(QEDAIOCB *acb)
290
assert(c->role != &child_backing);
121
{
291
bdrv_ref(to);
122
BDRVQEDState *s = acb_to_s(acb);
292
- bdrv_replace_child(c, to);
123
uint64_t offset = acb->cur_cluster +
293
+ /* FIXME Are we sure that bdrv_replace_child() can't run into
124
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_main(QEDAIOCB *acb)
294
+ * &error_abort because of permissions? */
125
/**
295
+ bdrv_replace_child(c, to, true);
126
* Populate untouched regions of new data cluster
296
bdrv_unref(from);
127
*/
297
}
128
-static int qed_aio_write_cow(QEDAIOCB *acb)
298
}
129
+static int coroutine_fn qed_aio_write_cow(QEDAIOCB *acb)
299
diff --git a/include/block/block_int.h b/include/block/block_int.h
130
{
131
BDRVQEDState *s = acb_to_s(acb);
132
uint64_t start, len, offset;
133
@@ -XXX,XX +XXX,XX @@ static bool qed_should_set_need_check(BDRVQEDState *s)
134
*
135
* This path is taken when writing to previously unallocated clusters.
136
*/
137
-static int qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
138
+static int coroutine_fn qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
139
{
140
BDRVQEDState *s = acb_to_s(acb);
141
int ret;
142
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
143
*
144
* This path is taken when writing to already allocated clusters.
145
*/
146
-static int qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
147
+static int coroutine_fn qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset,
148
+ size_t len)
149
{
150
/* Allocate buffer for zero writes */
151
if (acb->flags & QED_AIOCB_ZERO) {
152
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
153
* @offset: Cluster offset in bytes
154
* @len: Length in bytes
155
*/
156
-static int qed_aio_write_data(void *opaque, int ret,
157
- uint64_t offset, size_t len)
158
+static int coroutine_fn qed_aio_write_data(void *opaque, int ret,
159
+ uint64_t offset, size_t len)
160
{
161
QEDAIOCB *acb = opaque;
162
163
@@ -XXX,XX +XXX,XX @@ static int qed_aio_write_data(void *opaque, int ret,
164
* @offset: Cluster offset in bytes
165
* @len: Length in bytes
166
*/
167
-static int qed_aio_read_data(void *opaque, int ret, uint64_t offset, size_t len)
168
+static int coroutine_fn qed_aio_read_data(void *opaque, int ret,
169
+ uint64_t offset, size_t len)
170
{
171
QEDAIOCB *acb = opaque;
172
BDRVQEDState *s = acb_to_s(acb);
173
@@ -XXX,XX +XXX,XX @@ static int qed_aio_read_data(void *opaque, int ret, uint64_t offset, size_t len)
174
/**
175
* Begin next I/O or complete the request
176
*/
177
-static int qed_aio_next_io(QEDAIOCB *acb)
178
+static int coroutine_fn qed_aio_next_io(QEDAIOCB *acb)
179
{
180
BDRVQEDState *s = acb_to_s(acb);
181
uint64_t offset;
182
diff --git a/block/qed.h b/block/qed.h
300
index XXXXXXX..XXXXXXX 100644
183
index XXXXXXX..XXXXXXX 100644
301
--- a/include/block/block_int.h
184
--- a/block/qed.h
302
+++ b/include/block/block_int.h
185
+++ b/block/qed.h
303
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
186
@@ -XXX,XX +XXX,XX @@ int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
304
void (*bdrv_del_child)(BlockDriverState *parent, BdrvChild *child,
187
/**
305
Error **errp);
188
* Cluster functions
306
189
*/
307
+ /**
190
-int qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
308
+ * Informs the block driver that a permission change is intended. The
191
- size_t *len, uint64_t *img_offset);
309
+ * driver checks whether the change is permissible and may take other
192
+int coroutine_fn qed_find_cluster(BDRVQEDState *s, QEDRequest *request,
310
+ * preparations for the change (e.g. get file system locks). This operation
193
+ uint64_t pos, size_t *len,
311
+ * is always followed either by a call to either .bdrv_set_perm or
194
+ uint64_t *img_offset);
312
+ * .bdrv_abort_perm_update.
195
313
+ *
196
/**
314
+ * Checks whether the requested set of cumulative permissions in @perm
197
* Consistency check
315
+ * can be granted for accessing @bs and whether no other users are using
316
+ * permissions other than those given in @shared (both arguments take
317
+ * BLK_PERM_* bitmasks).
318
+ *
319
+ * If both conditions are met, 0 is returned. Otherwise, -errno is returned
320
+ * and errp is set to an error describing the conflict.
321
+ */
322
+ int (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm,
323
+ uint64_t shared, Error **errp);
324
+
325
+ /**
326
+ * Called to inform the driver that the set of cumulative set of used
327
+ * permissions for @bs has changed to @perm, and the set of sharable
328
+ * permission to @shared. The driver can use this to propagate changes to
329
+ * its children (i.e. request permissions only if a parent actually needs
330
+ * them).
331
+ *
332
+ * This function is only invoked after bdrv_check_perm(), so block drivers
333
+ * may rely on preparations made in their .bdrv_check_perm implementation.
334
+ */
335
+ void (*bdrv_set_perm)(BlockDriverState *bs, uint64_t perm, uint64_t shared);
336
+
337
+ /*
338
+ * Called to inform the driver that after a previous bdrv_check_perm()
339
+ * call, the permission update is not performed and any preparations made
340
+ * for it (e.g. taken file locks) need to be undone.
341
+ *
342
+ * This function can be called even for nodes that never saw a
343
+ * bdrv_check_perm() call. It is a no-op then.
344
+ */
345
+ void (*bdrv_abort_perm_update)(BlockDriverState *bs);
346
+
347
+ /**
348
+ * Returns in @nperm and @nshared the permissions that the driver for @bs
349
+ * needs on its child @c, based on the cumulative permissions requested by
350
+ * the parents in @parent_perm and @parent_shared.
351
+ *
352
+ * If @c is NULL, return the permissions for attaching a new child for the
353
+ * given @role.
354
+ */
355
+ void (*bdrv_child_perm)(BlockDriverState *bs, BdrvChild *c,
356
+ const BdrvChildRole *role,
357
+ uint64_t parent_perm, uint64_t parent_shared,
358
+ uint64_t *nperm, uint64_t *nshared);
359
+
360
QLIST_ENTRY(BlockDriver) list;
361
};
362
363
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
364
void *opaque, Error **errp);
365
void bdrv_root_unref_child(BdrvChild *child);
366
367
+int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
368
+ Error **errp);
369
+void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
370
+void bdrv_child_abort_perm_update(BdrvChild *c);
371
+int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
372
+ Error **errp);
373
+
374
+
375
const char *bdrv_get_parent_name(const BlockDriverState *bs);
376
void blk_dev_change_media_cb(BlockBackend *blk, bool load);
377
bool blk_dev_has_removable_media(BlockBackend *blk);
378
--
198
--
379
1.8.3.1
199
1.8.3.1
380
200
381
201
diff view generated by jsdifflib
1
Not all callers of bdrv_set_backing_hd() know for sure that attaching
1
All functions that are marked coroutine_fn can directly call the
2
the backing file will be allowed by the permission system. Return the
2
bdrv_co_* version of functions instead of going through the wrapper.
3
error from the function rather than aborting.
4
3
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
5
Reviewed-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
7
---
9
block.c | 30 +++++++++++++++++++++++-------
8
block/qed.c | 16 +++++++++-------
10
block/commit.c | 14 +++++++-------
9
1 file changed, 9 insertions(+), 7 deletions(-)
11
block/mirror.c | 7 ++++++-
12
block/stream.c | 9 ++++++++-
13
block/vvfat.c | 2 +-
14
include/block/block.h | 3 ++-
15
6 files changed, 47 insertions(+), 18 deletions(-)
16
10
17
diff --git a/block.c b/block.c
11
diff --git a/block/qed.c b/block/qed.c
18
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
19
--- a/block.c
13
--- a/block/qed.c
20
+++ b/block.c
14
+++ b/block/qed.c
21
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs)
15
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qed_write_header(BDRVQEDState *s)
22
* Sets the backing file link of a BDS. A new reference is created; callers
16
};
23
* which don't need their own reference any more must call bdrv_unref().
17
qemu_iovec_init_external(&qiov, &iov, 1);
24
*/
18
25
-void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
19
- ret = bdrv_preadv(s->bs->file, 0, &qiov);
26
+void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
20
+ ret = bdrv_co_preadv(s->bs->file, 0, qiov.size, &qiov, 0);
27
+ Error **errp)
21
if (ret < 0) {
28
{
29
if (backing_hd) {
30
bdrv_ref(backing_hd);
31
@@ -XXX,XX +XXX,XX @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
32
bs->backing = NULL;
33
goto out;
22
goto out;
34
}
23
}
35
- /* FIXME Error handling */
24
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qed_write_header(BDRVQEDState *s)
36
+
25
/* Update header */
37
bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
26
qed_header_cpu_to_le(&s->header, (QEDHeader *) buf);
38
- &error_abort);
27
39
+ errp);
28
- ret = bdrv_pwritev(s->bs->file, 0, &qiov);
40
+ if (!bs->backing) {
29
+ ret = bdrv_co_pwritev(s->bs->file, 0, qiov.size, &qiov, 0);
41
+ bdrv_unref(backing_hd);
30
if (ret < 0) {
42
+ }
31
goto out;
43
44
out:
45
bdrv_refresh_limits(bs, NULL);
46
@@ -XXX,XX +XXX,XX @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
47
48
/* Hook up the backing file link; drop our reference, bs owns the
49
* backing_hd reference now */
50
- bdrv_set_backing_hd(bs, backing_hd);
51
+ bdrv_set_backing_hd(bs, backing_hd, &local_err);
52
bdrv_unref(backing_hd);
53
+ if (local_err) {
54
+ ret = -EINVAL;
55
+ goto free_exit;
56
+ }
57
58
qdict_del(parent_options, bdref_key);
59
60
@@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs)
61
bs->drv->bdrv_close(bs);
62
bs->drv = NULL;
63
64
- bdrv_set_backing_hd(bs, NULL);
65
+ bdrv_set_backing_hd(bs, NULL, &error_abort);
66
67
if (bs->file != NULL) {
68
bdrv_unref_child(bs, bs->file);
69
@@ -XXX,XX +XXX,XX @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
70
bdrv_ref(bs_top);
71
72
change_parent_backing_link(bs_top, bs_new);
73
- bdrv_set_backing_hd(bs_new, bs_top);
74
+ /* FIXME Error handling */
75
+ bdrv_set_backing_hd(bs_new, bs_top, &error_abort);
76
bdrv_unref(bs_top);
77
78
/* bs_new is now referenced by its new parents, we don't need the
79
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
80
BlockDriverState *base, const char *backing_file_str)
81
{
82
BlockDriverState *new_top_bs = NULL;
83
+ Error *local_err = NULL;
84
int ret = -EIO;
85
86
if (!top->drv || !base->drv) {
87
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
88
if (ret) {
89
goto exit;
90
}
32
}
91
- bdrv_set_backing_hd(new_top_bs, base);
33
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
92
+
34
qemu_iovec_concat(*backing_qiov, qiov, 0, size);
93
+ bdrv_set_backing_hd(new_top_bs, base, &local_err);
35
94
+ if (local_err) {
36
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO);
95
+ ret = -EPERM;
37
- ret = bdrv_preadv(s->bs->backing, pos, *backing_qiov);
96
+ error_report_err(local_err);
38
+ ret = bdrv_co_preadv(s->bs->backing, pos, size, *backing_qiov, 0);
97
+ goto exit;
39
if (ret < 0) {
98
+ }
40
return ret;
99
100
ret = 0;
101
exit:
102
diff --git a/block/commit.c b/block/commit.c
103
index XXXXXXX..XXXXXXX 100644
104
--- a/block/commit.c
105
+++ b/block/commit.c
106
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
107
* filter driver from the backing chain. Do this as the final step so that
108
* the 'consistent read' permission can be granted. */
109
if (remove_commit_top_bs) {
110
- bdrv_set_backing_hd(overlay_bs, top);
111
+ bdrv_set_backing_hd(overlay_bs, top, &error_abort);
112
}
41
}
113
}
42
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
114
115
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
116
goto fail;
117
}
43
}
118
44
119
- bdrv_set_backing_hd(commit_top_bs, top);
45
BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE);
120
- bdrv_set_backing_hd(overlay_bs, commit_top_bs);
46
- ret = bdrv_pwritev(s->bs->file, offset, &qiov);
121
+ bdrv_set_backing_hd(commit_top_bs, top, &error_abort);
47
+ ret = bdrv_co_pwritev(s->bs->file, offset, qiov.size, &qiov, 0);
122
+ bdrv_set_backing_hd(overlay_bs, commit_top_bs, &error_abort);
48
if (ret < 0) {
123
49
goto out;
124
s->commit_top_bs = commit_top_bs;
125
bdrv_unref(commit_top_bs);
126
@@ -XXX,XX +XXX,XX @@ fail:
127
blk_unref(s->top);
128
}
50
}
129
if (commit_top_bs) {
51
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qed_aio_write_main(QEDAIOCB *acb)
130
- bdrv_set_backing_hd(overlay_bs, top);
52
trace_qed_aio_write_main(s, acb, 0, offset, acb->cur_qiov.size);
131
+ bdrv_set_backing_hd(overlay_bs, top, &error_abort);
53
54
BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO);
55
- ret = bdrv_pwritev(s->bs->file, offset, &acb->cur_qiov);
56
+ ret = bdrv_co_pwritev(s->bs->file, offset, acb->cur_qiov.size,
57
+ &acb->cur_qiov, 0);
58
if (ret < 0) {
59
return ret;
132
}
60
}
133
block_job_unref(&s->common);
61
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qed_aio_write_main(QEDAIOCB *acb)
134
}
62
* region. The solution is to flush after writing a new data
135
@@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs)
63
* cluster and before updating the L2 table.
136
goto ro_cleanup;
64
*/
65
- ret = bdrv_flush(s->bs->file->bs);
66
+ ret = bdrv_co_flush(s->bs->file->bs);
67
if (ret < 0) {
68
return ret;
69
}
70
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qed_aio_read_data(void *opaque, int ret,
137
}
71
}
138
72
139
- bdrv_set_backing_hd(commit_top_bs, backing_file_bs);
73
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
140
- bdrv_set_backing_hd(bs, commit_top_bs);
74
- ret = bdrv_preadv(bs->file, offset, &acb->cur_qiov);
141
+ bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort);
75
+ ret = bdrv_co_preadv(bs->file, offset, acb->cur_qiov.size,
142
+ bdrv_set_backing_hd(bs, commit_top_bs, &error_abort);
76
+ &acb->cur_qiov, 0);
143
144
ret = blk_insert_bs(backing, backing_file_bs, &local_err);
145
if (ret < 0) {
77
if (ret < 0) {
146
@@ -XXX,XX +XXX,XX @@ ro_cleanup:
78
return ret;
147
148
blk_unref(backing);
149
if (backing_file_bs) {
150
- bdrv_set_backing_hd(bs, backing_file_bs);
151
+ bdrv_set_backing_hd(bs, backing_file_bs, &error_abort);
152
}
79
}
153
bdrv_unref(commit_top_bs);
154
blk_unref(src);
155
diff --git a/block/mirror.c b/block/mirror.c
156
index XXXXXXX..XXXXXXX 100644
157
--- a/block/mirror.c
158
+++ b/block/mirror.c
159
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
160
BlockDriverState *src = s->source;
161
BlockDriverState *target_bs = blk_bs(s->target);
162
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
163
+ Error *local_err = NULL;
164
165
/* Make sure that the source BDS doesn't go away before we called
166
* block_job_completed(). */
167
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
168
if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
169
BlockDriverState *backing = s->is_none_mode ? src : s->base;
170
if (backing_bs(target_bs) != backing) {
171
- bdrv_set_backing_hd(target_bs, backing);
172
+ bdrv_set_backing_hd(target_bs, backing, &local_err);
173
+ if (local_err) {
174
+ error_report_err(local_err);
175
+ data->ret = -EPERM;
176
+ }
177
}
178
}
179
180
diff --git a/block/stream.c b/block/stream.c
181
index XXXXXXX..XXXXXXX 100644
182
--- a/block/stream.c
183
+++ b/block/stream.c
184
@@ -XXX,XX +XXX,XX @@ static void stream_complete(BlockJob *job, void *opaque)
185
StreamCompleteData *data = opaque;
186
BlockDriverState *bs = blk_bs(job->blk);
187
BlockDriverState *base = s->base;
188
+ Error *local_err = NULL;
189
190
if (!block_job_is_cancelled(&s->common) && data->reached_end &&
191
data->ret == 0) {
192
@@ -XXX,XX +XXX,XX @@ static void stream_complete(BlockJob *job, void *opaque)
193
}
194
}
195
data->ret = bdrv_change_backing_file(bs, base_id, base_fmt);
196
- bdrv_set_backing_hd(bs, base);
197
+ bdrv_set_backing_hd(bs, base, &local_err);
198
+ if (local_err) {
199
+ error_report_err(local_err);
200
+ data->ret = -EPERM;
201
+ goto out;
202
+ }
203
}
204
205
+out:
206
/* Reopen the image back in read-only mode if necessary */
207
if (s->bs_flags != bdrv_get_flags(bs)) {
208
/* Give up write permissions before making it read-only */
209
diff --git a/block/vvfat.c b/block/vvfat.c
210
index XXXXXXX..XXXXXXX 100644
211
--- a/block/vvfat.c
212
+++ b/block/vvfat.c
213
@@ -XXX,XX +XXX,XX @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
214
&error_abort);
215
*(void**) backing->opaque = s;
216
217
- bdrv_set_backing_hd(s->bs, backing);
218
+ bdrv_set_backing_hd(s->bs, backing, &error_abort);
219
bdrv_unref(backing);
220
221
return 0;
222
diff --git a/include/block/block.h b/include/block/block.h
223
index XXXXXXX..XXXXXXX 100644
224
--- a/include/block/block.h
225
+++ b/include/block/block.h
226
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename,
227
BlockDriverState* parent,
228
const BdrvChildRole *child_role,
229
bool allow_none, Error **errp);
230
-void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd);
231
+void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
232
+ Error **errp);
233
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
234
const char *bdref_key, Error **errp);
235
BlockDriverState *bdrv_open(const char *filename, const char *reference,
236
--
80
--
237
1.8.3.1
81
1.8.3.1
238
82
239
83
diff view generated by jsdifflib
1
In some cases, we want to remove op blockers on intermediate nodes
1
From: "sochin.jiang" <sochin.jiang@huawei.com>
2
before the whole block job transaction has completed (because they block
3
restoring the final graph state during completion). Provide a function
4
for this.
5
2
6
The whole block job lifecycle is a bit messed up and it's hard to
3
img_commit could fall into an infinite loop calling run_block_job() if
7
actually do all things in the right order, but I'll leave simplifying
4
its blockjob fails on any I/O error, fix this already known problem.
8
this for another day.
9
5
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: sochin.jiang <sochin.jiang@huawei.com>
11
Acked-by: Fam Zheng <famz@redhat.com>
7
Message-id: 1497509253-28941-1-git-send-email-sochin.jiang@huawei.com
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
9
---
14
blockjob.c | 20 +++++++++++++-------
10
blockjob.c | 4 ++--
15
include/block/blockjob.h | 9 +++++++++
11
include/block/blockjob.h | 18 ++++++++++++++++++
16
2 files changed, 22 insertions(+), 7 deletions(-)
12
qemu-img.c | 20 +++++++++++++-------
13
3 files changed, 33 insertions(+), 9 deletions(-)
17
14
18
diff --git a/blockjob.c b/blockjob.c
15
diff --git a/blockjob.c b/blockjob.c
19
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
20
--- a/blockjob.c
17
--- a/blockjob.c
21
+++ b/blockjob.c
18
+++ b/blockjob.c
22
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
19
@@ -XXX,XX +XXX,XX @@ static void block_job_resume(BlockJob *job)
23
block_job_unref(job);
20
block_job_enter(job);
24
}
21
}
25
22
26
+void block_job_remove_all_bdrv(BlockJob *job)
23
-static void block_job_ref(BlockJob *job)
27
+{
24
+void block_job_ref(BlockJob *job)
28
+ GSList *l;
29
+ for (l = job->nodes; l; l = l->next) {
30
+ BdrvChild *c = l->data;
31
+ bdrv_op_unblock_all(c->bs, job->blocker);
32
+ bdrv_root_unref_child(c);
33
+ }
34
+ g_slist_free(job->nodes);
35
+ job->nodes = NULL;
36
+}
37
+
38
int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
39
uint64_t perm, uint64_t shared_perm, Error **errp)
40
{
25
{
41
@@ -XXX,XX +XXX,XX @@ void block_job_ref(BlockJob *job)
26
++job->refcnt;
42
void block_job_unref(BlockJob *job)
27
}
28
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
29
void *opaque);
30
static void block_job_detach_aio_context(void *opaque);
31
32
-static void block_job_unref(BlockJob *job)
33
+void block_job_unref(BlockJob *job)
43
{
34
{
44
if (--job->refcnt == 0) {
35
if (--job->refcnt == 0) {
45
- GSList *l;
46
BlockDriverState *bs = blk_bs(job->blk);
36
BlockDriverState *bs = blk_bs(job->blk);
47
bs->job = NULL;
48
- for (l = job->nodes; l; l = l->next) {
49
- BdrvChild *c = l->data;
50
- bdrv_op_unblock_all(c->bs, job->blocker);
51
- bdrv_root_unref_child(c);
52
- }
53
- g_slist_free(job->nodes);
54
+ block_job_remove_all_bdrv(job);
55
blk_remove_aio_context_notifier(job->blk,
56
block_job_attached_aio_context,
57
block_job_detach_aio_context, job);
58
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
37
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
59
index XXXXXXX..XXXXXXX 100644
38
index XXXXXXX..XXXXXXX 100644
60
--- a/include/block/blockjob.h
39
--- a/include/block/blockjob.h
61
+++ b/include/block/blockjob.h
40
+++ b/include/block/blockjob.h
62
@@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
41
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job);
63
uint64_t perm, uint64_t shared_perm, Error **errp);
42
BlockJobTxn *block_job_txn_new(void);
64
43
65
/**
44
/**
66
+ * block_job_remove_all_bdrv:
45
+ * block_job_ref:
67
+ * @job: The block job
68
+ *
46
+ *
69
+ * Remove all BlockDriverStates from the list of nodes that are involved in the
47
+ * Add a reference to BlockJob refcnt, it will be decreased with
70
+ * job. This removes the blockers added with block_job_add_bdrv().
48
+ * block_job_unref, and then be freed if it comes to be the last
49
+ * reference.
71
+ */
50
+ */
72
+void block_job_remove_all_bdrv(BlockJob *job);
51
+void block_job_ref(BlockJob *job);
73
+
52
+
74
+/**
53
+/**
75
* block_job_set_speed:
54
+ * block_job_unref:
76
* @job: The job to set the speed for.
55
+ *
77
* @speed: The new value
56
+ * Release a reference that was previously acquired with block_job_ref
57
+ * or block_job_create. If it's the last reference to the object, it will be
58
+ * freed.
59
+ */
60
+void block_job_unref(BlockJob *job);
61
+
62
+/**
63
* block_job_txn_unref:
64
*
65
* Release a reference that was previously acquired with block_job_txn_add_job
66
diff --git a/qemu-img.c b/qemu-img.c
67
index XXXXXXX..XXXXXXX 100644
68
--- a/qemu-img.c
69
+++ b/qemu-img.c
70
@@ -XXX,XX +XXX,XX @@ static void common_block_job_cb(void *opaque, int ret)
71
static void run_block_job(BlockJob *job, Error **errp)
72
{
73
AioContext *aio_context = blk_get_aio_context(job->blk);
74
+ int ret = 0;
75
76
- /* FIXME In error cases, the job simply goes away and we access a dangling
77
- * pointer below. */
78
aio_context_acquire(aio_context);
79
+ block_job_ref(job);
80
do {
81
aio_poll(aio_context, true);
82
qemu_progress_print(job->len ?
83
((float)job->offset / job->len * 100.f) : 0.0f, 0);
84
- } while (!job->ready);
85
+ } while (!job->ready && !job->completed);
86
87
- block_job_complete_sync(job, errp);
88
+ if (!job->completed) {
89
+ ret = block_job_complete_sync(job, errp);
90
+ } else {
91
+ ret = job->ret;
92
+ }
93
+ block_job_unref(job);
94
aio_context_release(aio_context);
95
96
- /* A block job may finish instantaneously without publishing any progress,
97
- * so just signal completion here */
98
- qemu_progress_print(100.f, 0);
99
+ /* publish completion progress only when success */
100
+ if (!ret) {
101
+ qemu_progress_print(100.f, 0);
102
+ }
103
}
104
105
static int img_commit(int argc, char **argv)
78
--
106
--
79
1.8.3.1
107
1.8.3.1
80
108
81
109
diff view generated by jsdifflib
1
All callers will have to request permissions for all of their child
1
From: Max Reitz <mreitz@redhat.com>
2
nodes. Block drivers that act as simply filters can use the default
3
implementation of .bdrv_child_perm().
4
2
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
The bs->exact_filename field may not be sufficient to store the full
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
4
blkdebug node filename. In this case, we should not generate a filename
7
Acked-by: Fam Zheng <famz@redhat.com>
5
at all instead of an unusable one.
6
7
Cc: qemu-stable@nongnu.org
8
Reported-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Message-id: 20170613172006.19685-2-mreitz@redhat.com
11
Reviewed-by: Alberto Garcia <berto@igalia.com>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
---
14
---
9
block/blkdebug.c | 2 ++
15
block/blkdebug.c | 10 +++++++---
10
block/blkreplay.c | 1 +
16
1 file changed, 7 insertions(+), 3 deletions(-)
11
block/blkverify.c | 1 +
12
block/quorum.c | 2 ++
13
block/raw-format.c | 1 +
14
block/replication.c | 1 +
15
6 files changed, 8 insertions(+)
16
17
17
diff --git a/block/blkdebug.c b/block/blkdebug.c
18
diff --git a/block/blkdebug.c b/block/blkdebug.c
18
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
19
--- a/block/blkdebug.c
20
--- a/block/blkdebug.c
20
+++ b/block/blkdebug.c
21
+++ b/block/blkdebug.c
21
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = {
22
@@ -XXX,XX +XXX,XX @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
22
.bdrv_file_open = blkdebug_open,
23
}
23
.bdrv_close = blkdebug_close,
24
24
.bdrv_reopen_prepare = blkdebug_reopen_prepare,
25
if (!force_json && bs->file->bs->exact_filename[0]) {
25
+ .bdrv_child_perm = bdrv_filter_default_perms,
26
- snprintf(bs->exact_filename, sizeof(bs->exact_filename),
26
+
27
- "blkdebug:%s:%s", s->config_file ?: "",
27
.bdrv_getlength = blkdebug_getlength,
28
- bs->file->bs->exact_filename);
28
.bdrv_truncate = blkdebug_truncate,
29
+ int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
29
.bdrv_refresh_filename = blkdebug_refresh_filename,
30
+ "blkdebug:%s:%s", s->config_file ?: "",
30
diff --git a/block/blkreplay.c b/block/blkreplay.c
31
+ bs->file->bs->exact_filename);
31
index XXXXXXX..XXXXXXX 100755
32
+ if (ret >= sizeof(bs->exact_filename)) {
32
--- a/block/blkreplay.c
33
+ /* An overflow makes the filename unusable, so do not report any */
33
+++ b/block/blkreplay.c
34
+ bs->exact_filename[0] = 0;
34
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkreplay = {
35
+ }
35
36
}
36
.bdrv_file_open = blkreplay_open,
37
37
.bdrv_close = blkreplay_close,
38
opts = qdict_new();
38
+ .bdrv_child_perm = bdrv_filter_default_perms,
39
.bdrv_getlength = blkreplay_getlength,
40
41
.bdrv_co_preadv = blkreplay_co_preadv,
42
diff --git a/block/blkverify.c b/block/blkverify.c
43
index XXXXXXX..XXXXXXX 100644
44
--- a/block/blkverify.c
45
+++ b/block/blkverify.c
46
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkverify = {
47
.bdrv_parse_filename = blkverify_parse_filename,
48
.bdrv_file_open = blkverify_open,
49
.bdrv_close = blkverify_close,
50
+ .bdrv_child_perm = bdrv_filter_default_perms,
51
.bdrv_getlength = blkverify_getlength,
52
.bdrv_refresh_filename = blkverify_refresh_filename,
53
54
diff --git a/block/quorum.c b/block/quorum.c
55
index XXXXXXX..XXXXXXX 100644
56
--- a/block/quorum.c
57
+++ b/block/quorum.c
58
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
59
.bdrv_add_child = quorum_add_child,
60
.bdrv_del_child = quorum_del_child,
61
62
+ .bdrv_child_perm = bdrv_filter_default_perms,
63
+
64
.is_filter = true,
65
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
66
};
67
diff --git a/block/raw-format.c b/block/raw-format.c
68
index XXXXXXX..XXXXXXX 100644
69
--- a/block/raw-format.c
70
+++ b/block/raw-format.c
71
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
72
.bdrv_reopen_abort = &raw_reopen_abort,
73
.bdrv_open = &raw_open,
74
.bdrv_close = &raw_close,
75
+ .bdrv_child_perm = bdrv_filter_default_perms,
76
.bdrv_create = &raw_create,
77
.bdrv_co_preadv = &raw_co_preadv,
78
.bdrv_co_pwritev = &raw_co_pwritev,
79
diff --git a/block/replication.c b/block/replication.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/block/replication.c
82
+++ b/block/replication.c
83
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_replication = {
84
85
.bdrv_open = replication_open,
86
.bdrv_close = replication_close,
87
+ .bdrv_child_perm = bdrv_filter_default_perms,
88
89
.bdrv_getlength = replication_getlength,
90
.bdrv_co_readv = replication_co_readv,
91
--
39
--
92
1.8.3.1
40
1.8.3.1
93
41
94
42
diff view generated by jsdifflib
1
Backing files are somewhat special compared to other kinds of children
1
From: Max Reitz <mreitz@redhat.com>
2
because they are attached and detached using bdrv_set_backing_hd()
3
rather than the normal set of functions, which does a few more things
4
like setting backing blockers, toggling the BDRV_O_NO_BACKING flag,
5
setting parent_bs->backing_file, etc.
6
2
7
These special features are a reason why change_parent_backing_link()
3
The bs->exact_filename field may not be sufficient to store the full
8
can't handle backing files yet. With abstracting the additional features
4
blkverify node filename. In this case, we should not generate a filename
9
into .attach/.detach callbacks, we get a step closer to a function that
5
at all instead of an unusable one.
10
can actually deal with this.
11
6
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Cc: qemu-stable@nongnu.org
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reported-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
14
Acked-by: Fam Zheng <famz@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Message-id: 20170613172006.19685-3-mreitz@redhat.com
11
Reviewed-by: Alberto Garcia <berto@igalia.com>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
14
---
16
block.c | 95 ++++++++++++++++++++++++++++++-----------------
15
block/blkverify.c | 12 ++++++++----
17
include/block/block_int.h | 3 ++
16
1 file changed, 8 insertions(+), 4 deletions(-)
18
2 files changed, 63 insertions(+), 35 deletions(-)
19
17
20
diff --git a/block.c b/block.c
18
diff --git a/block/blkverify.c b/block/blkverify.c
21
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
22
--- a/block.c
20
--- a/block/blkverify.c
23
+++ b/block.c
21
+++ b/block/blkverify.c
24
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = {
22
@@ -XXX,XX +XXX,XX @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
25
.drained_end = bdrv_child_cb_drained_end,
23
if (bs->file->bs->exact_filename[0]
26
};
24
&& s->test_file->bs->exact_filename[0])
27
25
{
28
+static void bdrv_backing_attach(BdrvChild *c)
26
- snprintf(bs->exact_filename, sizeof(bs->exact_filename),
29
+{
27
- "blkverify:%s:%s",
30
+ BlockDriverState *parent = c->opaque;
28
- bs->file->bs->exact_filename,
31
+ BlockDriverState *backing_hd = c->bs;
29
- s->test_file->bs->exact_filename);
32
+
30
+ int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
33
+ assert(!parent->backing_blocker);
31
+ "blkverify:%s:%s",
34
+ error_setg(&parent->backing_blocker,
32
+ bs->file->bs->exact_filename,
35
+ "node is used as backing hd of '%s'",
33
+ s->test_file->bs->exact_filename);
36
+ bdrv_get_device_or_node_name(parent));
34
+ if (ret >= sizeof(bs->exact_filename)) {
37
+
35
+ /* An overflow makes the filename unusable, so do not report any */
38
+ parent->open_flags &= ~BDRV_O_NO_BACKING;
36
+ bs->exact_filename[0] = 0;
39
+ pstrcpy(parent->backing_file, sizeof(parent->backing_file),
40
+ backing_hd->filename);
41
+ pstrcpy(parent->backing_format, sizeof(parent->backing_format),
42
+ backing_hd->drv ? backing_hd->drv->format_name : "");
43
+
44
+ bdrv_op_block_all(backing_hd, parent->backing_blocker);
45
+ /* Otherwise we won't be able to commit or stream */
46
+ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET,
47
+ parent->backing_blocker);
48
+ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM,
49
+ parent->backing_blocker);
50
+ /*
51
+ * We do backup in 3 ways:
52
+ * 1. drive backup
53
+ * The target bs is new opened, and the source is top BDS
54
+ * 2. blockdev backup
55
+ * Both the source and the target are top BDSes.
56
+ * 3. internal backup(used for block replication)
57
+ * Both the source and the target are backing file
58
+ *
59
+ * In case 1 and 2, neither the source nor the target is the backing file.
60
+ * In case 3, we will block the top BDS, so there is only one block job
61
+ * for the top BDS and its backing chain.
62
+ */
63
+ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_SOURCE,
64
+ parent->backing_blocker);
65
+ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
66
+ parent->backing_blocker);
67
+}
68
+
69
+static void bdrv_backing_detach(BdrvChild *c)
70
+{
71
+ BlockDriverState *parent = c->opaque;
72
+
73
+ assert(parent->backing_blocker);
74
+ bdrv_op_unblock_all(c->bs, parent->backing_blocker);
75
+ error_free(parent->backing_blocker);
76
+ parent->backing_blocker = NULL;
77
+}
78
+
79
/*
80
* Returns the options and flags that bs->backing should get, based on the
81
* given options and flags for the parent BDS
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
83
84
const BdrvChildRole child_backing = {
85
.get_parent_desc = bdrv_child_get_parent_desc,
86
+ .attach = bdrv_backing_attach,
87
+ .detach = bdrv_backing_detach,
88
.inherit_options = bdrv_backing_options,
89
.drained_begin = bdrv_child_cb_drained_begin,
90
.drained_end = bdrv_child_cb_drained_end,
91
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
92
if (old_bs->quiesce_counter && child->role->drained_end) {
93
child->role->drained_end(child);
94
}
95
+ if (child->role->detach) {
96
+ child->role->detach(child);
97
+ }
98
QLIST_REMOVE(child, next_parent);
99
100
/* Update permissions for old node. This is guaranteed to succeed
101
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
102
bdrv_check_perm(new_bs, perm, shared_perm, &error_abort);
103
}
104
bdrv_set_perm(new_bs, perm, shared_perm);
105
+
106
+ if (child->role->attach) {
107
+ child->role->attach(child);
108
+ }
37
+ }
109
}
38
}
110
}
39
}
111
40
112
@@ -XXX,XX +XXX,XX @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
113
}
114
115
if (bs->backing) {
116
- assert(bs->backing_blocker);
117
- bdrv_op_unblock_all(bs->backing->bs, bs->backing_blocker);
118
bdrv_unref_child(bs, bs->backing);
119
- } else if (backing_hd) {
120
- error_setg(&bs->backing_blocker,
121
- "node is used as backing hd of '%s'",
122
- bdrv_get_device_or_node_name(bs));
123
}
124
125
if (!backing_hd) {
126
- error_free(bs->backing_blocker);
127
- bs->backing_blocker = NULL;
128
bs->backing = NULL;
129
goto out;
130
}
131
/* FIXME Error handling */
132
bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
133
&error_abort);
134
- bs->open_flags &= ~BDRV_O_NO_BACKING;
135
- pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
136
- pstrcpy(bs->backing_format, sizeof(bs->backing_format),
137
- backing_hd->drv ? backing_hd->drv->format_name : "");
138
139
- bdrv_op_block_all(backing_hd, bs->backing_blocker);
140
- /* Otherwise we won't be able to commit or stream */
141
- bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET,
142
- bs->backing_blocker);
143
- bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM,
144
- bs->backing_blocker);
145
- /*
146
- * We do backup in 3 ways:
147
- * 1. drive backup
148
- * The target bs is new opened, and the source is top BDS
149
- * 2. blockdev backup
150
- * Both the source and the target are top BDSes.
151
- * 3. internal backup(used for block replication)
152
- * Both the source and the target are backing file
153
- *
154
- * In case 1 and 2, neither the source nor the target is the backing file.
155
- * In case 3, we will block the top BDS, so there is only one block job
156
- * for the top BDS and its backing chain.
157
- */
158
- bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_SOURCE,
159
- bs->backing_blocker);
160
- bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
161
- bs->backing_blocker);
162
out:
163
bdrv_refresh_limits(bs, NULL);
164
}
165
diff --git a/include/block/block_int.h b/include/block/block_int.h
166
index XXXXXXX..XXXXXXX 100644
167
--- a/include/block/block_int.h
168
+++ b/include/block/block_int.h
169
@@ -XXX,XX +XXX,XX @@ struct BdrvChildRole {
170
*/
171
void (*drained_begin)(BdrvChild *child);
172
void (*drained_end)(BdrvChild *child);
173
+
174
+ void (*attach)(BdrvChild *child);
175
+ void (*detach)(BdrvChild *child);
176
};
177
178
extern const BdrvChildRole child_file;
179
--
41
--
180
1.8.3.1
42
1.8.3.1
181
43
182
44
diff view generated by jsdifflib
1
From: Peter Lieven <pl@kamp.de>
1
From: Max Reitz <mreitz@redhat.com>
2
2
3
the convert process is currently completely implemented with sync operations.
3
uri_parse(...)->scheme may be NULL. In fact, probably every field may be
4
That means it reads one buffer and then writes it. No parallelism and each sync
4
NULL, and the callers do test this for all of the other fields but not
5
request takes as long as it takes until it is completed.
5
for scheme (except for block/gluster.c; block/vxhs.c does not access
6
that field at all).
6
7
7
This can be a big performance hit when the convert process reads and writes
8
We can easily fix this by using g_strcmp0() instead of strcmp().
8
to devices which do not benefit from kernel readahead or pagecache.
9
In our environment we heavily have the following two use cases when using
10
qemu-img convert.
11
9
12
a) reading from NFS and writing to iSCSI for deploying templates
10
Cc: qemu-stable@nongnu.org
13
b) reading from iSCSI and writing to NFS for backups
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
Message-id: 20170613205726.13544-1-mreitz@redhat.com
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
block/nbd.c | 6 +++---
17
block/nfs.c | 2 +-
18
block/sheepdog.c | 6 +++---
19
block/ssh.c | 2 +-
20
4 files changed, 8 insertions(+), 8 deletions(-)
14
21
15
In both processes we use libiscsi and libnfs so we have no kernel cache.
22
diff --git a/block/nbd.c b/block/nbd.c
16
17
This patch changes the convert process to work with parallel running coroutines
18
which can significantly improve performance for network storage devices:
19
20
qemu-img (master)
21
nfs -> iscsi 22.8 secs
22
nfs -> ram 11.7 secs
23
ram -> iscsi 12.3 secs
24
25
qemu-img-async (8 coroutines, in-order write disabled)
26
nfs -> iscsi 11.0 secs
27
nfs -> ram 10.4 secs
28
ram -> iscsi 9.0 secs
29
30
This patches introduces 2 new cmdline parameters. The -m parameter to specify
31
the number of coroutines running in parallel (defaults to 8). And the -W parameter to
32
allow qemu-img to write to the target out of order rather than sequential. This improves
33
performance as the writes do not have to wait for each other to complete.
34
35
Signed-off-by: Peter Lieven <pl@kamp.de>
36
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
37
---
38
qemu-img-cmds.hx | 4 +-
39
qemu-img.c | 322 ++++++++++++++++++++++++++++++++++++++-----------------
40
qemu-img.texi | 16 ++-
41
3 files changed, 243 insertions(+), 99 deletions(-)
42
43
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
44
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
45
--- a/qemu-img-cmds.hx
24
--- a/block/nbd.c
46
+++ b/qemu-img-cmds.hx
25
+++ b/block/nbd.c
47
@@ -XXX,XX +XXX,XX @@ STEXI
26
@@ -XXX,XX +XXX,XX @@ static int nbd_parse_uri(const char *filename, QDict *options)
48
ETEXI
27
}
49
28
50
DEF("convert", img_convert,
29
/* transport */
51
- "convert [--object objectdef] [--image-opts] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] filename [filename2 [...]] output_filename")
30
- if (!strcmp(uri->scheme, "nbd")) {
52
+ "convert [--object objectdef] [--image-opts] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
31
+ if (!g_strcmp0(uri->scheme, "nbd")) {
53
STEXI
32
is_unix = false;
54
-@item convert [--object @var{objectdef}] [--image-opts] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
33
- } else if (!strcmp(uri->scheme, "nbd+tcp")) {
55
+@item convert [--object @var{objectdef}] [--image-opts] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
34
+ } else if (!g_strcmp0(uri->scheme, "nbd+tcp")) {
56
ETEXI
35
is_unix = false;
57
36
- } else if (!strcmp(uri->scheme, "nbd+unix")) {
58
DEF("dd", img_dd,
37
+ } else if (!g_strcmp0(uri->scheme, "nbd+unix")) {
59
diff --git a/qemu-img.c b/qemu-img.c
38
is_unix = true;
39
} else {
40
ret = -EINVAL;
41
diff --git a/block/nfs.c b/block/nfs.c
60
index XXXXXXX..XXXXXXX 100644
42
index XXXXXXX..XXXXXXX 100644
61
--- a/qemu-img.c
43
--- a/block/nfs.c
62
+++ b/qemu-img.c
44
+++ b/block/nfs.c
63
@@ -XXX,XX +XXX,XX @@ static void QEMU_NORETURN help(void)
45
@@ -XXX,XX +XXX,XX @@ static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
64
" kinds of errors, with a higher risk of choosing the wrong fix or\n"
46
error_setg(errp, "Invalid URI specified");
65
" hiding corruption that has already occurred.\n"
47
goto out;
66
"\n"
67
+ "Parameters to convert subcommand:\n"
68
+ " '-m' specifies how many coroutines work in parallel during the convert\n"
69
+ " process (defaults to 8)\n"
70
+ " '-W' allow to write to the target out of order rather than sequential\n"
71
+ "\n"
72
"Parameters to snapshot subcommand:\n"
73
" 'snapshot' is the name of the snapshot to create, apply or delete\n"
74
" '-a' applies a snapshot (revert disk to saved state)\n"
75
@@ -XXX,XX +XXX,XX @@ enum ImgConvertBlockStatus {
76
BLK_BACKING_FILE,
77
};
78
79
+#define MAX_COROUTINES 16
80
+
81
typedef struct ImgConvertState {
82
BlockBackend **src;
83
int64_t *src_sectors;
84
- int src_cur, src_num;
85
- int64_t src_cur_offset;
86
+ int src_num;
87
int64_t total_sectors;
88
int64_t allocated_sectors;
89
+ int64_t allocated_done;
90
+ int64_t sector_num;
91
+ int64_t wr_offs;
92
enum ImgConvertBlockStatus status;
93
int64_t sector_next_status;
94
BlockBackend *target;
95
bool has_zero_init;
96
bool compressed;
97
bool target_has_backing;
98
+ bool wr_in_order;
99
int min_sparse;
100
size_t cluster_sectors;
101
size_t buf_sectors;
102
+ int num_coroutines;
103
+ int running_coroutines;
104
+ Coroutine *co[MAX_COROUTINES];
105
+ int64_t wait_sector_num[MAX_COROUTINES];
106
+ CoMutex lock;
107
+ int ret;
108
} ImgConvertState;
109
110
-static void convert_select_part(ImgConvertState *s, int64_t sector_num)
111
+static void convert_select_part(ImgConvertState *s, int64_t sector_num,
112
+ int *src_cur, int64_t *src_cur_offset)
113
{
114
- assert(sector_num >= s->src_cur_offset);
115
- while (sector_num - s->src_cur_offset >= s->src_sectors[s->src_cur]) {
116
- s->src_cur_offset += s->src_sectors[s->src_cur];
117
- s->src_cur++;
118
- assert(s->src_cur < s->src_num);
119
+ *src_cur = 0;
120
+ *src_cur_offset = 0;
121
+ while (sector_num - *src_cur_offset >= s->src_sectors[*src_cur]) {
122
+ *src_cur_offset += s->src_sectors[*src_cur];
123
+ (*src_cur)++;
124
+ assert(*src_cur < s->src_num);
125
}
48
}
126
}
49
- if (strcmp(uri->scheme, "nfs") != 0) {
127
50
+ if (g_strcmp0(uri->scheme, "nfs") != 0) {
128
static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
51
error_setg(errp, "URI scheme must be 'nfs'");
129
{
52
goto out;
130
- int64_t ret;
131
- int n;
132
+ int64_t ret, src_cur_offset;
133
+ int n, src_cur;
134
135
- convert_select_part(s, sector_num);
136
+ convert_select_part(s, sector_num, &src_cur, &src_cur_offset);
137
138
assert(s->total_sectors > sector_num);
139
n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS);
140
141
if (s->sector_next_status <= sector_num) {
142
BlockDriverState *file;
143
- ret = bdrv_get_block_status(blk_bs(s->src[s->src_cur]),
144
- sector_num - s->src_cur_offset,
145
+ ret = bdrv_get_block_status(blk_bs(s->src[src_cur]),
146
+ sector_num - src_cur_offset,
147
n, &n, &file);
148
if (ret < 0) {
149
return ret;
150
@@ -XXX,XX +XXX,XX @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
151
/* Check block status of the backing file chain to avoid
152
* needlessly reading zeroes and limiting the iteration to the
153
* buffer size */
154
- ret = bdrv_get_block_status_above(blk_bs(s->src[s->src_cur]), NULL,
155
- sector_num - s->src_cur_offset,
156
+ ret = bdrv_get_block_status_above(blk_bs(s->src[src_cur]), NULL,
157
+ sector_num - src_cur_offset,
158
n, &n, &file);
159
if (ret < 0) {
160
return ret;
161
@@ -XXX,XX +XXX,XX @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
162
return n;
163
}
164
165
-static int convert_read(ImgConvertState *s, int64_t sector_num, int nb_sectors,
166
- uint8_t *buf)
167
+static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
168
+ int nb_sectors, uint8_t *buf)
169
{
170
- int n;
171
- int ret;
172
+ int n, ret;
173
+ QEMUIOVector qiov;
174
+ struct iovec iov;
175
176
assert(nb_sectors <= s->buf_sectors);
177
while (nb_sectors > 0) {
178
BlockBackend *blk;
179
- int64_t bs_sectors;
180
+ int src_cur;
181
+ int64_t bs_sectors, src_cur_offset;
182
183
/* In the case of compression with multiple source files, we can get a
184
* nb_sectors that spreads into the next part. So we must be able to
185
* read across multiple BDSes for one convert_read() call. */
186
- convert_select_part(s, sector_num);
187
- blk = s->src[s->src_cur];
188
- bs_sectors = s->src_sectors[s->src_cur];
189
-
190
- n = MIN(nb_sectors, bs_sectors - (sector_num - s->src_cur_offset));
191
- ret = blk_pread(blk,
192
- (sector_num - s->src_cur_offset) << BDRV_SECTOR_BITS,
193
- buf, n << BDRV_SECTOR_BITS);
194
+ convert_select_part(s, sector_num, &src_cur, &src_cur_offset);
195
+ blk = s->src[src_cur];
196
+ bs_sectors = s->src_sectors[src_cur];
197
+
198
+ n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset));
199
+ iov.iov_base = buf;
200
+ iov.iov_len = n << BDRV_SECTOR_BITS;
201
+ qemu_iovec_init_external(&qiov, &iov, 1);
202
+
203
+ ret = blk_co_preadv(
204
+ blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS,
205
+ n << BDRV_SECTOR_BITS, &qiov, 0);
206
if (ret < 0) {
207
return ret;
208
}
209
@@ -XXX,XX +XXX,XX @@ static int convert_read(ImgConvertState *s, int64_t sector_num, int nb_sectors,
210
return 0;
211
}
212
213
-static int convert_write(ImgConvertState *s, int64_t sector_num, int nb_sectors,
214
- const uint8_t *buf)
215
+
216
+static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num,
217
+ int nb_sectors, uint8_t *buf,
218
+ enum ImgConvertBlockStatus status)
219
{
220
int ret;
221
+ QEMUIOVector qiov;
222
+ struct iovec iov;
223
224
while (nb_sectors > 0) {
225
int n = nb_sectors;
226
-
227
- switch (s->status) {
228
+ switch (status) {
229
case BLK_BACKING_FILE:
230
/* If we have a backing file, leave clusters unallocated that are
231
* unallocated in the source image, so that the backing file is
232
@@ -XXX,XX +XXX,XX @@ static int convert_write(ImgConvertState *s, int64_t sector_num, int nb_sectors,
233
break;
234
}
235
236
- ret = blk_pwrite_compressed(s->target,
237
- sector_num << BDRV_SECTOR_BITS,
238
- buf, n << BDRV_SECTOR_BITS);
239
+ iov.iov_base = buf;
240
+ iov.iov_len = n << BDRV_SECTOR_BITS;
241
+ qemu_iovec_init_external(&qiov, &iov, 1);
242
+
243
+ ret = blk_co_pwritev(s->target, sector_num << BDRV_SECTOR_BITS,
244
+ n << BDRV_SECTOR_BITS, &qiov,
245
+ BDRV_REQ_WRITE_COMPRESSED);
246
if (ret < 0) {
247
return ret;
248
}
249
@@ -XXX,XX +XXX,XX @@ static int convert_write(ImgConvertState *s, int64_t sector_num, int nb_sectors,
250
if (!s->min_sparse ||
251
is_allocated_sectors_min(buf, n, &n, s->min_sparse))
252
{
253
- ret = blk_pwrite(s->target, sector_num << BDRV_SECTOR_BITS,
254
- buf, n << BDRV_SECTOR_BITS, 0);
255
+ iov.iov_base = buf;
256
+ iov.iov_len = n << BDRV_SECTOR_BITS;
257
+ qemu_iovec_init_external(&qiov, &iov, 1);
258
+
259
+ ret = blk_co_pwritev(s->target, sector_num << BDRV_SECTOR_BITS,
260
+ n << BDRV_SECTOR_BITS, &qiov, 0);
261
if (ret < 0) {
262
return ret;
263
}
264
@@ -XXX,XX +XXX,XX @@ static int convert_write(ImgConvertState *s, int64_t sector_num, int nb_sectors,
265
if (s->has_zero_init) {
266
break;
267
}
268
- ret = blk_pwrite_zeroes(s->target, sector_num << BDRV_SECTOR_BITS,
269
- n << BDRV_SECTOR_BITS, 0);
270
+ ret = blk_co_pwrite_zeroes(s->target,
271
+ sector_num << BDRV_SECTOR_BITS,
272
+ n << BDRV_SECTOR_BITS, 0);
273
if (ret < 0) {
274
return ret;
275
}
276
@@ -XXX,XX +XXX,XX @@ static int convert_write(ImgConvertState *s, int64_t sector_num, int nb_sectors,
277
return 0;
278
}
279
280
-static int convert_do_copy(ImgConvertState *s)
281
+static void coroutine_fn convert_co_do_copy(void *opaque)
282
{
283
+ ImgConvertState *s = opaque;
284
uint8_t *buf = NULL;
285
- int64_t sector_num, allocated_done;
286
- int ret;
287
- int n;
288
+ int ret, i;
289
+ int index = -1;
290
+
291
+ for (i = 0; i < s->num_coroutines; i++) {
292
+ if (s->co[i] == qemu_coroutine_self()) {
293
+ index = i;
294
+ break;
295
+ }
296
+ }
297
+ assert(index >= 0);
298
+
299
+ s->running_coroutines++;
300
+ buf = blk_blockalign(s->target, s->buf_sectors * BDRV_SECTOR_SIZE);
301
+
302
+ while (1) {
303
+ int n;
304
+ int64_t sector_num;
305
+ enum ImgConvertBlockStatus status;
306
+
307
+ qemu_co_mutex_lock(&s->lock);
308
+ if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) {
309
+ qemu_co_mutex_unlock(&s->lock);
310
+ goto out;
311
+ }
312
+ n = convert_iteration_sectors(s, s->sector_num);
313
+ if (n < 0) {
314
+ qemu_co_mutex_unlock(&s->lock);
315
+ s->ret = n;
316
+ goto out;
317
+ }
318
+ /* save current sector and allocation status to local variables */
319
+ sector_num = s->sector_num;
320
+ status = s->status;
321
+ if (!s->min_sparse && s->status == BLK_ZERO) {
322
+ n = MIN(n, s->buf_sectors);
323
+ }
324
+ /* increment global sector counter so that other coroutines can
325
+ * already continue reading beyond this request */
326
+ s->sector_num += n;
327
+ qemu_co_mutex_unlock(&s->lock);
328
+
329
+ if (status == BLK_DATA || (!s->min_sparse && status == BLK_ZERO)) {
330
+ s->allocated_done += n;
331
+ qemu_progress_print(100.0 * s->allocated_done /
332
+ s->allocated_sectors, 0);
333
+ }
334
+
335
+ if (status == BLK_DATA) {
336
+ ret = convert_co_read(s, sector_num, n, buf);
337
+ if (ret < 0) {
338
+ error_report("error while reading sector %" PRId64
339
+ ": %s", sector_num, strerror(-ret));
340
+ s->ret = ret;
341
+ goto out;
342
+ }
343
+ } else if (!s->min_sparse && status == BLK_ZERO) {
344
+ status = BLK_DATA;
345
+ memset(buf, 0x00, n * BDRV_SECTOR_SIZE);
346
+ }
347
+
348
+ if (s->wr_in_order) {
349
+ /* keep writes in order */
350
+ while (s->wr_offs != sector_num) {
351
+ if (s->ret != -EINPROGRESS) {
352
+ goto out;
353
+ }
354
+ s->wait_sector_num[index] = sector_num;
355
+ qemu_coroutine_yield();
356
+ }
357
+ s->wait_sector_num[index] = -1;
358
+ }
359
+
360
+ ret = convert_co_write(s, sector_num, n, buf, status);
361
+ if (ret < 0) {
362
+ error_report("error while writing sector %" PRId64
363
+ ": %s", sector_num, strerror(-ret));
364
+ s->ret = ret;
365
+ goto out;
366
+ }
367
+
368
+ if (s->wr_in_order) {
369
+ /* reenter the coroutine that might have waited
370
+ * for this write to complete */
371
+ s->wr_offs = sector_num + n;
372
+ for (i = 0; i < s->num_coroutines; i++) {
373
+ if (s->co[i] && s->wait_sector_num[i] == s->wr_offs) {
374
+ /*
375
+ * A -> B -> A cannot occur because A has
376
+ * s->wait_sector_num[i] == -1 during A -> B. Therefore
377
+ * B will never enter A during this time window.
378
+ */
379
+ qemu_coroutine_enter(s->co[i]);
380
+ break;
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+out:
387
+ qemu_vfree(buf);
388
+ s->co[index] = NULL;
389
+ s->running_coroutines--;
390
+ if (!s->running_coroutines && s->ret == -EINPROGRESS) {
391
+ /* the convert job finished successfully */
392
+ s->ret = 0;
393
+ }
394
+}
395
+
396
+static int convert_do_copy(ImgConvertState *s)
397
+{
398
+ int ret, i, n;
399
+ int64_t sector_num = 0;
400
401
/* Check whether we have zero initialisation or can get it efficiently */
402
s->has_zero_init = s->min_sparse && !s->target_has_backing
403
@@ -XXX,XX +XXX,XX @@ static int convert_do_copy(ImgConvertState *s)
404
if (s->compressed) {
405
if (s->cluster_sectors <= 0 || s->cluster_sectors > s->buf_sectors) {
406
error_report("invalid cluster size");
407
- ret = -EINVAL;
408
- goto fail;
409
+ return -EINVAL;
410
}
411
s->buf_sectors = s->cluster_sectors;
412
}
53
}
413
- buf = blk_blockalign(s->target, s->buf_sectors * BDRV_SECTOR_SIZE);
54
diff --git a/block/sheepdog.c b/block/sheepdog.c
414
55
index XXXXXXX..XXXXXXX 100644
415
- /* Calculate allocated sectors for progress */
56
--- a/block/sheepdog.c
416
- s->allocated_sectors = 0;
57
+++ b/block/sheepdog.c
417
- sector_num = 0;
58
@@ -XXX,XX +XXX,XX @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename,
418
while (sector_num < s->total_sectors) {
419
n = convert_iteration_sectors(s, sector_num);
420
if (n < 0) {
421
- ret = n;
422
- goto fail;
423
+ return n;
424
}
425
if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO))
426
{
427
@@ -XXX,XX +XXX,XX @@ static int convert_do_copy(ImgConvertState *s)
428
}
59
}
429
60
430
/* Do the copy */
61
/* transport */
431
- s->src_cur = 0;
62
- if (!strcmp(uri->scheme, "sheepdog")) {
432
- s->src_cur_offset = 0;
63
+ if (!g_strcmp0(uri->scheme, "sheepdog")) {
433
s->sector_next_status = 0;
64
is_unix = false;
434
+ s->ret = -EINPROGRESS;
65
- } else if (!strcmp(uri->scheme, "sheepdog+tcp")) {
435
66
+ } else if (!g_strcmp0(uri->scheme, "sheepdog+tcp")) {
436
- sector_num = 0;
67
is_unix = false;
437
- allocated_done = 0;
68
- } else if (!strcmp(uri->scheme, "sheepdog+unix")) {
438
-
69
+ } else if (!g_strcmp0(uri->scheme, "sheepdog+unix")) {
439
- while (sector_num < s->total_sectors) {
70
is_unix = true;
440
- n = convert_iteration_sectors(s, sector_num);
71
} else {
441
- if (n < 0) {
72
error_setg(&err, "URI scheme must be 'sheepdog', 'sheepdog+tcp',"
442
- ret = n;
73
diff --git a/block/ssh.c b/block/ssh.c
443
- goto fail;
74
index XXXXXXX..XXXXXXX 100644
444
- }
75
--- a/block/ssh.c
445
- if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO))
76
+++ b/block/ssh.c
446
- {
77
@@ -XXX,XX +XXX,XX @@ static int parse_uri(const char *filename, QDict *options, Error **errp)
447
- allocated_done += n;
78
return -EINVAL;
448
- qemu_progress_print(100.0 * allocated_done / s->allocated_sectors,
449
- 0);
450
- }
451
-
452
- if (s->status == BLK_DATA) {
453
- ret = convert_read(s, sector_num, n, buf);
454
- if (ret < 0) {
455
- error_report("error while reading sector %" PRId64
456
- ": %s", sector_num, strerror(-ret));
457
- goto fail;
458
- }
459
- } else if (!s->min_sparse && s->status == BLK_ZERO) {
460
- n = MIN(n, s->buf_sectors);
461
- memset(buf, 0, n * BDRV_SECTOR_SIZE);
462
- s->status = BLK_DATA;
463
- }
464
-
465
- ret = convert_write(s, sector_num, n, buf);
466
- if (ret < 0) {
467
- error_report("error while writing sector %" PRId64
468
- ": %s", sector_num, strerror(-ret));
469
- goto fail;
470
- }
471
+ qemu_co_mutex_init(&s->lock);
472
+ for (i = 0; i < s->num_coroutines; i++) {
473
+ s->co[i] = qemu_coroutine_create(convert_co_do_copy, s);
474
+ s->wait_sector_num[i] = -1;
475
+ qemu_coroutine_enter(s->co[i]);
476
+ }
477
478
- sector_num += n;
479
+ while (s->ret == -EINPROGRESS) {
480
+ main_loop_wait(false);
481
}
79
}
482
80
483
- if (s->compressed) {
81
- if (strcmp(uri->scheme, "ssh") != 0) {
484
+ if (s->compressed && !s->ret) {
82
+ if (g_strcmp0(uri->scheme, "ssh") != 0) {
485
/* signal EOF to align */
83
error_setg(errp, "URI scheme must be 'ssh'");
486
ret = blk_pwrite_compressed(s->target, 0, NULL, 0);
84
goto err;
487
if (ret < 0) {
488
- goto fail;
489
+ return ret;
490
}
491
}
85
}
492
493
- ret = 0;
494
-fail:
495
- qemu_vfree(buf);
496
- return ret;
497
+ return s->ret;
498
}
499
500
static int img_convert(int argc, char **argv)
501
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
502
QemuOpts *sn_opts = NULL;
503
ImgConvertState state;
504
bool image_opts = false;
505
+ bool wr_in_order = true;
506
+ long num_coroutines = 8;
507
508
fmt = NULL;
509
out_fmt = "raw";
510
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
511
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
512
{0, 0, 0, 0}
513
};
514
- c = getopt_long(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qn",
515
+ c = getopt_long(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qnm:W",
516
long_options, NULL);
517
if (c == -1) {
518
break;
519
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
520
case 'n':
521
skip_create = 1;
522
break;
523
+ case 'm':
524
+ if (qemu_strtol(optarg, NULL, 0, &num_coroutines) ||
525
+ num_coroutines < 1 || num_coroutines > MAX_COROUTINES) {
526
+ error_report("Invalid number of coroutines. Allowed number of"
527
+ " coroutines is between 1 and %d", MAX_COROUTINES);
528
+ ret = -1;
529
+ goto fail_getopt;
530
+ }
531
+ break;
532
+ case 'W':
533
+ wr_in_order = false;
534
+ break;
535
case OPTION_OBJECT:
536
opts = qemu_opts_parse_noisily(&qemu_object_opts,
537
optarg, true);
538
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
539
goto fail_getopt;
540
}
541
542
+ if (!wr_in_order && compress) {
543
+ error_report("Out of order write and compress are mutually exclusive");
544
+ ret = -1;
545
+ goto fail_getopt;
546
+ }
547
+
548
/* Initialize before goto out */
549
if (quiet) {
550
progress = 0;
551
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
552
.min_sparse = min_sparse,
553
.cluster_sectors = cluster_sectors,
554
.buf_sectors = bufsectors,
555
+ .wr_in_order = wr_in_order,
556
+ .num_coroutines = num_coroutines,
557
};
558
ret = convert_do_copy(&state);
559
560
diff --git a/qemu-img.texi b/qemu-img.texi
561
index XXXXXXX..XXXXXXX 100644
562
--- a/qemu-img.texi
563
+++ b/qemu-img.texi
564
@@ -XXX,XX +XXX,XX @@ Parameters to convert subcommand:
565
566
@item -n
567
Skip the creation of the target volume
568
+@item -m
569
+Number of parallel coroutines for the convert process
570
+@item -W
571
+Allow out-of-order writes to the destination. This option improves performance,
572
+but is only recommended for preallocated devices like host devices or other
573
+raw block devices.
574
@end table
575
576
Parameters to dd subcommand:
577
@@ -XXX,XX +XXX,XX @@ Error on reading data
578
579
@end table
580
581
-@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
582
+@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-m @var{num_coroutines}] [-W] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
583
584
Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated)
585
to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c}
586
@@ -XXX,XX +XXX,XX @@ skipped. This is useful for formats such as @code{rbd} if the target
587
volume has already been created with site specific options that cannot
588
be supplied through qemu-img.
589
590
+Out of order writes can be enabled with @code{-W} to improve performance.
591
+This is only recommended for preallocated devices like host devices or other
592
+raw block devices. Out of order write does not work in combination with
593
+creating compressed images.
594
+
595
+@var{num_coroutines} specifies how many coroutines work in parallel during
596
+the convert process (defaults to 8).
597
+
598
@item dd [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
599
600
Dd copies from @var{input} file to @var{output} file converting it from
601
--
86
--
602
1.8.3.1
87
1.8.3.1
603
88
604
89
diff view generated by jsdifflib
Deleted patch
1
This patch defines the permission categories that will be used by the
2
new op blocker system.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
7
---
8
include/block/block.h | 36 ++++++++++++++++++++++++++++++++++++
9
1 file changed, 36 insertions(+)
10
11
diff --git a/include/block/block.h b/include/block/block.h
12
index XXXXXXX..XXXXXXX 100644
13
--- a/include/block/block.h
14
+++ b/include/block/block.h
15
@@ -XXX,XX +XXX,XX @@ typedef enum BlockOpType {
16
BLOCK_OP_TYPE_MAX,
17
} BlockOpType;
18
19
+/* Block node permission constants */
20
+enum {
21
+ /**
22
+ * A user that has the "permission" of consistent reads is guaranteed that
23
+ * their view of the contents of the block device is complete and
24
+ * self-consistent, representing the contents of a disk at a specific
25
+ * point.
26
+ *
27
+ * For most block devices (including their backing files) this is true, but
28
+ * the property cannot be maintained in a few situations like for
29
+ * intermediate nodes of a commit block job.
30
+ */
31
+ BLK_PERM_CONSISTENT_READ = 0x01,
32
+
33
+ /** This permission is required to change the visible disk contents. */
34
+ BLK_PERM_WRITE = 0x02,
35
+
36
+ /**
37
+ * This permission (which is weaker than BLK_PERM_WRITE) is both enough and
38
+ * required for writes to the block node when the caller promises that
39
+ * the visible disk content doesn't change.
40
+ */
41
+ BLK_PERM_WRITE_UNCHANGED = 0x04,
42
+
43
+ /** This permission is required to change the size of a block node. */
44
+ BLK_PERM_RESIZE = 0x08,
45
+
46
+ /**
47
+ * This permission is required to change the node that this BdrvChild
48
+ * points to.
49
+ */
50
+ BLK_PERM_GRAPH_MOD = 0x10,
51
+
52
+ BLK_PERM_ALL = 0x1f,
53
+};
54
+
55
/* disk I/O throttling */
56
void bdrv_init(void);
57
void bdrv_init_with_whitelist(void);
58
--
59
1.8.3.1
60
61
diff view generated by jsdifflib
Deleted patch
1
Most filters need permissions related to read and write for their
2
children, but only if the node has a parent that wants to use the same
3
operation on the filter. The same is true for resize.
4
1
5
This adds a default implementation that simply forwards all necessary
6
permissions to all children of the node and leaves the other permissions
7
unchanged.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Acked-by: Fam Zheng <famz@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
---
13
block.c | 23 +++++++++++++++++++++++
14
include/block/block_int.h | 8 ++++++++
15
2 files changed, 31 insertions(+)
16
17
diff --git a/block.c b/block.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block.c
20
+++ b/block.c
21
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
22
return 0;
23
}
24
25
+#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
26
+ | BLK_PERM_WRITE \
27
+ | BLK_PERM_WRITE_UNCHANGED \
28
+ | BLK_PERM_RESIZE)
29
+#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH)
30
+
31
+void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
32
+ const BdrvChildRole *role,
33
+ uint64_t perm, uint64_t shared,
34
+ uint64_t *nperm, uint64_t *nshared)
35
+{
36
+ if (c == NULL) {
37
+ *nperm = perm & DEFAULT_PERM_PASSTHROUGH;
38
+ *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
39
+ return;
40
+ }
41
+
42
+ *nperm = (perm & DEFAULT_PERM_PASSTHROUGH) |
43
+ (c->perm & DEFAULT_PERM_UNCHANGED);
44
+ *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) |
45
+ (c->shared_perm & DEFAULT_PERM_UNCHANGED);
46
+}
47
+
48
static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
49
bool check_new_perm)
50
{
51
diff --git a/include/block/block_int.h b/include/block/block_int.h
52
index XXXXXXX..XXXXXXX 100644
53
--- a/include/block/block_int.h
54
+++ b/include/block/block_int.h
55
@@ -XXX,XX +XXX,XX @@ void bdrv_child_abort_perm_update(BdrvChild *c);
56
int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
57
Error **errp);
58
59
+/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by
60
+ * block filters: Forward CONSISTENT_READ, WRITE, WRITE_UNCHANGED and RESIZE to
61
+ * all children */
62
+void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
63
+ const BdrvChildRole *role,
64
+ uint64_t perm, uint64_t shared,
65
+ uint64_t *nperm, uint64_t *nshared);
66
+
67
68
const char *bdrv_get_parent_name(const BlockDriverState *bs);
69
void blk_dev_change_media_cb(BlockBackend *blk, bool load);
70
--
71
1.8.3.1
72
73
diff view generated by jsdifflib
Deleted patch
1
We can figure out the necessary permissions from the flags that the
2
caller passed.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Acked-by: Fam Zheng <famz@redhat.com>
7
---
8
block/block-backend.c | 22 +++++++++++++++++++---
9
1 file changed, 19 insertions(+), 3 deletions(-)
10
11
diff --git a/block/block-backend.c b/block/block-backend.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block/block-backend.c
14
+++ b/block/block-backend.c
15
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
16
{
17
BlockBackend *blk;
18
BlockDriverState *bs;
19
+ uint64_t perm;
20
+
21
+ /* blk_new_open() is mainly used in .bdrv_create implementations and the
22
+ * tools where sharing isn't a concern because the BDS stays private, so we
23
+ * just request permission according to the flags.
24
+ *
25
+ * The exceptions are xen_disk and blockdev_init(); in these cases, the
26
+ * caller of blk_new_open() doesn't make use of the permissions, but they
27
+ * shouldn't hurt either. We can still share everything here because the
28
+ * guest devices will add their own blockers if they can't share. */
29
+ perm = BLK_PERM_CONSISTENT_READ;
30
+ if (flags & BDRV_O_RDWR) {
31
+ perm |= BLK_PERM_WRITE;
32
+ }
33
+ if (flags & BDRV_O_RESIZE) {
34
+ perm |= BLK_PERM_RESIZE;
35
+ }
36
37
- blk = blk_new(0, BLK_PERM_ALL);
38
+ blk = blk_new(perm, BLK_PERM_ALL);
39
bs = bdrv_open(filename, reference, options, flags, errp);
40
if (!bs) {
41
blk_unref(blk);
42
return NULL;
43
}
44
45
- /* FIXME Use real permissions */
46
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
47
- 0, BLK_PERM_ALL, blk, &error_abort);
48
+ perm, BLK_PERM_ALL, blk, &error_abort);
49
50
return blk;
51
}
52
--
53
1.8.3.1
54
55
diff view generated by jsdifflib
Deleted patch
1
By default, don't allow another writer for block devices that are
2
attached to a guest device. For the cases where this setup is intended
3
(e.g. using a cluster filesystem on the disk), the new option can be
4
used to allow it.
5
1
6
This change affects only devices using DEFINE_BLOCK_PROPERTIES().
7
Devices directly using DEFINE_PROP_DRIVE() still accept writers
8
unconditionally.
9
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Acked-by: Fam Zheng <famz@redhat.com>
13
---
14
hw/block/block.c | 6 ++++--
15
include/hw/block/block.h | 5 ++++-
16
tests/qemu-iotests/172.out | 53 ++++++++++++++++++++++++++++++++++++++++++++++
17
3 files changed, 61 insertions(+), 3 deletions(-)
18
19
diff --git a/hw/block/block.c b/hw/block/block.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/hw/block/block.c
22
+++ b/hw/block/block.c
23
@@ -XXX,XX +XXX,XX @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
24
perm |= BLK_PERM_WRITE;
25
}
26
27
- /* TODO Remove BLK_PERM_WRITE unless explicitly configured so */
28
shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
29
- BLK_PERM_GRAPH_MOD | BLK_PERM_WRITE;
30
+ BLK_PERM_GRAPH_MOD;
31
if (resizable) {
32
shared_perm |= BLK_PERM_RESIZE;
33
}
34
+ if (conf->share_rw) {
35
+ shared_perm |= BLK_PERM_WRITE;
36
+ }
37
38
ret = blk_set_perm(blk, perm, shared_perm, errp);
39
if (ret < 0) {
40
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
41
index XXXXXXX..XXXXXXX 100644
42
--- a/include/hw/block/block.h
43
+++ b/include/hw/block/block.h
44
@@ -XXX,XX +XXX,XX @@ typedef struct BlockConf {
45
/* geometry, not all devices use this */
46
uint32_t cyls, heads, secs;
47
OnOffAuto wce;
48
+ bool share_rw;
49
BlockdevOnError rerror;
50
BlockdevOnError werror;
51
} BlockConf;
52
@@ -XXX,XX +XXX,XX @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
53
DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \
54
DEFINE_PROP_UINT32("discard_granularity", _state, \
55
_conf.discard_granularity, -1), \
56
- DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, ON_OFF_AUTO_AUTO)
57
+ DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, \
58
+ ON_OFF_AUTO_AUTO), \
59
+ DEFINE_PROP_BOOL("share-rw", _state, _conf.share_rw, false)
60
61
#define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf) \
62
DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0), \
63
diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out
64
index XXXXXXX..XXXXXXX 100644
65
--- a/tests/qemu-iotests/172.out
66
+++ b/tests/qemu-iotests/172.out
67
@@ -XXX,XX +XXX,XX @@ Testing:
68
opt_io_size = 0 (0x0)
69
discard_granularity = 4294967295 (0xffffffff)
70
write-cache = "auto"
71
+ share-rw = false
72
drive-type = "288"
73
74
75
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2
76
opt_io_size = 0 (0x0)
77
discard_granularity = 4294967295 (0xffffffff)
78
write-cache = "auto"
79
+ share-rw = false
80
drive-type = "144"
81
82
Testing: -fdb TEST_DIR/t.qcow2
83
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2
84
opt_io_size = 0 (0x0)
85
discard_granularity = 4294967295 (0xffffffff)
86
write-cache = "auto"
87
+ share-rw = false
88
drive-type = "144"
89
dev: floppy, id ""
90
unit = 0 (0x0)
91
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2
92
opt_io_size = 0 (0x0)
93
discard_granularity = 4294967295 (0xffffffff)
94
write-cache = "auto"
95
+ share-rw = false
96
drive-type = "288"
97
98
Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2
99
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2
100
opt_io_size = 0 (0x0)
101
discard_granularity = 4294967295 (0xffffffff)
102
write-cache = "auto"
103
+ share-rw = false
104
drive-type = "144"
105
dev: floppy, id ""
106
unit = 0 (0x0)
107
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2
108
opt_io_size = 0 (0x0)
109
discard_granularity = 4294967295 (0xffffffff)
110
write-cache = "auto"
111
+ share-rw = false
112
drive-type = "144"
113
114
115
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2
116
opt_io_size = 0 (0x0)
117
discard_granularity = 4294967295 (0xffffffff)
118
write-cache = "auto"
119
+ share-rw = false
120
drive-type = "144"
121
122
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
123
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
124
opt_io_size = 0 (0x0)
125
discard_granularity = 4294967295 (0xffffffff)
126
write-cache = "auto"
127
+ share-rw = false
128
drive-type = "144"
129
dev: floppy, id ""
130
unit = 0 (0x0)
131
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
132
opt_io_size = 0 (0x0)
133
discard_granularity = 4294967295 (0xffffffff)
134
write-cache = "auto"
135
+ share-rw = false
136
drive-type = "288"
137
138
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
139
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t
140
opt_io_size = 0 (0x0)
141
discard_granularity = 4294967295 (0xffffffff)
142
write-cache = "auto"
143
+ share-rw = false
144
drive-type = "144"
145
dev: floppy, id ""
146
unit = 0 (0x0)
147
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t
148
opt_io_size = 0 (0x0)
149
discard_granularity = 4294967295 (0xffffffff)
150
write-cache = "auto"
151
+ share-rw = false
152
drive-type = "144"
153
154
155
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
156
opt_io_size = 0 (0x0)
157
discard_granularity = 4294967295 (0xffffffff)
158
write-cache = "auto"
159
+ share-rw = false
160
drive-type = "144"
161
162
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
163
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
164
opt_io_size = 0 (0x0)
165
discard_granularity = 4294967295 (0xffffffff)
166
write-cache = "auto"
167
+ share-rw = false
168
drive-type = "144"
169
170
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1
171
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
172
opt_io_size = 0 (0x0)
173
discard_granularity = 4294967295 (0xffffffff)
174
write-cache = "auto"
175
+ share-rw = false
176
drive-type = "144"
177
dev: floppy, id ""
178
unit = 0 (0x0)
179
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
180
opt_io_size = 0 (0x0)
181
discard_granularity = 4294967295 (0xffffffff)
182
write-cache = "auto"
183
+ share-rw = false
184
drive-type = "144"
185
186
187
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
188
opt_io_size = 0 (0x0)
189
discard_granularity = 4294967295 (0xffffffff)
190
write-cache = "auto"
191
+ share-rw = false
192
drive-type = "144"
193
194
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
195
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
196
opt_io_size = 0 (0x0)
197
discard_granularity = 4294967295 (0xffffffff)
198
write-cache = "auto"
199
+ share-rw = false
200
drive-type = "144"
201
202
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 -device floppy,drive=none1,unit=1
203
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
204
opt_io_size = 0 (0x0)
205
discard_granularity = 4294967295 (0xffffffff)
206
write-cache = "auto"
207
+ share-rw = false
208
drive-type = "144"
209
dev: floppy, id ""
210
unit = 0 (0x0)
211
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
212
opt_io_size = 0 (0x0)
213
discard_granularity = 4294967295 (0xffffffff)
214
write-cache = "auto"
215
+ share-rw = false
216
drive-type = "144"
217
218
219
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
220
opt_io_size = 0 (0x0)
221
discard_granularity = 4294967295 (0xffffffff)
222
write-cache = "auto"
223
+ share-rw = false
224
drive-type = "144"
225
dev: floppy, id ""
226
unit = 0 (0x0)
227
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
228
opt_io_size = 0 (0x0)
229
discard_granularity = 4294967295 (0xffffffff)
230
write-cache = "auto"
231
+ share-rw = false
232
drive-type = "144"
233
234
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
235
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
236
opt_io_size = 0 (0x0)
237
discard_granularity = 4294967295 (0xffffffff)
238
write-cache = "auto"
239
+ share-rw = false
240
drive-type = "144"
241
dev: floppy, id ""
242
unit = 0 (0x0)
243
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
244
opt_io_size = 0 (0x0)
245
discard_granularity = 4294967295 (0xffffffff)
246
write-cache = "auto"
247
+ share-rw = false
248
drive-type = "144"
249
250
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0
251
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
252
opt_io_size = 0 (0x0)
253
discard_granularity = 4294967295 (0xffffffff)
254
write-cache = "auto"
255
+ share-rw = false
256
drive-type = "144"
257
258
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0
259
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-
260
opt_io_size = 0 (0x0)
261
discard_granularity = 4294967295 (0xffffffff)
262
write-cache = "auto"
263
+ share-rw = false
264
drive-type = "144"
265
266
267
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
268
opt_io_size = 0 (0x0)
269
discard_granularity = 4294967295 (0xffffffff)
270
write-cache = "auto"
271
+ share-rw = false
272
drive-type = "144"
273
dev: floppy, id ""
274
unit = 0 (0x0)
275
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
276
opt_io_size = 0 (0x0)
277
discard_granularity = 4294967295 (0xffffffff)
278
write-cache = "auto"
279
+ share-rw = false
280
drive-type = "144"
281
282
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
283
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
284
opt_io_size = 0 (0x0)
285
discard_granularity = 4294967295 (0xffffffff)
286
write-cache = "auto"
287
+ share-rw = false
288
drive-type = "144"
289
dev: floppy, id ""
290
unit = 0 (0x0)
291
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
292
opt_io_size = 0 (0x0)
293
discard_granularity = 4294967295 (0xffffffff)
294
write-cache = "auto"
295
+ share-rw = false
296
drive-type = "144"
297
298
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
299
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
300
opt_io_size = 0 (0x0)
301
discard_granularity = 4294967295 (0xffffffff)
302
write-cache = "auto"
303
+ share-rw = false
304
drive-type = "144"
305
dev: floppy, id ""
306
unit = 1 (0x1)
307
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
308
opt_io_size = 0 (0x0)
309
discard_granularity = 4294967295 (0xffffffff)
310
write-cache = "auto"
311
+ share-rw = false
312
drive-type = "144"
313
314
Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
315
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
316
opt_io_size = 0 (0x0)
317
discard_granularity = 4294967295 (0xffffffff)
318
write-cache = "auto"
319
+ share-rw = false
320
drive-type = "144"
321
dev: floppy, id ""
322
unit = 1 (0x1)
323
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device flop
324
opt_io_size = 0 (0x0)
325
discard_granularity = 4294967295 (0xffffffff)
326
write-cache = "auto"
327
+ share-rw = false
328
drive-type = "144"
329
330
Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
331
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
332
opt_io_size = 0 (0x0)
333
discard_granularity = 4294967295 (0xffffffff)
334
write-cache = "auto"
335
+ share-rw = false
336
drive-type = "144"
337
dev: floppy, id ""
338
unit = 0 (0x0)
339
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
340
opt_io_size = 0 (0x0)
341
discard_granularity = 4294967295 (0xffffffff)
342
write-cache = "auto"
343
+ share-rw = false
344
drive-type = "144"
345
346
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
347
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
348
opt_io_size = 0 (0x0)
349
discard_granularity = 4294967295 (0xffffffff)
350
write-cache = "auto"
351
+ share-rw = false
352
drive-type = "144"
353
dev: floppy, id ""
354
unit = 0 (0x0)
355
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
356
opt_io_size = 0 (0x0)
357
discard_granularity = 4294967295 (0xffffffff)
358
write-cache = "auto"
359
+ share-rw = false
360
drive-type = "144"
361
362
Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0
363
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
364
opt_io_size = 0 (0x0)
365
discard_granularity = 4294967295 (0xffffffff)
366
write-cache = "auto"
367
+ share-rw = false
368
drive-type = "144"
369
dev: floppy, id ""
370
unit = 0 (0x0)
371
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
372
opt_io_size = 0 (0x0)
373
discard_granularity = 4294967295 (0xffffffff)
374
write-cache = "auto"
375
+ share-rw = false
376
drive-type = "144"
377
378
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1
379
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
380
opt_io_size = 0 (0x0)
381
discard_granularity = 4294967295 (0xffffffff)
382
write-cache = "auto"
383
+ share-rw = false
384
drive-type = "144"
385
dev: floppy, id ""
386
unit = 0 (0x0)
387
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
388
opt_io_size = 0 (0x0)
389
discard_granularity = 4294967295 (0xffffffff)
390
write-cache = "auto"
391
+ share-rw = false
392
drive-type = "144"
393
394
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1
395
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
396
opt_io_size = 0 (0x0)
397
discard_granularity = 4294967295 (0xffffffff)
398
write-cache = "auto"
399
+ share-rw = false
400
drive-type = "144"
401
dev: floppy, id ""
402
unit = 1 (0x1)
403
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
404
opt_io_size = 0 (0x0)
405
discard_granularity = 4294967295 (0xffffffff)
406
write-cache = "auto"
407
+ share-rw = false
408
drive-type = "144"
409
410
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0
411
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
412
opt_io_size = 0 (0x0)
413
discard_granularity = 4294967295 (0xffffffff)
414
write-cache = "auto"
415
+ share-rw = false
416
drive-type = "144"
417
dev: floppy, id ""
418
unit = 1 (0x1)
419
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
420
opt_io_size = 0 (0x0)
421
discard_granularity = 4294967295 (0xffffffff)
422
write-cache = "auto"
423
+ share-rw = false
424
drive-type = "144"
425
426
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0
427
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy
428
opt_io_size = 0 (0x0)
429
discard_granularity = 4294967295 (0xffffffff)
430
write-cache = "auto"
431
+ share-rw = false
432
drive-type = "288"
433
434
Testing: -device floppy,drive-type=120
435
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=120
436
opt_io_size = 0 (0x0)
437
discard_granularity = 4294967295 (0xffffffff)
438
write-cache = "auto"
439
+ share-rw = false
440
drive-type = "120"
441
442
Testing: -device floppy,drive-type=144
443
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=144
444
opt_io_size = 0 (0x0)
445
discard_granularity = 4294967295 (0xffffffff)
446
write-cache = "auto"
447
+ share-rw = false
448
drive-type = "144"
449
450
Testing: -device floppy,drive-type=288
451
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=288
452
opt_io_size = 0 (0x0)
453
discard_granularity = 4294967295 (0xffffffff)
454
write-cache = "auto"
455
+ share-rw = false
456
drive-type = "288"
457
458
459
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t
460
opt_io_size = 0 (0x0)
461
discard_granularity = 4294967295 (0xffffffff)
462
write-cache = "auto"
463
+ share-rw = false
464
drive-type = "120"
465
466
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-type=288
467
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t
468
opt_io_size = 0 (0x0)
469
discard_granularity = 4294967295 (0xffffffff)
470
write-cache = "auto"
471
+ share-rw = false
472
drive-type = "288"
473
474
475
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical
476
opt_io_size = 0 (0x0)
477
discard_granularity = 4294967295 (0xffffffff)
478
write-cache = "auto"
479
+ share-rw = false
480
drive-type = "144"
481
482
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physical_block_size=512
483
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physica
484
opt_io_size = 0 (0x0)
485
discard_granularity = 4294967295 (0xffffffff)
486
write-cache = "auto"
487
+ share-rw = false
488
drive-type = "144"
489
490
Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical_block_size=4096
491
--
492
1.8.3.1
493
494
diff view generated by jsdifflib