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 @@ q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K | ||
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 |