1 | The following changes since commit 9514f2648ca05b38e852b490a12b8cd98d5808c1: | 1 | The following changes since commit b98a66201dbc7cf3b962f4bb260f66100cc75578: |
---|---|---|---|
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/palmer/tags/riscv-for-master-4.0-rc0-2' into staging (2019-03-19 12:55:02 +0000) |
4 | 4 | ||
5 | are available in the git repository at: | 5 | are available in the Git repository at: |
6 | |||
7 | 6 | ||
8 | git://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream |
9 | 8 | ||
10 | for you to fetch changes up to b2c2832c6140cfe3ddc0de2d77eeb0b77dea8fd3: | 9 | for you to fetch changes up to 59fba0aaee7438002d9803a86c888f21a1070cc8: |
11 | 10 | ||
12 | block: Add Error parameter to bdrv_append() (2017-02-28 20:47:51 +0100) | 11 | qemu-iotests: Treat custom TEST_DIR in 051 (2019-03-19 15:51:31 +0100) |
13 | 12 | ||
14 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
15 | Block layer patches | 14 | Block layer patches: |
15 | |||
16 | - mirror: Fix early return from drain (could cause deadlocks) | ||
17 | - vmdk: Fixed probing for version 3 images | ||
18 | - vl: Fix to create migration object before block backends again (fixes | ||
19 | segfault for block drivers that set migration blockers) | ||
20 | - Several minor fixes, documentation and test case improvements | ||
16 | 21 | ||
17 | ---------------------------------------------------------------- | 22 | ---------------------------------------------------------------- |
18 | Kevin Wolf (44): | 23 | Alberto Garcia (1): |
19 | block: Add op blocker permission constants | 24 | block: Make bdrv_{copy_on_read,crypto_luks,replication} static |
20 | block: Add Error argument to bdrv_attach_child() | 25 | |
21 | block: Let callers request permissions when attaching a child node | 26 | Kevin Wolf (3): |
22 | block: Involve block drivers in permission granting | 27 | qcow2: Fix data file error condition in qcow2_co_create() |
23 | block: Default .bdrv_child_perm() for filter drivers | 28 | block: Silence Coverity in bdrv_drop_intermediate() |
24 | block: Request child permissions in filter drivers | 29 | qemu-iotests: Fix 232 for non-qcow2 |
25 | block: Default .bdrv_child_perm() for format drivers | 30 | |
26 | block: Request child permissions in format drivers | 31 | Lukáš Doktor (1): |
27 | vvfat: Implement .bdrv_child_perm() | 32 | qemu-iotests: Treat custom TEST_DIR in 051 |
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 | 33 | ||
64 | Markus Armbruster (1): | 34 | Markus Armbruster (1): |
65 | option: Tweak invalid size error message and unbreak iotest 049 | 35 | vl: Fix to create migration object before block backends again |
66 | 36 | ||
67 | Peter Lieven (1): | 37 | Max Reitz (1): |
68 | qemu-img: make convert async | 38 | blockdev: Check @replaces in blockdev_mirror_common |
69 | 39 | ||
70 | block.c | 583 ++++++++++++++++++++++++++++++++++----- | 40 | Sam Eiderman (1): |
71 | block/backup.c | 22 +- | 41 | vmdk: Support version=3 in VMDK descriptor files |
72 | block/blkdebug.c | 2 + | ||
73 | block/blkreplay.c | 1 + | ||
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 | 42 | ||
43 | Sergio Lopez (2): | ||
44 | mirror: Confirm we're quiesced only if the job is paused or cancelled | ||
45 | iotests: 153: Wait for an answer to QMP commands | ||
46 | |||
47 | Vladimir Sementsov-Ogievskiy (2): | ||
48 | qapi: fix block-latency-histogram-set description and examples | ||
49 | blockjob: fix user pause in block_job_error_action | ||
50 | |||
51 | qapi/block-core.json | 10 +++--- | ||
52 | block.c | 7 ++-- | ||
53 | block/copy-on-read.c | 2 +- | ||
54 | block/crypto.c | 2 +- | ||
55 | block/mirror.c | 16 ++++++++++ | ||
56 | block/qcow2.c | 2 +- | ||
57 | block/replication.c | 2 +- | ||
58 | block/vmdk.c | 6 ++-- | ||
59 | blockdev.c | 55 +++++++++++++++++++------------- | ||
60 | blockjob.c | 8 +++-- | ||
61 | vl.c | 15 +++++---- | ||
62 | tests/qemu-iotests/051 | 2 +- | ||
63 | tests/qemu-iotests/153 | 12 +++---- | ||
64 | tests/qemu-iotests/153.out | 6 ++++ | ||
65 | tests/qemu-iotests/232 | 30 ------------------ | ||
66 | tests/qemu-iotests/232.out | 20 ------------ | ||
67 | tests/qemu-iotests/247 | 79 ++++++++++++++++++++++++++++++++++++++++++++++ | ||
68 | tests/qemu-iotests/247.out | 22 +++++++++++++ | ||
69 | tests/qemu-iotests/group | 1 + | ||
70 | 19 files changed, 193 insertions(+), 104 deletions(-) | ||
71 | create mode 100755 tests/qemu-iotests/247 | ||
72 | create mode 100644 tests/qemu-iotests/247.out | ||
73 | diff view generated by jsdifflib |
1 | The mirror block job is mainly used for two different scenarios: | 1 | From: Sergio Lopez <slp@redhat.com> |
---|---|---|---|
2 | Mirroring to an otherwise unused, independent target node, or for active | ||
3 | commit where the target node is part of the backing chain of the source. | ||
4 | 2 | ||
5 | Similarly to the commit block job patch, we need to insert a new filter | 3 | While child_job_drained_begin() calls to job_pause(), the job doesn't |
6 | node to keep the permissions correct during active commit. | 4 | actually transition between states until it runs again and reaches a |
5 | pause point. This means bdrv_drained_begin() may return with some jobs | ||
6 | using the node still having 'busy == true'. | ||
7 | 7 | ||
8 | Note that one change this implies is that job->blk points to | 8 | As a consequence, block_job_detach_aio_context() may get into a |
9 | mirror_top_bs as its root now, and mirror_top_bs (rather than the actual | 9 | deadlock, waiting for the job to be actually paused, while the coroutine |
10 | source node) contains the bs->job pointer. This requires qemu-img commit | 10 | servicing the job is yielding and doesn't get the opportunity to get |
11 | to get the job by name now rather than just taking bs->job. | 11 | scheduled again. This situation can be reproduced by issuing a |
12 | 'block-commit' immediately followed by a 'device_del'. | ||
12 | 13 | ||
14 | To ensure bdrv_drained_begin() only returns when the jobs have been | ||
15 | paused, we change mirror_drained_poll() to only confirm it's quiesced | ||
16 | when job->paused == true and there aren't any in-flight requests, except | ||
17 | if we reached that point by a drained section initiated by the | ||
18 | mirror/commit job itself. | ||
19 | |||
20 | The other block jobs shouldn't need any changes, as the default | ||
21 | drained_poll() behavior is to only confirm it's quiesced if the job is | ||
22 | not busy or completed. | ||
23 | |||
24 | Signed-off-by: Sergio Lopez <slp@redhat.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 25 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | Acked-by: Fam Zheng <famz@redhat.com> | ||
15 | Acked-by: Max Reitz <mreitz@redhat.com> | ||
16 | --- | 26 | --- |
17 | block/mirror.c | 216 ++++++++++++++++++++++++++++++++++++++------- | 27 | block/mirror.c | 16 ++++++++++++++++ |
18 | qemu-img.c | 6 +- | 28 | 1 file changed, 16 insertions(+) |
19 | tests/qemu-iotests/141 | 2 +- | ||
20 | tests/qemu-iotests/141.out | 4 +- | ||
21 | 4 files changed, 190 insertions(+), 38 deletions(-) | ||
22 | 29 | ||
23 | diff --git a/block/mirror.c b/block/mirror.c | 30 | diff --git a/block/mirror.c b/block/mirror.c |
24 | index XXXXXXX..XXXXXXX 100644 | 31 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/block/mirror.c | 32 | --- a/block/mirror.c |
26 | +++ b/block/mirror.c | 33 | +++ b/block/mirror.c |
27 | @@ -XXX,XX +XXX,XX @@ typedef struct MirrorBlockJob { | 34 | @@ -XXX,XX +XXX,XX @@ typedef struct MirrorBlockJob { |
28 | BlockJob common; | 35 | bool initial_zeroing_ongoing; |
29 | RateLimit limit; | 36 | int in_active_write_counter; |
30 | BlockBackend *target; | 37 | bool prepared; |
31 | + BlockDriverState *mirror_top_bs; | 38 | + bool in_drain; |
32 | + BlockDriverState *source; | 39 | } MirrorBlockJob; |
33 | BlockDriverState *base; | 40 | |
34 | + | 41 | typedef struct MirrorBDSOpaque { |
35 | /* The name of the graph node to replace */ | 42 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) |
36 | char *replaces; | 43 | |
37 | /* The BDS to replace */ | 44 | /* The mirror job has no requests in flight any more, but we need to |
38 | @@ -XXX,XX +XXX,XX @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s, | 45 | * drain potential other users of the BDS before changing the graph. */ |
39 | 46 | + assert(s->in_drain); | |
40 | static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | ||
41 | { | ||
42 | - BlockDriverState *source = blk_bs(s->common.blk); | ||
43 | + BlockDriverState *source = s->source; | ||
44 | int64_t sector_num, first_chunk; | ||
45 | uint64_t delay_ns = 0; | ||
46 | /* At least the first dirty chunk is mirrored in one iteration. */ | ||
47 | @@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque) | ||
48 | MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); | ||
49 | MirrorExitData *data = opaque; | ||
50 | AioContext *replace_aio_context = NULL; | ||
51 | - BlockDriverState *src = blk_bs(s->common.blk); | ||
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); | 47 | bdrv_drained_begin(target_bs); |
76 | bdrv_replace_in_backing_chain(to_replace, target_bs); | 48 | bdrv_replace_node(to_replace, target_bs, &local_err); |
77 | bdrv_drained_end(target_bs); | 49 | bdrv_drained_end(target_bs); |
78 | - | 50 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) |
79 | - /* We just changed the BDS the job BB refers to, so switch the BB back | 51 | bs_opaque->job = NULL; |
80 | - * so the cleanup does the right thing. We don't need any permissions | 52 | |
81 | - * any more now. */ | 53 | bdrv_drained_end(src); |
82 | - blk_remove_bs(job->blk); | 54 | + s->in_drain = false; |
83 | - blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); | 55 | bdrv_unref(mirror_top_bs); |
84 | - blk_insert_bs(job->blk, src, &error_abort); | 56 | bdrv_unref(src); |
57 | |||
58 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp) | ||
59 | */ | ||
60 | trace_mirror_before_drain(s, cnt); | ||
61 | |||
62 | + s->in_drain = true; | ||
63 | bdrv_drained_begin(bs); | ||
64 | cnt = bdrv_get_dirty_count(s->dirty_bitmap); | ||
65 | if (cnt > 0 || mirror_flush(s) < 0) { | ||
66 | bdrv_drained_end(bs); | ||
67 | + s->in_drain = false; | ||
68 | continue; | ||
69 | } | ||
70 | |||
71 | @@ -XXX,XX +XXX,XX @@ immediate_exit: | ||
72 | bdrv_dirty_iter_free(s->dbi); | ||
73 | |||
74 | if (need_drain) { | ||
75 | + s->in_drain = true; | ||
76 | bdrv_drained_begin(bs); | ||
85 | } | 77 | } |
86 | if (s->to_replace) { | 78 | |
87 | bdrv_op_unblock_all(s->to_replace, s->replace_blocker); | 79 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_pause(Job *job) |
88 | @@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque) | 80 | static bool mirror_drained_poll(BlockJob *job) |
89 | g_free(s->replaces); | ||
90 | blk_unref(s->target); | ||
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 | { | 81 | { |
136 | MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); | 82 | 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 | + | 83 | + |
179 | +static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, | 84 | + /* If the job isn't paused nor cancelled, we can't be sure that it won't |
180 | + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) | 85 | + * issue more requests. We make an exception if we've reached this point |
181 | +{ | 86 | + * from one of our own drain sections, to avoid a deadlock waiting for |
182 | + return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); | 87 | + * ourselves. |
183 | +} | 88 | + */ |
184 | + | 89 | + if (!s->common.job.paused && !s->common.job.cancelled && !s->in_drain) { |
185 | +static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) | 90 | + return true; |
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 | + } | 91 | + } |
226 | + | 92 | + |
227 | + *nshared = BLK_PERM_ALL; | 93 | return !!s->in_flight; |
228 | +} | ||
229 | + | ||
230 | +/* Dummy node that provides consistent read to its users without requiring it | ||
231 | + * from its backing file and that allows writes on the backing file chain. */ | ||
232 | +static BlockDriver bdrv_mirror_top = { | ||
233 | + .format_name = "mirror_top", | ||
234 | + .bdrv_co_preadv = bdrv_mirror_top_preadv, | ||
235 | + .bdrv_co_pwritev = bdrv_mirror_top_pwritev, | ||
236 | + .bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes, | ||
237 | + .bdrv_co_pdiscard = bdrv_mirror_top_pdiscard, | ||
238 | + .bdrv_co_flush = bdrv_mirror_top_flush, | ||
239 | + .bdrv_co_get_block_status = bdrv_mirror_top_get_block_status, | ||
240 | + .bdrv_close = bdrv_mirror_top_close, | ||
241 | + .bdrv_child_perm = bdrv_mirror_top_child_perm, | ||
242 | +}; | ||
243 | + | ||
244 | static void mirror_start_job(const char *job_id, BlockDriverState *bs, | ||
245 | int creation_flags, BlockDriverState *target, | ||
246 | const char *replaces, int64_t speed, | ||
247 | @@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, | ||
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 | } | ||
291 | - | ||
292 | - /* FIXME Use real permissions */ | ||
293 | - s->target = blk_new(0, BLK_PERM_ALL); | ||
294 | + s->source = bs; | ||
295 | + s->mirror_top_bs = mirror_top_bs; | ||
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 | } | 94 | } |
366 | 95 | ||
367 | void mirror_start(const char *job_id, BlockDriverState *bs, | ||
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 | -- | 96 | -- |
435 | 1.8.3.1 | 97 | 2.20.1 |
436 | 98 | ||
437 | 99 | diff view generated by jsdifflib |
1 | We want every user to be specific about the permissions it needs, so | 1 | We were trying to check whether bdrv_open_blockdev_ref() returned |
---|---|---|---|
2 | we'll pass the initial permissions as parameters to blk_new(). A user | 2 | success, but accidentally checked the wrong variable. Spotted by |
3 | only needs to call blk_set_perm() if it wants to change the permissions | 3 | Coverity (CID 1399703). |
4 | after the fact. | ||
5 | |||
6 | The permissions are stored in the BlockBackend and applied whenever a | ||
7 | BlockDriverState should be attached in blk_insert_bs(). | ||
8 | |||
9 | This does not include actually choosing the right set of permissions | ||
10 | everywhere yet. Instead, the usual FIXME comment is added to each place | ||
11 | and will be addressed in individual patches. | ||
12 | 4 | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
15 | Acked-by: Fam Zheng <famz@redhat.com> | 7 | Reviewed-by: Stefano Garzarella <sgarzare@redhat.com> |
16 | --- | 8 | --- |
17 | block.c | 2 +- | 9 | block/qcow2.c | 2 +- |
18 | block/backup.c | 3 ++- | 10 | 1 file changed, 1 insertion(+), 1 deletion(-) |
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 | 11 | ||
37 | diff --git a/block.c b/block.c | ||
38 | index XXXXXXX..XXXXXXX 100644 | ||
39 | --- a/block.c | ||
40 | +++ b/block.c | ||
41 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | ||
42 | goto fail; | ||
43 | } | ||
44 | if (file_bs != NULL) { | ||
45 | - file = blk_new(); | ||
46 | + file = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); | ||
47 | blk_insert_bs(file, file_bs); | ||
48 | bdrv_unref(file_bs); | ||
49 | |||
50 | diff --git a/block/backup.c b/block/backup.c | ||
51 | index XXXXXXX..XXXXXXX 100644 | ||
52 | --- a/block/backup.c | ||
53 | +++ b/block/backup.c | ||
54 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
55 | goto error; | ||
56 | } | ||
57 | |||
58 | - job->target = blk_new(); | ||
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 | 12 | diff --git a/block/qcow2.c b/block/qcow2.c |
167 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
168 | --- a/block/qcow2.c | 14 | --- a/block/qcow2.c |
169 | +++ b/block/qcow2.c | 15 | +++ b/block/qcow2.c |
170 | @@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, | 16 | @@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) |
171 | } | 17 | goto out; |
172 | |||
173 | if (new_size) { | ||
174 | - BlockBackend *blk = blk_new(); | ||
175 | + BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL); | ||
176 | blk_insert_bs(blk, bs); | ||
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 | } | ||
195 | |||
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 | } | 18 | } |
207 | } | 19 | data_bs = bdrv_open_blockdev_ref(qcow2_opts->data_file, errp); |
208 | 20 | - if (bs == NULL) { | |
209 | - blk = blk_new(); | 21 | + if (data_bs == NULL) { |
210 | + /* FIXME Use real permissions */ | 22 | ret = -EIO; |
211 | + blk = blk_new(0, BLK_PERM_ALL); | 23 | goto out; |
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 | } | 24 | } |
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 | -- | 25 | -- |
359 | 1.8.3.1 | 26 | 2.20.1 |
360 | 27 | ||
361 | 28 | diff view generated by jsdifflib |
1 | Management tools need to be able to know about every node in the graph | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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-commit allows the client to set a node-name | 3 | There no @device parameter, only the @id one. |
7 | for the automatically inserted filter driver, and at the same time | ||
8 | serves as a witness for a future libvirt that this version of qemu does | ||
9 | automatically insert a filter driver. | ||
10 | 4 | ||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | Acked-by: Fam Zheng <famz@redhat.com> | ||
13 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
14 | --- | 8 | --- |
15 | block/commit.c | 5 +++-- | 9 | qapi/block-core.json | 10 +++++----- |
16 | block/mirror.c | 3 ++- | 10 | 1 file changed, 5 insertions(+), 5 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 | 11 | ||
24 | diff --git a/block/commit.c b/block/commit.c | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/block/commit.c | ||
27 | +++ b/block/commit.c | ||
28 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_commit_top = { | ||
29 | void commit_start(const char *job_id, BlockDriverState *bs, | ||
30 | BlockDriverState *base, BlockDriverState *top, int64_t speed, | ||
31 | BlockdevOnError on_error, const char *backing_file_str, | ||
32 | - Error **errp) | ||
33 | + const char *filter_node_name, Error **errp) | ||
34 | { | ||
35 | CommitBlockJob *s; | ||
36 | BlockReopenQueue *reopen_queue = NULL; | ||
37 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||
38 | |||
39 | /* Insert commit_top block node above top, so we can block consistent read | ||
40 | * on the backing chain below it */ | ||
41 | - commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, 0, errp); | ||
42 | + commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, filter_node_name, 0, | ||
43 | + errp); | ||
44 | if (commit_top_bs == NULL) { | ||
45 | goto fail; | ||
46 | } | ||
47 | diff --git a/block/mirror.c b/block/mirror.c | ||
48 | index XXXXXXX..XXXXXXX 100644 | ||
49 | --- a/block/mirror.c | ||
50 | +++ b/block/mirror.c | ||
51 | @@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs, | ||
52 | void commit_active_start(const char *job_id, BlockDriverState *bs, | ||
53 | BlockDriverState *base, int creation_flags, | ||
54 | int64_t speed, BlockdevOnError on_error, | ||
55 | + const char *filter_node_name, | ||
56 | BlockCompletionFunc *cb, void *opaque, Error **errp, | ||
57 | bool auto_complete) | ||
58 | { | ||
59 | @@ -XXX,XX +XXX,XX @@ void commit_active_start(const char *job_id, BlockDriverState *bs, | ||
60 | MIRROR_LEAVE_BACKING_CHAIN, | ||
61 | on_error, on_error, true, cb, opaque, &local_err, | ||
62 | &commit_active_job_driver, false, base, auto_complete, | ||
63 | - NULL); | ||
64 | + filter_node_name); | ||
65 | if (local_err) { | ||
66 | error_propagate(errp, local_err); | ||
67 | goto error_restore_flags; | ||
68 | diff --git a/block/replication.c b/block/replication.c | ||
69 | index XXXXXXX..XXXXXXX 100644 | ||
70 | --- a/block/replication.c | ||
71 | +++ b/block/replication.c | ||
72 | @@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | ||
73 | s->replication_state = BLOCK_REPLICATION_FAILOVER; | ||
74 | commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, | ||
75 | BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, | ||
76 | - replication_done, bs, errp, true); | ||
77 | + NULL, replication_done, bs, errp, true); | ||
78 | break; | ||
79 | default: | ||
80 | aio_context_release(aio_context); | ||
81 | diff --git a/blockdev.c b/blockdev.c | ||
82 | index XXXXXXX..XXXXXXX 100644 | ||
83 | --- a/blockdev.c | ||
84 | +++ b/blockdev.c | ||
85 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, | ||
86 | bool has_top, const char *top, | ||
87 | bool has_backing_file, const char *backing_file, | ||
88 | bool has_speed, int64_t speed, | ||
89 | + bool has_filter_node_name, const char *filter_node_name, | ||
90 | Error **errp) | ||
91 | { | ||
92 | BlockDriverState *bs; | ||
93 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, | ||
94 | if (!has_speed) { | ||
95 | speed = 0; | ||
96 | } | ||
97 | + if (!has_filter_node_name) { | ||
98 | + filter_node_name = NULL; | ||
99 | + } | ||
100 | |||
101 | /* Important Note: | ||
102 | * libvirt relies on the DeviceNotFound error class in order to probe for | ||
103 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, | ||
104 | goto out; | ||
105 | } | ||
106 | commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, | ||
107 | - BLOCK_JOB_DEFAULT, speed, on_error, NULL, NULL, | ||
108 | - &local_err, false); | ||
109 | + BLOCK_JOB_DEFAULT, speed, on_error, | ||
110 | + filter_node_name, NULL, NULL, &local_err, false); | ||
111 | } else { | ||
112 | BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); | ||
113 | if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { | ||
114 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, | ||
115 | } | ||
116 | commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed, | ||
117 | on_error, has_backing_file ? backing_file : NULL, | ||
118 | - &local_err); | ||
119 | + filter_node_name, &local_err); | ||
120 | } | ||
121 | if (local_err != NULL) { | ||
122 | error_propagate(errp, local_err); | ||
123 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
124 | index XXXXXXX..XXXXXXX 100644 | ||
125 | --- a/include/block/block_int.h | ||
126 | +++ b/include/block/block_int.h | ||
127 | @@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs, | ||
128 | * @speed: The maximum speed, in bytes per second, or 0 for unlimited. | ||
129 | * @on_error: The action to take upon error. | ||
130 | * @backing_file_str: String to use as the backing file in @top's overlay | ||
131 | + * @filter_node_name: The node name that should be assigned to the filter | ||
132 | + * driver that the commit job inserts into the graph above @top. NULL means | ||
133 | + * that a node name should be autogenerated. | ||
134 | * @errp: Error object. | ||
135 | * | ||
136 | */ | ||
137 | void commit_start(const char *job_id, BlockDriverState *bs, | ||
138 | BlockDriverState *base, BlockDriverState *top, int64_t speed, | ||
139 | BlockdevOnError on_error, const char *backing_file_str, | ||
140 | - Error **errp); | ||
141 | + const char *filter_node_name, Error **errp); | ||
142 | /** | ||
143 | * commit_active_start: | ||
144 | * @job_id: The id of the newly-created job, or %NULL to use the | ||
145 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||
146 | * See @BlockJobCreateFlags | ||
147 | * @speed: The maximum speed, in bytes per second, or 0 for unlimited. | ||
148 | * @on_error: The action to take upon error. | ||
149 | + * @filter_node_name: The node name that should be assigned to the filter | ||
150 | + * driver that the commit job inserts into the graph above @bs. NULL means that | ||
151 | + * a node name should be autogenerated. | ||
152 | * @cb: Completion function for the job. | ||
153 | * @opaque: Opaque pointer value passed to @cb. | ||
154 | * @errp: Error object. | ||
155 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||
156 | void commit_active_start(const char *job_id, BlockDriverState *bs, | ||
157 | BlockDriverState *base, int creation_flags, | ||
158 | int64_t speed, BlockdevOnError on_error, | ||
159 | - BlockCompletionFunc *cb, | ||
160 | - void *opaque, Error **errp, bool auto_complete); | ||
161 | + const char *filter_node_name, | ||
162 | + BlockCompletionFunc *cb, void *opaque, Error **errp, | ||
163 | + bool auto_complete); | ||
164 | /* | ||
165 | * mirror_start: | ||
166 | * @job_id: The id of the newly-created job, or %NULL to use the | ||
167 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 12 | diff --git a/qapi/block-core.json b/qapi/block-core.json |
168 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
169 | --- a/qapi/block-core.json | 14 | --- a/qapi/block-core.json |
170 | +++ b/qapi/block-core.json | 15 | +++ b/qapi/block-core.json |
171 | @@ -XXX,XX +XXX,XX @@ | 16 | @@ -XXX,XX +XXX,XX @@ |
172 | # | 17 | # |
173 | # @speed: #optional the maximum speed, in bytes per second | 18 | # Manage read, write and flush latency histograms for the device. |
174 | # | 19 | # |
175 | +# @filter-node-name: #optional the node name that should be assigned to the | 20 | -# If only @device parameter is specified, remove all present latency histograms |
176 | +# filter driver that the commit job inserts into the graph | 21 | +# If only @id parameter is specified, remove all present latency histograms |
177 | +# above @top. If this option is not given, a node name is | 22 | # for the device. Otherwise, add/reset some of (or all) latency histograms. |
178 | +# autogenerated. (Since: 2.9) | 23 | # |
179 | +# | 24 | # @id: The name or QOM path of the guest device. |
180 | # Returns: Nothing on success | ||
181 | # If commit or stream is already active on this device, DeviceInUse | ||
182 | # If @device does not exist, DeviceNotFound | ||
183 | @@ -XXX,XX +XXX,XX @@ | 25 | @@ -XXX,XX +XXX,XX @@ |
26 | # [0, 10), [10, 50), [50, 100), [100, +inf): | ||
27 | # | ||
28 | # -> { "execute": "block-latency-histogram-set", | ||
29 | -# "arguments": { "device": "drive0", | ||
30 | +# "arguments": { "id": "drive0", | ||
31 | # "boundaries": [10, 50, 100] } } | ||
32 | # <- { "return": {} } | ||
33 | # | ||
34 | @@ -XXX,XX +XXX,XX @@ | ||
35 | # not changed (or not created): | ||
36 | # | ||
37 | # -> { "execute": "block-latency-histogram-set", | ||
38 | -# "arguments": { "device": "drive0", | ||
39 | +# "arguments": { "id": "drive0", | ||
40 | # "boundaries-write": [10, 50, 100] } } | ||
41 | # <- { "return": {} } | ||
42 | # | ||
43 | @@ -XXX,XX +XXX,XX @@ | ||
44 | # write: [0, 1000), [1000, 5000), [5000, +inf) | ||
45 | # | ||
46 | # -> { "execute": "block-latency-histogram-set", | ||
47 | -# "arguments": { "device": "drive0", | ||
48 | +# "arguments": { "id": "drive0", | ||
49 | # "boundaries": [10, 50, 100], | ||
50 | # "boundaries-write": [1000, 5000] } } | ||
51 | # <- { "return": {} } | ||
52 | @@ -XXX,XX +XXX,XX @@ | ||
53 | # Example: remove all latency histograms: | ||
54 | # | ||
55 | # -> { "execute": "block-latency-histogram-set", | ||
56 | -# "arguments": { "device": "drive0" } } | ||
57 | +# "arguments": { "id": "drive0" } } | ||
58 | # <- { "return": {} } | ||
184 | ## | 59 | ## |
185 | { 'command': 'block-commit', | 60 | { 'command': 'block-latency-histogram-set', |
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 | -- | 61 | -- |
209 | 1.8.3.1 | 62 | 2.20.1 |
210 | 63 | ||
211 | 64 | diff view generated by jsdifflib |
1 | This makes use of the .bdrv_child_perm() implementation for formats that | 1 | From: Sam Eiderman <shmuel.eiderman@oracle.com> |
---|---|---|---|
2 | we just added. All format drivers expose the permissions they actually | ||
3 | need nows, so that they can be set accordingly and updated when parents | ||
4 | are attached or detached. | ||
5 | 2 | ||
6 | The only format not included here is raw, which was already converted | 3 | Commit 509d39aa22909c0ed1aabf896865f19c81fb38a1 added support for read |
7 | with the other filter drivers. | 4 | only VMDKs of version 3. |
8 | 5 | ||
6 | This commit fixes the probe function to correctly handle descriptors of | ||
7 | version 3. | ||
8 | |||
9 | This commit has two effects: | ||
10 | 1. We no longer need to supply '-f vmdk' when pointing to descriptor | ||
11 | files of version 3 in qemu/qemu-img command line arguments. | ||
12 | 2. This fixes the scenario where a VMDK points to a parent version 3 | ||
13 | descriptor file which is being probed as "raw" instead of "vmdk". | ||
14 | |||
15 | Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com> | ||
16 | Reviewed-by: Mark Kanda <mark.kanda@oracle.com> | ||
17 | Signed-off-by: Shmuel Eiderman <shmuel.eiderman@oracle.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
11 | Acked-by: Fam Zheng <famz@redhat.com> | ||
12 | --- | 19 | --- |
13 | block/bochs.c | 1 + | 20 | block/vmdk.c | 6 ++++-- |
14 | block/cloop.c | 1 + | 21 | 1 file changed, 4 insertions(+), 2 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 | 22 | ||
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 | ||
112 | index XXXXXXX..XXXXXXX 100644 | ||
113 | --- a/block/qed.c | ||
114 | +++ b/block/qed.c | ||
115 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = { | ||
116 | .bdrv_open = bdrv_qed_open, | ||
117 | .bdrv_close = bdrv_qed_close, | ||
118 | .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, | ||
119 | + .bdrv_child_perm = bdrv_format_default_perms, | ||
120 | .bdrv_create = bdrv_qed_create, | ||
121 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
122 | .bdrv_co_get_block_status = bdrv_qed_co_get_block_status, | ||
123 | diff --git a/block/vdi.c b/block/vdi.c | ||
124 | index XXXXXXX..XXXXXXX 100644 | ||
125 | --- a/block/vdi.c | ||
126 | +++ b/block/vdi.c | ||
127 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vdi = { | ||
128 | .bdrv_open = vdi_open, | ||
129 | .bdrv_close = vdi_close, | ||
130 | .bdrv_reopen_prepare = vdi_reopen_prepare, | ||
131 | + .bdrv_child_perm = bdrv_format_default_perms, | ||
132 | .bdrv_create = vdi_create, | ||
133 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
134 | .bdrv_co_get_block_status = vdi_co_get_block_status, | ||
135 | diff --git a/block/vhdx.c b/block/vhdx.c | ||
136 | index XXXXXXX..XXXXXXX 100644 | ||
137 | --- a/block/vhdx.c | ||
138 | +++ b/block/vhdx.c | ||
139 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vhdx = { | ||
140 | .bdrv_open = vhdx_open, | ||
141 | .bdrv_close = vhdx_close, | ||
142 | .bdrv_reopen_prepare = vhdx_reopen_prepare, | ||
143 | + .bdrv_child_perm = bdrv_format_default_perms, | ||
144 | .bdrv_co_readv = vhdx_co_readv, | ||
145 | .bdrv_co_writev = vhdx_co_writev, | ||
146 | .bdrv_create = vhdx_create, | ||
147 | diff --git a/block/vmdk.c b/block/vmdk.c | 23 | diff --git a/block/vmdk.c b/block/vmdk.c |
148 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
149 | --- a/block/vmdk.c | 25 | --- a/block/vmdk.c |
150 | +++ b/block/vmdk.c | 26 | +++ b/block/vmdk.c |
151 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vmdk = { | 27 | @@ -XXX,XX +XXX,XX @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) |
152 | .bdrv_open = vmdk_open, | 28 | } |
153 | .bdrv_check = vmdk_check, | 29 | if (end - p >= strlen("version=X\n")) { |
154 | .bdrv_reopen_prepare = vmdk_reopen_prepare, | 30 | if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 || |
155 | + .bdrv_child_perm = bdrv_format_default_perms, | 31 | - strncmp("version=2\n", p, strlen("version=2\n")) == 0) { |
156 | .bdrv_co_preadv = vmdk_co_preadv, | 32 | + strncmp("version=2\n", p, strlen("version=2\n")) == 0 || |
157 | .bdrv_co_pwritev = vmdk_co_pwritev, | 33 | + strncmp("version=3\n", p, strlen("version=3\n")) == 0) { |
158 | .bdrv_co_pwritev_compressed = vmdk_co_pwritev_compressed, | 34 | return 100; |
159 | diff --git a/block/vpc.c b/block/vpc.c | 35 | } |
160 | index XXXXXXX..XXXXXXX 100644 | 36 | } |
161 | --- a/block/vpc.c | 37 | if (end - p >= strlen("version=X\r\n")) { |
162 | +++ b/block/vpc.c | 38 | if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 || |
163 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vpc = { | 39 | - strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) { |
164 | .bdrv_open = vpc_open, | 40 | + strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0 || |
165 | .bdrv_close = vpc_close, | 41 | + strncmp("version=3\r\n", p, strlen("version=3\r\n")) == 0) { |
166 | .bdrv_reopen_prepare = vpc_reopen_prepare, | 42 | return 100; |
167 | + .bdrv_child_perm = bdrv_format_default_perms, | 43 | } |
168 | .bdrv_create = vpc_create, | 44 | } |
169 | |||
170 | .bdrv_co_preadv = vpc_co_preadv, | ||
171 | -- | 45 | -- |
172 | 1.8.3.1 | 46 | 2.20.1 |
173 | 47 | ||
174 | 48 | diff view generated by jsdifflib |
1 | Now that the backing file child role implements .attach/.detach | 1 | Coverity doesn't like that the return value of bdrv_check_update_perm() |
---|---|---|---|
2 | callbacks, nothing prevents us from modifying the graph even if that | 2 | stays unused only in this place (CID 1399710). |
3 | involves changing backing file links. | 3 | |
4 | Even if checking local_err should be equivalent to checking ret < 0, | ||
5 | let's switch to using the return value to be more consistent (and in | ||
6 | case of a bug somewhere down the call chain, forgetting to assign errp | ||
7 | is more likely than returning 0 for an error case). | ||
4 | 8 | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 10 | Reviewed-by: Alberto Garcia <berto@igalia.com> |
7 | Acked-by: Fam Zheng <famz@redhat.com> | 11 | Reviewed-by: Peter Maydell <peter.maydell@linaro.org> |
12 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | ||
8 | --- | 13 | --- |
9 | block.c | 7 +++---- | 14 | block.c | 7 +++---- |
10 | 1 file changed, 3 insertions(+), 4 deletions(-) | 15 | 1 file changed, 3 insertions(+), 4 deletions(-) |
11 | 16 | ||
12 | diff --git a/block.c b/block.c | 17 | diff --git a/block.c b/block.c |
13 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/block.c | 19 | --- a/block.c |
15 | +++ b/block.c | 20 | +++ b/block.c |
16 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | 21 | @@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, |
17 | continue; | 22 | QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) { |
23 | /* Check whether we are allowed to switch c from top to base */ | ||
24 | GSList *ignore_children = g_slist_prepend(NULL, c); | ||
25 | - bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, | ||
26 | - ignore_children, &local_err); | ||
27 | + ret = bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, | ||
28 | + ignore_children, &local_err); | ||
29 | g_slist_free(ignore_children); | ||
30 | - if (local_err) { | ||
31 | - ret = -EPERM; | ||
32 | + if (ret < 0) { | ||
33 | error_report_err(local_err); | ||
34 | goto exit; | ||
18 | } | 35 | } |
19 | if (c->role == &child_backing) { | ||
20 | - /* @from is generally not allowed to be a backing file, except for | ||
21 | - * when @to is the overlay. In that case, @from may not be replaced | ||
22 | - * by @to as @to's backing node. */ | ||
23 | + /* If @from is a backing file of @to, ignore the child to avoid | ||
24 | + * creating a loop. We only want to change the pointer of other | ||
25 | + * parents. */ | ||
26 | QLIST_FOREACH(to_c, &to->children, next) { | ||
27 | if (to_c == c) { | ||
28 | break; | ||
29 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | ||
30 | } | ||
31 | } | ||
32 | |||
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 | -- | 36 | -- |
38 | 1.8.3.1 | 37 | 2.20.1 |
39 | 38 | ||
40 | 39 | diff view generated by jsdifflib |
1 | Aborting on error in bdrv_append() isn't correct. This patch fixes it | 1 | From: Sergio Lopez <slp@redhat.com> |
---|---|---|---|
2 | and lets the callers handle failures. | ||
3 | 2 | ||
4 | Test case 085 needs a reference output update. This is caused by the | 3 | There are various actions in this test that must be executed |
5 | reversed order of bdrv_set_backing_hd() and change_parent_backing_link() | 4 | sequentially, as the result of it depends on the state triggered by the |
6 | in bdrv_append(): When the backing file of the new node is set, the | 5 | previous one. |
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 | 6 | ||
7 | If the last argument of _send_qemu_cmd() is an empty string, it just | ||
8 | sends the QMP commands without waiting for an answer. While unlikely, it | ||
9 | may happen that the next action in the test gets invoked before QEMU | ||
10 | processes the QMP request. | ||
11 | |||
12 | This issue seems to be easier to reproduce on servers with limited | ||
13 | resources or highly loaded. | ||
14 | |||
15 | With this change, we wait for an answer on all _send_qemu_cmd() calls. | ||
16 | |||
17 | Signed-off-by: Sergio Lopez <slp@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 18 | 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 | --- | 19 | --- |
14 | block.c | 23 +++++++++++++++++------ | 20 | tests/qemu-iotests/153 | 12 ++++++------ |
15 | block/mirror.c | 9 ++++++++- | 21 | tests/qemu-iotests/153.out | 6 ++++++ |
16 | blockdev.c | 18 +++++++++++++++--- | 22 | 2 files changed, 12 insertions(+), 6 deletions(-) |
17 | include/block/block.h | 3 ++- | ||
18 | tests/qemu-iotests/085.out | 2 +- | ||
19 | 5 files changed, 43 insertions(+), 12 deletions(-) | ||
20 | 23 | ||
21 | diff --git a/block.c b/block.c | 24 | diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 |
25 | index XXXXXXX..XXXXXXX 100755 | ||
26 | --- a/tests/qemu-iotests/153 | ||
27 | +++ b/tests/qemu-iotests/153 | ||
28 | @@ -XXX,XX +XXX,XX @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do | ||
29 | _img_info -U | grep 'file format' | ||
30 | fi | ||
31 | done | ||
32 | - _send_qemu_cmd $h "{ 'execute': 'quit', }" "" | ||
33 | + _send_qemu_cmd $h "{ 'execute': 'quit' }" '' | ||
34 | echo | ||
35 | echo "Round done" | ||
36 | _cleanup_qemu | ||
37 | @@ -XXX,XX +XXX,XX @@ echo "Adding drive" | ||
38 | _send_qemu_cmd $QEMU_HANDLE \ | ||
39 | "{ 'execute': 'human-monitor-command', | ||
40 | 'arguments': { 'command-line': 'drive_add 0 if=none,id=d0,file=${TEST_IMG}' } }" \ | ||
41 | - "" | ||
42 | + 'return' | ||
43 | |||
44 | _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' | ||
45 | |||
46 | @@ -XXX,XX +XXX,XX @@ echo "== Closing an image should unlock it ==" | ||
47 | _send_qemu_cmd $QEMU_HANDLE \ | ||
48 | "{ 'execute': 'human-monitor-command', | ||
49 | 'arguments': { 'command-line': 'drive_del d0' } }" \ | ||
50 | - "" | ||
51 | + 'return' | ||
52 | |||
53 | _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' | ||
54 | |||
55 | @@ -XXX,XX +XXX,XX @@ for d in d0 d1; do | ||
56 | _send_qemu_cmd $QEMU_HANDLE \ | ||
57 | "{ 'execute': 'human-monitor-command', | ||
58 | 'arguments': { 'command-line': 'drive_add 0 if=none,id=$d,file=${TEST_IMG},readonly=on' } }" \ | ||
59 | - "" | ||
60 | + 'return' | ||
61 | done | ||
62 | |||
63 | _run_cmd $QEMU_IMG info "${TEST_IMG}" | ||
64 | @@ -XXX,XX +XXX,XX @@ _run_cmd $QEMU_IMG info "${TEST_IMG}" | ||
65 | _send_qemu_cmd $QEMU_HANDLE \ | ||
66 | "{ 'execute': 'human-monitor-command', | ||
67 | 'arguments': { 'command-line': 'drive_del d0' } }" \ | ||
68 | - "" | ||
69 | + 'return' | ||
70 | |||
71 | _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' | ||
72 | |||
73 | @@ -XXX,XX +XXX,XX @@ echo "Closing the other" | ||
74 | _send_qemu_cmd $QEMU_HANDLE \ | ||
75 | "{ 'execute': 'human-monitor-command', | ||
76 | 'arguments': { 'command-line': 'drive_del d1' } }" \ | ||
77 | - "" | ||
78 | + 'return' | ||
79 | |||
80 | _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' | ||
81 | |||
82 | diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out | ||
22 | index XXXXXXX..XXXXXXX 100644 | 83 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/block.c | 84 | --- a/tests/qemu-iotests/153.out |
24 | +++ b/block.c | 85 | +++ b/tests/qemu-iotests/153.out |
25 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | 86 | @@ -XXX,XX +XXX,XX @@ Is another process using the image [TEST_DIR/t.qcow2]? |
26 | int64_t total_size; | 87 | _qemu_img_wrapper commit -b TEST_DIR/t.qcow2.b TEST_DIR/t.qcow2.c |
27 | QemuOpts *opts = NULL; | 88 | {"return": {}} |
28 | BlockDriverState *bs_snapshot; | 89 | Adding drive |
29 | + Error *local_err = NULL; | 90 | +{"return": "OKrn"} |
30 | int ret; | 91 | |
31 | 92 | _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 | |
32 | /* if snapshot, we create a temporary backing file and open it | 93 | can't open device TEST_DIR/t.qcow2: Failed to get "write" lock |
33 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | 94 | @@ -XXX,XX +XXX,XX @@ Creating overlay with qemu-img when the guest is running should be allowed |
34 | * call bdrv_unref() on it), so in order to be able to return one, we have | 95 | |
35 | * to increase bs_snapshot's refcount here */ | 96 | _qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay |
36 | bdrv_ref(bs_snapshot); | 97 | == Closing an image should unlock it == |
37 | - bdrv_append(bs_snapshot, bs); | 98 | +{"return": ""} |
38 | + bdrv_append(bs_snapshot, bs, &local_err); | 99 | |
39 | + if (local_err) { | 100 | _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 |
40 | + error_propagate(errp, local_err); | 101 | Adding two and closing one |
41 | + ret = -EINVAL; | 102 | +{"return": "OKrn"} |
42 | + goto out; | 103 | +{"return": "OKrn"} |
43 | + } | 104 | |
44 | 105 | _qemu_img_wrapper info TEST_DIR/t.qcow2 | |
45 | g_free(tmp_filename); | 106 | +{"return": ""} |
46 | return bs_snapshot; | 107 | |
47 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | 108 | _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 |
48 | * parents of bs_top after bdrv_append() returns. If the caller needs to keep a | 109 | can't open device TEST_DIR/t.qcow2: Failed to get "write" lock |
49 | * reference of its own, it must call bdrv_ref(). | 110 | Is another process using the image [TEST_DIR/t.qcow2]? |
50 | */ | 111 | Closing the other |
51 | -void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) | 112 | +{"return": ""} |
52 | +void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | 113 | |
53 | + Error **errp) | 114 | _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 |
54 | { | ||
55 | + Error *local_err = NULL; | ||
56 | + | ||
57 | assert(!atomic_read(&bs_top->in_flight)); | ||
58 | assert(!atomic_read(&bs_new->in_flight)); | ||
59 | |||
60 | - bdrv_ref(bs_top); | ||
61 | + bdrv_set_backing_hd(bs_new, bs_top, &local_err); | ||
62 | + if (local_err) { | ||
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 | } | ||
77 | |||
78 | diff --git a/block/mirror.c b/block/mirror.c | ||
79 | index XXXXXXX..XXXXXXX 100644 | ||
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 | } | ||
127 | } | ||
128 | |||
129 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_commit(BlkActionState *common) | ||
130 | |||
131 | bdrv_set_aio_context(state->new_bs, state->aio_context); | ||
132 | |||
133 | - /* This removes our old bs and adds the new bs */ | ||
134 | - bdrv_append(state->new_bs, state->old_bs); | ||
135 | /* We don't need (or want) to use the transactional | ||
136 | * bdrv_reopen_multiple() across all the entries at once, because we | ||
137 | * don't want to abort all of them if one of them fails the reopen */ | ||
138 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(BlkActionState *common) | ||
139 | ExternalSnapshotState *state = | ||
140 | DO_UPCAST(ExternalSnapshotState, common, common); | ||
141 | if (state->new_bs) { | ||
142 | - bdrv_unref(state->new_bs); | ||
143 | + if (state->new_bs->backing) { | ||
144 | + bdrv_replace_in_backing_chain(state->new_bs, state->old_bs); | ||
145 | + } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_clean(BlkActionState *common) | ||
150 | if (state->aio_context) { | ||
151 | bdrv_drained_end(state->old_bs); | ||
152 | aio_context_release(state->aio_context); | ||
153 | + bdrv_unref(state->new_bs); | ||
154 | } | ||
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 | 115 | ||
184 | -- | 116 | -- |
185 | 1.8.3.1 | 117 | 2.20.1 |
186 | 118 | ||
187 | 119 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | From: Markus Armbruster <armbru@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Commit 75cdcd1 neglected to update tests/qemu-iotests/049.out, and | 3 | Recent commit cda4aa9a5a0 moved block backend creation before machine |
4 | made the error message for negative size worse. Fix that. | 4 | property evaluation. This broke qemu-iotests 055. Turns out we need |
5 | to create the migration object before block backends, so block | ||
6 | backends can add migration blockers. Fix by calling | ||
7 | migration_object_init() earlier, right before configure_blockdev(). | ||
5 | 8 | ||
6 | Reported-by: Thomas Huth <thuth@redhat.com> | 9 | Fixes: cda4aa9a5a08777cf13e164c0543bd4888b8adce |
10 | Reported-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 11 | Signed-off-by: Markus Armbruster <armbru@redhat.com> |
8 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Thomas Huth <thuth@redhat.com> | ||
10 | Tested-by: Christian Borntraeger <borntraeger@de.ibm.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 13 | --- |
13 | tests/qemu-iotests/049.out | 14 +++++++++----- | 14 | vl.c | 15 ++++++++------- |
14 | util/qemu-option.c | 2 +- | 15 | 1 file changed, 8 insertions(+), 7 deletions(-) |
15 | 2 files changed, 10 insertions(+), 6 deletions(-) | ||
16 | 16 | ||
17 | diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out | 17 | diff --git a/vl.c b/vl.c |
18 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/tests/qemu-iotests/049.out | 19 | --- a/vl.c |
20 | +++ b/tests/qemu-iotests/049.out | 20 | +++ b/vl.c |
21 | @@ -XXX,XX +XXX,XX @@ qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024 | 21 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) |
22 | qemu-img: Image size must be less than 8 EiB! | 22 | exit(0); |
23 | 23 | } | |
24 | qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 | 24 | |
25 | -qemu-img: Parameter 'size' expects a non-negative number below 2^64 | 25 | + /* |
26 | +qemu-img: Value '-1024' is out of range for parameter 'size' | 26 | + * Migration object can only be created after global properties |
27 | qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' | 27 | + * are applied correctly. |
28 | 28 | + */ | |
29 | qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k | 29 | + migration_object_init(); |
30 | qemu-img: Image size must be less than 8 EiB! | 30 | + |
31 | 31 | /* | |
32 | qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 | 32 | * Note: we need to create block backends before |
33 | -qemu-img: Parameter 'size' expects a non-negative number below 2^64 | 33 | * machine_set_property(), so machine properties can refer to |
34 | +qemu-img: Value '-1k' is out of range for parameter 'size' | 34 | - * them. |
35 | qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' | 35 | + * them, and after migration_object_init(), so we can create |
36 | 36 | + * migration blockers. | |
37 | qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte | 37 | */ |
38 | @@ -XXX,XX +XXX,XX @@ qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes | 38 | configure_blockdev(&bdo_queue, machine_class, snapshot); |
39 | qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. | 39 | |
40 | 40 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) | |
41 | qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 | 41 | machine_class->name, machine_class->deprecation_reason); |
42 | -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 | 42 | } |
43 | +qemu-img: Parameter 'size' expects a non-negative number below 2^64 | 43 | |
44 | +Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- | 44 | - /* |
45 | +and exabytes, respectively. | 45 | - * Migration object can only be created after global properties |
46 | +qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' | 46 | - * are applied correctly. |
47 | 47 | - */ | |
48 | qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar | 48 | - migration_object_init(); |
49 | qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for | 49 | - |
50 | qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. | 50 | if (qtest_chrdev) { |
51 | 51 | qtest_init(qtest_chrdev, qtest_log, &error_fatal); | |
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 | } | 52 | } |
74 | -- | 53 | -- |
75 | 1.8.3.1 | 54 | 2.20.1 |
76 | 55 | ||
77 | 56 | diff view generated by jsdifflib |
1 | This adds an assertion that ensures that the necessary resize permission | 1 | 232 is marked as generic, but commit 12efe428c9e added code that assumes |
---|---|---|---|
2 | has been granted before bdrv_truncate() is called. | 2 | qcow2. What the new test really needs is backing files and support for |
3 | updating the backing file link (.bdrv_change_backing_file). | ||
4 | |||
5 | Split the non-generic code into a new test case 247 and make it work | ||
6 | with qed, too. | ||
3 | 7 | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
5 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
6 | Acked-by: Fam Zheng <famz@redhat.com> | ||
7 | --- | 9 | --- |
8 | block.c | 3 +++ | 10 | tests/qemu-iotests/232 | 30 --------------- |
9 | block/io.c | 1 + | 11 | tests/qemu-iotests/232.out | 20 ---------- |
10 | 2 files changed, 4 insertions(+) | 12 | tests/qemu-iotests/247 | 79 ++++++++++++++++++++++++++++++++++++++ |
11 | 13 | tests/qemu-iotests/247.out | 22 +++++++++++ | |
12 | diff --git a/block.c b/block.c | 14 | tests/qemu-iotests/group | 1 + |
15 | 5 files changed, 102 insertions(+), 50 deletions(-) | ||
16 | create mode 100755 tests/qemu-iotests/247 | ||
17 | create mode 100644 tests/qemu-iotests/247.out | ||
18 | |||
19 | diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232 | ||
20 | index XXXXXXX..XXXXXXX 100755 | ||
21 | --- a/tests/qemu-iotests/232 | ||
22 | +++ b/tests/qemu-iotests/232 | ||
23 | @@ -XXX,XX +XXX,XX @@ run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,a | ||
24 | run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on | ||
25 | run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0 | ||
26 | |||
27 | -echo | ||
28 | -echo "=== Try commit to backing file with auto-read-only ===" | ||
29 | -echo | ||
30 | - | ||
31 | -TEST_IMG="$TEST_IMG.0" _make_test_img $size | ||
32 | -TEST_IMG="$TEST_IMG.1" _make_test_img $size | ||
33 | -TEST_IMG="$TEST_IMG.2" _make_test_img $size | ||
34 | -TEST_IMG="$TEST_IMG.3" _make_test_img $size | ||
35 | -TEST_IMG="$TEST_IMG.4" _make_test_img $size | ||
36 | - | ||
37 | -(cat <<EOF | ||
38 | -{"execute":"qmp_capabilities"} | ||
39 | -{"execute":"block-commit", | ||
40 | - "arguments":{"device":"format-4", "top-node": "format-2", "base-node":"format-0", "job-id":"job0"}} | ||
41 | -EOF | ||
42 | -sleep 1 | ||
43 | -echo '{"execute":"quit"}' | ||
44 | -) | $QEMU -qmp stdio -nographic -nodefaults \ | ||
45 | - -blockdev file,node-name=file-0,filename=$TEST_IMG.0,auto-read-only=on \ | ||
46 | - -blockdev qcow2,node-name=format-0,file=file-0,read-only=on \ | ||
47 | - -blockdev file,node-name=file-1,filename=$TEST_IMG.1,auto-read-only=on \ | ||
48 | - -blockdev qcow2,node-name=format-1,file=file-1,read-only=on,backing=format-0 \ | ||
49 | - -blockdev file,node-name=file-2,filename=$TEST_IMG.2,auto-read-only=on \ | ||
50 | - -blockdev qcow2,node-name=format-2,file=file-2,read-only=on,backing=format-1 \ | ||
51 | - -blockdev file,node-name=file-3,filename=$TEST_IMG.3,auto-read-only=on \ | ||
52 | - -blockdev qcow2,node-name=format-3,file=file-3,read-only=on,backing=format-2 \ | ||
53 | - -blockdev file,node-name=file-4,filename=$TEST_IMG.4,auto-read-only=on \ | ||
54 | - -blockdev qcow2,node-name=format-4,file=file-4,read-only=on,backing=format-3 | | ||
55 | - _filter_qmp | ||
56 | - | ||
57 | # success, all done | ||
58 | echo "*** done" | ||
59 | rm -f $seq.full | ||
60 | diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out | ||
13 | index XXXXXXX..XXXXXXX 100644 | 61 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/block.c | 62 | --- a/tests/qemu-iotests/232.out |
15 | +++ b/block.c | 63 | +++ b/tests/qemu-iotests/232.out |
16 | @@ -XXX,XX +XXX,XX @@ int bdrv_truncate(BdrvChild *child, int64_t offset) | 64 | @@ -XXX,XX +XXX,XX @@ QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read |
17 | BlockDriverState *bs = child->bs; | 65 | QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied |
18 | BlockDriver *drv = bs->drv; | 66 | node0: TEST_DIR/t.IMGFMT (file) |
19 | int ret; | 67 | QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied |
20 | + | 68 | - |
21 | + assert(child->perm & BLK_PERM_RESIZE); | 69 | -=== Try commit to backing file with auto-read-only === |
22 | + | 70 | - |
23 | if (!drv) | 71 | -Formatting 'TEST_DIR/t.IMGFMT.0', fmt=IMGFMT size=134217728 |
24 | return -ENOMEDIUM; | 72 | -Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=134217728 |
25 | if (!drv->bdrv_truncate) | 73 | -Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728 |
26 | diff --git a/block/io.c b/block/io.c | 74 | -Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=134217728 |
75 | -Formatting 'TEST_DIR/t.IMGFMT.4', fmt=IMGFMT size=134217728 | ||
76 | -QMP_VERSION | ||
77 | -{"return": {}} | ||
78 | -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} | ||
79 | -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} | ||
80 | -{"return": {}} | ||
81 | -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} | ||
82 | -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} | ||
83 | -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}} | ||
84 | -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} | ||
85 | -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} | ||
86 | -{"return": {}} | ||
87 | -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} | ||
88 | *** done | ||
89 | diff --git a/tests/qemu-iotests/247 b/tests/qemu-iotests/247 | ||
90 | new file mode 100755 | ||
91 | index XXXXXXX..XXXXXXX | ||
92 | --- /dev/null | ||
93 | +++ b/tests/qemu-iotests/247 | ||
94 | @@ -XXX,XX +XXX,XX @@ | ||
95 | +#!/usr/bin/env bash | ||
96 | +# | ||
97 | +# Test for auto-read-only with commit block job | ||
98 | +# | ||
99 | +# Copyright (C) 2019 Red Hat, Inc. | ||
100 | +# | ||
101 | +# This program is free software; you can redistribute it and/or modify | ||
102 | +# it under the terms of the GNU General Public License as published by | ||
103 | +# the Free Software Foundation; either version 2 of the License, or | ||
104 | +# (at your option) any later version. | ||
105 | +# | ||
106 | +# This program is distributed in the hope that it will be useful, | ||
107 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
108 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
109 | +# GNU General Public License for more details. | ||
110 | +# | ||
111 | +# You should have received a copy of the GNU General Public License | ||
112 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
113 | +# | ||
114 | + | ||
115 | +# creator | ||
116 | +owner=kwolf@redhat.com | ||
117 | + | ||
118 | +seq=`basename $0` | ||
119 | +echo "QA output created by $seq" | ||
120 | + | ||
121 | +status=1 # failure is the default! | ||
122 | + | ||
123 | +_cleanup() | ||
124 | +{ | ||
125 | + _cleanup_test_img | ||
126 | + rm -f $TEST_IMG.[01234] | ||
127 | +} | ||
128 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
129 | + | ||
130 | +# get standard environment, filters and checks | ||
131 | +. ./common.rc | ||
132 | +. ./common.filter | ||
133 | + | ||
134 | +# Requires backing files and .bdrv_change_backing_file support | ||
135 | +_supported_fmt qcow2 qed | ||
136 | +_supported_proto file | ||
137 | +_supported_os Linux | ||
138 | + | ||
139 | +size=128M | ||
140 | + | ||
141 | +echo | ||
142 | +echo "=== Try commit to backing file with auto-read-only ===" | ||
143 | +echo | ||
144 | +TEST_IMG="$TEST_IMG.0" _make_test_img $size | ||
145 | +TEST_IMG="$TEST_IMG.1" _make_test_img $size | ||
146 | +TEST_IMG="$TEST_IMG.2" _make_test_img $size | ||
147 | +TEST_IMG="$TEST_IMG.3" _make_test_img $size | ||
148 | +TEST_IMG="$TEST_IMG.4" _make_test_img $size | ||
149 | + | ||
150 | +(cat <<EOF | ||
151 | +{"execute":"qmp_capabilities"} | ||
152 | +{"execute":"block-commit", | ||
153 | + "arguments":{"device":"format-4", "top-node": "format-2", "base-node":"format-0", "job-id":"job0"}} | ||
154 | +EOF | ||
155 | +sleep 1 | ||
156 | +echo '{"execute":"quit"}' | ||
157 | +) | $QEMU -qmp stdio -nographic -nodefaults \ | ||
158 | + -blockdev file,node-name=file-0,filename=$TEST_IMG.0,auto-read-only=on \ | ||
159 | + -blockdev $IMGFMT,node-name=format-0,file=file-0,read-only=on \ | ||
160 | + -blockdev file,node-name=file-1,filename=$TEST_IMG.1,auto-read-only=on \ | ||
161 | + -blockdev $IMGFMT,node-name=format-1,file=file-1,read-only=on,backing=format-0 \ | ||
162 | + -blockdev file,node-name=file-2,filename=$TEST_IMG.2,auto-read-only=on \ | ||
163 | + -blockdev $IMGFMT,node-name=format-2,file=file-2,read-only=on,backing=format-1 \ | ||
164 | + -blockdev file,node-name=file-3,filename=$TEST_IMG.3,auto-read-only=on \ | ||
165 | + -blockdev $IMGFMT,node-name=format-3,file=file-3,read-only=on,backing=format-2 \ | ||
166 | + -blockdev file,node-name=file-4,filename=$TEST_IMG.4,auto-read-only=on \ | ||
167 | + -blockdev $IMGFMT,node-name=format-4,file=file-4,read-only=on,backing=format-3 | | ||
168 | + _filter_qmp | ||
169 | + | ||
170 | +# success, all done | ||
171 | +echo "*** done" | ||
172 | +rm -f $seq.full | ||
173 | +status=0 | ||
174 | diff --git a/tests/qemu-iotests/247.out b/tests/qemu-iotests/247.out | ||
175 | new file mode 100644 | ||
176 | index XXXXXXX..XXXXXXX | ||
177 | --- /dev/null | ||
178 | +++ b/tests/qemu-iotests/247.out | ||
179 | @@ -XXX,XX +XXX,XX @@ | ||
180 | +QA output created by 247 | ||
181 | + | ||
182 | +=== Try commit to backing file with auto-read-only === | ||
183 | + | ||
184 | +Formatting 'TEST_DIR/t.IMGFMT.0', fmt=IMGFMT size=134217728 | ||
185 | +Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=134217728 | ||
186 | +Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728 | ||
187 | +Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=134217728 | ||
188 | +Formatting 'TEST_DIR/t.IMGFMT.4', fmt=IMGFMT size=134217728 | ||
189 | +QMP_VERSION | ||
190 | +{"return": {}} | ||
191 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} | ||
192 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} | ||
193 | +{"return": {}} | ||
194 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} | ||
195 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} | ||
196 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}} | ||
197 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} | ||
198 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} | ||
199 | +{"return": {}} | ||
200 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} | ||
201 | +*** done | ||
202 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
27 | index XXXXXXX..XXXXXXX 100644 | 203 | index XXXXXXX..XXXXXXX 100644 |
28 | --- a/block/io.c | 204 | --- a/tests/qemu-iotests/group |
29 | +++ b/block/io.c | 205 | +++ b/tests/qemu-iotests/group |
30 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, | 206 | @@ -XXX,XX +XXX,XX @@ |
31 | assert(req->overlap_offset <= offset); | 207 | 244 rw auto quick |
32 | assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); | 208 | 245 rw auto |
33 | assert(child->perm & BLK_PERM_WRITE); | 209 | 246 rw auto quick |
34 | + assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); | 210 | +247 rw auto quick |
35 | |||
36 | ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); | ||
37 | |||
38 | -- | 211 | -- |
39 | 1.8.3.1 | 212 | 2.20.1 |
40 | 213 | ||
41 | 214 | diff view generated by jsdifflib |
1 | In some cases, we want to remove op blockers on intermediate nodes | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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 | Job (especially mirror) may call block_job_error_action several |
7 | actually do all things in the right order, but I'll leave simplifying | 4 | times before actual pause if it has several in-flight requests. |
8 | this for another day. | ||
9 | 5 | ||
6 | block_job_error_action will call job_pause more than once in this case, | ||
7 | which lead to following block-job-resume qmp command can't actually | ||
8 | resume the job. | ||
9 | |||
10 | Fix it by do not increase pause level in block_job_error_action if | ||
11 | user_paused already set. | ||
12 | |||
13 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 14 | 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 | --- | 15 | --- |
14 | blockjob.c | 20 +++++++++++++------- | 16 | blockjob.c | 8 +++++--- |
15 | include/block/blockjob.h | 9 +++++++++ | 17 | 1 file changed, 5 insertions(+), 3 deletions(-) |
16 | 2 files changed, 22 insertions(+), 7 deletions(-) | ||
17 | 18 | ||
18 | diff --git a/blockjob.c b/blockjob.c | 19 | diff --git a/blockjob.c b/blockjob.c |
19 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/blockjob.c | 21 | --- a/blockjob.c |
21 | +++ b/blockjob.c | 22 | +++ b/blockjob.c |
22 | @@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque) | 23 | @@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, |
23 | block_job_unref(job); | 24 | action); |
24 | } | 25 | } |
25 | 26 | if (action == BLOCK_ERROR_ACTION_STOP) { | |
26 | +void block_job_remove_all_bdrv(BlockJob *job) | 27 | - job_pause(&job->job); |
27 | +{ | 28 | - /* make the pause user visible, which will be resumed from QMP. */ |
28 | + GSList *l; | 29 | - job->job.user_paused = true; |
29 | + for (l = job->nodes; l; l = l->next) { | 30 | + if (!job->job.user_paused) { |
30 | + BdrvChild *c = l->data; | 31 | + job_pause(&job->job); |
31 | + bdrv_op_unblock_all(c->bs, job->blocker); | 32 | + /* make the pause user visible, which will be resumed from QMP. */ |
32 | + bdrv_root_unref_child(c); | 33 | + job->job.user_paused = true; |
33 | + } | 34 | + } |
34 | + g_slist_free(job->nodes); | 35 | block_job_iostatus_set_err(job, error); |
35 | + job->nodes = NULL; | 36 | } |
36 | +} | 37 | return action; |
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 | { | ||
41 | @@ -XXX,XX +XXX,XX @@ void block_job_ref(BlockJob *job) | ||
42 | void block_job_unref(BlockJob *job) | ||
43 | { | ||
44 | if (--job->refcnt == 0) { | ||
45 | - GSList *l; | ||
46 | 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 | ||
59 | index XXXXXXX..XXXXXXX 100644 | ||
60 | --- a/include/block/blockjob.h | ||
61 | +++ b/include/block/blockjob.h | ||
62 | @@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, | ||
63 | uint64_t perm, uint64_t shared_perm, Error **errp); | ||
64 | |||
65 | /** | ||
66 | + * block_job_remove_all_bdrv: | ||
67 | + * @job: The block job | ||
68 | + * | ||
69 | + * Remove all BlockDriverStates from the list of nodes that are involved in the | ||
70 | + * job. This removes the blockers added with block_job_add_bdrv(). | ||
71 | + */ | ||
72 | +void block_job_remove_all_bdrv(BlockJob *job); | ||
73 | + | ||
74 | +/** | ||
75 | * block_job_set_speed: | ||
76 | * @job: The job to set the speed for. | ||
77 | * @speed: The new value | ||
78 | -- | 38 | -- |
79 | 1.8.3.1 | 39 | 2.20.1 |
80 | 40 | ||
81 | 41 | diff view generated by jsdifflib |
1 | All callers will have to request permissions for all of their child | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | nodes. Block drivers that act as simply filters can use the default | ||
3 | implementation of .bdrv_child_perm(). | ||
4 | 2 | ||
3 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
7 | Acked-by: Fam Zheng <famz@redhat.com> | ||
8 | --- | 5 | --- |
9 | block/blkdebug.c | 2 ++ | 6 | block/copy-on-read.c | 2 +- |
10 | block/blkreplay.c | 1 + | 7 | block/crypto.c | 2 +- |
11 | block/blkverify.c | 1 + | 8 | block/replication.c | 2 +- |
12 | block/quorum.c | 2 ++ | 9 | 3 files changed, 3 insertions(+), 3 deletions(-) |
13 | block/raw-format.c | 1 + | ||
14 | block/replication.c | 1 + | ||
15 | 6 files changed, 8 insertions(+) | ||
16 | 10 | ||
17 | diff --git a/block/blkdebug.c b/block/blkdebug.c | 11 | diff --git a/block/copy-on-read.c b/block/copy-on-read.c |
18 | index XXXXXXX..XXXXXXX 100644 | 12 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/block/blkdebug.c | 13 | --- a/block/copy-on-read.c |
20 | +++ b/block/blkdebug.c | 14 | +++ b/block/copy-on-read.c |
21 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = { | 15 | @@ -XXX,XX +XXX,XX @@ static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, |
22 | .bdrv_file_open = blkdebug_open, | 16 | } |
23 | .bdrv_close = blkdebug_close, | 17 | |
24 | .bdrv_reopen_prepare = blkdebug_reopen_prepare, | 18 | |
25 | + .bdrv_child_perm = bdrv_filter_default_perms, | 19 | -BlockDriver bdrv_copy_on_read = { |
26 | + | 20 | +static BlockDriver bdrv_copy_on_read = { |
27 | .bdrv_getlength = blkdebug_getlength, | 21 | .format_name = "copy-on-read", |
28 | .bdrv_truncate = blkdebug_truncate, | 22 | |
29 | .bdrv_refresh_filename = blkdebug_refresh_filename, | 23 | .bdrv_open = cor_open, |
30 | diff --git a/block/blkreplay.c b/block/blkreplay.c | 24 | diff --git a/block/crypto.c b/block/crypto.c |
31 | index XXXXXXX..XXXXXXX 100755 | ||
32 | --- a/block/blkreplay.c | ||
33 | +++ b/block/blkreplay.c | ||
34 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkreplay = { | ||
35 | |||
36 | .bdrv_file_open = blkreplay_open, | ||
37 | .bdrv_close = blkreplay_close, | ||
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 | 25 | index XXXXXXX..XXXXXXX 100644 |
44 | --- a/block/blkverify.c | 26 | --- a/block/crypto.c |
45 | +++ b/block/blkverify.c | 27 | +++ b/block/crypto.c |
46 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkverify = { | 28 | @@ -XXX,XX +XXX,XX @@ static const char *const block_crypto_strong_runtime_opts[] = { |
47 | .bdrv_parse_filename = blkverify_parse_filename, | 29 | NULL |
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 | }; | 30 | }; |
67 | diff --git a/block/raw-format.c b/block/raw-format.c | 31 | |
68 | index XXXXXXX..XXXXXXX 100644 | 32 | -BlockDriver bdrv_crypto_luks = { |
69 | --- a/block/raw-format.c | 33 | +static BlockDriver bdrv_crypto_luks = { |
70 | +++ b/block/raw-format.c | 34 | .format_name = "luks", |
71 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = { | 35 | .instance_size = sizeof(BlockCrypto), |
72 | .bdrv_reopen_abort = &raw_reopen_abort, | 36 | .bdrv_probe = block_crypto_probe_luks, |
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 | 37 | diff --git a/block/replication.c b/block/replication.c |
80 | index XXXXXXX..XXXXXXX 100644 | 38 | index XXXXXXX..XXXXXXX 100644 |
81 | --- a/block/replication.c | 39 | --- a/block/replication.c |
82 | +++ b/block/replication.c | 40 | +++ b/block/replication.c |
83 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_replication = { | 41 | @@ -XXX,XX +XXX,XX @@ static const char *const replication_strong_runtime_opts[] = { |
84 | 42 | NULL | |
85 | .bdrv_open = replication_open, | 43 | }; |
86 | .bdrv_close = replication_close, | 44 | |
87 | + .bdrv_child_perm = bdrv_filter_default_perms, | 45 | -BlockDriver bdrv_replication = { |
88 | 46 | +static BlockDriver bdrv_replication = { | |
89 | .bdrv_getlength = replication_getlength, | 47 | .format_name = "replication", |
90 | .bdrv_co_readv = replication_co_readv, | 48 | .instance_size = sizeof(BDRVReplicationState), |
49 | |||
91 | -- | 50 | -- |
92 | 1.8.3.1 | 51 | 2.20.1 |
93 | 52 | ||
94 | 53 | 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 | There is no reason why the constraints we put on @replaces should be |
4 | That means it reads one buffer and then writes it. No parallelism and each sync | 4 | limited to drive-mirror. Therefore, move the sanity checks from |
5 | request takes as long as it takes until it is completed. | 5 | qmp_drive_mirror() to blockdev_mirror_common() so they apply to |
6 | blockdev-mirror as well. | ||
6 | 7 | ||
7 | This can be a big performance hit when the convert process reads and writes | 8 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
8 | to devices which do not benefit from kernel readahead or pagecache. | 9 | Reviewed-by: Eric Blake <eblake@redhat.com> |
9 | In our environment we heavily have the following two use cases when using | 10 | Reviewed-by: Alberto Garcia <berto@igalia.com> |
10 | qemu-img convert. | ||
11 | |||
12 | a) reading from NFS and writing to iSCSI for deploying templates | ||
13 | b) reading from iSCSI and writing to NFS for backups | ||
14 | |||
15 | In both processes we use libiscsi and libnfs so we have no kernel cache. | ||
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> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
37 | --- | 12 | --- |
38 | qemu-img-cmds.hx | 4 +- | 13 | blockdev.c | 55 ++++++++++++++++++++++++++++++++---------------------- |
39 | qemu-img.c | 322 ++++++++++++++++++++++++++++++++++++++----------------- | 14 | 1 file changed, 33 insertions(+), 22 deletions(-) |
40 | qemu-img.texi | 16 ++- | ||
41 | 3 files changed, 243 insertions(+), 99 deletions(-) | ||
42 | 15 | ||
43 | diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx | 16 | diff --git a/blockdev.c b/blockdev.c |
44 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
45 | --- a/qemu-img-cmds.hx | 18 | --- a/blockdev.c |
46 | +++ b/qemu-img-cmds.hx | 19 | +++ b/blockdev.c |
47 | @@ -XXX,XX +XXX,XX @@ STEXI | 20 | @@ -XXX,XX +XXX,XX @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, |
48 | ETEXI | 21 | sync = MIRROR_SYNC_MODE_FULL; |
49 | 22 | } | |
50 | DEF("convert", img_convert, | 23 | |
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") | 24 | + if (has_replaces) { |
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") | 25 | + BlockDriverState *to_replace_bs; |
53 | STEXI | 26 | + AioContext *replace_aio_context; |
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} | 27 | + int64_t bs_size, replace_size; |
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} | ||
56 | ETEXI | ||
57 | |||
58 | DEF("dd", img_dd, | ||
59 | diff --git a/qemu-img.c b/qemu-img.c | ||
60 | index XXXXXXX..XXXXXXX 100644 | ||
61 | --- a/qemu-img.c | ||
62 | +++ b/qemu-img.c | ||
63 | @@ -XXX,XX +XXX,XX @@ static void QEMU_NORETURN help(void) | ||
64 | " kinds of errors, with a higher risk of choosing the wrong fix or\n" | ||
65 | " hiding corruption that has already occurred.\n" | ||
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 | + | 28 | + |
81 | typedef struct ImgConvertState { | 29 | + bs_size = bdrv_getlength(bs); |
82 | BlockBackend **src; | 30 | + if (bs_size < 0) { |
83 | int64_t *src_sectors; | 31 | + error_setg_errno(errp, -bs_size, "Failed to query device's size"); |
84 | - int src_cur, src_num; | 32 | + return; |
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 | } | ||
126 | } | ||
127 | |||
128 | static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) | ||
129 | { | ||
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 | + } | 33 | + } |
334 | + | 34 | + |
335 | + if (status == BLK_DATA) { | 35 | + to_replace_bs = check_to_replace_node(bs, replaces, errp); |
336 | + ret = convert_co_read(s, sector_num, n, buf); | 36 | + if (!to_replace_bs) { |
337 | + if (ret < 0) { | 37 | + return; |
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 | + } | 38 | + } |
347 | + | 39 | + |
348 | + if (s->wr_in_order) { | 40 | + replace_aio_context = bdrv_get_aio_context(to_replace_bs); |
349 | + /* keep writes in order */ | 41 | + aio_context_acquire(replace_aio_context); |
350 | + while (s->wr_offs != sector_num) { | 42 | + replace_size = bdrv_getlength(to_replace_bs); |
351 | + if (s->ret != -EINPROGRESS) { | 43 | + aio_context_release(replace_aio_context); |
352 | + goto out; | 44 | + |
353 | + } | 45 | + if (replace_size < 0) { |
354 | + s->wait_sector_num[index] = sector_num; | 46 | + error_setg_errno(errp, -replace_size, |
355 | + qemu_coroutine_yield(); | 47 | + "Failed to query the replacement node's size"); |
356 | + } | 48 | + return; |
357 | + s->wait_sector_num[index] = -1; | ||
358 | + } | 49 | + } |
359 | + | 50 | + if (bs_size != replace_size) { |
360 | + ret = convert_co_write(s, sector_num, n, buf, status); | 51 | + error_setg(errp, "cannot replace image with a mirror image of " |
361 | + if (ret < 0) { | 52 | + "different size"); |
362 | + error_report("error while writing sector %" PRId64 | 53 | + return; |
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 | + } | 54 | + } |
384 | + } | 55 | + } |
385 | + | 56 | + |
386 | +out: | 57 | /* pass the node name to replace to mirror start since it's loose coupling |
387 | + qemu_vfree(buf); | 58 | * and will allow to check whether the node still exist at mirror completion |
388 | + s->co[index] = NULL; | 59 | */ |
389 | + s->running_coroutines--; | 60 | @@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) |
390 | + if (!s->running_coroutines && s->ret == -EINPROGRESS) { | 61 | } |
391 | + /* the convert job finished successfully */ | 62 | |
392 | + s->ret = 0; | 63 | if (arg->has_replaces) { |
393 | + } | 64 | - BlockDriverState *to_replace_bs; |
394 | +} | 65 | - AioContext *replace_aio_context; |
395 | + | 66 | - int64_t replace_size; |
396 | +static int convert_do_copy(ImgConvertState *s) | 67 | - |
397 | +{ | 68 | if (!arg->has_node_name) { |
398 | + int ret, i, n; | 69 | error_setg(errp, "a node-name must be provided when replacing a" |
399 | + int64_t sector_num = 0; | 70 | " named node of the graph"); |
400 | 71 | goto out; | |
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 | } | 72 | } |
411 | s->buf_sectors = s->cluster_sectors; | ||
412 | } | ||
413 | - buf = blk_blockalign(s->target, s->buf_sectors * BDRV_SECTOR_SIZE); | ||
414 | |||
415 | - /* Calculate allocated sectors for progress */ | ||
416 | - s->allocated_sectors = 0; | ||
417 | - sector_num = 0; | ||
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 | } | ||
429 | |||
430 | /* Do the copy */ | ||
431 | - s->src_cur = 0; | ||
432 | - s->src_cur_offset = 0; | ||
433 | s->sector_next_status = 0; | ||
434 | + s->ret = -EINPROGRESS; | ||
435 | |||
436 | - sector_num = 0; | ||
437 | - allocated_done = 0; | ||
438 | - | 73 | - |
439 | - while (sector_num < s->total_sectors) { | 74 | - to_replace_bs = check_to_replace_node(bs, arg->replaces, &local_err); |
440 | - n = convert_iteration_sectors(s, sector_num); | 75 | - |
441 | - if (n < 0) { | 76 | - if (!to_replace_bs) { |
442 | - ret = n; | 77 | - error_propagate(errp, local_err); |
443 | - goto fail; | 78 | - goto out; |
444 | - } | ||
445 | - if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO)) | ||
446 | - { | ||
447 | - allocated_done += n; | ||
448 | - qemu_progress_print(100.0 * allocated_done / s->allocated_sectors, | ||
449 | - 0); | ||
450 | - } | 79 | - } |
451 | - | 80 | - |
452 | - if (s->status == BLK_DATA) { | 81 | - replace_aio_context = bdrv_get_aio_context(to_replace_bs); |
453 | - ret = convert_read(s, sector_num, n, buf); | 82 | - aio_context_acquire(replace_aio_context); |
454 | - if (ret < 0) { | 83 | - replace_size = bdrv_getlength(to_replace_bs); |
455 | - error_report("error while reading sector %" PRId64 | 84 | - aio_context_release(replace_aio_context); |
456 | - ": %s", sector_num, strerror(-ret)); | 85 | - |
457 | - goto fail; | 86 | - if (size != replace_size) { |
458 | - } | 87 | - error_setg(errp, "cannot replace image with a mirror image of " |
459 | - } else if (!s->min_sparse && s->status == BLK_ZERO) { | 88 | - "different size"); |
460 | - n = MIN(n, s->buf_sectors); | 89 | - goto out; |
461 | - memset(buf, 0, n * BDRV_SECTOR_SIZE); | ||
462 | - s->status = BLK_DATA; | ||
463 | - } | 90 | - } |
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 | } | 91 | } |
482 | 92 | ||
483 | - if (s->compressed) { | 93 | if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) { |
484 | + if (s->compressed && !s->ret) { | ||
485 | /* signal EOF to align */ | ||
486 | ret = blk_pwrite_compressed(s->target, 0, NULL, 0); | ||
487 | if (ret < 0) { | ||
488 | - goto fail; | ||
489 | + return ret; | ||
490 | } | ||
491 | } | ||
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 | -- | 94 | -- |
602 | 1.8.3.1 | 95 | 2.20.1 |
603 | 96 | ||
604 | 97 | 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 | It will have to return an error soon, so prepare the callers for it. | ||
2 | 1 | ||
3 | 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 | --- | ||
7 | block.c | 16 +++++++++++++--- | ||
8 | block/quorum.c | 9 ++++++++- | ||
9 | include/block/block.h | 3 ++- | ||
10 | 3 files changed, 23 insertions(+), 5 deletions(-) | ||
11 | |||
12 | diff --git a/block.c b/block.c | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/block.c | ||
15 | +++ b/block.c | ||
16 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
17 | BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
18 | BlockDriverState *child_bs, | ||
19 | const char *child_name, | ||
20 | - const BdrvChildRole *child_role) | ||
21 | + const BdrvChildRole *child_role, | ||
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 | + } | ||
55 | + | ||
56 | + return c; | ||
57 | } | ||
58 | |||
59 | static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||
60 | diff --git a/block/quorum.c b/block/quorum.c | ||
61 | index XXXXXXX..XXXXXXX 100644 | ||
62 | --- a/block/quorum.c | ||
63 | +++ b/block/quorum.c | ||
64 | @@ -XXX,XX +XXX,XX @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, | ||
65 | |||
66 | /* We can safely add the child now */ | ||
67 | bdrv_ref(child_bs); | ||
68 | - child = bdrv_attach_child(bs, child_bs, indexstr, &child_format); | ||
69 | + | ||
70 | + child = bdrv_attach_child(bs, child_bs, indexstr, &child_format, errp); | ||
71 | + if (child == NULL) { | ||
72 | + s->next_child_index--; | ||
73 | + bdrv_unref(child_bs); | ||
74 | + goto out; | ||
75 | + } | ||
76 | s->children = g_renew(BdrvChild *, s->children, s->num_children + 1); | ||
77 | s->children[s->num_children++] = child; | ||
78 | |||
79 | +out: | ||
80 | bdrv_drained_end(bs); | ||
81 | } | ||
82 | |||
83 | diff --git a/include/block/block.h b/include/block/block.h | ||
84 | index XXXXXXX..XXXXXXX 100644 | ||
85 | --- a/include/block/block.h | ||
86 | +++ b/include/block/block.h | ||
87 | @@ -XXX,XX +XXX,XX @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); | ||
88 | BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
89 | BlockDriverState *child_bs, | ||
90 | const char *child_name, | ||
91 | - const BdrvChildRole *child_role); | ||
92 | + const BdrvChildRole *child_role, | ||
93 | + Error **errp); | ||
94 | |||
95 | bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); | ||
96 | void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason); | ||
97 | -- | ||
98 | 1.8.3.1 | ||
99 | |||
100 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | When attaching a node as a child to a new parent, the required and | ||
2 | shared permissions for this parent are checked against all other parents | ||
3 | of the node now, and an error is returned if there is a conflict. | ||
4 | 1 | ||
5 | This allows error returns to a function that previously always | ||
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 | ||
27 | --- a/block.c | ||
28 | +++ b/block.c | ||
29 | @@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename, | ||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | +static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm, | ||
34 | + uint64_t new_shared_perm, | ||
35 | + BdrvChild *ignore_child, Error **errp) | ||
36 | +{ | ||
37 | + BdrvChild *c; | ||
38 | + | ||
39 | + /* There is no reason why anyone couldn't tolerate write_unchanged */ | ||
40 | + assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED); | ||
41 | + | ||
42 | + QLIST_FOREACH(c, &bs->parents, next_parent) { | ||
43 | + if (c == ignore_child) { | ||
44 | + continue; | ||
45 | + } | ||
46 | + | ||
47 | + if ((new_used_perm & c->shared_perm) != new_used_perm || | ||
48 | + (c->perm & new_shared_perm) != c->perm) | ||
49 | + { | ||
50 | + const char *user = NULL; | ||
51 | + if (c->role->get_name) { | ||
52 | + user = c->role->get_name(c); | ||
53 | + if (user && !*user) { | ||
54 | + user = NULL; | ||
55 | + } | ||
56 | + } | ||
57 | + error_setg(errp, "Conflicts with %s", user ?: "another operation"); | ||
58 | + return -EPERM; | ||
59 | + } | ||
60 | + } | ||
61 | + | ||
62 | + return 0; | ||
63 | +} | ||
64 | + | ||
65 | static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) | ||
66 | { | ||
67 | BlockDriverState *old_bs = child->bs; | ||
68 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) | ||
69 | BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
70 | const char *child_name, | ||
71 | const BdrvChildRole *child_role, | ||
72 | - void *opaque) | ||
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 | + } | ||
84 | + | ||
85 | + child = g_new(BdrvChild, 1); | ||
86 | *child = (BdrvChild) { | ||
87 | - .bs = NULL, | ||
88 | - .name = g_strdup(child_name), | ||
89 | - .role = child_role, | ||
90 | - .opaque = opaque, | ||
91 | + .bs = NULL, | ||
92 | + .name = g_strdup(child_name), | ||
93 | + .role = child_role, | ||
94 | + .perm = perm, | ||
95 | + .shared_perm = shared_perm, | ||
96 | + .opaque = opaque, | ||
97 | }; | ||
98 | |||
99 | bdrv_replace_child(child, child_bs); | ||
100 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
101 | const BdrvChildRole *child_role, | ||
102 | Error **errp) | ||
103 | { | ||
104 | - BdrvChild *child = bdrv_root_attach_child(child_bs, child_name, child_role, | ||
105 | - parent_bs); | ||
106 | + BdrvChild *child; | ||
107 | + | ||
108 | + /* FIXME Use real permissions */ | ||
109 | + child = bdrv_root_attach_child(child_bs, child_name, child_role, | ||
110 | + 0, BLK_PERM_ALL, parent_bs, errp); | ||
111 | + if (child == NULL) { | ||
112 | + return NULL; | ||
113 | + } | ||
114 | + | ||
115 | QLIST_INSERT_HEAD(&parent_bs->children, child, next); | ||
116 | return child; | ||
117 | } | ||
118 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
119 | index XXXXXXX..XXXXXXX 100644 | ||
120 | --- a/block/block-backend.c | ||
121 | +++ b/block/block-backend.c | ||
122 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new_open(const char *filename, const char *reference, | ||
123 | return NULL; | ||
124 | } | ||
125 | |||
126 | - blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk); | ||
127 | + /* FIXME Use real permissions */ | ||
128 | + blk->root = bdrv_root_attach_child(bs, "root", &child_root, | ||
129 | + 0, BLK_PERM_ALL, blk, &error_abort); | ||
130 | |||
131 | return blk; | ||
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 | -- | ||
178 | 1.8.3.1 | ||
179 | |||
180 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | In many cases, the required permissions of one node on its children | ||
2 | depend on what its parents require from it. For example, the raw format | ||
3 | or most filter drivers only need to request consistent reads if that's | ||
4 | something that one of their parents wants. | ||
5 | 1 | ||
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 | |||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
15 | Acked-by: Fam Zheng <famz@redhat.com> | ||
16 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
17 | --- | ||
18 | block.c | 206 +++++++++++++++++++++++++++++++++++++++++++++- | ||
19 | include/block/block_int.h | 61 ++++++++++++++ | ||
20 | 2 files changed, 263 insertions(+), 4 deletions(-) | ||
21 | |||
22 | diff --git a/block.c b/block.c | ||
23 | index XXXXXXX..XXXXXXX 100644 | ||
24 | --- a/block.c | ||
25 | +++ b/block.c | ||
26 | @@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename, | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | +/* | ||
31 | + * Check whether permissions on this node can be changed in a way that | ||
32 | + * @cumulative_perms and @cumulative_shared_perms are the new cumulative | ||
33 | + * permissions of all its parents. This involves checking whether all necessary | ||
34 | + * permission changes to child nodes can be performed. | ||
35 | + * | ||
36 | + * A call to this function must always be followed by a call to bdrv_set_perm() | ||
37 | + * or bdrv_abort_perm_update(). | ||
38 | + */ | ||
39 | +static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms, | ||
40 | + uint64_t cumulative_shared_perms, Error **errp) | ||
41 | +{ | ||
42 | + BlockDriver *drv = bs->drv; | ||
43 | + BdrvChild *c; | ||
44 | + int ret; | ||
45 | + | ||
46 | + /* Write permissions never work with read-only images */ | ||
47 | + if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && | ||
48 | + bdrv_is_read_only(bs)) | ||
49 | + { | ||
50 | + error_setg(errp, "Block node is read-only"); | ||
51 | + return -EPERM; | ||
52 | + } | ||
53 | + | ||
54 | + /* Check this node */ | ||
55 | + if (!drv) { | ||
56 | + return 0; | ||
57 | + } | ||
58 | + | ||
59 | + if (drv->bdrv_check_perm) { | ||
60 | + return drv->bdrv_check_perm(bs, cumulative_perms, | ||
61 | + cumulative_shared_perms, errp); | ||
62 | + } | ||
63 | + | ||
64 | + /* Drivers may not have .bdrv_child_perm() */ | ||
65 | + if (!drv->bdrv_child_perm) { | ||
66 | + return 0; | ||
67 | + } | ||
68 | + | ||
69 | + /* Check all children */ | ||
70 | + QLIST_FOREACH(c, &bs->children, next) { | ||
71 | + uint64_t cur_perm, cur_shared; | ||
72 | + drv->bdrv_child_perm(bs, c, c->role, | ||
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 | } | ||
259 | } | ||
260 | |||
261 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
262 | |||
263 | ret = bdrv_check_update_perm(child_bs, perm, shared_perm, NULL, errp); | ||
264 | if (ret < 0) { | ||
265 | + bdrv_abort_perm_update(child_bs); | ||
266 | return NULL; | ||
267 | } | ||
268 | |||
269 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
270 | .opaque = opaque, | ||
271 | }; | ||
272 | |||
273 | - bdrv_replace_child(child, child_bs); | ||
274 | + /* This performs the matching bdrv_set_perm() for the above check. */ | ||
275 | + bdrv_replace_child(child, child_bs, false); | ||
276 | |||
277 | return child; | ||
278 | } | ||
279 | @@ -XXX,XX +XXX,XX @@ static void bdrv_detach_child(BdrvChild *child) | ||
280 | child->next.le_prev = NULL; | ||
281 | } | ||
282 | |||
283 | - bdrv_replace_child(child, NULL); | ||
284 | + bdrv_replace_child(child, NULL, false); | ||
285 | |||
286 | g_free(child->name); | ||
287 | g_free(child); | ||
288 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | ||
289 | |||
290 | assert(c->role != &child_backing); | ||
291 | bdrv_ref(to); | ||
292 | - bdrv_replace_child(c, to); | ||
293 | + /* FIXME Are we sure that bdrv_replace_child() can't run into | ||
294 | + * &error_abort because of permissions? */ | ||
295 | + bdrv_replace_child(c, to, true); | ||
296 | bdrv_unref(from); | ||
297 | } | ||
298 | } | ||
299 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
300 | index XXXXXXX..XXXXXXX 100644 | ||
301 | --- a/include/block/block_int.h | ||
302 | +++ b/include/block/block_int.h | ||
303 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | ||
304 | void (*bdrv_del_child)(BlockDriverState *parent, BdrvChild *child, | ||
305 | Error **errp); | ||
306 | |||
307 | + /** | ||
308 | + * Informs the block driver that a permission change is intended. The | ||
309 | + * driver checks whether the change is permissible and may take other | ||
310 | + * preparations for the change (e.g. get file system locks). This operation | ||
311 | + * is always followed either by a call to either .bdrv_set_perm or | ||
312 | + * .bdrv_abort_perm_update. | ||
313 | + * | ||
314 | + * Checks whether the requested set of cumulative permissions in @perm | ||
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 | -- | ||
379 | 1.8.3.1 | ||
380 | |||
381 | 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 | Almost all format drivers have the same characteristics as far as | ||
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 | 1 | ||
9 | This provides a default implementation that can be shared by most of | ||
10 | our format drivers. | ||
11 | |||
12 | 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 | --- | ||
16 | block.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ | ||
17 | include/block/block_int.h | 8 ++++++++ | ||
18 | 2 files changed, 52 insertions(+) | ||
19 | |||
20 | diff --git a/block.c b/block.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/block.c | ||
23 | +++ b/block.c | ||
24 | @@ -XXX,XX +XXX,XX @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, | ||
25 | (c->shared_perm & DEFAULT_PERM_UNCHANGED); | ||
26 | } | ||
27 | |||
28 | +void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, | ||
29 | + const BdrvChildRole *role, | ||
30 | + uint64_t perm, uint64_t shared, | ||
31 | + uint64_t *nperm, uint64_t *nshared) | ||
32 | +{ | ||
33 | + bool backing = (role == &child_backing); | ||
34 | + assert(role == &child_backing || role == &child_file); | ||
35 | + | ||
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 | +} | ||
71 | + | ||
72 | static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs, | ||
73 | bool check_new_perm) | ||
74 | { | ||
75 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
76 | index XXXXXXX..XXXXXXX 100644 | ||
77 | --- a/include/block/block_int.h | ||
78 | +++ b/include/block/block_int.h | ||
79 | @@ -XXX,XX +XXX,XX @@ void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, | ||
80 | uint64_t perm, uint64_t shared, | ||
81 | uint64_t *nperm, uint64_t *nshared); | ||
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 | -- | ||
95 | 1.8.3.1 | ||
96 | |||
97 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | vvfat is the last remaining driver that can have children, but doesn't | ||
2 | implement .bdrv_child_perm() yet. The default handlers aren't suitable | ||
3 | here, so let's implement a very simple driver-specific one that protects | ||
4 | the internal child from being used by other users as good as our | ||
5 | permissions permit. | ||
6 | 1 | ||
7 | 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 | --- | ||
11 | block.c | 2 +- | ||
12 | block/vvfat.c | 22 ++++++++++++++++++++++ | ||
13 | include/block/block_int.h | 1 + | ||
14 | 3 files changed, 24 insertions(+), 1 deletion(-) | ||
15 | |||
16 | diff --git a/block.c b/block.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/block.c | ||
19 | +++ b/block.c | ||
20 | @@ -XXX,XX +XXX,XX @@ static void bdrv_backing_options(int *child_flags, QDict *child_options, | ||
21 | *child_flags = flags; | ||
22 | } | ||
23 | |||
24 | -static const BdrvChildRole child_backing = { | ||
25 | +const BdrvChildRole child_backing = { | ||
26 | .inherit_options = bdrv_backing_options, | ||
27 | .drained_begin = bdrv_child_cb_drained_begin, | ||
28 | .drained_end = bdrv_child_cb_drained_end, | ||
29 | diff --git a/block/vvfat.c b/block/vvfat.c | ||
30 | index XXXXXXX..XXXXXXX 100644 | ||
31 | --- a/block/vvfat.c | ||
32 | +++ b/block/vvfat.c | ||
33 | @@ -XXX,XX +XXX,XX @@ err: | ||
34 | return ret; | ||
35 | } | ||
36 | |||
37 | +static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
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 | { | ||
60 | BDRVVVFATState *s = bs->opaque; | ||
61 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vvfat = { | ||
62 | .bdrv_file_open = vvfat_open, | ||
63 | .bdrv_refresh_limits = vvfat_refresh_limits, | ||
64 | .bdrv_close = vvfat_close, | ||
65 | + .bdrv_child_perm = vvfat_child_perm, | ||
66 | |||
67 | .bdrv_co_preadv = vvfat_co_preadv, | ||
68 | .bdrv_co_pwritev = vvfat_co_pwritev, | ||
69 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
70 | index XXXXXXX..XXXXXXX 100644 | ||
71 | --- a/include/block/block_int.h | ||
72 | +++ b/include/block/block_int.h | ||
73 | @@ -XXX,XX +XXX,XX @@ struct BdrvChildRole { | ||
74 | |||
75 | extern const BdrvChildRole child_file; | ||
76 | extern const BdrvChildRole child_format; | ||
77 | +extern const BdrvChildRole child_backing; | ||
78 | |||
79 | struct BdrvChild { | ||
80 | BlockDriverState *bs; | ||
81 | -- | ||
82 | 1.8.3.1 | ||
83 | |||
84 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | All block drivers that can have child nodes implement .bdrv_child_perm() | ||
2 | now. Make this officially a requirement by asserting that only drivers | ||
3 | without children can omit .bdrv_child_perm(). | ||
4 | 1 | ||
5 | 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 | --- | ||
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 | ||
14 | --- a/block.c | ||
15 | +++ b/block.c | ||
16 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_perm(BlockDriverState *bs, uint64_t cumulative_perms, | ||
17 | cumulative_shared_perms, errp); | ||
18 | } | ||
19 | |||
20 | - /* Drivers may not have .bdrv_child_perm() */ | ||
21 | + /* Drivers that never have children can omit .bdrv_child_perm() */ | ||
22 | if (!drv->bdrv_child_perm) { | ||
23 | + assert(QLIST_EMPTY(&bs->children)); | ||
24 | return 0; | ||
25 | } | ||
26 | |||
27 | @@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, | ||
28 | drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms); | ||
29 | } | ||
30 | |||
31 | - /* Drivers may not have .bdrv_child_perm() */ | ||
32 | + /* Drivers that never have children can omit .bdrv_child_perm() */ | ||
33 | if (!drv->bdrv_child_perm) { | ||
34 | + assert(QLIST_EMPTY(&bs->children)); | ||
35 | return; | ||
36 | } | ||
37 | |||
38 | -- | ||
39 | 1.8.3.1 | ||
40 | |||
41 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Now that all block drivers with children tell us what permissions they | ||
2 | need from each of their children, bdrv_attach_child() can use this | ||
3 | information and make the right requirements while trying to attach new | ||
4 | children. | ||
5 | 1 | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | Acked-by: Fam Zheng <famz@redhat.com> | ||
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
9 | --- | ||
10 | block.c | 10 ++++++++-- | ||
11 | 1 file changed, 8 insertions(+), 2 deletions(-) | ||
12 | |||
13 | diff --git a/block.c b/block.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block.c | ||
16 | +++ b/block.c | ||
17 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
18 | Error **errp) | ||
19 | { | ||
20 | BdrvChild *child; | ||
21 | + uint64_t perm, shared_perm; | ||
22 | + | ||
23 | + bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); | ||
24 | + | ||
25 | + assert(parent_bs->drv); | ||
26 | + parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role, | ||
27 | + perm, shared_perm, &perm, &shared_perm); | ||
28 | |||
29 | - /* FIXME Use real permissions */ | ||
30 | child = bdrv_root_attach_child(child_bs, child_name, child_role, | ||
31 | - 0, BLK_PERM_ALL, parent_bs, errp); | ||
32 | + perm, shared_perm, parent_bs, errp); | ||
33 | if (child == NULL) { | ||
34 | return NULL; | ||
35 | } | ||
36 | -- | ||
37 | 1.8.3.1 | ||
38 | |||
39 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | The BlockBackend can now store the permissions that its user requires. | ||
2 | This is necessary because nodes can be ejected from or inserted into a | ||
3 | BlockBackend and all of these operations must make sure that the user | ||
4 | still gets what it requested initially. | ||
5 | 1 | ||
6 | 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 | --- | ||
10 | block/block-backend.c | 27 +++++++++++++++++++++++++++ | ||
11 | include/sysemu/block-backend.h | 2 ++ | ||
12 | 2 files changed, 29 insertions(+) | ||
13 | |||
14 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block/block-backend.c | ||
17 | +++ b/block/block-backend.c | ||
18 | @@ -XXX,XX +XXX,XX @@ struct BlockBackend { | ||
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 | } | ||
39 | } | ||
40 | |||
41 | +/* | ||
42 | + * Sets the permission bitmasks that the user of the BlockBackend needs. | ||
43 | + */ | ||
44 | +int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
45 | + Error **errp) | ||
46 | +{ | ||
47 | + int ret; | ||
48 | + | ||
49 | + if (blk->root) { | ||
50 | + ret = bdrv_child_try_set_perm(blk->root, perm, shared_perm, errp); | ||
51 | + if (ret < 0) { | ||
52 | + return ret; | ||
53 | + } | ||
54 | + } | ||
55 | + | ||
56 | + blk->perm = perm; | ||
57 | + blk->shared_perm = shared_perm; | ||
58 | + | ||
59 | + return 0; | ||
60 | +} | ||
61 | + | ||
62 | static int blk_do_attach_dev(BlockBackend *blk, void *dev) | ||
63 | { | ||
64 | if (blk->dev) { | ||
65 | @@ -XXX,XX +XXX,XX @@ void blk_detach_dev(BlockBackend *blk, void *dev) | ||
66 | blk->dev_ops = NULL; | ||
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 | } | ||
72 | |||
73 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h | ||
74 | index XXXXXXX..XXXXXXX 100644 | ||
75 | --- a/include/sysemu/block-backend.h | ||
76 | +++ b/include/sysemu/block-backend.h | ||
77 | @@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk); | ||
78 | void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs); | ||
79 | bool bdrv_has_blk(BlockDriverState *bs); | ||
80 | bool bdrv_is_root_node(BlockDriverState *bs); | ||
81 | +int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
82 | + Error **errp); | ||
83 | |||
84 | void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow); | ||
85 | void blk_iostatus_enable(BlockBackend *blk); | ||
86 | -- | ||
87 | 1.8.3.1 | ||
88 | |||
89 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Now that blk_insert_bs() requests the BlockBackend permissions for the | ||
2 | node it attaches to, it can fail. Instead of aborting, pass the errors | ||
3 | to the callers. | ||
4 | 1 | ||
5 | 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 | --- | ||
9 | block.c | 5 ++++- | ||
10 | block/backup.c | 5 ++++- | ||
11 | block/block-backend.c | 13 ++++++++----- | ||
12 | block/commit.c | 38 ++++++++++++++++++++++++++++++-------- | ||
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 | |||
25 | diff --git a/block.c b/block.c | ||
26 | index XXXXXXX..XXXXXXX 100644 | ||
27 | --- a/block.c | ||
28 | +++ b/block.c | ||
29 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename, | ||
30 | } | ||
31 | if (file_bs != NULL) { | ||
32 | file = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); | ||
33 | - blk_insert_bs(file, file_bs); | ||
34 | + blk_insert_bs(file, file_bs, &local_err); | ||
35 | bdrv_unref(file_bs); | ||
36 | + if (local_err) { | ||
37 | + goto fail; | ||
38 | + } | ||
39 | |||
40 | qdict_put(options, "file", | ||
41 | qstring_from_str(bdrv_get_node_name(file_bs))); | ||
42 | diff --git a/block/backup.c b/block/backup.c | ||
43 | index XXXXXXX..XXXXXXX 100644 | ||
44 | --- a/block/backup.c | ||
45 | +++ b/block/backup.c | ||
46 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
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 | } | ||
110 | } | ||
111 | |||
112 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||
113 | |||
114 | /* FIXME Use real permissions */ | ||
115 | s->base = blk_new(0, BLK_PERM_ALL); | ||
116 | - blk_insert_bs(s->base, base); | ||
117 | + ret = blk_insert_bs(s->base, base, errp); | ||
118 | + if (ret < 0) { | ||
119 | + goto fail; | ||
120 | + } | ||
121 | |||
122 | /* FIXME Use real permissions */ | ||
123 | s->top = blk_new(0, BLK_PERM_ALL); | ||
124 | - blk_insert_bs(s->top, top); | ||
125 | + ret = blk_insert_bs(s->top, top, errp); | ||
126 | + if (ret < 0) { | ||
127 | + goto fail; | ||
128 | + } | ||
129 | |||
130 | s->active = bs; | ||
131 | |||
132 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||
133 | |||
134 | trace_commit_start(bs, base, top, s); | ||
135 | block_job_start(&s->common); | ||
136 | + return; | ||
137 | + | ||
138 | +fail: | ||
139 | + if (s->base) { | ||
140 | + blk_unref(s->base); | ||
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 | } | ||
188 | if (s->to_replace) { | ||
189 | bdrv_op_unblock_all(s->to_replace, s->replace_blocker); | ||
190 | @@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, | ||
191 | bool auto_complete) | ||
192 | { | ||
193 | MirrorBlockJob *s; | ||
194 | + int ret; | ||
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 | ||
213 | index XXXXXXX..XXXXXXX 100644 | ||
214 | --- a/block/qcow2.c | ||
215 | +++ b/block/qcow2.c | ||
216 | @@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, | ||
217 | uint64_t cluster_size = s->cluster_size; | ||
218 | bool encrypt; | ||
219 | int refcount_bits = s->refcount_bits; | ||
220 | + Error *local_err = NULL; | ||
221 | int ret; | ||
222 | QemuOptDesc *desc = opts->list->desc; | ||
223 | Qcow2AmendHelperCBInfo helper_cb_info; | ||
224 | @@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, | ||
225 | |||
226 | if (new_size) { | ||
227 | BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL); | ||
228 | - blk_insert_bs(blk, bs); | ||
229 | + ret = blk_insert_bs(blk, bs, &local_err); | ||
230 | + if (ret < 0) { | ||
231 | + error_report_err(local_err); | ||
232 | + blk_unref(blk); | ||
233 | + return ret; | ||
234 | + } | ||
235 | + | ||
236 | ret = blk_truncate(blk, new_size); | ||
237 | blk_unref(blk); | ||
238 | - | ||
239 | if (ret < 0) { | ||
240 | return ret; | ||
241 | } | ||
242 | diff --git a/blockdev.c b/blockdev.c | ||
243 | index XXXXXXX..XXXXXXX 100644 | ||
244 | --- a/blockdev.c | ||
245 | +++ b/blockdev.c | ||
246 | @@ -XXX,XX +XXX,XX @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, | ||
247 | BlockDriverState *bs, Error **errp) | ||
248 | { | ||
249 | bool has_device; | ||
250 | + int ret; | ||
251 | |||
252 | /* For BBs without a device, we can exchange the BDS tree at will */ | ||
253 | has_device = blk_get_attached_dev(blk); | ||
254 | @@ -XXX,XX +XXX,XX @@ static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | - blk_insert_bs(blk, bs); | ||
259 | + ret = blk_insert_bs(blk, bs, errp); | ||
260 | + if (ret < 0) { | ||
261 | + return; | ||
262 | + } | ||
263 | |||
264 | if (!blk_dev_has_tray(blk)) { | ||
265 | /* For tray-less devices, blockdev-close-tray is a no-op (or may not be | ||
266 | @@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device, | ||
267 | } | ||
268 | |||
269 | blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL); | ||
270 | - blk_insert_bs(blk, bs); | ||
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; | ||
326 | } | ||
327 | diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c | ||
328 | index XXXXXXX..XXXXXXX 100644 | ||
329 | --- a/hw/core/qdev-properties-system.c | ||
330 | +++ b/hw/core/qdev-properties-system.c | ||
331 | @@ -XXX,XX +XXX,XX @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr, | ||
332 | { | ||
333 | BlockBackend *blk; | ||
334 | bool blk_created = false; | ||
335 | + int ret; | ||
336 | |||
337 | blk = blk_by_name(str); | ||
338 | if (!blk) { | ||
339 | @@ -XXX,XX +XXX,XX @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr, | ||
340 | if (bs) { | ||
341 | /* FIXME Use real permissions */ | ||
342 | blk = blk_new(0, BLK_PERM_ALL); | ||
343 | - blk_insert_bs(blk, bs); | ||
344 | blk_created = true; | ||
345 | + | ||
346 | + ret = blk_insert_bs(blk, bs, errp); | ||
347 | + if (ret < 0) { | ||
348 | + goto fail; | ||
349 | + } | ||
350 | } | ||
351 | } | ||
352 | if (!blk) { | ||
353 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h | ||
354 | index XXXXXXX..XXXXXXX 100644 | ||
355 | --- a/include/sysemu/block-backend.h | ||
356 | +++ b/include/sysemu/block-backend.h | ||
357 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_by_public(BlockBackendPublic *public); | ||
358 | |||
359 | BlockDriverState *blk_bs(BlockBackend *blk); | ||
360 | void blk_remove_bs(BlockBackend *blk); | ||
361 | -void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs); | ||
362 | +int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); | ||
363 | bool bdrv_has_blk(BlockDriverState *bs); | ||
364 | bool bdrv_is_root_node(BlockDriverState *bs); | ||
365 | int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
366 | diff --git a/migration/block.c b/migration/block.c | ||
367 | index XXXXXXX..XXXXXXX 100644 | ||
368 | --- a/migration/block.c | ||
369 | +++ b/migration/block.c | ||
370 | @@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f) | ||
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 | -- | ||
413 | 1.8.3.1 | ||
414 | |||
415 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | blk_new_open() is a convenience function that processes flags rather | ||
2 | than QDict options as a simple way to just open an image file. | ||
3 | 1 | ||
4 | In order to keep it convenient in the future, it must automatically | ||
5 | request the necessary permissions. This can easily be inferred from the | ||
6 | flags for read and write, but we need another flag that tells us whether | ||
7 | to get the resize permission. | ||
8 | |||
9 | We can't just always request it because that means that no block jobs | ||
10 | can run on the resulting BlockBackend (which is something that e.g. | ||
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 | |||
14 | The solution is to introduce another flag that is passed by all users | ||
15 | that want to resize the image. | ||
16 | |||
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
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 | ||
36 | --- a/block/parallels.c | ||
37 | +++ b/block/parallels.c | ||
38 | @@ -XXX,XX +XXX,XX @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) | ||
39 | } | ||
40 | |||
41 | file = blk_new_open(filename, NULL, NULL, | ||
42 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||
43 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||
44 | + &local_err); | ||
45 | if (file == NULL) { | ||
46 | error_propagate(errp, local_err); | ||
47 | return -EIO; | ||
48 | diff --git a/block/qcow.c b/block/qcow.c | ||
49 | index XXXXXXX..XXXXXXX 100644 | ||
50 | --- a/block/qcow.c | ||
51 | +++ b/block/qcow.c | ||
52 | @@ -XXX,XX +XXX,XX @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) | ||
53 | } | ||
54 | |||
55 | qcow_blk = blk_new_open(filename, NULL, NULL, | ||
56 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||
57 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||
58 | + &local_err); | ||
59 | if (qcow_blk == NULL) { | ||
60 | error_propagate(errp, local_err); | ||
61 | ret = -EIO; | ||
62 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
63 | index XXXXXXX..XXXXXXX 100644 | ||
64 | --- a/block/qcow2.c | ||
65 | +++ b/block/qcow2.c | ||
66 | @@ -XXX,XX +XXX,XX @@ static int qcow2_create2(const char *filename, int64_t total_size, | ||
67 | } | ||
68 | |||
69 | blk = blk_new_open(filename, NULL, NULL, | ||
70 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||
71 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||
72 | + &local_err); | ||
73 | if (blk == NULL) { | ||
74 | error_propagate(errp, local_err); | ||
75 | return -EIO; | ||
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 | } | ||
92 | |||
93 | blk = blk_new_open(filename, NULL, NULL, | ||
94 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||
95 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||
96 | + &local_err); | ||
97 | if (blk == NULL) { | ||
98 | error_propagate(errp, local_err); | ||
99 | return -EIO; | ||
100 | diff --git a/block/sheepdog.c b/block/sheepdog.c | ||
101 | index XXXXXXX..XXXXXXX 100644 | ||
102 | --- a/block/sheepdog.c | ||
103 | +++ b/block/sheepdog.c | ||
104 | @@ -XXX,XX +XXX,XX @@ static int sd_prealloc(const char *filename, Error **errp) | ||
105 | int ret; | ||
106 | |||
107 | blk = blk_new_open(filename, NULL, NULL, | ||
108 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, errp); | ||
109 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | ||
110 | if (blk == NULL) { | ||
111 | ret = -EIO; | ||
112 | goto out_with_err_set; | ||
113 | diff --git a/block/vdi.c b/block/vdi.c | ||
114 | index XXXXXXX..XXXXXXX 100644 | ||
115 | --- a/block/vdi.c | ||
116 | +++ b/block/vdi.c | ||
117 | @@ -XXX,XX +XXX,XX @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) | ||
118 | } | ||
119 | |||
120 | blk = blk_new_open(filename, NULL, NULL, | ||
121 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||
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 | } | ||
133 | |||
134 | blk = blk_new_open(filename, NULL, NULL, | ||
135 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||
136 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||
137 | + &local_err); | ||
138 | if (blk == NULL) { | ||
139 | error_propagate(errp, local_err); | ||
140 | ret = -EIO; | ||
141 | diff --git a/block/vmdk.c b/block/vmdk.c | ||
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 | } | ||
147 | |||
148 | blk = blk_new_open(filename, NULL, NULL, | ||
149 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||
150 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||
151 | + &local_err); | ||
152 | if (blk == NULL) { | ||
153 | error_propagate(errp, local_err); | ||
154 | ret = -EIO; | ||
155 | @@ -XXX,XX +XXX,XX @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) | ||
156 | } | ||
157 | |||
158 | new_blk = blk_new_open(filename, NULL, NULL, | ||
159 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | ||
160 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | ||
161 | + &local_err); | ||
162 | if (new_blk == NULL) { | ||
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 | -- | ||
205 | 1.8.3.1 | ||
206 | |||
207 | 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 | Some devices allow a media change between read-only and read-write | ||
2 | media. They need to adapt the permissions in their .change_media_cb() | ||
3 | implementation, which can fail. So add an Error parameter to the | ||
4 | function. | ||
5 | 1 | ||
6 | 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 | --- | ||
10 | block/block-backend.c | 20 +++++++++++++++----- | ||
11 | blockdev.c | 19 +++++++++++++++---- | ||
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 | |||
20 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/block/block-backend.c | ||
23 | +++ b/block/block-backend.c | ||
24 | @@ -XXX,XX +XXX,XX @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, | ||
25 | |||
26 | /* | ||
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 | */ | ||
38 | -void blk_dev_change_media_cb(BlockBackend *blk, bool load) | ||
39 | +void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) | ||
40 | { | ||
41 | if (blk->dev_ops && blk->dev_ops->change_media_cb) { | ||
42 | bool tray_was_open, tray_is_open; | ||
43 | + Error *local_err = NULL; | ||
44 | |||
45 | assert(!blk->legacy_dev); | ||
46 | |||
47 | tray_was_open = blk_dev_is_tray_open(blk); | ||
48 | - blk->dev_ops->change_media_cb(blk->dev_opaque, load); | ||
49 | + blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); | ||
50 | + if (local_err) { | ||
51 | + assert(load == true); | ||
52 | + error_propagate(errp, local_err); | ||
53 | + return; | ||
54 | + } | ||
55 | tray_is_open = blk_dev_is_tray_open(blk); | ||
56 | |||
57 | if (tray_was_open != tray_is_open) { | ||
58 | @@ -XXX,XX +XXX,XX @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load) | ||
59 | |||
60 | static void blk_root_change_media(BdrvChild *child, bool load) | ||
61 | { | ||
62 | - blk_dev_change_media_cb(child->opaque, load); | ||
63 | + blk_dev_change_media_cb(child->opaque, load, NULL); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | diff --git a/blockdev.c b/blockdev.c | ||
68 | index XXXXXXX..XXXXXXX 100644 | ||
69 | --- a/blockdev.c | ||
70 | +++ b/blockdev.c | ||
71 | @@ -XXX,XX +XXX,XX @@ static int do_open_tray(const char *blk_name, const char *qdev_id, | ||
72 | } | ||
73 | |||
74 | if (!locked || force) { | ||
75 | - blk_dev_change_media_cb(blk, false); | ||
76 | + blk_dev_change_media_cb(blk, false, &error_abort); | ||
77 | } | ||
78 | |||
79 | if (locked && !force) { | ||
80 | @@ -XXX,XX +XXX,XX @@ void qmp_blockdev_close_tray(bool has_device, const char *device, | ||
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; | ||
90 | } | ||
91 | |||
92 | - blk_dev_change_media_cb(blk, true); | ||
93 | + blk_dev_change_media_cb(blk, true, &local_err); | ||
94 | + if (local_err) { | ||
95 | + error_propagate(errp, local_err); | ||
96 | + return; | ||
97 | + } | ||
98 | } | ||
99 | |||
100 | void qmp_x_blockdev_remove_medium(bool has_device, const char *device, | ||
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 | -- | ||
211 | 1.8.3.1 | ||
212 | |||
213 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This makes all device emulations with a qdev drive property request | ||
2 | permissions on their BlockBackend. The only thing we block at this point | ||
3 | is resizing images for some devices that can't support it. | ||
4 | 1 | ||
5 | 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 | --- | ||
9 | hw/block/block.c | 22 +++++++++++++++++++++- | ||
10 | hw/block/fdc.c | 25 +++++++++++++++++++++++-- | ||
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 | |||
28 | diff --git a/hw/block/block.c b/hw/block/block.c | ||
29 | index XXXXXXX..XXXXXXX 100644 | ||
30 | --- a/hw/block/block.c | ||
31 | +++ b/hw/block/block.c | ||
32 | @@ -XXX,XX +XXX,XX @@ void blkconf_blocksizes(BlockConf *conf) | ||
33 | } | ||
34 | } | ||
35 | |||
36 | -void blkconf_apply_backend_options(BlockConf *conf) | ||
37 | +void blkconf_apply_backend_options(BlockConf *conf, bool readonly, | ||
38 | + bool resizable, Error **errp) | ||
39 | { | ||
40 | BlockBackend *blk = conf->blk; | ||
41 | BlockdevOnError rerror, werror; | ||
42 | + uint64_t perm, shared_perm; | ||
43 | bool wce; | ||
44 | + int ret; | ||
45 | + | ||
46 | + perm = BLK_PERM_CONSISTENT_READ; | ||
47 | + if (!readonly) { | ||
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; | ||
61 | + } | ||
62 | |||
63 | switch (conf->wce) { | ||
64 | case ON_OFF_AUTO_ON: wce = true; break; | ||
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 | + | ||
83 | + if (!load) { | ||
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 | + | ||
119 | + blkconf_apply_backend_options(&dev->conf, blk_is_read_only(dev->conf.blk), | ||
120 | + false, &local_err); | ||
121 | + if (local_err) { | ||
122 | + error_report_err(local_err); | ||
123 | + return -1; | ||
124 | + } | ||
125 | |||
126 | /* 'enospc' is the default for -drive, 'report' is what blk_new() gives us | ||
127 | * for empty drives. */ | ||
128 | @@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev) | ||
129 | return -1; | ||
130 | } | ||
131 | |||
132 | + drive->conf = &dev->conf; | ||
133 | drive->blk = dev->conf.blk; | ||
134 | drive->fdctrl = bus->fdc; | ||
135 | |||
136 | diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c | ||
137 | index XXXXXXX..XXXXXXX 100644 | ||
138 | --- a/hw/block/m25p80.c | ||
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 | - } | ||
270 | - | ||
271 | /* Default to devices being used at their maximum device width. This was | ||
272 | * assumed before the device_width support was added. | ||
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 | - | ||
308 | pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); | ||
309 | pfl->wcycle = 0; | ||
310 | pfl->cmd = 0; | ||
311 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
312 | index XXXXXXX..XXXXXXX 100644 | ||
313 | --- a/hw/block/virtio-blk.c | ||
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 | -- | ||
501 | 1.8.3.1 | ||
502 | |||
503 | 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 |
Deleted patch | |||
---|---|---|---|
1 | This functions creates a BlockBackend internally, so the block jobs need | ||
2 | to tell it what they want to do with the BB. | ||
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/backup.c | 5 +++-- | ||
9 | block/commit.c | 5 +++-- | ||
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 | |||
18 | diff --git a/block/backup.c b/block/backup.c | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/block/backup.c | ||
21 | +++ b/block/backup.c | ||
22 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
23 | goto error; | ||
24 | } | ||
25 | |||
26 | - job = block_job_create(job_id, &backup_job_driver, bs, speed, | ||
27 | - creation_flags, cb, opaque, errp); | ||
28 | + /* FIXME Use real permissions */ | ||
29 | + job = block_job_create(job_id, &backup_job_driver, bs, 0, BLK_PERM_ALL, | ||
30 | + speed, creation_flags, cb, opaque, errp); | ||
31 | if (!job) { | ||
32 | goto error; | ||
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 | return; | ||
40 | } | ||
41 | |||
42 | - s = block_job_create(job_id, &commit_job_driver, bs, speed, | ||
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 | } | ||
88 | |||
89 | void *block_job_create(const char *job_id, const BlockJobDriver *driver, | ||
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 | -- | ||
162 | 1.8.3.1 | ||
163 | |||
164 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | For meaningful error messages in the permission system, we need to get | ||
2 | some human-readable description of the parent of a BdrvChild. | ||
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.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 | ||
15 | --- a/block.c | ||
16 | +++ b/block.c | ||
17 | @@ -XXX,XX +XXX,XX @@ int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough) | ||
18 | return 0; | ||
19 | } | ||
20 | |||
21 | +static char *bdrv_child_get_parent_desc(BdrvChild *c) | ||
22 | +{ | ||
23 | + BlockDriverState *parent = c->opaque; | ||
24 | + return g_strdup(bdrv_get_device_or_node_name(parent)); | ||
25 | +} | ||
26 | + | ||
27 | static void bdrv_child_cb_drained_begin(BdrvChild *child) | ||
28 | { | ||
29 | BlockDriverState *bs = child->opaque; | ||
30 | @@ -XXX,XX +XXX,XX @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, | ||
31 | } | ||
32 | |||
33 | const BdrvChildRole child_file = { | ||
34 | + .get_parent_desc = bdrv_child_get_parent_desc, | ||
35 | .inherit_options = bdrv_inherited_options, | ||
36 | .drained_begin = bdrv_child_cb_drained_begin, | ||
37 | .drained_end = bdrv_child_cb_drained_end, | ||
38 | @@ -XXX,XX +XXX,XX @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options, | ||
39 | } | ||
40 | |||
41 | const BdrvChildRole child_format = { | ||
42 | + .get_parent_desc = bdrv_child_get_parent_desc, | ||
43 | .inherit_options = bdrv_inherited_fmt_options, | ||
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 | } | ||
48 | |||
49 | const BdrvChildRole child_backing = { | ||
50 | + .get_parent_desc = bdrv_child_get_parent_desc, | ||
51 | .inherit_options = bdrv_backing_options, | ||
52 | .drained_begin = bdrv_child_cb_drained_begin, | ||
53 | .drained_end = bdrv_child_cb_drained_end, | ||
54 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
55 | index XXXXXXX..XXXXXXX 100644 | ||
56 | --- a/block/block-backend.c | ||
57 | +++ b/block/block-backend.c | ||
58 | @@ -XXX,XX +XXX,XX @@ static const AIOCBInfo block_backend_aiocb_info = { | ||
59 | |||
60 | static void drive_info_del(DriveInfo *dinfo); | ||
61 | static BlockBackend *bdrv_first_blk(BlockDriverState *bs); | ||
62 | +static char *blk_get_attached_dev_id(BlockBackend *blk); | ||
63 | |||
64 | /* All BlockBackends */ | ||
65 | static QTAILQ_HEAD(, BlockBackend) block_backends = | ||
66 | @@ -XXX,XX +XXX,XX @@ static void blk_root_drained_end(BdrvChild *child); | ||
67 | static void blk_root_change_media(BdrvChild *child, bool load); | ||
68 | static void blk_root_resize(BdrvChild *child); | ||
69 | |||
70 | +static char *blk_root_get_parent_desc(BdrvChild *child) | ||
71 | +{ | ||
72 | + BlockBackend *blk = child->opaque; | ||
73 | + char *dev_id; | ||
74 | + | ||
75 | + if (blk->name) { | ||
76 | + return g_strdup(blk->name); | ||
77 | + } | ||
78 | + | ||
79 | + dev_id = blk_get_attached_dev_id(blk); | ||
80 | + if (*dev_id) { | ||
81 | + return dev_id; | ||
82 | + } else { | ||
83 | + /* TODO Callback into the BB owner for something more detailed */ | ||
84 | + g_free(dev_id); | ||
85 | + return g_strdup("a block device"); | ||
86 | + } | ||
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 | ||
102 | --- a/include/block/block_int.h | ||
103 | +++ b/include/block/block_int.h | ||
104 | @@ -XXX,XX +XXX,XX @@ struct BdrvChildRole { | ||
105 | * name), or NULL if the parent can't provide a better name. */ | ||
106 | const char* (*get_name)(BdrvChild *child); | ||
107 | |||
108 | + /* Returns a malloced string that describes the parent of the child for a | ||
109 | + * human reader. This could be a node-name, BlockBackend name, qdev ID or | ||
110 | + * QOM path of the device owning the BlockBackend, job type and ID etc. The | ||
111 | + * caller is responsible for freeing the memory. */ | ||
112 | + char* (*get_parent_desc)(BdrvChild *child); | ||
113 | + | ||
114 | /* | ||
115 | * If this pair of functions is implemented, the parent doesn't issue new | ||
116 | * requests after returning from .drained_begin() until .drained_end() is | ||
117 | -- | ||
118 | 1.8.3.1 | ||
119 | |||
120 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Instead of just telling that there was some conflict, we can be specific | ||
2 | and tell which permissions were in conflict and which way the conflict | ||
3 | is. | ||
4 | 1 | ||
5 | 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 | --- | ||
9 | block.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- | ||
10 | 1 file changed, 56 insertions(+), 11 deletions(-) | ||
11 | |||
12 | diff --git a/block.c b/block.c | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/block.c | ||
15 | +++ b/block.c | ||
16 | @@ -XXX,XX +XXX,XX @@ static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
17 | *shared_perm = cumulative_shared_perms; | ||
18 | } | ||
19 | |||
20 | +static char *bdrv_child_user_desc(BdrvChild *c) | ||
21 | +{ | ||
22 | + if (c->role->get_parent_desc) { | ||
23 | + return c->role->get_parent_desc(c); | ||
24 | + } | ||
25 | + | ||
26 | + return g_strdup("another user"); | ||
27 | +} | ||
28 | + | ||
29 | +static char *bdrv_perm_names(uint64_t perm) | ||
30 | +{ | ||
31 | + struct perm_name { | ||
32 | + uint64_t perm; | ||
33 | + const char *name; | ||
34 | + } permissions[] = { | ||
35 | + { BLK_PERM_CONSISTENT_READ, "consistent read" }, | ||
36 | + { BLK_PERM_WRITE, "write" }, | ||
37 | + { BLK_PERM_WRITE_UNCHANGED, "write unchanged" }, | ||
38 | + { BLK_PERM_RESIZE, "resize" }, | ||
39 | + { BLK_PERM_GRAPH_MOD, "change children" }, | ||
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 | |||
97 | -- | ||
98 | 1.8.3.1 | ||
99 | |||
100 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | When the parents' child links are updated in bdrv_append() or | ||
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 | 1 | ||
7 | 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 | --- | ||
11 | block.c | 3 +++ | ||
12 | include/block/block_int.h | 4 ++++ | ||
13 | 2 files changed, 7 insertions(+) | ||
14 | |||
15 | diff --git a/block.c b/block.c | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/block.c | ||
18 | +++ b/block.c | ||
19 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | ||
20 | BdrvChild *c, *next, *to_c; | ||
21 | |||
22 | QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { | ||
23 | + if (c->role->stay_at_node) { | ||
24 | + continue; | ||
25 | + } | ||
26 | if (c->role == &child_backing) { | ||
27 | /* @from is generally not allowed to be a backing file, except for | ||
28 | * when @to is the overlay. In that case, @from may not be replaced | ||
29 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
30 | index XXXXXXX..XXXXXXX 100644 | ||
31 | --- a/include/block/block_int.h | ||
32 | +++ b/include/block/block_int.h | ||
33 | @@ -XXX,XX +XXX,XX @@ typedef struct BdrvAioNotifier { | ||
34 | } BdrvAioNotifier; | ||
35 | |||
36 | struct BdrvChildRole { | ||
37 | + /* If true, bdrv_replace_in_backing_chain() doesn't change the node this | ||
38 | + * BdrvChild points to. */ | ||
39 | + bool stay_at_node; | ||
40 | + | ||
41 | void (*inherit_options)(int *child_flags, QDict *child_options, | ||
42 | int parent_flags, QDict *parent_options); | ||
43 | |||
44 | -- | ||
45 | 1.8.3.1 | ||
46 | |||
47 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Block jobs don't actually do I/O through the the reference they create | ||
2 | with block_job_add_bdrv(), but they might want to use the permisssion | ||
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 | 1 | ||
7 | 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 | --- | ||
11 | block/backup.c | 4 +++- | ||
12 | block/commit.c | 8 ++++++-- | ||
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 | |||
19 | diff --git a/block/backup.c b/block/backup.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/backup.c | ||
22 | +++ b/block/backup.c | ||
23 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
24 | job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); | ||
25 | } | ||
26 | |||
27 | - block_job_add_bdrv(&job->common, target); | ||
28 | + /* FIXME Use real permissions */ | ||
29 | + block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | ||
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 | } | ||
47 | /* overlay_bs must be blocked because it needs to be modified to | ||
48 | * update the backing image string, but if it's the root node then | ||
49 | * don't block it again */ | ||
50 | if (bs != overlay_bs) { | ||
51 | - block_job_add_bdrv(&s->common, overlay_bs); | ||
52 | + /* FIXME Use real permissions */ | ||
53 | + block_job_add_bdrv(&s->common, "overlay of top", overlay_bs, 0, | ||
54 | + BLK_PERM_ALL, &error_abort); | ||
55 | } | ||
56 | |||
57 | /* FIXME Use real permissions */ | ||
58 | diff --git a/block/mirror.c b/block/mirror.c | ||
59 | index XXXXXXX..XXXXXXX 100644 | ||
60 | --- a/block/mirror.c | ||
61 | +++ b/block/mirror.c | ||
62 | @@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, | ||
63 | return; | ||
64 | } | ||
65 | |||
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 | } | ||
81 | } | ||
82 | |||
83 | diff --git a/block/stream.c b/block/stream.c | ||
84 | index XXXXXXX..XXXXXXX 100644 | ||
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 | } | ||
96 | |||
97 | s->base = base; | ||
98 | diff --git a/blockjob.c b/blockjob.c | ||
99 | index XXXXXXX..XXXXXXX 100644 | ||
100 | --- a/blockjob.c | ||
101 | +++ b/blockjob.c | ||
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; | ||
144 | } | ||
145 | |||
146 | void *block_job_create(const char *job_id, const BlockJobDriver *driver, | ||
147 | @@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, | ||
148 | job = g_malloc0(driver->instance_size); | ||
149 | error_setg(&job->blocker, "block device is in use by block job: %s", | ||
150 | BlockJobType_lookup[driver->job_type]); | ||
151 | - block_job_add_bdrv(job, bs); | ||
152 | + block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); | ||
153 | bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); | ||
154 | |||
155 | job->driver = driver; | ||
156 | @@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job) | ||
157 | BlockDriverState *bs = blk_bs(job->blk); | ||
158 | bs->job = NULL; | ||
159 | for (l = job->nodes; l; l = l->next) { | ||
160 | - bs = l->data; | ||
161 | - bdrv_op_unblock_all(bs, job->blocker); | ||
162 | - bdrv_unref(bs); | ||
163 | + BdrvChild *c = l->data; | ||
164 | + bdrv_op_unblock_all(c->bs, job->blocker); | ||
165 | + bdrv_root_unref_child(c); | ||
166 | } | ||
167 | g_slist_free(job->nodes); | ||
168 | blk_remove_aio_context_notifier(job->blk, | ||
169 | diff --git a/include/block/blockjob.h b/include/block/blockjob.h | ||
170 | index XXXXXXX..XXXXXXX 100644 | ||
171 | --- a/include/block/blockjob.h | ||
172 | +++ b/include/block/blockjob.h | ||
173 | @@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id); | ||
174 | /** | ||
175 | * block_job_add_bdrv: | ||
176 | * @job: A block job | ||
177 | + * @name: The name to assign to the new BdrvChild | ||
178 | * @bs: A BlockDriverState that is involved in @job | ||
179 | + * @perm, @shared_perm: Permissions to request on the node | ||
180 | * | ||
181 | * Add @bs to the list of BlockDriverState that are involved in | ||
182 | * @job. This means that all operations will be blocked on @bs while | ||
183 | * @job exists. | ||
184 | */ | ||
185 | -void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs); | ||
186 | +int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, | ||
187 | + uint64_t perm, uint64_t shared_perm, Error **errp); | ||
188 | |||
189 | /** | ||
190 | * block_job_set_speed: | ||
191 | -- | ||
192 | 1.8.3.1 | ||
193 | |||
194 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This is probably one of the most interesting conversions to the new | ||
2 | op blocker system because a commit block job intentionally leaves some | ||
3 | intermediate block nodes in the backing chain that aren't valid on their | ||
4 | own any more; only the whole chain together results in a valid view. | ||
5 | 1 | ||
6 | In order to provide the 'consistent read' permission to the parents of | ||
7 | the 'top' node of the commit job, a new filter block driver is inserted | ||
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 | |||
12 | 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 | --- | ||
16 | block/commit.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++--------- | ||
17 | 1 file changed, 95 insertions(+), 18 deletions(-) | ||
18 | |||
19 | diff --git a/block/commit.c b/block/commit.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/commit.c | ||
22 | +++ 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) | ||
58 | } | ||
59 | g_free(s->backing_file_str); | ||
60 | blk_unref(s->top); | ||
61 | - blk_unref(s->base); | ||
62 | block_job_completed(&s->common, ret); | ||
63 | 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 | |||
212 | -- | ||
213 | 1.8.3.1 | ||
214 | |||
215 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This is a little simpler than the commit block job because it's | ||
2 | synchronous and only commits into the immediate backing file, but | ||
3 | otherwise doing more or less the same. | ||
4 | 1 | ||
5 | 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 | --- | ||
9 | block/commit.c | 33 +++++++++++++++++++++++++++------ | ||
10 | 1 file changed, 27 insertions(+), 6 deletions(-) | ||
11 | |||
12 | diff --git a/block/commit.c b/block/commit.c | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/block/commit.c | ||
15 | +++ b/block/commit.c | ||
16 | @@ -XXX,XX +XXX,XX @@ fail: | ||
17 | int bdrv_commit(BlockDriverState *bs) | ||
18 | { | ||
19 | BlockBackend *src, *backing; | ||
20 | + BlockDriverState *backing_file_bs = NULL; | ||
21 | + BlockDriverState *commit_top_bs = NULL; | ||
22 | BlockDriver *drv = bs->drv; | ||
23 | int64_t sector, total_sectors, length, backing_length; | ||
24 | int n, ro, open_flags; | ||
25 | int ret = 0; | ||
26 | uint8_t *buf = NULL; | ||
27 | + Error *local_err = NULL; | ||
28 | |||
29 | if (!drv) | ||
30 | return -ENOMEDIUM; | ||
31 | @@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | - /* FIXME Use real permissions */ | ||
36 | - src = blk_new(0, BLK_PERM_ALL); | ||
37 | - backing = blk_new(0, BLK_PERM_ALL); | ||
38 | + src = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); | ||
39 | + backing = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); | ||
40 | |||
41 | - ret = blk_insert_bs(src, bs, NULL); | ||
42 | + ret = blk_insert_bs(src, bs, &local_err); | ||
43 | if (ret < 0) { | ||
44 | + error_report_err(local_err); | ||
45 | + goto ro_cleanup; | ||
46 | + } | ||
47 | + | ||
48 | + /* Insert commit_top block node above backing, so we can write to it */ | ||
49 | + backing_file_bs = backing_bs(bs); | ||
50 | + | ||
51 | + commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, BDRV_O_RDWR, | ||
52 | + &local_err); | ||
53 | + if (commit_top_bs == NULL) { | ||
54 | + error_report_err(local_err); | ||
55 | goto ro_cleanup; | ||
56 | } | ||
57 | |||
58 | - ret = blk_insert_bs(backing, bs->backing->bs, NULL); | ||
59 | + bdrv_set_backing_hd(commit_top_bs, backing_file_bs); | ||
60 | + bdrv_set_backing_hd(bs, commit_top_bs); | ||
61 | + | ||
62 | + ret = blk_insert_bs(backing, backing_file_bs, &local_err); | ||
63 | if (ret < 0) { | ||
64 | + error_report_err(local_err); | ||
65 | goto ro_cleanup; | ||
66 | } | ||
67 | |||
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 | -- | ||
83 | 1.8.3.1 | ||
84 | |||
85 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | The backup block job doesn't have very complicated requirements: It | ||
2 | needs to read from the source and write to the target, but it's fine | ||
3 | with either side being changed. The only restriction is that we can't | ||
4 | resize the image because the job uses a cached value. | ||
5 | 1 | ||
6 | qemu-iotests 055 needs to be changed because it used a target which was | ||
7 | already attached to a virtio-blk device. The permission system correctly | ||
8 | forbids this (virtio-blk can't accept another writer with its default | ||
9 | share-rw=off). | ||
10 | |||
11 | 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 | --- | ||
15 | block/backup.c | 15 ++++++++++----- | ||
16 | tests/qemu-iotests/055 | 11 +++++++---- | ||
17 | 2 files changed, 17 insertions(+), 9 deletions(-) | ||
18 | |||
19 | diff --git a/block/backup.c b/block/backup.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/block/backup.c | ||
22 | +++ b/block/backup.c | ||
23 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
24 | goto error; | ||
25 | } | ||
26 | |||
27 | - /* FIXME Use real permissions */ | ||
28 | - job = block_job_create(job_id, &backup_job_driver, bs, 0, BLK_PERM_ALL, | ||
29 | + /* job->common.len is fixed, so we can't allow resize */ | ||
30 | + job = block_job_create(job_id, &backup_job_driver, bs, | ||
31 | + BLK_PERM_CONSISTENT_READ, | ||
32 | + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | | ||
33 | + BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD, | ||
34 | speed, creation_flags, cb, opaque, errp); | ||
35 | if (!job) { | ||
36 | goto error; | ||
37 | } | ||
38 | |||
39 | - /* FIXME Use real permissions */ | ||
40 | - job->target = blk_new(0, BLK_PERM_ALL); | ||
41 | + /* The target must match the source in size, so no resize here either */ | ||
42 | + job->target = blk_new(BLK_PERM_WRITE, | ||
43 | + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | | ||
44 | + BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD); | ||
45 | ret = blk_insert_bs(job->target, target, errp); | ||
46 | if (ret < 0) { | ||
47 | goto error; | ||
48 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
49 | job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); | ||
50 | } | ||
51 | |||
52 | - /* FIXME Use real permissions */ | ||
53 | + /* Required permissions are already taken with target's blk_new() */ | ||
54 | block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | ||
55 | &error_abort); | ||
56 | job->common.len = len; | ||
57 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 | ||
58 | index XXXXXXX..XXXXXXX 100755 | ||
59 | --- a/tests/qemu-iotests/055 | ||
60 | +++ b/tests/qemu-iotests/055 | ||
61 | @@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase): | ||
62 | def setUp(self): | ||
63 | qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len)) | ||
64 | |||
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 | -- | ||
101 | 1.8.3.1 | ||
102 | |||
103 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | bdrv_append() cares about isolation of the node that it modifies, but | ||
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 | 1 | ||
6 | 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 | --- | ||
10 | block.c | 4 ++-- | ||
11 | 1 file changed, 2 insertions(+), 2 deletions(-) | ||
12 | |||
13 | diff --git a/block.c b/block.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block.c | ||
16 | +++ b/block.c | ||
17 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | ||
18 | */ | ||
19 | void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) | ||
20 | { | ||
21 | - assert(!bdrv_requests_pending(bs_top)); | ||
22 | - assert(!bdrv_requests_pending(bs_new)); | ||
23 | + assert(!atomic_read(&bs_top->in_flight)); | ||
24 | + assert(!atomic_read(&bs_new->in_flight)); | ||
25 | |||
26 | bdrv_ref(bs_top); | ||
27 | |||
28 | -- | ||
29 | 1.8.3.1 | ||
30 | |||
31 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Backing files are somewhat special compared to other kinds of children | ||
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 | 1 | ||
7 | These special features are a reason why change_parent_backing_link() | ||
8 | can't handle backing files yet. With abstracting the additional features | ||
9 | into .attach/.detach callbacks, we get a step closer to a function that | ||
10 | can actually deal with this. | ||
11 | |||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
14 | Acked-by: Fam Zheng <famz@redhat.com> | ||
15 | --- | ||
16 | block.c | 95 ++++++++++++++++++++++++++++++----------------- | ||
17 | include/block/block_int.h | 3 ++ | ||
18 | 2 files changed, 63 insertions(+), 35 deletions(-) | ||
19 | |||
20 | diff --git a/block.c b/block.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/block.c | ||
23 | +++ b/block.c | ||
24 | @@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = { | ||
25 | .drained_end = bdrv_child_cb_drained_end, | ||
26 | }; | ||
27 | |||
28 | +static void bdrv_backing_attach(BdrvChild *c) | ||
29 | +{ | ||
30 | + BlockDriverState *parent = c->opaque; | ||
31 | + BlockDriverState *backing_hd = c->bs; | ||
32 | + | ||
33 | + assert(!parent->backing_blocker); | ||
34 | + error_setg(&parent->backing_blocker, | ||
35 | + "node is used as backing hd of '%s'", | ||
36 | + bdrv_get_device_or_node_name(parent)); | ||
37 | + | ||
38 | + parent->open_flags &= ~BDRV_O_NO_BACKING; | ||
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 | + } | ||
109 | } | ||
110 | } | ||
111 | |||
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 | -- | ||
180 | 1.8.3.1 | ||
181 | |||
182 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | The correct permissions are relatively obvious here (and explained in | ||
2 | code comments). For intermediate streaming, we need to reopen the top | ||
3 | node read-write before creating the job now because the permissions | ||
4 | system catches attempts to get the BLK_PERM_WRITE_UNCHANGED permission | ||
5 | on a read-only node. | ||
6 | 1 | ||
7 | 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 | --- | ||
11 | block/stream.c | 39 +++++++++++++++++++++++++++------------ | ||
12 | 1 file changed, 27 insertions(+), 12 deletions(-) | ||
13 | |||
14 | diff --git a/block/stream.c b/block/stream.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block/stream.c | ||
17 | +++ b/block/stream.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static void stream_complete(BlockJob *job, void *opaque) | ||
19 | |||
20 | /* Reopen the image back in read-only mode if necessary */ | ||
21 | if (s->bs_flags != bdrv_get_flags(bs)) { | ||
22 | + /* Give up write permissions before making it read-only */ | ||
23 | + blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); | ||
24 | bdrv_reopen(bs, s->bs_flags, NULL); | ||
25 | } | ||
26 | |||
27 | @@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs, | ||
28 | BlockDriverState *iter; | ||
29 | int orig_bs_flags; | ||
30 | |||
31 | - /* FIXME Use real permissions */ | ||
32 | - s = block_job_create(job_id, &stream_job_driver, bs, 0, BLK_PERM_ALL, | ||
33 | - speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); | ||
34 | - if (!s) { | ||
35 | - return; | ||
36 | - } | ||
37 | - | ||
38 | /* Make sure that the image is opened in read-write mode */ | ||
39 | orig_bs_flags = bdrv_get_flags(bs); | ||
40 | if (!(orig_bs_flags & BDRV_O_RDWR)) { | ||
41 | if (bdrv_reopen(bs, orig_bs_flags | BDRV_O_RDWR, errp) != 0) { | ||
42 | - block_job_unref(&s->common); | ||
43 | return; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | - /* Block all intermediate nodes between bs and base, because they | ||
48 | - * will disappear from the chain after this operation */ | ||
49 | + /* Prevent concurrent jobs trying to modify the graph structure here, we | ||
50 | + * already have our own plans. Also don't allow resize as the image size is | ||
51 | + * queried only at the job start and then cached. */ | ||
52 | + s = block_job_create(job_id, &stream_job_driver, bs, | ||
53 | + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | | ||
54 | + BLK_PERM_GRAPH_MOD, | ||
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 | + } | ||
61 | + | ||
62 | + /* Block all intermediate nodes between bs and base, because they will | ||
63 | + * disappear from the chain after this operation. The streaming job reads | ||
64 | + * every block only once, assuming that it doesn't change, so block writes | ||
65 | + * and resizes. */ | ||
66 | for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) { | ||
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 | } | ||
73 | |||
74 | s->base = base; | ||
75 | @@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs, | ||
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 | + } | ||
85 | } | ||
86 | -- | ||
87 | 1.8.3.1 | ||
88 | |||
89 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Management tools need to be able to know about every node in the graph | ||
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 | 1 | ||
6 | This new option to blockdev-mirror allows the client to set a node-name | ||
7 | for the automatically inserted filter driver, and at the same time | ||
8 | serves as a witness for a future libvirt that this version of qemu does | ||
9 | automatically insert a filter driver. | ||
10 | |||
11 | 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 | --- | ||
15 | block/mirror.c | 14 ++++++++------ | ||
16 | blockdev.c | 12 +++++++++++- | ||
17 | include/block/block_int.h | 5 ++++- | ||
18 | qapi/block-core.json | 8 +++++++- | ||
19 | 4 files changed, 30 insertions(+), 9 deletions(-) | ||
20 | |||
21 | diff --git a/block/mirror.c b/block/mirror.c | ||
22 | index XXXXXXX..XXXXXXX 100644 | ||
23 | --- a/block/mirror.c | ||
24 | +++ b/block/mirror.c | ||
25 | @@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, | ||
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 | } | ||
45 | @@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs, | ||
46 | MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, | ||
47 | BlockdevOnError on_source_error, | ||
48 | BlockdevOnError on_target_error, | ||
49 | - bool unmap, Error **errp) | ||
50 | + bool unmap, const char *filter_node_name, Error **errp) | ||
51 | { | ||
52 | bool is_none_mode; | ||
53 | BlockDriverState *base; | ||
54 | @@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs, | ||
55 | mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces, | ||
56 | speed, granularity, buf_size, backing_mode, | ||
57 | on_source_error, on_target_error, unmap, NULL, NULL, errp, | ||
58 | - &mirror_job_driver, is_none_mode, base, false); | ||
59 | + &mirror_job_driver, is_none_mode, base, false, | ||
60 | + filter_node_name); | ||
61 | } | ||
62 | |||
63 | void commit_active_start(const char *job_id, BlockDriverState *bs, | ||
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 | -- | ||
182 | 1.8.3.1 | ||
183 | |||
184 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | The HMP command 'qemu-io' is a bit tricky because it wants to work on | ||
2 | the original BlockBackend, but additional permissions could be required. | ||
3 | The details are explained in a comment in the code, but in summary, just | ||
4 | request whatever permissions the current qemu-io command needs. | ||
5 | 1 | ||
6 | 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 | --- | ||
10 | block/block-backend.c | 6 ++++++ | ||
11 | hmp.c | 26 +++++++++++++++++++++++++- | ||
12 | include/qemu-io.h | 1 + | ||
13 | include/sysemu/block-backend.h | 1 + | ||
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 | --- a/block/block-backend.c | ||
20 | +++ b/block/block-backend.c | ||
21 | @@ -XXX,XX +XXX,XX @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
22 | return 0; | ||
23 | } | ||
24 | |||
25 | +void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) | ||
26 | +{ | ||
27 | + *perm = blk->perm; | ||
28 | + *shared_perm = blk->shared_perm; | ||
29 | +} | ||
30 | + | ||
31 | static int blk_do_attach_dev(BlockBackend *blk, void *dev) | ||
32 | { | ||
33 | if (blk->dev) { | ||
34 | diff --git a/hmp.c b/hmp.c | ||
35 | index XXXXXXX..XXXXXXX 100644 | ||
36 | --- a/hmp.c | ||
37 | +++ b/hmp.c | ||
38 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | ||
39 | if (!blk) { | ||
40 | BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err); | ||
41 | if (bs) { | ||
42 | - /* FIXME Use real permissions */ | ||
43 | blk = local_blk = blk_new(0, BLK_PERM_ALL); | ||
44 | ret = blk_insert_bs(blk, bs, &err); | ||
45 | if (ret < 0) { | ||
46 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | ||
47 | aio_context = blk_get_aio_context(blk); | ||
48 | aio_context_acquire(aio_context); | ||
49 | |||
50 | + /* | ||
51 | + * Notably absent: Proper permission management. This is sad, but it seems | ||
52 | + * almost impossible to achieve without changing the semantics and thereby | ||
53 | + * limiting the use cases of the qemu-io HMP command. | ||
54 | + * | ||
55 | + * In an ideal world we would unconditionally create a new BlockBackend for | ||
56 | + * qemuio_command(), but we have commands like 'reopen' and want them to | ||
57 | + * take effect on the exact BlockBackend whose name the user passed instead | ||
58 | + * of just on a temporary copy of it. | ||
59 | + * | ||
60 | + * Another problem is that deleting the temporary BlockBackend involves | ||
61 | + * draining all requests on it first, but some qemu-iotests cases want to | ||
62 | + * issue multiple aio_read/write requests and expect them to complete in | ||
63 | + * the background while the monitor has already returned. | ||
64 | + * | ||
65 | + * This is also what prevents us from saving the original permissions and | ||
66 | + * restoring them later: We can't revoke permissions until all requests | ||
67 | + * have completed, and we don't know when that is nor can we really let | ||
68 | + * anything else run before we have revoken them to avoid race conditions. | ||
69 | + * | ||
70 | + * What happens now is that command() in qemu-io-cmds.c can extend the | ||
71 | + * permissions if necessary for the qemu-io command. And they simply stay | ||
72 | + * extended, possibly resulting in a read-only guest device keeping write | ||
73 | + * permissions. Ugly, but it appears to be the lesser evil. | ||
74 | + */ | ||
75 | qemuio_command(blk, command); | ||
76 | |||
77 | aio_context_release(aio_context); | ||
78 | diff --git a/include/qemu-io.h b/include/qemu-io.h | ||
79 | index XXXXXXX..XXXXXXX 100644 | ||
80 | --- a/include/qemu-io.h | ||
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; | ||
109 | } | ||
110 | + | ||
111 | + /* Request additional permissions if necessary for this command. The caller | ||
112 | + * is responsible for restoring the original permissions afterwards if this | ||
113 | + * is what it wants. */ | ||
114 | + if (ct->perm && blk_is_available(blk)) { | ||
115 | + uint64_t orig_perm, orig_shared_perm; | ||
116 | + blk_get_perm(blk, &orig_perm, &orig_shared_perm); | ||
117 | + | ||
118 | + if (ct->perm & ~orig_perm) { | ||
119 | + uint64_t new_perm; | ||
120 | + Error *local_err = NULL; | ||
121 | + int ret; | ||
122 | + | ||
123 | + new_perm = orig_perm | ct->perm; | ||
124 | + | ||
125 | + ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err); | ||
126 | + if (ret < 0) { | ||
127 | + error_report_err(local_err); | ||
128 | + return 0; | ||
129 | + } | ||
130 | + } | ||
131 | + } | ||
132 | + | ||
133 | optind = 0; | ||
134 | return ct->cfunc(blk, argc, argv); | ||
135 | } | ||
136 | @@ -XXX,XX +XXX,XX @@ static const cmdinfo_t write_cmd = { | ||
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 | -- | ||
177 | 1.8.3.1 | ||
178 | |||
179 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Request BLK_PERM_CONSISTENT_READ for the source of block migration, and | ||
2 | handle potential permission errors as good as we can in this place | ||
3 | (which is not very good, but it matches the other failure cases). | ||
4 | 1 | ||
5 | 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 | --- | ||
9 | migration/block.c | 22 +++++++++++++++++----- | ||
10 | 1 file changed, 17 insertions(+), 5 deletions(-) | ||
11 | |||
12 | diff --git a/migration/block.c b/migration/block.c | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/migration/block.c | ||
15 | +++ b/migration/block.c | ||
16 | @@ -XXX,XX +XXX,XX @@ static void unset_dirty_tracking(void) | ||
17 | } | ||
18 | } | ||
19 | |||
20 | -static void init_blk_migration(QEMUFile *f) | ||
21 | +static int init_blk_migration(QEMUFile *f) | ||
22 | { | ||
23 | BlockDriverState *bs; | ||
24 | BlkMigDevState *bmds; | ||
25 | @@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f) | ||
26 | BlkMigDevState *bmds; | ||
27 | BlockDriverState *bs; | ||
28 | } *bmds_bs; | ||
29 | + Error *local_err = NULL; | ||
30 | + int ret; | ||
31 | |||
32 | block_mig_state.submitted = 0; | ||
33 | block_mig_state.read_done = 0; | ||
34 | @@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f) | ||
35 | |||
36 | sectors = bdrv_nb_sectors(bs); | ||
37 | if (sectors <= 0) { | ||
38 | + ret = sectors; | ||
39 | goto out; | ||
40 | } | ||
41 | |||
42 | bmds = g_new0(BlkMigDevState, 1); | ||
43 | - /* FIXME Use real permissions */ | ||
44 | - bmds->blk = blk_new(0, BLK_PERM_ALL); | ||
45 | + bmds->blk = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); | ||
46 | bmds->blk_name = g_strdup(bdrv_get_device_name(bs)); | ||
47 | bmds->bulk_completed = 0; | ||
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 | } | ||
65 | |||
66 | + ret = 0; | ||
67 | out: | ||
68 | g_free(bmds_bs); | ||
69 | + return ret; | ||
70 | } | ||
71 | |||
72 | /* Called with no lock taken. */ | ||
73 | @@ -XXX,XX +XXX,XX @@ static int block_save_setup(QEMUFile *f, void *opaque) | ||
74 | block_mig_state.submitted, block_mig_state.transferred); | ||
75 | |||
76 | qemu_mutex_lock_iothread(); | ||
77 | - init_blk_migration(f); | ||
78 | + ret = init_blk_migration(f); | ||
79 | + if (ret < 0) { | ||
80 | + qemu_mutex_unlock_iothread(); | ||
81 | + return ret; | ||
82 | + } | ||
83 | |||
84 | /* start track dirty blocks */ | ||
85 | ret = set_dirty_tracking(); | ||
86 | -- | ||
87 | 1.8.3.1 | ||
88 | |||
89 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | NBD can't cope with device size changes, so resize must be forbidden, | ||
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 | 1 | ||
5 | 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 | --- | ||
9 | nbd/server.c | 11 +++++++++-- | ||
10 | 1 file changed, 9 insertions(+), 2 deletions(-) | ||
11 | |||
12 | diff --git a/nbd/server.c b/nbd/server.c | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/nbd/server.c | ||
15 | +++ b/nbd/server.c | ||
16 | @@ -XXX,XX +XXX,XX @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, | ||
17 | { | ||
18 | BlockBackend *blk; | ||
19 | NBDExport *exp = g_malloc0(sizeof(NBDExport)); | ||
20 | + uint64_t perm; | ||
21 | int ret; | ||
22 | |||
23 | - /* FIXME Use real permissions */ | ||
24 | - blk = blk_new(0, BLK_PERM_ALL); | ||
25 | + /* Don't allow resize while the NBD server is running, otherwise we don't | ||
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) { | ||
35 | goto fail; | ||
36 | -- | ||
37 | 1.8.3.1 | ||
38 | |||
39 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Not requesting any permissions is actually correct for these test cases | ||
2 | because no actual I/O or other operation covered by the permission | ||
3 | system is performed. | ||
4 | 1 | ||
5 | 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 | --- | ||
9 | tests/test-blockjob.c | 2 +- | ||
10 | tests/test-throttle.c | 2 +- | ||
11 | 2 files changed, 2 insertions(+), 2 deletions(-) | ||
12 | |||
13 | diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/tests/test-blockjob.c | ||
16 | +++ b/tests/test-blockjob.c | ||
17 | @@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, | ||
18 | * BlockDriverState inserted. */ | ||
19 | static BlockBackend *create_blk(const char *name) | ||
20 | { | ||
21 | - /* FIXME Use real permissions */ | ||
22 | + /* No I/O is performed on this device */ | ||
23 | BlockBackend *blk = blk_new(0, BLK_PERM_ALL); | ||
24 | BlockDriverState *bs; | ||
25 | |||
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 | -- | ||
40 | 1.8.3.1 | ||
41 | |||
42 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This is where we want to check the permissions, so we need to have the | ||
2 | BdrvChild around where they are stored. | ||
3 | 1 | ||
4 | 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 | --- | ||
8 | block/io.c | 37 +++++++++++++++++++++---------------- | ||
9 | 1 file changed, 21 insertions(+), 16 deletions(-) | ||
10 | |||
11 | diff --git a/block/io.c b/block/io.c | ||
12 | index XXXXXXX..XXXXXXX 100644 | ||
13 | --- a/block/io.c | ||
14 | +++ b/block/io.c | ||
15 | @@ -XXX,XX +XXX,XX @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset, | ||
16 | return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov); | ||
17 | } | ||
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 | -- | ||
171 | 1.8.3.1 | ||
172 | |||
173 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This adds assertions that ensure that the necessary write permissions | ||
2 | have been granted before someone attempts to write to a node. | ||
3 | 1 | ||
4 | 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 | --- | ||
8 | block/io.c | 3 +++ | ||
9 | 1 file changed, 3 insertions(+) | ||
10 | |||
11 | diff --git a/block/io.c b/block/io.c | ||
12 | index XXXXXXX..XXXXXXX 100644 | ||
13 | --- a/block/io.c | ||
14 | +++ b/block/io.c | ||
15 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, | ||
16 | size_t skip_bytes; | ||
17 | int ret; | ||
18 | |||
19 | + assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); | ||
20 | + | ||
21 | /* Cover entire cluster so no additional backing file I/O is required when | ||
22 | * allocating cluster in the image file. | ||
23 | */ | ||
24 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, | ||
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 | -- | ||
33 | 1.8.3.1 | ||
34 | |||
35 | diff view generated by jsdifflib |
1 | Not all callers of bdrv_set_backing_hd() know for sure that attaching | 1 | From: Lukáš Doktor <ldoktor@redhat.com> |
---|---|---|---|
2 | the backing file will be allowed by the permission system. Return the | ||
3 | error from the function rather than aborting. | ||
4 | 2 | ||
3 | When custom TEST_DIR is specified the output includes it without leading | ||
4 | '/': | ||
5 | |||
6 | $ TEST_DIR=/var/tmp ./check -file -qcow2 051 | ||
7 | .... | ||
8 | -drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": | ||
9 | {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", | ||
10 | "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, | ||
11 | read-only) | ||
12 | +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": | ||
13 | {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", | ||
14 | "file": {"driver": "file", "filename": "TEST_DIR/vl.ziHfeP"}} (qcow2, | ||
15 | read-only) | ||
16 | |||
17 | Let's remove it from the sed regexp. | ||
18 | |||
19 | Signed-off-by: Lukáš Doktor <ldoktor@redhat.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 20 | 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 | --- | 21 | --- |
9 | block.c | 30 +++++++++++++++++++++++------- | 22 | tests/qemu-iotests/051 | 2 +- |
10 | block/commit.c | 14 +++++++------- | 23 | 1 file changed, 1 insertion(+), 1 deletion(-) |
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 | 24 | ||
17 | diff --git a/block.c b/block.c | 25 | diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 |
18 | index XXXXXXX..XXXXXXX 100644 | 26 | index XXXXXXX..XXXXXXX 100755 |
19 | --- a/block.c | 27 | --- a/tests/qemu-iotests/051 |
20 | +++ b/block.c | 28 | +++ b/tests/qemu-iotests/051 |
21 | @@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs) | 29 | @@ -XXX,XX +XXX,XX @@ TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on |
22 | * Sets the backing file link of a BDS. A new reference is created; callers | 30 | echo "info block" | |
23 | * which don't need their own reference any more must call bdrv_unref(). | 31 | run_qemu -drive file="$TEST_IMG",snapshot=on,read-only=on,if=none,id=$device_id | |
24 | */ | 32 | _filter_qemu_io | |
25 | -void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd) | 33 | - sed -e 's#"/[^"]*/vl\.[A-Za-z0-9]\{6\}"#SNAPSHOT_PATH#g' |
26 | +void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | 34 | + sed -e 's#"[^"]*/vl\.[A-Za-z0-9]\{6\}"#SNAPSHOT_PATH#g' |
27 | + Error **errp) | 35 | |
28 | { | 36 | |
29 | if (backing_hd) { | 37 | # success, all done |
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; | ||
34 | } | ||
35 | - /* FIXME Error handling */ | ||
36 | + | ||
37 | bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing, | ||
38 | - &error_abort); | ||
39 | + errp); | ||
40 | + if (!bs->backing) { | ||
41 | + bdrv_unref(backing_hd); | ||
42 | + } | ||
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 | } | ||
91 | - bdrv_set_backing_hd(new_top_bs, base); | ||
92 | + | ||
93 | + bdrv_set_backing_hd(new_top_bs, base, &local_err); | ||
94 | + if (local_err) { | ||
95 | + ret = -EPERM; | ||
96 | + error_report_err(local_err); | ||
97 | + goto exit; | ||
98 | + } | ||
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 | } | ||
113 | } | ||
114 | |||
115 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | ||
116 | goto fail; | ||
117 | } | ||
118 | |||
119 | - bdrv_set_backing_hd(commit_top_bs, top); | ||
120 | - bdrv_set_backing_hd(overlay_bs, commit_top_bs); | ||
121 | + bdrv_set_backing_hd(commit_top_bs, top, &error_abort); | ||
122 | + bdrv_set_backing_hd(overlay_bs, commit_top_bs, &error_abort); | ||
123 | |||
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 | } | ||
129 | if (commit_top_bs) { | ||
130 | - bdrv_set_backing_hd(overlay_bs, top); | ||
131 | + bdrv_set_backing_hd(overlay_bs, top, &error_abort); | ||
132 | } | ||
133 | block_job_unref(&s->common); | ||
134 | } | ||
135 | @@ -XXX,XX +XXX,XX @@ int bdrv_commit(BlockDriverState *bs) | ||
136 | goto ro_cleanup; | ||
137 | } | ||
138 | |||
139 | - bdrv_set_backing_hd(commit_top_bs, backing_file_bs); | ||
140 | - bdrv_set_backing_hd(bs, commit_top_bs); | ||
141 | + bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort); | ||
142 | + bdrv_set_backing_hd(bs, commit_top_bs, &error_abort); | ||
143 | |||
144 | ret = blk_insert_bs(backing, backing_file_bs, &local_err); | ||
145 | if (ret < 0) { | ||
146 | @@ -XXX,XX +XXX,XX @@ ro_cleanup: | ||
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 | } | ||
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 | -- | 38 | -- |
237 | 1.8.3.1 | 39 | 2.20.1 |
238 | 40 | ||
239 | 41 | diff view generated by jsdifflib |