1 | The following changes since commit 9514f2648ca05b38e852b490a12b8cd98d5808c1: | 1 | The following changes since commit 1b8c45899715d292398152ba97ef755ccaf84680: |
---|---|---|---|
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/dgilbert/tags/pull-migration-20200507a' into staging (2020-05-07 18:43:20 +0100) |
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 47e0b38a13935cb666f88964c3096654092f42d6: |
11 | 10 | ||
12 | block: Add Error parameter to bdrv_append() (2017-02-28 20:47:51 +0100) | 11 | block: Drop unused .bdrv_has_zero_init_truncate (2020-05-08 13:26:35 +0200) |
13 | 12 | ||
14 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
15 | Block layer patches | 14 | Block layer patches: |
15 | |||
16 | - qcow2: Fix preallocation on block devices | ||
17 | - backup: Make sure that source and target size match | ||
18 | - vmdk: Fix zero cluster handling | ||
19 | - Follow-up cleanups and fixes for the truncate changes | ||
20 | - iotests: Skip more tests if required drivers are missing | ||
16 | 21 | ||
17 | ---------------------------------------------------------------- | 22 | ---------------------------------------------------------------- |
18 | Kevin Wolf (44): | 23 | Alberto Garcia (1): |
19 | block: Add op blocker permission constants | 24 | qcow2: Avoid integer wraparound in qcow2_co_truncate() |
20 | block: Add Error argument to bdrv_attach_child() | ||
21 | block: Let callers request permissions when attaching a child node | ||
22 | block: Involve block drivers in permission granting | ||
23 | block: Default .bdrv_child_perm() for filter drivers | ||
24 | block: Request child permissions in filter drivers | ||
25 | block: Default .bdrv_child_perm() for format drivers | ||
26 | block: Request child permissions in format drivers | ||
27 | vvfat: Implement .bdrv_child_perm() | ||
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 | 25 | ||
64 | Markus Armbruster (1): | 26 | Eric Blake (9): |
65 | option: Tweak invalid size error message and unbreak iotest 049 | 27 | gluster: Drop useless has_zero_init callback |
28 | file-win32: Support BDRV_REQ_ZERO_WRITE for truncate | ||
29 | nfs: Support BDRV_REQ_ZERO_WRITE for truncate | ||
30 | rbd: Support BDRV_REQ_ZERO_WRITE for truncate | ||
31 | sheepdog: Support BDRV_REQ_ZERO_WRITE for truncate | ||
32 | ssh: Support BDRV_REQ_ZERO_WRITE for truncate | ||
33 | parallels: Rework truncation logic | ||
34 | vhdx: Rework truncation logic | ||
35 | block: Drop unused .bdrv_has_zero_init_truncate | ||
66 | 36 | ||
67 | Peter Lieven (1): | 37 | Kevin Wolf (11): |
68 | qemu-img: make convert async | 38 | vmdk: Rename VmdkMetaData.valid to new_allocation |
39 | vmdk: Fix zero cluster allocation | ||
40 | vmdk: Fix partial overwrite of zero cluster | ||
41 | vmdk: Don't update L2 table for zero write on zero cluster | ||
42 | vmdk: Flush only once in vmdk_L2update() | ||
43 | iotests: vmdk: Enable zeroed_grained=on by default | ||
44 | iotests/283: Use consistent size for source and target | ||
45 | backup: Improve error for bdrv_getlength() failure | ||
46 | backup: Make sure that source and target size match | ||
47 | iotests: Backup with different source/target size | ||
48 | iotests/055: Use cache.no-flush for vmdk target | ||
69 | 49 | ||
70 | block.c | 583 ++++++++++++++++++++++++++++++++++----- | 50 | Max Reitz (1): |
71 | block/backup.c | 22 +- | 51 | qcow2: Fix preallocation on block devices |
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 | 52 | ||
53 | Vladimir Sementsov-Ogievskiy (8): | ||
54 | iotests: handle tmpfs | ||
55 | iotests/082: require bochs | ||
56 | iotests/148: use skip_if_unsupported | ||
57 | iotests/041: drop self.assert_no_active_block_jobs() | ||
58 | iotests/055: refactor compressed backup to vmdk | ||
59 | iotests/055: skip vmdk target tests if vmdk is not whitelisted | ||
60 | iotests/109: mark required formats as required to support whitelisting | ||
61 | iotests/113: mark bochs as required to support whitelisting | ||
62 | |||
63 | include/block/block.h | 1 - | ||
64 | include/block/block_int.h | 7 --- | ||
65 | block.c | 21 -------- | ||
66 | block/backup-top.c | 14 +++-- | ||
67 | block/backup.c | 18 +++++-- | ||
68 | block/file-posix.c | 1 - | ||
69 | block/file-win32.c | 4 +- | ||
70 | block/gluster.c | 14 ----- | ||
71 | block/nfs.c | 4 +- | ||
72 | block/parallels.c | 25 +++++---- | ||
73 | block/qcow2.c | 23 ++++++--- | ||
74 | block/qed.c | 1 - | ||
75 | block/raw-format.c | 6 --- | ||
76 | block/rbd.c | 4 +- | ||
77 | block/sheepdog.c | 4 +- | ||
78 | block/ssh.c | 5 +- | ||
79 | block/vhdx.c | 89 ++++++++++++++++++-------------- | ||
80 | block/vmdk.c | 47 ++++++++++------- | ||
81 | tests/qemu-iotests/041 | 8 --- | ||
82 | tests/qemu-iotests/055 | 120 ++++++++++++++++++++++++++++++------------- | ||
83 | tests/qemu-iotests/055.out | 4 +- | ||
84 | tests/qemu-iotests/059 | 6 +-- | ||
85 | tests/qemu-iotests/082 | 1 + | ||
86 | tests/qemu-iotests/091 | 2 +- | ||
87 | tests/qemu-iotests/109 | 1 + | ||
88 | tests/qemu-iotests/113 | 4 +- | ||
89 | tests/qemu-iotests/148 | 1 + | ||
90 | tests/qemu-iotests/283 | 6 ++- | ||
91 | tests/qemu-iotests/283.out | 2 +- | ||
92 | tests/qemu-iotests/292 | 73 ++++++++++++++++++++++++++ | ||
93 | tests/qemu-iotests/292.out | 24 +++++++++ | ||
94 | tests/qemu-iotests/check | 3 ++ | ||
95 | tests/qemu-iotests/common.rc | 37 ++++++++++++- | ||
96 | tests/qemu-iotests/group | 1 + | ||
97 | 34 files changed, 386 insertions(+), 195 deletions(-) | ||
98 | create mode 100755 tests/qemu-iotests/292 | ||
99 | create mode 100644 tests/qemu-iotests/292.out | ||
100 | |||
101 | diff view generated by jsdifflib |
1 | In many cases, the required permissions of one node on its children | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
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 | 2 | ||
6 | In order to achieve this, this patch introduces two new BlockDriver | 3 | Some tests requires O_DIRECT, or want it by default. Introduce smarter |
7 | callbacks. The first one lets drivers first check (recursively) whether | 4 | O_DIRECT handling: |
8 | the requested permissions can be set; the second one actually sets the | ||
9 | new permission bitmask. | ||
10 | 5 | ||
11 | Also add helper functions that drivers can use in their implementation | 6 | - Check O_DIRECT in common.rc, if it is requested by selected |
12 | of the callbacks to update their permissions on a specific child. | 7 | cache-mode. |
13 | 8 | ||
9 | - Support second fall-through argument in _default_cache_mode | ||
10 | |||
11 | Inspired-by: Max's 23e1d054112cec1e | ||
12 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
13 | Message-Id: <20200430124713.3067-2-vsementsov@virtuozzo.com> | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 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 | --- | 15 | --- |
18 | block.c | 206 +++++++++++++++++++++++++++++++++++++++++++++- | 16 | tests/qemu-iotests/091 | 2 +- |
19 | include/block/block_int.h | 61 ++++++++++++++ | 17 | tests/qemu-iotests/common.rc | 37 ++++++++++++++++++++++++++++++++++-- |
20 | 2 files changed, 263 insertions(+), 4 deletions(-) | 18 | 2 files changed, 36 insertions(+), 3 deletions(-) |
21 | 19 | ||
22 | diff --git a/block.c b/block.c | 20 | diff --git a/tests/qemu-iotests/091 b/tests/qemu-iotests/091 |
21 | index XXXXXXX..XXXXXXX 100755 | ||
22 | --- a/tests/qemu-iotests/091 | ||
23 | +++ b/tests/qemu-iotests/091 | ||
24 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
25 | _supported_fmt qcow2 | ||
26 | _supported_proto file | ||
27 | _supported_os Linux | ||
28 | -_default_cache_mode none | ||
29 | _supported_cache_modes writethrough none writeback | ||
30 | +_default_cache_mode none writeback | ||
31 | |||
32 | size=1G | ||
33 | |||
34 | diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc | ||
23 | index XXXXXXX..XXXXXXX 100644 | 35 | index XXXXXXX..XXXXXXX 100644 |
24 | --- a/block.c | 36 | --- a/tests/qemu-iotests/common.rc |
25 | +++ b/block.c | 37 | +++ b/tests/qemu-iotests/common.rc |
26 | @@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename, | 38 | @@ -XXX,XX +XXX,XX @@ _supported_cache_modes() |
27 | return 0; | 39 | _notrun "not suitable for cache mode: $CACHEMODE" |
28 | } | 40 | } |
29 | 41 | ||
30 | +/* | 42 | +# Check whether the filesystem supports O_DIRECT |
31 | + * Check whether permissions on this node can be changed in a way that | 43 | +_check_o_direct() |
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 | +{ | 44 | +{ |
42 | + BlockDriver *drv = bs->drv; | 45 | + $QEMU_IMG create -f raw "$TEST_IMG".test_o_direct 1M > /dev/null |
43 | + BdrvChild *c; | 46 | + out=$($QEMU_IO -f raw -t none -c quit "$TEST_IMG".test_o_direct 2>&1) |
44 | + int ret; | 47 | + rm -f "$TEST_IMG".test_o_direct |
45 | + | 48 | + |
46 | + /* Write permissions never work with read-only images */ | 49 | + [[ "$out" != *"O_DIRECT"* ]] |
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 | +} | 50 | +} |
83 | + | 51 | + |
84 | +/* | 52 | +_require_o_direct() |
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 | +{ | 53 | +{ |
93 | + BlockDriver *drv = bs->drv; | 54 | + if ! _check_o_direct; then |
94 | + BdrvChild *c; | 55 | + _notrun "file system on $TEST_DIR does not support O_DIRECT" |
95 | + | 56 | + fi |
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 | +} | 57 | +} |
108 | + | 58 | + |
109 | +static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, | 59 | +_check_cache_mode() |
110 | + uint64_t cumulative_shared_perms) | ||
111 | +{ | 60 | +{ |
112 | + BlockDriver *drv = bs->drv; | 61 | + if [ $CACHEMODE == "none" ] || [ $CACHEMODE == "directsync" ]; then |
113 | + BdrvChild *c; | 62 | + _require_o_direct |
114 | + | 63 | + fi |
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 | +} | 64 | +} |
138 | + | 65 | + |
139 | +static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | 66 | +_check_cache_mode |
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 | + | 67 | + |
146 | + QLIST_FOREACH(c, &bs->parents, next_parent) { | 68 | +# $1 - cache mode to use by default |
147 | + cumulative_perms |= c->perm; | 69 | +# $2 - (optional) cache mode to use by default if O_DIRECT is not supported |
148 | + cumulative_shared_perms &= c->shared_perm; | 70 | _default_cache_mode() |
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 | { | 71 | { |
167 | BdrvChild *c; | 72 | if $CACHEMODE_IS_DEFAULT; then |
168 | + uint64_t cumulative_perms = new_used_perm; | 73 | - CACHEMODE="$1" |
169 | + uint64_t cumulative_shared_perms = new_shared_perm; | 74 | - QEMU_IO="$QEMU_IO --cache $1" |
170 | 75 | + if [ -z "$2" ] || _check_o_direct; then | |
171 | /* There is no reason why anyone couldn't tolerate write_unchanged */ | 76 | + CACHEMODE="$1" |
172 | assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED); | 77 | + else |
173 | @@ -XXX,XX +XXX,XX @@ static int bdrv_check_update_perm(BlockDriverState *bs, uint64_t new_used_perm, | 78 | + CACHEMODE="$2" |
174 | error_setg(errp, "Conflicts with %s", user ?: "another operation"); | 79 | + fi |
175 | return -EPERM; | 80 | + QEMU_IO="$QEMU_IO --cache $CACHEMODE" |
176 | } | 81 | + _check_cache_mode |
177 | + | 82 | return |
178 | + cumulative_perms |= c->perm; | 83 | fi |
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 | } | 84 | } |
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 | -- | 85 | -- |
379 | 1.8.3.1 | 86 | 2.25.3 |
380 | 87 | ||
381 | 88 | diff view generated by jsdifflib |
1 | Aborting on error in bdrv_append() isn't correct. This patch fixes it | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.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 | Test fails if bochs not whitelisted, so, skip it in this case. |
5 | reversed order of bdrv_set_backing_hd() and change_parent_backing_link() | ||
6 | in bdrv_append(): When the backing file of the new node is set, the | ||
7 | parent nodes are still pointing to the old top, so the backing blocker | ||
8 | is now initialised with the node name rather than the BlockBackend name. | ||
9 | 4 | ||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Message-Id: <20200430124713.3067-3-vsementsov@virtuozzo.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | 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 | --- | 8 | --- |
14 | block.c | 23 +++++++++++++++++------ | 9 | tests/qemu-iotests/082 | 1 + |
15 | block/mirror.c | 9 ++++++++- | 10 | 1 file changed, 1 insertion(+) |
16 | blockdev.c | 18 +++++++++++++++--- | ||
17 | include/block/block.h | 3 ++- | ||
18 | tests/qemu-iotests/085.out | 2 +- | ||
19 | 5 files changed, 43 insertions(+), 12 deletions(-) | ||
20 | 11 | ||
21 | diff --git a/block.c b/block.c | 12 | diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 |
22 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100755 |
23 | --- a/block.c | 14 | --- a/tests/qemu-iotests/082 |
24 | +++ b/block.c | 15 | +++ b/tests/qemu-iotests/082 |
25 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | 16 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 |
26 | int64_t total_size; | 17 | |
27 | QemuOpts *opts = NULL; | 18 | _supported_fmt qcow2 |
28 | BlockDriverState *bs_snapshot; | 19 | _supported_proto file nfs |
29 | + Error *local_err = NULL; | 20 | +_require_drivers bochs |
30 | int ret; | 21 | |
31 | 22 | run_qemu_img() | |
32 | /* if snapshot, we create a temporary backing file and open it | ||
33 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, | ||
34 | * call bdrv_unref() on it), so in order to be able to return one, we have | ||
35 | * to increase bs_snapshot's refcount here */ | ||
36 | bdrv_ref(bs_snapshot); | ||
37 | - bdrv_append(bs_snapshot, bs); | ||
38 | + bdrv_append(bs_snapshot, bs, &local_err); | ||
39 | + if (local_err) { | ||
40 | + error_propagate(errp, local_err); | ||
41 | + ret = -EINVAL; | ||
42 | + goto out; | ||
43 | + } | ||
44 | |||
45 | g_free(tmp_filename); | ||
46 | return bs_snapshot; | ||
47 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | ||
48 | * parents of bs_top after bdrv_append() returns. If the caller needs to keep a | ||
49 | * reference of its own, it must call bdrv_ref(). | ||
50 | */ | ||
51 | -void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) | ||
52 | +void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
53 | + Error **errp) | ||
54 | { | 23 | { |
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 | |||
184 | -- | 24 | -- |
185 | 1.8.3.1 | 25 | 2.25.3 |
186 | 26 | ||
187 | 27 | diff view generated by jsdifflib |
1 | This adds an assertion that ensures that the necessary resize permission | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | has been granted before bdrv_truncate() is called. | ||
3 | 2 | ||
3 | Skip test-case with quorum if quorum is not whitelisted. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Message-Id: <20200430124713.3067-4-vsementsov@virtuozzo.com> | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | 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 | --- |
8 | block.c | 3 +++ | 9 | tests/qemu-iotests/148 | 1 + |
9 | block/io.c | 1 + | 10 | 1 file changed, 1 insertion(+) |
10 | 2 files changed, 4 insertions(+) | ||
11 | 11 | ||
12 | diff --git a/block.c b/block.c | 12 | diff --git a/tests/qemu-iotests/148 b/tests/qemu-iotests/148 |
13 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100755 |
14 | --- a/block.c | 14 | --- a/tests/qemu-iotests/148 |
15 | +++ b/block.c | 15 | +++ b/tests/qemu-iotests/148 |
16 | @@ -XXX,XX +XXX,XX @@ int bdrv_truncate(BdrvChild *child, int64_t offset) | 16 | @@ -XXX,XX +XXX,XX @@ sector = "%d" |
17 | BlockDriverState *bs = child->bs; | 17 | ''' % bad_sector) |
18 | BlockDriver *drv = bs->drv; | 18 | file.close() |
19 | int ret; | 19 | |
20 | + | 20 | + @iotests.skip_if_unsupported(['quorum']) |
21 | + assert(child->perm & BLK_PERM_RESIZE); | 21 | def setUp(self): |
22 | + | 22 | driveopts = ['driver=quorum', 'vote-threshold=2'] |
23 | if (!drv) | 23 | driveopts.append('read-pattern=%s' % self.read_pattern) |
24 | return -ENOMEDIUM; | ||
25 | if (!drv->bdrv_truncate) | ||
26 | diff --git a/block/io.c b/block/io.c | ||
27 | index XXXXXXX..XXXXXXX 100644 | ||
28 | --- a/block/io.c | ||
29 | +++ b/block/io.c | ||
30 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, | ||
31 | assert(req->overlap_offset <= offset); | ||
32 | assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); | ||
33 | assert(child->perm & BLK_PERM_WRITE); | ||
34 | + assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); | ||
35 | |||
36 | ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); | ||
37 | |||
38 | -- | 24 | -- |
39 | 1.8.3.1 | 25 | 2.25.3 |
40 | 26 | ||
41 | 27 | diff view generated by jsdifflib |
1 | This adds assertions that ensure that the necessary write permissions | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | have been granted before someone attempts to write to a node. | ||
3 | 2 | ||
3 | Drop check for no block-jobs: it's obvious that there no jobs | ||
4 | immediately after vm.launch(). | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Message-Id: <20200430124713.3067-5-vsementsov@virtuozzo.com> | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | 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 | --- | 9 | --- |
8 | block/io.c | 3 +++ | 10 | tests/qemu-iotests/041 | 8 -------- |
9 | 1 file changed, 3 insertions(+) | 11 | 1 file changed, 8 deletions(-) |
10 | 12 | ||
11 | diff --git a/block/io.c b/block/io.c | 13 | diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 |
12 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100755 |
13 | --- a/block/io.c | 15 | --- a/tests/qemu-iotests/041 |
14 | +++ b/block/io.c | 16 | +++ b/tests/qemu-iotests/041 |
15 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, | 17 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): |
16 | size_t skip_bytes; | 18 | pass |
17 | int ret; | 19 | |
18 | 20 | def test_complete(self): | |
19 | + assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); | 21 | - self.assert_no_active_block_jobs() |
20 | + | 22 | - |
21 | /* Cover entire cluster so no additional backing file I/O is required when | 23 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
22 | * allocating cluster in the image file. | 24 | sync='full', node_name="repair0", replaces="img1", |
23 | */ | 25 | target=quorum_repair_img, format=iotests.imgfmt) |
24 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, | 26 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): |
25 | assert(!waited || !req->serialising); | 27 | 'target image does not match source after mirroring') |
26 | assert(req->overlap_offset <= offset); | 28 | |
27 | assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); | 29 | def test_cancel(self): |
28 | + assert(child->perm & BLK_PERM_WRITE); | 30 | - self.assert_no_active_block_jobs() |
29 | 31 | - | |
30 | ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); | 32 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
31 | 33 | sync='full', node_name="repair0", replaces="img1", | |
34 | target=quorum_repair_img, format=iotests.imgfmt) | ||
35 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): | ||
36 | self.assert_has_block_node(None, quorum_img3) | ||
37 | |||
38 | def test_cancel_after_ready(self): | ||
39 | - self.assert_no_active_block_jobs() | ||
40 | - | ||
41 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | ||
42 | sync='full', node_name="repair0", replaces="img1", | ||
43 | target=quorum_repair_img, format=iotests.imgfmt) | ||
44 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): | ||
45 | 'target image does not match source after mirroring') | ||
46 | |||
47 | def test_pause(self): | ||
48 | - self.assert_no_active_block_jobs() | ||
49 | - | ||
50 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | ||
51 | sync='full', node_name="repair0", replaces="img1", | ||
52 | target=quorum_repair_img, format=iotests.imgfmt) | ||
32 | -- | 53 | -- |
33 | 1.8.3.1 | 54 | 2.25.3 |
34 | 55 | ||
35 | 56 | diff view generated by jsdifflib |
1 | This is where we want to check the permissions, so we need to have the | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | BdrvChild around where they are stored. | ||
3 | 2 | ||
3 | Instead of looping in each test, let's better refactor vmdk target case | ||
4 | as a subclass. | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Message-Id: <20200430124713.3067-6-vsementsov@virtuozzo.com> | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | 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 | --- | 9 | --- |
8 | block/io.c | 37 +++++++++++++++++++++---------------- | 10 | tests/qemu-iotests/055 | 70 ++++++++++++++++++++------------------ |
9 | 1 file changed, 21 insertions(+), 16 deletions(-) | 11 | tests/qemu-iotests/055.out | 4 +-- |
12 | 2 files changed, 39 insertions(+), 35 deletions(-) | ||
10 | 13 | ||
11 | diff --git a/block/io.c b/block/io.c | 14 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 |
15 | index XXXXXXX..XXXXXXX 100755 | ||
16 | --- a/tests/qemu-iotests/055 | ||
17 | +++ b/tests/qemu-iotests/055 | ||
18 | @@ -XXX,XX +XXX,XX @@ class TestSingleTransaction(iotests.QMPTestCase): | ||
19 | self.assert_no_active_block_jobs() | ||
20 | |||
21 | |||
22 | -class TestDriveCompression(iotests.QMPTestCase): | ||
23 | +class TestCompressedToQcow2(iotests.QMPTestCase): | ||
24 | image_len = 64 * 1024 * 1024 # MB | ||
25 | - fmt_supports_compression = [{'type': 'qcow2', 'args': ()}, | ||
26 | - {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}] | ||
27 | + target_fmt = {'type': 'qcow2', 'args': ()} | ||
28 | |||
29 | def tearDown(self): | ||
30 | self.vm.shutdown() | ||
31 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | ||
32 | except OSError: | ||
33 | pass | ||
34 | |||
35 | - def do_prepare_drives(self, fmt, args, attach_target): | ||
36 | + def do_prepare_drives(self, attach_target): | ||
37 | self.vm = iotests.VM().add_drive('blkdebug::' + test_img) | ||
38 | |||
39 | - qemu_img('create', '-f', fmt, blockdev_target_img, | ||
40 | - str(TestDriveCompression.image_len), *args) | ||
41 | + qemu_img('create', '-f', self.target_fmt['type'], blockdev_target_img, | ||
42 | + str(self.image_len), *self.target_fmt['args']) | ||
43 | if attach_target: | ||
44 | self.vm.add_drive(blockdev_target_img, | ||
45 | - img_format=fmt, interface="none") | ||
46 | + img_format=self.target_fmt['type'], | ||
47 | + interface="none") | ||
48 | |||
49 | self.vm.launch() | ||
50 | |||
51 | - def do_test_compress_complete(self, cmd, format, attach_target, **args): | ||
52 | - self.do_prepare_drives(format['type'], format['args'], attach_target) | ||
53 | + def do_test_compress_complete(self, cmd, attach_target, **args): | ||
54 | + self.do_prepare_drives(attach_target) | ||
55 | |||
56 | self.assert_no_active_block_jobs() | ||
57 | |||
58 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | ||
59 | |||
60 | self.vm.shutdown() | ||
61 | self.assertTrue(iotests.compare_images(test_img, blockdev_target_img, | ||
62 | - iotests.imgfmt, format['type']), | ||
63 | + iotests.imgfmt, | ||
64 | + self.target_fmt['type']), | ||
65 | 'target image does not match source after backup') | ||
66 | |||
67 | def test_complete_compress_drive_backup(self): | ||
68 | - for format in TestDriveCompression.fmt_supports_compression: | ||
69 | - self.do_test_compress_complete('drive-backup', format, False, | ||
70 | - target=blockdev_target_img, mode='existing') | ||
71 | + self.do_test_compress_complete('drive-backup', False, | ||
72 | + target=blockdev_target_img, | ||
73 | + mode='existing') | ||
74 | |||
75 | def test_complete_compress_blockdev_backup(self): | ||
76 | - for format in TestDriveCompression.fmt_supports_compression: | ||
77 | - self.do_test_compress_complete('blockdev-backup', format, True, | ||
78 | - target='drive1') | ||
79 | + self.do_test_compress_complete('blockdev-backup', | ||
80 | + True, target='drive1') | ||
81 | |||
82 | - def do_test_compress_cancel(self, cmd, format, attach_target, **args): | ||
83 | - self.do_prepare_drives(format['type'], format['args'], attach_target) | ||
84 | + def do_test_compress_cancel(self, cmd, attach_target, **args): | ||
85 | + self.do_prepare_drives(attach_target) | ||
86 | |||
87 | self.assert_no_active_block_jobs() | ||
88 | |||
89 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | ||
90 | self.vm.shutdown() | ||
91 | |||
92 | def test_compress_cancel_drive_backup(self): | ||
93 | - for format in TestDriveCompression.fmt_supports_compression: | ||
94 | - self.do_test_compress_cancel('drive-backup', format, False, | ||
95 | - target=blockdev_target_img, mode='existing') | ||
96 | + self.do_test_compress_cancel('drive-backup', False, | ||
97 | + target=blockdev_target_img, | ||
98 | + mode='existing') | ||
99 | |||
100 | def test_compress_cancel_blockdev_backup(self): | ||
101 | - for format in TestDriveCompression.fmt_supports_compression: | ||
102 | - self.do_test_compress_cancel('blockdev-backup', format, True, | ||
103 | - target='drive1') | ||
104 | + self.do_test_compress_cancel('blockdev-backup', True, | ||
105 | + target='drive1') | ||
106 | |||
107 | - def do_test_compress_pause(self, cmd, format, attach_target, **args): | ||
108 | - self.do_prepare_drives(format['type'], format['args'], attach_target) | ||
109 | + def do_test_compress_pause(self, cmd, attach_target, **args): | ||
110 | + self.do_prepare_drives(attach_target) | ||
111 | |||
112 | self.assert_no_active_block_jobs() | ||
113 | |||
114 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | ||
115 | |||
116 | self.vm.shutdown() | ||
117 | self.assertTrue(iotests.compare_images(test_img, blockdev_target_img, | ||
118 | - iotests.imgfmt, format['type']), | ||
119 | + iotests.imgfmt, | ||
120 | + self.target_fmt['type']), | ||
121 | 'target image does not match source after backup') | ||
122 | |||
123 | def test_compress_pause_drive_backup(self): | ||
124 | - for format in TestDriveCompression.fmt_supports_compression: | ||
125 | - self.do_test_compress_pause('drive-backup', format, False, | ||
126 | - target=blockdev_target_img, mode='existing') | ||
127 | + self.do_test_compress_pause('drive-backup', False, | ||
128 | + target=blockdev_target_img, | ||
129 | + mode='existing') | ||
130 | |||
131 | def test_compress_pause_blockdev_backup(self): | ||
132 | - for format in TestDriveCompression.fmt_supports_compression: | ||
133 | - self.do_test_compress_pause('blockdev-backup', format, True, | ||
134 | - target='drive1') | ||
135 | + self.do_test_compress_pause('blockdev-backup', True, | ||
136 | + target='drive1') | ||
137 | + | ||
138 | + | ||
139 | +class TestCompressedToVmdk(TestCompressedToQcow2): | ||
140 | + target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')} | ||
141 | + | ||
142 | |||
143 | if __name__ == '__main__': | ||
144 | iotests.main(supported_fmts=['raw', 'qcow2'], | ||
145 | diff --git a/tests/qemu-iotests/055.out b/tests/qemu-iotests/055.out | ||
12 | index XXXXXXX..XXXXXXX 100644 | 146 | index XXXXXXX..XXXXXXX 100644 |
13 | --- a/block/io.c | 147 | --- a/tests/qemu-iotests/055.out |
14 | +++ b/block/io.c | 148 | +++ b/tests/qemu-iotests/055.out |
15 | @@ -XXX,XX +XXX,XX @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset, | 149 | @@ -XXX,XX +XXX,XX @@ |
16 | return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov); | 150 | -.............................. |
17 | } | 151 | +.................................... |
18 | 152 | ---------------------------------------------------------------------- | |
19 | -static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs, | 153 | -Ran 30 tests |
20 | +static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, | 154 | +Ran 36 tests |
21 | int64_t offset, unsigned int bytes, QEMUIOVector *qiov) | 155 | |
22 | { | 156 | OK |
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 | -- | 157 | -- |
171 | 1.8.3.1 | 158 | 2.25.3 |
172 | 159 | ||
173 | 160 | diff view generated by jsdifflib |
1 | Not requesting any permissions is actually correct for these test cases | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | because no actual I/O or other operation covered by the permission | ||
3 | system is performed. | ||
4 | 2 | ||
3 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
4 | Message-Id: <20200430124713.3067-7-vsementsov@virtuozzo.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
7 | Acked-by: Fam Zheng <famz@redhat.com> | ||
8 | --- | 6 | --- |
9 | tests/test-blockjob.c | 2 +- | 7 | tests/qemu-iotests/055 | 4 ++++ |
10 | tests/test-throttle.c | 2 +- | 8 | 1 file changed, 4 insertions(+) |
11 | 2 files changed, 2 insertions(+), 2 deletions(-) | ||
12 | 9 | ||
13 | diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c | 10 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 |
14 | index XXXXXXX..XXXXXXX 100644 | 11 | index XXXXXXX..XXXXXXX 100755 |
15 | --- a/tests/test-blockjob.c | 12 | --- a/tests/qemu-iotests/055 |
16 | +++ b/tests/test-blockjob.c | 13 | +++ b/tests/qemu-iotests/055 |
17 | @@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, | 14 | @@ -XXX,XX +XXX,XX @@ class TestCompressedToQcow2(iotests.QMPTestCase): |
18 | * BlockDriverState inserted. */ | 15 | class TestCompressedToVmdk(TestCompressedToQcow2): |
19 | static BlockBackend *create_blk(const char *name) | 16 | target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')} |
20 | { | 17 | |
21 | - /* FIXME Use real permissions */ | 18 | + @iotests.skip_if_unsupported(['vmdk']) |
22 | + /* No I/O is performed on this device */ | 19 | + def setUp(self): |
23 | BlockBackend *blk = blk_new(0, BLK_PERM_ALL); | 20 | + pass |
24 | BlockDriverState *bs; | 21 | + |
25 | 22 | ||
26 | diff --git a/tests/test-throttle.c b/tests/test-throttle.c | 23 | if __name__ == '__main__': |
27 | index XXXXXXX..XXXXXXX 100644 | 24 | iotests.main(supported_fmts=['raw', 'qcow2'], |
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 | -- | 25 | -- |
40 | 1.8.3.1 | 26 | 2.25.3 |
41 | 27 | ||
42 | 28 | diff view generated by jsdifflib |
1 | When the parents' child links are updated in bdrv_append() or | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | bdrv_replace_in_backing_chain(), this should affect all child links of | ||
3 | BlockBackends or other nodes, but not on child links held for other | ||
4 | purposes (like for setting permissions). This patch allows to control | ||
5 | the behaviour per BdrvChildRole. | ||
6 | 2 | ||
3 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
4 | Message-Id: <20200430124713.3067-8-vsementsov@virtuozzo.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
9 | Acked-by: Fam Zheng <famz@redhat.com> | ||
10 | --- | 6 | --- |
11 | block.c | 3 +++ | 7 | tests/qemu-iotests/109 | 1 + |
12 | include/block/block_int.h | 4 ++++ | 8 | 1 file changed, 1 insertion(+) |
13 | 2 files changed, 7 insertions(+) | ||
14 | 9 | ||
15 | diff --git a/block.c b/block.c | 10 | diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 |
16 | index XXXXXXX..XXXXXXX 100644 | 11 | index XXXXXXX..XXXXXXX 100755 |
17 | --- a/block.c | 12 | --- a/tests/qemu-iotests/109 |
18 | +++ b/block.c | 13 | +++ b/tests/qemu-iotests/109 |
19 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | 14 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 |
20 | BdrvChild *c, *next, *to_c; | 15 | _supported_fmt raw |
21 | 16 | _supported_proto file | |
22 | QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { | 17 | _supported_os Linux |
23 | + if (c->role->stay_at_node) { | 18 | +_require_drivers qcow qcow2 qed vdi vmdk vpc |
24 | + continue; | 19 | |
25 | + } | 20 | qemu_comm_method=qmp |
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 | 21 | ||
44 | -- | 22 | -- |
45 | 1.8.3.1 | 23 | 2.25.3 |
46 | 24 | ||
47 | 25 | diff view generated by jsdifflib |
1 | bdrv_append() cares about isolation of the node that it modifies, but | 1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
---|---|---|---|
2 | not about activity in some subtree below it. Instead of using the | ||
3 | recursive bdrv_requests_pending(), directly check bs->in_flight, which | ||
4 | considers only the node in question. | ||
5 | 2 | ||
3 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
4 | Message-Id: <20200430124713.3067-9-vsementsov@virtuozzo.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | 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 | --- | 6 | --- |
10 | block.c | 4 ++-- | 7 | tests/qemu-iotests/113 | 4 ++-- |
11 | 1 file changed, 2 insertions(+), 2 deletions(-) | 8 | 1 file changed, 2 insertions(+), 2 deletions(-) |
12 | 9 | ||
13 | diff --git a/block.c b/block.c | 10 | diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113 |
14 | index XXXXXXX..XXXXXXX 100644 | 11 | index XXXXXXX..XXXXXXX 100755 |
15 | --- a/block.c | 12 | --- a/tests/qemu-iotests/113 |
16 | +++ b/block.c | 13 | +++ b/tests/qemu-iotests/113 |
17 | @@ -XXX,XX +XXX,XX @@ static void change_parent_backing_link(BlockDriverState *from, | 14 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 |
18 | */ | 15 | . ./common.rc |
19 | void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) | 16 | . ./common.filter |
20 | { | 17 | |
21 | - assert(!bdrv_requests_pending(bs_top)); | 18 | -# Some of these test cases use bochs, but others do use raw, so this |
22 | - assert(!bdrv_requests_pending(bs_new)); | 19 | -# is only half a lie. |
23 | + assert(!atomic_read(&bs_top->in_flight)); | 20 | +# Some of these test cases use bochs, but others do use raw |
24 | + assert(!atomic_read(&bs_new->in_flight)); | 21 | +_require_drivers bochs |
25 | 22 | _supported_fmt raw | |
26 | bdrv_ref(bs_top); | 23 | _supported_proto file |
27 | 24 | _supported_os Linux | |
28 | -- | 25 | -- |
29 | 1.8.3.1 | 26 | 2.25.3 |
30 | 27 | ||
31 | 28 | diff view generated by jsdifflib |
1 | Now that blk_insert_bs() requests the BlockBackend permissions for the | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | node it attaches to, it can fail. Instead of aborting, pass the errors | ||
3 | to the callers. | ||
4 | 2 | ||
3 | After commit f01643fb8b47e8a70c04bbf45e0f12a9e5bc54de when an image is | ||
4 | extended and BDRV_REQ_ZERO_WRITE is set then the new clusters are | ||
5 | zeroized. | ||
6 | |||
7 | The code however does not detect correctly situations when the old and | ||
8 | the new end of the image are within the same cluster. The problem can | ||
9 | be reproduced with these steps: | ||
10 | |||
11 | qemu-img create -f qcow2 backing.qcow2 1M | ||
12 | qemu-img create -f qcow2 -F qcow2 -b backing.qcow2 top.qcow2 | ||
13 | qemu-img resize --shrink top.qcow2 520k | ||
14 | qemu-img resize top.qcow2 567k | ||
15 | |||
16 | In the last step offset - zero_start causes an integer wraparound. | ||
17 | |||
18 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
19 | Message-Id: <20200504155217.10325-1-berto@igalia.com> | ||
20 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 21 | 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 | --- | 22 | --- |
9 | block.c | 5 ++++- | 23 | block/qcow2.c | 12 ++++--- |
10 | block/backup.c | 5 ++++- | 24 | tests/qemu-iotests/292 | 73 ++++++++++++++++++++++++++++++++++++++ |
11 | block/block-backend.c | 13 ++++++++----- | 25 | tests/qemu-iotests/292.out | 24 +++++++++++++ |
12 | block/commit.c | 38 ++++++++++++++++++++++++++++++-------- | 26 | tests/qemu-iotests/group | 1 + |
13 | block/mirror.c | 15 ++++++++++++--- | 27 | 4 files changed, 105 insertions(+), 5 deletions(-) |
14 | block/qcow2.c | 10 ++++++++-- | 28 | create mode 100755 tests/qemu-iotests/292 |
15 | blockdev.c | 11 +++++++++-- | 29 | create mode 100644 tests/qemu-iotests/292.out |
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 | 30 | ||
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 | 31 | diff --git a/block/qcow2.c b/block/qcow2.c |
213 | index XXXXXXX..XXXXXXX 100644 | 32 | index XXXXXXX..XXXXXXX 100644 |
214 | --- a/block/qcow2.c | 33 | --- a/block/qcow2.c |
215 | +++ b/block/qcow2.c | 34 | +++ b/block/qcow2.c |
216 | @@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, | 35 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, |
217 | uint64_t cluster_size = s->cluster_size; | 36 | * requires a cluster-aligned start. The end may be unaligned if it is |
218 | bool encrypt; | 37 | * at the end of the image (which it is here). |
219 | int refcount_bits = s->refcount_bits; | 38 | */ |
220 | + Error *local_err = NULL; | 39 | - ret = qcow2_cluster_zeroize(bs, zero_start, offset - zero_start, 0); |
221 | int ret; | 40 | - if (ret < 0) { |
222 | QemuOptDesc *desc = opts->list->desc; | 41 | - error_setg_errno(errp, -ret, "Failed to zero out new clusters"); |
223 | Qcow2AmendHelperCBInfo helper_cb_info; | 42 | - goto fail; |
224 | @@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, | 43 | + if (offset > zero_start) { |
225 | 44 | + ret = qcow2_cluster_zeroize(bs, zero_start, offset - zero_start, 0); | |
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) { | 45 | + if (ret < 0) { |
322 | + goto fail; | 46 | + error_setg_errno(errp, -ret, "Failed to zero out new clusters"); |
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; | 47 | + goto fail; |
349 | + } | 48 | + } |
350 | } | 49 | } |
351 | } | 50 | |
352 | if (!blk) { | 51 | /* Write explicit zeros for the unaligned head */ |
353 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h | 52 | if (zero_start > old_length) { |
53 | - uint64_t len = zero_start - old_length; | ||
54 | + uint64_t len = MIN(zero_start, offset) - old_length; | ||
55 | uint8_t *buf = qemu_blockalign0(bs, len); | ||
56 | QEMUIOVector qiov; | ||
57 | qemu_iovec_init_buf(&qiov, buf, len); | ||
58 | diff --git a/tests/qemu-iotests/292 b/tests/qemu-iotests/292 | ||
59 | new file mode 100755 | ||
60 | index XXXXXXX..XXXXXXX | ||
61 | --- /dev/null | ||
62 | +++ b/tests/qemu-iotests/292 | ||
63 | @@ -XXX,XX +XXX,XX @@ | ||
64 | +#!/usr/bin/env bash | ||
65 | +# | ||
66 | +# Test resizing a qcow2 image with a backing file | ||
67 | +# | ||
68 | +# Copyright (C) 2020 Igalia, S.L. | ||
69 | +# Author: Alberto Garcia <berto@igalia.com> | ||
70 | +# | ||
71 | +# This program is free software; you can redistribute it and/or modify | ||
72 | +# it under the terms of the GNU General Public License as published by | ||
73 | +# the Free Software Foundation; either version 2 of the License, or | ||
74 | +# (at your option) any later version. | ||
75 | +# | ||
76 | +# This program is distributed in the hope that it will be useful, | ||
77 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
78 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
79 | +# GNU General Public License for more details. | ||
80 | +# | ||
81 | +# You should have received a copy of the GNU General Public License | ||
82 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
83 | +# | ||
84 | + | ||
85 | +# creator | ||
86 | +owner=berto@igalia.com | ||
87 | + | ||
88 | +seq=`basename $0` | ||
89 | +echo "QA output created by $seq" | ||
90 | + | ||
91 | +status=1 # failure is the default! | ||
92 | + | ||
93 | +_cleanup() | ||
94 | +{ | ||
95 | + _cleanup_test_img | ||
96 | +} | ||
97 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
98 | + | ||
99 | +# get standard environment, filters and checks | ||
100 | +. ./common.rc | ||
101 | +. ./common.filter | ||
102 | + | ||
103 | +_supported_fmt qcow2 | ||
104 | +_supported_proto file | ||
105 | +_supported_os Linux | ||
106 | + | ||
107 | +echo '### Create the backing image' | ||
108 | +BACKING_IMG="$TEST_IMG.base" | ||
109 | +TEST_IMG="$BACKING_IMG" _make_test_img 1M | ||
110 | + | ||
111 | +echo '### Fill the backing image with data (0x11)' | ||
112 | +$QEMU_IO -c 'write -P 0x11 0 1M' "$BACKING_IMG" | _filter_qemu_io | ||
113 | + | ||
114 | +echo '### Create the top image' | ||
115 | +_make_test_img -F "$IMGFMT" -b "$BACKING_IMG" | ||
116 | + | ||
117 | +echo '### Fill the top image with data (0x22)' | ||
118 | +$QEMU_IO -c 'write -P 0x22 0 1M' "$TEST_IMG" | _filter_qemu_io | ||
119 | + | ||
120 | +# Both offsets are part of the same cluster. | ||
121 | +echo '### Shrink the image to 520k' | ||
122 | +$QEMU_IMG resize --shrink "$TEST_IMG" 520k | ||
123 | +echo '### Grow the image to 567k' | ||
124 | +$QEMU_IMG resize "$TEST_IMG" 567k | ||
125 | + | ||
126 | +echo '### Check that the tail of the image reads as zeroes' | ||
127 | +$QEMU_IO -c 'read -P 0x22 0 520k' "$TEST_IMG" | _filter_qemu_io | ||
128 | +$QEMU_IO -c 'read -P 0x00 520k 47k' "$TEST_IMG" | _filter_qemu_io | ||
129 | + | ||
130 | +echo '### Show output of qemu-img map' | ||
131 | +$QEMU_IMG map "$TEST_IMG" | _filter_testdir | ||
132 | + | ||
133 | +# success, all done | ||
134 | +echo "*** done" | ||
135 | +rm -f $seq.full | ||
136 | +status=0 | ||
137 | diff --git a/tests/qemu-iotests/292.out b/tests/qemu-iotests/292.out | ||
138 | new file mode 100644 | ||
139 | index XXXXXXX..XXXXXXX | ||
140 | --- /dev/null | ||
141 | +++ b/tests/qemu-iotests/292.out | ||
142 | @@ -XXX,XX +XXX,XX @@ | ||
143 | +QA output created by 292 | ||
144 | +### Create the backing image | ||
145 | +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 | ||
146 | +### Fill the backing image with data (0x11) | ||
147 | +wrote 1048576/1048576 bytes at offset 0 | ||
148 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
149 | +### Create the top image | ||
150 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
151 | +### Fill the top image with data (0x22) | ||
152 | +wrote 1048576/1048576 bytes at offset 0 | ||
153 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
154 | +### Shrink the image to 520k | ||
155 | +Image resized. | ||
156 | +### Grow the image to 567k | ||
157 | +Image resized. | ||
158 | +### Check that the tail of the image reads as zeroes | ||
159 | +read 532480/532480 bytes at offset 0 | ||
160 | +520 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
161 | +read 48128/48128 bytes at offset 532480 | ||
162 | +47 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
163 | +### Show output of qemu-img map | ||
164 | +Offset Length Mapped to File | ||
165 | +0 0x8dc00 0x50000 TEST_DIR/t.qcow2 | ||
166 | +*** done | ||
167 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
354 | index XXXXXXX..XXXXXXX 100644 | 168 | index XXXXXXX..XXXXXXX 100644 |
355 | --- a/include/sysemu/block-backend.h | 169 | --- a/tests/qemu-iotests/group |
356 | +++ b/include/sysemu/block-backend.h | 170 | +++ b/tests/qemu-iotests/group |
357 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_by_public(BlockBackendPublic *public); | 171 | @@ -XXX,XX +XXX,XX @@ |
358 | 172 | 288 quick | |
359 | BlockDriverState *blk_bs(BlockBackend *blk); | 173 | 289 rw quick |
360 | void blk_remove_bs(BlockBackend *blk); | 174 | 290 rw auto quick |
361 | -void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs); | 175 | +292 rw auto quick |
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 | -- | 176 | -- |
413 | 1.8.3.1 | 177 | 2.25.3 |
414 | 178 | ||
415 | 179 | diff view generated by jsdifflib |
1 | From: Peter Lieven <pl@kamp.de> | 1 | m_data is used for zero clusters even though valid == 0. It really only |
---|---|---|---|
2 | means that a new cluster was allocated in the image file. Rename it to | ||
3 | reflect this. | ||
2 | 4 | ||
3 | the convert process is currently completely implemented with sync operations. | 5 | While at it, change it from int to bool, too. |
4 | That means it reads one buffer and then writes it. No parallelism and each sync | ||
5 | request takes as long as it takes until it is completed. | ||
6 | 6 | ||
7 | This can be a big performance hit when the convert process reads and writes | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | to devices which do not benefit from kernel readahead or pagecache. | 8 | Message-Id: <20200430133007.170335-2-kwolf@redhat.com> |
9 | In our environment we heavily have the following two use cases when using | 9 | Reviewed-by: Eric Blake <eblake@redhat.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> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
37 | --- | 11 | --- |
38 | qemu-img-cmds.hx | 4 +- | 12 | block/vmdk.c | 8 ++++---- |
39 | qemu-img.c | 322 ++++++++++++++++++++++++++++++++++++++----------------- | 13 | 1 file changed, 4 insertions(+), 4 deletions(-) |
40 | qemu-img.texi | 16 ++- | ||
41 | 3 files changed, 243 insertions(+), 99 deletions(-) | ||
42 | 14 | ||
43 | diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx | 15 | diff --git a/block/vmdk.c b/block/vmdk.c |
44 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
45 | --- a/qemu-img-cmds.hx | 17 | --- a/block/vmdk.c |
46 | +++ b/qemu-img-cmds.hx | 18 | +++ b/block/vmdk.c |
47 | @@ -XXX,XX +XXX,XX @@ STEXI | 19 | @@ -XXX,XX +XXX,XX @@ typedef struct VmdkMetaData { |
48 | ETEXI | 20 | unsigned int l1_index; |
49 | 21 | unsigned int l2_index; | |
50 | DEF("convert", img_convert, | 22 | unsigned int l2_offset; |
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") | 23 | - int valid; |
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") | 24 | + bool new_allocation; |
53 | STEXI | 25 | uint32_t *l2_cache_entry; |
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} | 26 | } VmdkMetaData; |
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} | 27 | |
56 | ETEXI | 28 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, |
57 | 29 | unsigned int l2_size_bytes = extent->l2_size * extent->entry_size; | |
58 | DEF("dd", img_dd, | 30 | |
59 | diff --git a/qemu-img.c b/qemu-img.c | 31 | if (m_data) { |
60 | index XXXXXXX..XXXXXXX 100644 | 32 | - m_data->valid = 0; |
61 | --- a/qemu-img.c | 33 | + m_data->new_allocation = false; |
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 | + | ||
81 | typedef struct ImgConvertState { | ||
82 | BlockBackend **src; | ||
83 | int64_t *src_sectors; | ||
84 | - int src_cur, src_num; | ||
85 | - int64_t src_cur_offset; | ||
86 | + int src_num; | ||
87 | int64_t total_sectors; | ||
88 | int64_t allocated_sectors; | ||
89 | + int64_t allocated_done; | ||
90 | + int64_t sector_num; | ||
91 | + int64_t wr_offs; | ||
92 | enum ImgConvertBlockStatus status; | ||
93 | int64_t sector_next_status; | ||
94 | BlockBackend *target; | ||
95 | bool has_zero_init; | ||
96 | bool compressed; | ||
97 | bool target_has_backing; | ||
98 | + bool wr_in_order; | ||
99 | int min_sparse; | ||
100 | size_t cluster_sectors; | ||
101 | size_t buf_sectors; | ||
102 | + int num_coroutines; | ||
103 | + int running_coroutines; | ||
104 | + Coroutine *co[MAX_COROUTINES]; | ||
105 | + int64_t wait_sector_num[MAX_COROUTINES]; | ||
106 | + CoMutex lock; | ||
107 | + int ret; | ||
108 | } ImgConvertState; | ||
109 | |||
110 | -static void convert_select_part(ImgConvertState *s, int64_t sector_num) | ||
111 | +static void convert_select_part(ImgConvertState *s, int64_t sector_num, | ||
112 | + int *src_cur, int64_t *src_cur_offset) | ||
113 | { | ||
114 | - assert(sector_num >= s->src_cur_offset); | ||
115 | - while (sector_num - s->src_cur_offset >= s->src_sectors[s->src_cur]) { | ||
116 | - s->src_cur_offset += s->src_sectors[s->src_cur]; | ||
117 | - s->src_cur++; | ||
118 | - assert(s->src_cur < s->src_num); | ||
119 | + *src_cur = 0; | ||
120 | + *src_cur_offset = 0; | ||
121 | + while (sector_num - *src_cur_offset >= s->src_sectors[*src_cur]) { | ||
122 | + *src_cur_offset += s->src_sectors[*src_cur]; | ||
123 | + (*src_cur)++; | ||
124 | + assert(*src_cur < s->src_num); | ||
125 | } | 34 | } |
126 | } | 35 | if (extent->flat) { |
127 | 36 | *cluster_offset = extent->flat_start_offset; | |
128 | static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) | 37 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, |
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; | 38 | return ret; |
208 | } | 39 | } |
209 | @@ -XXX,XX +XXX,XX @@ static int convert_read(ImgConvertState *s, int64_t sector_num, int nb_sectors, | 40 | if (m_data) { |
210 | return 0; | 41 | - m_data->valid = 1; |
211 | } | 42 | + m_data->new_allocation = true; |
212 | 43 | m_data->l1_index = l1_index; | |
213 | -static int convert_write(ImgConvertState *s, int64_t sector_num, int nb_sectors, | 44 | m_data->l2_index = l2_index; |
214 | - const uint8_t *buf) | 45 | m_data->l2_offset = l2_offset; |
215 | + | 46 | @@ -XXX,XX +XXX,XX @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, |
216 | +static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, | 47 | if (ret) { |
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; | 48 | return ret; |
275 | } | 49 | } |
276 | @@ -XXX,XX +XXX,XX @@ static int convert_write(ImgConvertState *s, int64_t sector_num, int nb_sectors, | 50 | - if (m_data.valid) { |
277 | return 0; | 51 | + if (m_data.new_allocation) { |
278 | } | 52 | /* update L2 tables */ |
279 | 53 | if (vmdk_L2update(extent, &m_data, | |
280 | -static int convert_do_copy(ImgConvertState *s) | 54 | cluster_offset >> BDRV_SECTOR_BITS) |
281 | +static void coroutine_fn convert_co_do_copy(void *opaque) | ||
282 | { | ||
283 | + ImgConvertState *s = opaque; | ||
284 | uint8_t *buf = NULL; | ||
285 | - int64_t sector_num, allocated_done; | ||
286 | - int ret; | ||
287 | - int n; | ||
288 | + int ret, i; | ||
289 | + int index = -1; | ||
290 | + | ||
291 | + for (i = 0; i < s->num_coroutines; i++) { | ||
292 | + if (s->co[i] == qemu_coroutine_self()) { | ||
293 | + index = i; | ||
294 | + break; | ||
295 | + } | ||
296 | + } | ||
297 | + assert(index >= 0); | ||
298 | + | ||
299 | + s->running_coroutines++; | ||
300 | + buf = blk_blockalign(s->target, s->buf_sectors * BDRV_SECTOR_SIZE); | ||
301 | + | ||
302 | + while (1) { | ||
303 | + int n; | ||
304 | + int64_t sector_num; | ||
305 | + enum ImgConvertBlockStatus status; | ||
306 | + | ||
307 | + qemu_co_mutex_lock(&s->lock); | ||
308 | + if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) { | ||
309 | + qemu_co_mutex_unlock(&s->lock); | ||
310 | + goto out; | ||
311 | + } | ||
312 | + n = convert_iteration_sectors(s, s->sector_num); | ||
313 | + if (n < 0) { | ||
314 | + qemu_co_mutex_unlock(&s->lock); | ||
315 | + s->ret = n; | ||
316 | + goto out; | ||
317 | + } | ||
318 | + /* save current sector and allocation status to local variables */ | ||
319 | + sector_num = s->sector_num; | ||
320 | + status = s->status; | ||
321 | + if (!s->min_sparse && s->status == BLK_ZERO) { | ||
322 | + n = MIN(n, s->buf_sectors); | ||
323 | + } | ||
324 | + /* increment global sector counter so that other coroutines can | ||
325 | + * already continue reading beyond this request */ | ||
326 | + s->sector_num += n; | ||
327 | + qemu_co_mutex_unlock(&s->lock); | ||
328 | + | ||
329 | + if (status == BLK_DATA || (!s->min_sparse && status == BLK_ZERO)) { | ||
330 | + s->allocated_done += n; | ||
331 | + qemu_progress_print(100.0 * s->allocated_done / | ||
332 | + s->allocated_sectors, 0); | ||
333 | + } | ||
334 | + | ||
335 | + if (status == BLK_DATA) { | ||
336 | + ret = convert_co_read(s, sector_num, n, buf); | ||
337 | + if (ret < 0) { | ||
338 | + error_report("error while reading sector %" PRId64 | ||
339 | + ": %s", sector_num, strerror(-ret)); | ||
340 | + s->ret = ret; | ||
341 | + goto out; | ||
342 | + } | ||
343 | + } else if (!s->min_sparse && status == BLK_ZERO) { | ||
344 | + status = BLK_DATA; | ||
345 | + memset(buf, 0x00, n * BDRV_SECTOR_SIZE); | ||
346 | + } | ||
347 | + | ||
348 | + if (s->wr_in_order) { | ||
349 | + /* keep writes in order */ | ||
350 | + while (s->wr_offs != sector_num) { | ||
351 | + if (s->ret != -EINPROGRESS) { | ||
352 | + goto out; | ||
353 | + } | ||
354 | + s->wait_sector_num[index] = sector_num; | ||
355 | + qemu_coroutine_yield(); | ||
356 | + } | ||
357 | + s->wait_sector_num[index] = -1; | ||
358 | + } | ||
359 | + | ||
360 | + ret = convert_co_write(s, sector_num, n, buf, status); | ||
361 | + if (ret < 0) { | ||
362 | + error_report("error while writing sector %" PRId64 | ||
363 | + ": %s", sector_num, strerror(-ret)); | ||
364 | + s->ret = ret; | ||
365 | + goto out; | ||
366 | + } | ||
367 | + | ||
368 | + if (s->wr_in_order) { | ||
369 | + /* reenter the coroutine that might have waited | ||
370 | + * for this write to complete */ | ||
371 | + s->wr_offs = sector_num + n; | ||
372 | + for (i = 0; i < s->num_coroutines; i++) { | ||
373 | + if (s->co[i] && s->wait_sector_num[i] == s->wr_offs) { | ||
374 | + /* | ||
375 | + * A -> B -> A cannot occur because A has | ||
376 | + * s->wait_sector_num[i] == -1 during A -> B. Therefore | ||
377 | + * B will never enter A during this time window. | ||
378 | + */ | ||
379 | + qemu_coroutine_enter(s->co[i]); | ||
380 | + break; | ||
381 | + } | ||
382 | + } | ||
383 | + } | ||
384 | + } | ||
385 | + | ||
386 | +out: | ||
387 | + qemu_vfree(buf); | ||
388 | + s->co[index] = NULL; | ||
389 | + s->running_coroutines--; | ||
390 | + if (!s->running_coroutines && s->ret == -EINPROGRESS) { | ||
391 | + /* the convert job finished successfully */ | ||
392 | + s->ret = 0; | ||
393 | + } | ||
394 | +} | ||
395 | + | ||
396 | +static int convert_do_copy(ImgConvertState *s) | ||
397 | +{ | ||
398 | + int ret, i, n; | ||
399 | + int64_t sector_num = 0; | ||
400 | |||
401 | /* Check whether we have zero initialisation or can get it efficiently */ | ||
402 | s->has_zero_init = s->min_sparse && !s->target_has_backing | ||
403 | @@ -XXX,XX +XXX,XX @@ static int convert_do_copy(ImgConvertState *s) | ||
404 | if (s->compressed) { | ||
405 | if (s->cluster_sectors <= 0 || s->cluster_sectors > s->buf_sectors) { | ||
406 | error_report("invalid cluster size"); | ||
407 | - ret = -EINVAL; | ||
408 | - goto fail; | ||
409 | + return -EINVAL; | ||
410 | } | ||
411 | s->buf_sectors = s->cluster_sectors; | ||
412 | } | ||
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 | - | ||
439 | - while (sector_num < s->total_sectors) { | ||
440 | - n = convert_iteration_sectors(s, sector_num); | ||
441 | - if (n < 0) { | ||
442 | - ret = n; | ||
443 | - goto fail; | ||
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 | - } | ||
451 | - | ||
452 | - if (s->status == BLK_DATA) { | ||
453 | - ret = convert_read(s, sector_num, n, buf); | ||
454 | - if (ret < 0) { | ||
455 | - error_report("error while reading sector %" PRId64 | ||
456 | - ": %s", sector_num, strerror(-ret)); | ||
457 | - goto fail; | ||
458 | - } | ||
459 | - } else if (!s->min_sparse && s->status == BLK_ZERO) { | ||
460 | - n = MIN(n, s->buf_sectors); | ||
461 | - memset(buf, 0, n * BDRV_SECTOR_SIZE); | ||
462 | - s->status = BLK_DATA; | ||
463 | - } | ||
464 | - | ||
465 | - ret = convert_write(s, sector_num, n, buf); | ||
466 | - if (ret < 0) { | ||
467 | - error_report("error while writing sector %" PRId64 | ||
468 | - ": %s", sector_num, strerror(-ret)); | ||
469 | - goto fail; | ||
470 | - } | ||
471 | + qemu_co_mutex_init(&s->lock); | ||
472 | + for (i = 0; i < s->num_coroutines; i++) { | ||
473 | + s->co[i] = qemu_coroutine_create(convert_co_do_copy, s); | ||
474 | + s->wait_sector_num[i] = -1; | ||
475 | + qemu_coroutine_enter(s->co[i]); | ||
476 | + } | ||
477 | |||
478 | - sector_num += n; | ||
479 | + while (s->ret == -EINPROGRESS) { | ||
480 | + main_loop_wait(false); | ||
481 | } | ||
482 | |||
483 | - if (s->compressed) { | ||
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 | -- | 55 | -- |
602 | 1.8.3.1 | 56 | 2.25.3 |
603 | 57 | ||
604 | 58 | diff view generated by jsdifflib |
1 | Not all callers of bdrv_set_backing_hd() know for sure that attaching | 1 | m_data must contain valid data even for zero clusters when no cluster |
---|---|---|---|
2 | the backing file will be allowed by the permission system. Return the | 2 | was allocated in the image file. Without this, zero writes segfault with |
3 | error from the function rather than aborting. | 3 | images that have zeroed_grain=on. |
4 | 4 | ||
5 | For zero writes, we don't want to allocate a cluster in the image file | ||
6 | even in compressed files. | ||
7 | |||
8 | Fixes: 524089bce43fd1cd3daaca979872451efa2cf7c6 | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Acked-by: Fam Zheng <famz@redhat.com> | 10 | Message-Id: <20200430133007.170335-3-kwolf@redhat.com> |
7 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 11 | Reviewed-by: Eric Blake <eblake@redhat.com> |
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | 13 | --- |
9 | block.c | 30 +++++++++++++++++++++++------- | 14 | block/vmdk.c | 12 +++++++----- |
10 | block/commit.c | 14 +++++++------- | 15 | 1 file changed, 7 insertions(+), 5 deletions(-) |
11 | block/mirror.c | 7 ++++++- | ||
12 | block/stream.c | 9 ++++++++- | ||
13 | block/vvfat.c | 2 +- | ||
14 | include/block/block.h | 3 ++- | ||
15 | 6 files changed, 47 insertions(+), 18 deletions(-) | ||
16 | 16 | ||
17 | diff --git a/block.c b/block.c | 17 | diff --git a/block/vmdk.c b/block/vmdk.c |
18 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/block.c | 19 | --- a/block/vmdk.c |
20 | +++ b/block.c | 20 | +++ b/block/vmdk.c |
21 | @@ -XXX,XX +XXX,XX @@ static void bdrv_parent_cb_resize(BlockDriverState *bs) | 21 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, |
22 | * Sets the backing file link of a BDS. A new reference is created; callers | 22 | extent->l2_cache_counts[min_index] = 1; |
23 | * which don't need their own reference any more must call bdrv_unref(). | 23 | found: |
24 | */ | 24 | l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; |
25 | -void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd) | 25 | + if (m_data) { |
26 | +void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | 26 | + m_data->l1_index = l1_index; |
27 | + Error **errp) | 27 | + m_data->l2_index = l2_index; |
28 | { | 28 | + m_data->l2_offset = l2_offset; |
29 | if (backing_hd) { | 29 | + m_data->l2_cache_entry = ((uint32_t *)l2_table) + l2_index; |
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 | + } | 30 | + } |
43 | 31 | ||
44 | out: | 32 | if (extent->sesparse) { |
45 | bdrv_refresh_limits(bs, NULL); | 33 | cluster_sector = le64_to_cpu(((uint64_t *)l2_table)[l2_index]); |
46 | @@ -XXX,XX +XXX,XX @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, | 34 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, |
47 | 35 | } | |
48 | /* Hook up the backing file link; drop our reference, bs owns the | 36 | if (m_data) { |
49 | * backing_hd reference now */ | 37 | m_data->new_allocation = true; |
50 | - bdrv_set_backing_hd(bs, backing_hd); | 38 | - m_data->l1_index = l1_index; |
51 | + bdrv_set_backing_hd(bs, backing_hd, &local_err); | 39 | - m_data->l2_index = l2_index; |
52 | bdrv_unref(backing_hd); | 40 | - m_data->l2_offset = l2_offset; |
53 | + if (local_err) { | 41 | - m_data->l2_cache_entry = ((uint32_t *)l2_table) + l2_index; |
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 | } | 42 | } |
178 | } | 43 | } |
179 | 44 | *cluster_offset = cluster_sector << BDRV_SECTOR_BITS; | |
180 | diff --git a/block/stream.c b/block/stream.c | 45 | @@ -XXX,XX +XXX,XX @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, |
181 | index XXXXXXX..XXXXXXX 100644 | 46 | error_report("Could not write to allocated cluster" |
182 | --- a/block/stream.c | 47 | " for streamOptimized"); |
183 | +++ b/block/stream.c | 48 | return -EIO; |
184 | @@ -XXX,XX +XXX,XX @@ static void stream_complete(BlockJob *job, void *opaque) | 49 | - } else { |
185 | StreamCompleteData *data = opaque; | 50 | + } else if (!zeroed) { |
186 | BlockDriverState *bs = blk_bs(job->blk); | 51 | /* allocate */ |
187 | BlockDriverState *base = s->base; | 52 | ret = get_cluster_offset(bs, extent, &m_data, offset, |
188 | + Error *local_err = NULL; | 53 | true, &cluster_offset, 0, 0); |
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 | -- | 54 | -- |
237 | 1.8.3.1 | 55 | 2.25.3 |
238 | 56 | ||
239 | 57 | diff view generated by jsdifflib |
1 | NBD can't cope with device size changes, so resize must be forbidden, | 1 | When overwriting a zero cluster, we must not perform copy-on-write from |
---|---|---|---|
2 | but otherwise we can tolerate anything. Depending on whether the export | 2 | the backing file, but from a zeroed buffer. |
3 | is writable or not, we only require consistent reads and writes. | ||
4 | 3 | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 5 | Message-Id: <20200430133007.170335-4-kwolf@redhat.com> |
7 | Acked-by: Fam Zheng <famz@redhat.com> | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | 8 | --- |
9 | nbd/server.c | 11 +++++++++-- | 9 | block/vmdk.c | 18 ++++++++++++------ |
10 | 1 file changed, 9 insertions(+), 2 deletions(-) | 10 | 1 file changed, 12 insertions(+), 6 deletions(-) |
11 | 11 | ||
12 | diff --git a/nbd/server.c b/nbd/server.c | 12 | diff --git a/block/vmdk.c b/block/vmdk.c |
13 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/nbd/server.c | 14 | --- a/block/vmdk.c |
15 | +++ b/nbd/server.c | 15 | +++ b/block/vmdk.c |
16 | @@ -XXX,XX +XXX,XX @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, | 16 | @@ -XXX,XX +XXX,XX @@ static void vmdk_refresh_limits(BlockDriverState *bs, Error **errp) |
17 | * get_whole_cluster | ||
18 | * | ||
19 | * Copy backing file's cluster that covers @sector_num, otherwise write zero, | ||
20 | - * to the cluster at @cluster_sector_num. | ||
21 | + * to the cluster at @cluster_sector_num. If @zeroed is true, we're overwriting | ||
22 | + * a zeroed cluster in the current layer and must not copy data from the | ||
23 | + * backing file. | ||
24 | * | ||
25 | * If @skip_start_sector < @skip_end_sector, the relative range | ||
26 | * [@skip_start_sector, @skip_end_sector) is not copied or written, and leave | ||
27 | @@ -XXX,XX +XXX,XX @@ static int get_whole_cluster(BlockDriverState *bs, | ||
28 | uint64_t cluster_offset, | ||
29 | uint64_t offset, | ||
30 | uint64_t skip_start_bytes, | ||
31 | - uint64_t skip_end_bytes) | ||
32 | + uint64_t skip_end_bytes, | ||
33 | + bool zeroed) | ||
17 | { | 34 | { |
18 | BlockBackend *blk; | 35 | int ret = VMDK_OK; |
19 | NBDExport *exp = g_malloc0(sizeof(NBDExport)); | 36 | int64_t cluster_bytes; |
20 | + uint64_t perm; | 37 | uint8_t *whole_grain; |
21 | int ret; | 38 | + bool copy_from_backing; |
22 | 39 | ||
23 | - /* FIXME Use real permissions */ | 40 | /* For COW, align request sector_num to cluster start */ |
24 | - blk = blk_new(0, BLK_PERM_ALL); | 41 | cluster_bytes = extent->cluster_sectors << BDRV_SECTOR_BITS; |
25 | + /* Don't allow resize while the NBD server is running, otherwise we don't | 42 | offset = QEMU_ALIGN_DOWN(offset, cluster_bytes); |
26 | + * care what happens with the node. */ | 43 | whole_grain = qemu_blockalign(bs, cluster_bytes); |
27 | + perm = BLK_PERM_CONSISTENT_READ; | 44 | + copy_from_backing = bs->backing && !zeroed; |
28 | + if ((nbdflags & NBD_FLAG_READ_ONLY) == 0) { | 45 | |
29 | + perm |= BLK_PERM_WRITE; | 46 | - if (!bs->backing) { |
30 | + } | 47 | + if (!copy_from_backing) { |
31 | + blk = blk_new(perm, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | | 48 | memset(whole_grain, 0, skip_start_bytes); |
32 | + BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD); | 49 | memset(whole_grain + skip_end_bytes, 0, cluster_bytes - skip_end_bytes); |
33 | ret = blk_insert_bs(blk, bs, errp); | 50 | } |
34 | if (ret < 0) { | 51 | @@ -XXX,XX +XXX,XX @@ static int get_whole_cluster(BlockDriverState *bs, |
35 | goto fail; | 52 | |
53 | /* Read backing data before skip range */ | ||
54 | if (skip_start_bytes > 0) { | ||
55 | - if (bs->backing) { | ||
56 | + if (copy_from_backing) { | ||
57 | /* qcow2 emits this on bs->file instead of bs->backing */ | ||
58 | BLKDBG_EVENT(extent->file, BLKDBG_COW_READ); | ||
59 | ret = bdrv_pread(bs->backing, offset, whole_grain, | ||
60 | @@ -XXX,XX +XXX,XX @@ static int get_whole_cluster(BlockDriverState *bs, | ||
61 | } | ||
62 | /* Read backing data after skip range */ | ||
63 | if (skip_end_bytes < cluster_bytes) { | ||
64 | - if (bs->backing) { | ||
65 | + if (copy_from_backing) { | ||
66 | /* qcow2 emits this on bs->file instead of bs->backing */ | ||
67 | BLKDBG_EVENT(extent->file, BLKDBG_COW_READ); | ||
68 | ret = bdrv_pread(bs->backing, offset + skip_end_bytes, | ||
69 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
70 | * or inappropriate VM shutdown. | ||
71 | */ | ||
72 | ret = get_whole_cluster(bs, extent, cluster_sector * BDRV_SECTOR_SIZE, | ||
73 | - offset, skip_start_bytes, skip_end_bytes); | ||
74 | + offset, skip_start_bytes, skip_end_bytes, | ||
75 | + zeroed); | ||
76 | if (ret) { | ||
77 | return ret; | ||
78 | } | ||
36 | -- | 79 | -- |
37 | 1.8.3.1 | 80 | 2.25.3 |
38 | 81 | ||
39 | 82 | diff view generated by jsdifflib |
1 | This makes use of the .bdrv_child_perm() implementation for formats that | 1 | If a cluster is already zeroed, we don't have to call vmdk_L2update(), |
---|---|---|---|
2 | we just added. All format drivers expose the permissions they actually | 2 | which is rather slow because it flushes the image file. |
3 | need nows, so that they can be set accordingly and updated when parents | ||
4 | are attached or detached. | ||
5 | |||
6 | The only format not included here is raw, which was already converted | ||
7 | with the other filter drivers. | ||
8 | 3 | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 5 | Message-Id: <20200430133007.170335-5-kwolf@redhat.com> |
11 | Acked-by: Fam Zheng <famz@redhat.com> | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | --- | 8 | --- |
13 | block/bochs.c | 1 + | 9 | block/vmdk.c | 2 +- |
14 | block/cloop.c | 1 + | 10 | 1 file changed, 1 insertion(+), 1 deletion(-) |
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 | 11 | ||
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 | 12 | diff --git a/block/vmdk.c b/block/vmdk.c |
148 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
149 | --- a/block/vmdk.c | 14 | --- a/block/vmdk.c |
150 | +++ b/block/vmdk.c | 15 | +++ b/block/vmdk.c |
151 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vmdk = { | 16 | @@ -XXX,XX +XXX,XX @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, |
152 | .bdrv_open = vmdk_open, | 17 | offset_in_cluster == 0 && |
153 | .bdrv_check = vmdk_check, | 18 | n_bytes >= extent->cluster_sectors * BDRV_SECTOR_SIZE) { |
154 | .bdrv_reopen_prepare = vmdk_reopen_prepare, | 19 | n_bytes = extent->cluster_sectors * BDRV_SECTOR_SIZE; |
155 | + .bdrv_child_perm = bdrv_format_default_perms, | 20 | - if (!zero_dry_run) { |
156 | .bdrv_co_preadv = vmdk_co_preadv, | 21 | + if (!zero_dry_run && ret != VMDK_ZEROED) { |
157 | .bdrv_co_pwritev = vmdk_co_pwritev, | 22 | /* update L2 tables */ |
158 | .bdrv_co_pwritev_compressed = vmdk_co_pwritev_compressed, | 23 | if (vmdk_L2update(extent, &m_data, VMDK_GTE_ZEROED) |
159 | diff --git a/block/vpc.c b/block/vpc.c | 24 | != VMDK_OK) { |
160 | index XXXXXXX..XXXXXXX 100644 | ||
161 | --- a/block/vpc.c | ||
162 | +++ b/block/vpc.c | ||
163 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vpc = { | ||
164 | .bdrv_open = vpc_open, | ||
165 | .bdrv_close = vpc_close, | ||
166 | .bdrv_reopen_prepare = vpc_reopen_prepare, | ||
167 | + .bdrv_child_perm = bdrv_format_default_perms, | ||
168 | .bdrv_create = vpc_create, | ||
169 | |||
170 | .bdrv_co_preadv = vpc_co_preadv, | ||
171 | -- | 25 | -- |
172 | 1.8.3.1 | 26 | 2.25.3 |
173 | 27 | ||
174 | 28 | diff view generated by jsdifflib |
1 | Request BLK_PERM_CONSISTENT_READ for the source of block migration, and | 1 | If we have a backup L2 table, we currently flush once after writing to |
---|---|---|---|
2 | handle potential permission errors as good as we can in this place | 2 | the active L2 table and again after writing to the backup table. A |
3 | (which is not very good, but it matches the other failure cases). | 3 | single flush is enough and makes things a little less slow. |
4 | 4 | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Acked-by: Fam Zheng <famz@redhat.com> | 6 | Message-Id: <20200430133007.170335-6-kwolf@redhat.com> |
7 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 7 | Reviewed-by: Eric Blake <eblake@redhat.com> |
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | 9 | --- |
9 | migration/block.c | 22 +++++++++++++++++----- | 10 | block/vmdk.c | 7 +++++-- |
10 | 1 file changed, 17 insertions(+), 5 deletions(-) | 11 | 1 file changed, 5 insertions(+), 2 deletions(-) |
11 | 12 | ||
12 | diff --git a/migration/block.c b/migration/block.c | 13 | diff --git a/block/vmdk.c b/block/vmdk.c |
13 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/migration/block.c | 15 | --- a/block/vmdk.c |
15 | +++ b/migration/block.c | 16 | +++ b/block/vmdk.c |
16 | @@ -XXX,XX +XXX,XX @@ static void unset_dirty_tracking(void) | 17 | @@ -XXX,XX +XXX,XX @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, |
17 | } | 18 | offset = cpu_to_le32(offset); |
18 | } | 19 | /* update L2 table */ |
19 | 20 | BLKDBG_EVENT(extent->file, BLKDBG_L2_UPDATE); | |
20 | -static void init_blk_migration(QEMUFile *f) | 21 | - if (bdrv_pwrite_sync(extent->file, |
21 | +static int init_blk_migration(QEMUFile *f) | 22 | + if (bdrv_pwrite(extent->file, |
22 | { | 23 | ((int64_t)m_data->l2_offset * 512) |
23 | BlockDriverState *bs; | 24 | + (m_data->l2_index * sizeof(offset)), |
24 | BlkMigDevState *bmds; | 25 | &offset, sizeof(offset)) < 0) { |
25 | @@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f) | 26 | @@ -XXX,XX +XXX,XX @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, |
26 | BlkMigDevState *bmds; | 27 | /* update backup L2 table */ |
27 | BlockDriverState *bs; | 28 | if (extent->l1_backup_table_offset != 0) { |
28 | } *bmds_bs; | 29 | m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; |
29 | + Error *local_err = NULL; | 30 | - if (bdrv_pwrite_sync(extent->file, |
30 | + int ret; | 31 | + if (bdrv_pwrite(extent->file, |
31 | 32 | ((int64_t)m_data->l2_offset * 512) | |
32 | block_mig_state.submitted = 0; | 33 | + (m_data->l2_index * sizeof(offset)), |
33 | block_mig_state.read_done = 0; | 34 | &offset, sizeof(offset)) < 0) { |
34 | @@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f) | 35 | return VMDK_ERROR; |
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 | } | 36 | } |
64 | } | 37 | } |
65 | 38 | + if (bdrv_flush(extent->file->bs) < 0) { | |
66 | + ret = 0; | 39 | + return VMDK_ERROR; |
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 | + } | 40 | + } |
83 | 41 | if (m_data->l2_cache_entry) { | |
84 | /* start track dirty blocks */ | 42 | *m_data->l2_cache_entry = offset; |
85 | ret = set_dirty_tracking(); | 43 | } |
86 | -- | 44 | -- |
87 | 1.8.3.1 | 45 | 2.25.3 |
88 | 46 | ||
89 | 47 | diff view generated by jsdifflib |
1 | The HMP command 'qemu-io' is a bit tricky because it wants to work on | 1 | In order to avoid bitrot in the zero cluster code in VMDK, enable |
---|---|---|---|
2 | the original BlockBackend, but additional permissions could be required. | 2 | zeroed_grain=on by default for the tests. |
3 | The details are explained in a comment in the code, but in summary, just | 3 | |
4 | request whatever permissions the current qemu-io command needs. | 4 | 059 now unsets the default options because zeroed_grain=on works only |
5 | with some subformats and the test case tests many different subformats, | ||
6 | including those for which it doesn't work. | ||
5 | 7 | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 9 | Message-Id: <20200430133007.170335-7-kwolf@redhat.com> |
8 | Acked-by: Fam Zheng <famz@redhat.com> | 10 | Reviewed-by: Eric Blake <eblake@redhat.com> |
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | 12 | --- |
10 | block/block-backend.c | 6 ++++++ | 13 | tests/qemu-iotests/059 | 6 +++--- |
11 | hmp.c | 26 +++++++++++++++++++++++++- | 14 | tests/qemu-iotests/check | 3 +++ |
12 | include/qemu-io.h | 1 + | 15 | 2 files changed, 6 insertions(+), 3 deletions(-) |
13 | include/sysemu/block-backend.h | 1 + | ||
14 | qemu-io-cmds.c | 28 ++++++++++++++++++++++++++++ | ||
15 | 5 files changed, 61 insertions(+), 1 deletion(-) | ||
16 | 16 | ||
17 | diff --git a/block/block-backend.c b/block/block-backend.c | 17 | diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 |
18 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100755 |
19 | --- a/block/block-backend.c | 19 | --- a/tests/qemu-iotests/059 |
20 | +++ b/block/block-backend.c | 20 | +++ b/tests/qemu-iotests/059 |
21 | @@ -XXX,XX +XXX,XX @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | 21 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 |
22 | return 0; | 22 | _supported_fmt vmdk |
23 | } | 23 | _supported_proto file |
24 | 24 | _supported_os Linux | |
25 | +void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) | 25 | -_unsupported_imgopts "subformat=monolithicFlat" \ |
26 | +{ | 26 | - "subformat=twoGbMaxExtentFlat" \ |
27 | + *perm = blk->perm; | 27 | - "subformat=twoGbMaxExtentSparse" |
28 | + *shared_perm = blk->shared_perm; | ||
29 | +} | ||
30 | + | 28 | + |
31 | static int blk_do_attach_dev(BlockBackend *blk, void *dev) | 29 | +# We test all kinds of VMDK options here, so ignore user-specified options |
32 | { | 30 | +IMGOPTS="" |
33 | if (blk->dev) { | 31 | |
34 | diff --git a/hmp.c b/hmp.c | 32 | capacity_offset=16 |
35 | index XXXXXXX..XXXXXXX 100644 | 33 | granularity_offset=20 |
36 | --- a/hmp.c | 34 | diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check |
37 | +++ b/hmp.c | 35 | index XXXXXXX..XXXXXXX 100755 |
38 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | 36 | --- a/tests/qemu-iotests/check |
39 | if (!blk) { | 37 | +++ b/tests/qemu-iotests/check |
40 | BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err); | 38 | @@ -XXX,XX +XXX,XX @@ fi |
41 | if (bs) { | 39 | if [ "$IMGFMT" == "luks" ] && ! (echo "$IMGOPTS" | grep "iter-time=" > /dev/null); then |
42 | - /* FIXME Use real permissions */ | 40 | IMGOPTS=$(_optstr_add "$IMGOPTS" "iter-time=10") |
43 | blk = local_blk = blk_new(0, BLK_PERM_ALL); | 41 | fi |
44 | ret = blk_insert_bs(blk, bs, &err); | 42 | +if [ "$IMGFMT" == "vmdk" ] && ! (echo "$IMGOPTS" | grep "zeroed_grain=" > /dev/null); then |
45 | if (ret < 0) { | 43 | + IMGOPTS=$(_optstr_add "$IMGOPTS" "zeroed_grain=on") |
46 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | 44 | +fi |
47 | aio_context = blk_get_aio_context(blk); | 45 | |
48 | aio_context_acquire(aio_context); | 46 | if [ -z "$SAMPLE_IMG_DIR" ]; then |
49 | 47 | SAMPLE_IMG_DIR="$source_iotests/sample_images" | |
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 | -- | 48 | -- |
177 | 1.8.3.1 | 49 | 2.25.3 |
178 | 50 | ||
179 | 51 | diff view generated by jsdifflib |
1 | The mirror block job is mainly used for two different scenarios: | 1 | The test case forgot to specify the null-co size for the target node. |
---|---|---|---|
2 | Mirroring to an otherwise unused, independent target node, or for active | 2 | When adding a check to backup that both sizes match, this would fail |
3 | commit where the target node is part of the backing chain of the source. | 3 | because of the size mismatch and not the behaviour that the test really |
4 | wanted to test. | ||
4 | 5 | ||
5 | Similarly to the commit block job patch, we need to insert a new filter | 6 | Fixes: a541fcc27c98b96da187c7d4573f3270f3ddd283 |
6 | node to keep the permissions correct during active commit. | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | Message-Id: <20200430142755.315494-2-kwolf@redhat.com> | ||
9 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | tests/qemu-iotests/283 | 6 +++++- | ||
13 | tests/qemu-iotests/283.out | 2 +- | ||
14 | 2 files changed, 6 insertions(+), 2 deletions(-) | ||
7 | 15 | ||
8 | Note that one change this implies is that job->blk points to | 16 | diff --git a/tests/qemu-iotests/283 b/tests/qemu-iotests/283 |
9 | mirror_top_bs as its root now, and mirror_top_bs (rather than the actual | ||
10 | source node) contains the bs->job pointer. This requires qemu-img commit | ||
11 | to get the job by name now rather than just taking bs->job. | ||
12 | |||
13 | 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 | --- | ||
17 | block/mirror.c | 216 ++++++++++++++++++++++++++++++++++++++------- | ||
18 | qemu-img.c | 6 +- | ||
19 | tests/qemu-iotests/141 | 2 +- | ||
20 | tests/qemu-iotests/141.out | 4 +- | ||
21 | 4 files changed, 190 insertions(+), 38 deletions(-) | ||
22 | |||
23 | diff --git a/block/mirror.c b/block/mirror.c | ||
24 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/block/mirror.c | 18 | --- a/tests/qemu-iotests/283 |
26 | +++ b/block/mirror.c | 19 | +++ b/tests/qemu-iotests/283 |
27 | @@ -XXX,XX +XXX,XX @@ typedef struct MirrorBlockJob { | 20 | @@ -XXX,XX +XXX,XX @@ to check that crash is fixed :) |
28 | BlockJob common; | 21 | vm = iotests.VM() |
29 | RateLimit limit; | 22 | vm.launch() |
30 | BlockBackend *target; | 23 | |
31 | + BlockDriverState *mirror_top_bs; | 24 | -vm.qmp_log('blockdev-add', **{'node-name': 'target', 'driver': 'null-co'}) |
32 | + BlockDriverState *source; | 25 | +vm.qmp_log('blockdev-add', **{ |
33 | BlockDriverState *base; | 26 | + 'node-name': 'target', |
34 | + | 27 | + 'driver': 'null-co', |
35 | /* The name of the graph node to replace */ | 28 | + 'size': size, |
36 | char *replaces; | 29 | +}) |
37 | /* The BDS to replace */ | 30 | |
38 | @@ -XXX,XX +XXX,XX @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s, | 31 | vm.qmp_log('blockdev-add', **{ |
39 | 32 | 'node-name': 'source', | |
40 | static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) | 33 | diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out |
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); | ||
76 | bdrv_replace_in_backing_chain(to_replace, target_bs); | ||
77 | bdrv_drained_end(target_bs); | ||
78 | - | ||
79 | - /* We just changed the BDS the job BB refers to, so switch the BB back | ||
80 | - * so the cleanup does the right thing. We don't need any permissions | ||
81 | - * any more now. */ | ||
82 | - blk_remove_bs(job->blk); | ||
83 | - blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); | ||
84 | - blk_insert_bs(job->blk, src, &error_abort); | ||
85 | } | ||
86 | if (s->to_replace) { | ||
87 | bdrv_op_unblock_all(s->to_replace, s->replace_blocker); | ||
88 | @@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque) | ||
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 | { | ||
136 | MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); | ||
137 | - BlockDriverState *src, *target; | ||
138 | + BlockDriverState *target; | ||
139 | |||
140 | - src = blk_bs(job->blk); | ||
141 | target = blk_bs(s->target); | ||
142 | |||
143 | if (!s->synced) { | ||
144 | @@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp) | ||
145 | replace_aio_context = bdrv_get_aio_context(s->to_replace); | ||
146 | aio_context_acquire(replace_aio_context); | ||
147 | |||
148 | + /* TODO Translate this into permission system. Current definition of | ||
149 | + * GRAPH_MOD would require to request it for the parents; they might | ||
150 | + * not even be BlockDriverStates, however, so a BdrvChild can't address | ||
151 | + * them. May need redefinition of GRAPH_MOD. */ | ||
152 | error_setg(&s->replace_blocker, | ||
153 | "block device is in use by block-job-complete"); | ||
154 | bdrv_op_block_all(s->to_replace, s->replace_blocker); | ||
155 | @@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp) | ||
156 | aio_context_release(replace_aio_context); | ||
157 | } | ||
158 | |||
159 | - if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { | ||
160 | - BlockDriverState *backing = s->is_none_mode ? src : s->base; | ||
161 | - if (backing_bs(target) != backing) { | ||
162 | - bdrv_set_backing_hd(target, backing); | ||
163 | - } | ||
164 | - } | ||
165 | - | ||
166 | s->should_complete = true; | ||
167 | block_job_enter(&s->common); | ||
168 | } | ||
169 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = { | ||
170 | .drain = mirror_drain, | ||
171 | }; | ||
172 | |||
173 | +static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs, | ||
174 | + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) | ||
175 | +{ | ||
176 | + return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); | ||
177 | +} | ||
178 | + | ||
179 | +static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, | ||
180 | + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) | ||
181 | +{ | ||
182 | + return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); | ||
183 | +} | ||
184 | + | ||
185 | +static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) | ||
186 | +{ | ||
187 | + return bdrv_co_flush(bs->backing->bs); | ||
188 | +} | ||
189 | + | ||
190 | +static int64_t coroutine_fn bdrv_mirror_top_get_block_status( | ||
191 | + BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum, | ||
192 | + BlockDriverState **file) | ||
193 | +{ | ||
194 | + *pnum = nb_sectors; | ||
195 | + *file = bs->backing->bs; | ||
196 | + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA | | ||
197 | + (sector_num << BDRV_SECTOR_BITS); | ||
198 | +} | ||
199 | + | ||
200 | +static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, | ||
201 | + int64_t offset, int count, BdrvRequestFlags flags) | ||
202 | +{ | ||
203 | + return bdrv_co_pwrite_zeroes(bs->backing, offset, count, flags); | ||
204 | +} | ||
205 | + | ||
206 | +static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs, | ||
207 | + int64_t offset, int count) | ||
208 | +{ | ||
209 | + return bdrv_co_pdiscard(bs->backing->bs, offset, count); | ||
210 | +} | ||
211 | + | ||
212 | +static void bdrv_mirror_top_close(BlockDriverState *bs) | ||
213 | +{ | ||
214 | +} | ||
215 | + | ||
216 | +static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
217 | + const BdrvChildRole *role, | ||
218 | + uint64_t perm, uint64_t shared, | ||
219 | + uint64_t *nperm, uint64_t *nshared) | ||
220 | +{ | ||
221 | + /* Must be able to forward guest writes to the real image */ | ||
222 | + *nperm = 0; | ||
223 | + if (perm & BLK_PERM_WRITE) { | ||
224 | + *nperm |= BLK_PERM_WRITE; | ||
225 | + } | ||
226 | + | ||
227 | + *nshared = BLK_PERM_ALL; | ||
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 | } | ||
366 | |||
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 | 34 | index XXXXXXX..XXXXXXX 100644 |
370 | --- a/qemu-img.c | 35 | --- a/tests/qemu-iotests/283.out |
371 | +++ b/qemu-img.c | 36 | +++ b/tests/qemu-iotests/283.out |
372 | @@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp) | 37 | @@ -XXX,XX +XXX,XX @@ |
373 | { | 38 | -{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "target"}} |
374 | AioContext *aio_context = blk_get_aio_context(job->blk); | 39 | +{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "target", "size": 1048576}} |
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": {}} | 40 | {"return": {}} |
420 | -{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} | 41 | {"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": {"driver": "null-co", "node-name": "base", "size": 1048576}, "node-name": "source"}} |
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": {}} | 42 | {"return": {}} |
434 | -- | 43 | -- |
435 | 1.8.3.1 | 44 | 2.25.3 |
436 | 45 | ||
437 | 46 | diff view generated by jsdifflib |
1 | This functions creates a BlockBackend internally, so the block jobs need | 1 | bdrv_get_device_name() will be an empty string with modern management |
---|---|---|---|
2 | to tell it what they want to do with the BB. | 2 | tools that don't use -drive. Use bdrv_get_device_or_node_name() instead |
3 | so that the node name is used if the BlockBackend is anonymous. | ||
4 | |||
5 | While at it, start with upper case to make the message consistent with | ||
6 | the rest of the function. | ||
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> | 9 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
6 | Acked-by: Fam Zheng <famz@redhat.com> | 10 | Reviewed-by: Alberto Garcia <berto@igalia.com> |
11 | Message-Id: <20200430142755.315494-3-kwolf@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | --- | 13 | --- |
8 | block/backup.c | 5 +++-- | 14 | block/backup.c | 4 ++-- |
9 | block/commit.c | 5 +++-- | 15 | 1 file changed, 2 insertions(+), 2 deletions(-) |
10 | block/mirror.c | 5 +++-- | ||
11 | block/stream.c | 5 +++-- | ||
12 | blockjob.c | 6 +++--- | ||
13 | include/block/blockjob_int.h | 4 +++- | ||
14 | tests/test-blockjob-txn.c | 6 +++--- | ||
15 | tests/test-blockjob.c | 5 +++-- | ||
16 | 8 files changed, 24 insertions(+), 17 deletions(-) | ||
17 | 16 | ||
18 | diff --git a/block/backup.c b/block/backup.c | 17 | diff --git a/block/backup.c b/block/backup.c |
19 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/block/backup.c | 19 | --- a/block/backup.c |
21 | +++ b/block/backup.c | 20 | +++ b/block/backup.c |
22 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | 21 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, |
22 | |||
23 | len = bdrv_getlength(bs); | ||
24 | if (len < 0) { | ||
25 | - error_setg_errno(errp, -len, "unable to get length for '%s'", | ||
26 | - bdrv_get_device_name(bs)); | ||
27 | + error_setg_errno(errp, -len, "Unable to get length for '%s'", | ||
28 | + bdrv_get_device_or_node_name(bs)); | ||
23 | goto error; | 29 | goto error; |
24 | } | 30 | } |
25 | 31 | ||
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 | -- | 32 | -- |
162 | 1.8.3.1 | 33 | 2.25.3 |
163 | 34 | ||
164 | 35 | diff view generated by jsdifflib |
1 | Block jobs don't actually do I/O through the the reference they create | 1 | Since the introduction of a backup filter node in commit 00e30f05d, the |
---|---|---|---|
2 | with block_job_add_bdrv(), but they might want to use the permisssion | 2 | backup block job crashes when the target image is smaller than the |
3 | system to express what the block job does to intermediate nodes. This | 3 | source image because it will try to write after the end of the target |
4 | adds permissions to block_job_add_bdrv() to provide the means to request | 4 | node without having BLK_PERM_RESIZE. (Previously, the BlockBackend layer |
5 | permissions. | 5 | would have caught this and errored out gracefully.) |
6 | 6 | ||
7 | We can fix this and even do better than the old behaviour: Check that | ||
8 | source and target have the same image size at the start of the block job | ||
9 | and unshare BLK_PERM_RESIZE. (This permission was already unshared | ||
10 | before the same commit 00e30f05d, but the BlockBackend that was used to | ||
11 | make the restriction was removed without a replacement.) This will | ||
12 | immediately error out when starting the job instead of only when writing | ||
13 | to a block that doesn't exist in the target. | ||
14 | |||
15 | Longer target than source would technically work because we would never | ||
16 | write to blocks that don't exist, but semantically these are invalid, | ||
17 | too, because a backup is supposed to create a copy, not just an image | ||
18 | that starts with a copy. | ||
19 | |||
20 | Fixes: 00e30f05de1d19586345ec373970ef4c192c6270 | ||
21 | Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1778593 | ||
22 | Cc: qemu-stable@nongnu.org | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 23 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 24 | Message-Id: <20200430142755.315494-4-kwolf@redhat.com> |
9 | Acked-by: Fam Zheng <famz@redhat.com> | 25 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
26 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | 27 | --- |
11 | block/backup.c | 4 +++- | 28 | block/backup-top.c | 14 +++++++++----- |
12 | block/commit.c | 8 ++++++-- | 29 | block/backup.c | 14 +++++++++++++- |
13 | block/mirror.c | 9 +++++++-- | 30 | 2 files changed, 22 insertions(+), 6 deletions(-) |
14 | block/stream.c | 4 +++- | ||
15 | blockjob.c | 36 ++++++++++++++++++++++++++++++------ | ||
16 | include/block/blockjob.h | 5 ++++- | ||
17 | 6 files changed, 53 insertions(+), 13 deletions(-) | ||
18 | 31 | ||
32 | diff --git a/block/backup-top.c b/block/backup-top.c | ||
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/block/backup-top.c | ||
35 | +++ b/block/backup-top.c | ||
36 | @@ -XXX,XX +XXX,XX @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
37 | * | ||
38 | * Share write to target (child_file), to not interfere | ||
39 | * with guest writes to its disk which may be in target backing chain. | ||
40 | + * Can't resize during a backup block job because we check the size | ||
41 | + * only upfront. | ||
42 | */ | ||
43 | - *nshared = BLK_PERM_ALL; | ||
44 | + *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; | ||
45 | *nperm = BLK_PERM_WRITE; | ||
46 | } else { | ||
47 | /* Source child */ | ||
48 | @@ -XXX,XX +XXX,XX @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
49 | if (perm & BLK_PERM_WRITE) { | ||
50 | *nperm = *nperm | BLK_PERM_CONSISTENT_READ; | ||
51 | } | ||
52 | - *nshared &= ~BLK_PERM_WRITE; | ||
53 | + *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, | ||
58 | { | ||
59 | Error *local_err = NULL; | ||
60 | BDRVBackupTopState *state; | ||
61 | - BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter, | ||
62 | - filter_node_name, | ||
63 | - BDRV_O_RDWR, errp); | ||
64 | + BlockDriverState *top; | ||
65 | bool appended = false; | ||
66 | |||
67 | + assert(source->total_sectors == target->total_sectors); | ||
68 | + | ||
69 | + top = bdrv_new_open_driver(&bdrv_backup_top_filter, filter_node_name, | ||
70 | + BDRV_O_RDWR, errp); | ||
71 | if (!top) { | ||
72 | return NULL; | ||
73 | } | ||
19 | diff --git a/block/backup.c b/block/backup.c | 74 | diff --git a/block/backup.c b/block/backup.c |
20 | index XXXXXXX..XXXXXXX 100644 | 75 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/block/backup.c | 76 | --- a/block/backup.c |
22 | +++ b/block/backup.c | 77 | +++ b/block/backup.c |
23 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | 78 | @@ -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); | 79 | BlockCompletionFunc *cb, void *opaque, |
80 | JobTxn *txn, Error **errp) | ||
81 | { | ||
82 | - int64_t len; | ||
83 | + int64_t len, target_len; | ||
84 | BackupBlockJob *job = NULL; | ||
85 | int64_t cluster_size; | ||
86 | BdrvRequestFlags write_flags; | ||
87 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
88 | goto error; | ||
25 | } | 89 | } |
26 | 90 | ||
27 | - block_job_add_bdrv(&job->common, target); | 91 | + target_len = bdrv_getlength(target); |
28 | + /* FIXME Use real permissions */ | 92 | + if (target_len < 0) { |
29 | + block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | 93 | + error_setg_errno(errp, -target_len, "Unable to get length for '%s'", |
30 | + &error_abort); | 94 | + bdrv_get_device_or_node_name(bs)); |
31 | job->common.len = len; | 95 | + goto error; |
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 | + } | 96 | + } |
138 | + | 97 | + |
139 | + job->nodes = g_slist_prepend(job->nodes, c); | 98 | + if (target_len != len) { |
140 | bdrv_ref(bs); | 99 | + error_setg(errp, "Source and target image have different sizes"); |
141 | bdrv_op_block_all(bs, job->blocker); | 100 | + goto error; |
101 | + } | ||
142 | + | 102 | + |
143 | + return 0; | 103 | cluster_size = backup_calculate_cluster_size(target, errp); |
144 | } | 104 | if (cluster_size < 0) { |
145 | 105 | goto error; | |
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 | -- | 106 | -- |
192 | 1.8.3.1 | 107 | 2.25.3 |
193 | 108 | ||
194 | 109 | diff view generated by jsdifflib |
1 | The backup block job doesn't have very complicated requirements: It | 1 | This tests that the backup job catches situations where the target node |
---|---|---|---|
2 | needs to read from the source and write to the target, but it's fine | 2 | has a different size than the source node. It must also forbid resize |
3 | with either side being changed. The only restriction is that we can't | 3 | operations when the job is already running. |
4 | resize the image because the job uses a cached value. | ||
5 | |||
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 | 4 | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 6 | Message-Id: <20200430142755.315494-5-kwolf@redhat.com> |
13 | Acked-by: Fam Zheng <famz@redhat.com> | 7 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | --- | 9 | --- |
15 | block/backup.c | 15 ++++++++++----- | 10 | tests/qemu-iotests/055 | 41 ++++++++++++++++++++++++++++++++++++-- |
16 | tests/qemu-iotests/055 | 11 +++++++---- | 11 | tests/qemu-iotests/055.out | 4 ++-- |
17 | 2 files changed, 17 insertions(+), 9 deletions(-) | 12 | 2 files changed, 41 insertions(+), 4 deletions(-) |
18 | 13 | ||
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 | 14 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 |
58 | index XXXXXXX..XXXXXXX 100755 | 15 | index XXXXXXX..XXXXXXX 100755 |
59 | --- a/tests/qemu-iotests/055 | 16 | --- a/tests/qemu-iotests/055 |
60 | +++ b/tests/qemu-iotests/055 | 17 | +++ b/tests/qemu-iotests/055 |
61 | @@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase): | 18 | @@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase): |
62 | def setUp(self): | 19 | def setUp(self): |
63 | qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len)) | 20 | qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len)) |
64 | 21 | ||
65 | - self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) | 22 | - self.vm = iotests.VM().add_drive('blkdebug::' + test_img) |
66 | + self.vm = iotests.VM().add_drive(test_img) | 23 | - self.vm.add_drive(blockdev_target_img, interface="none") |
67 | + self.vm.add_drive(blockdev_target_img, interface="none") | 24 | + self.vm = iotests.VM() |
25 | + self.vm.add_drive('blkdebug::' + test_img, 'node-name=source') | ||
26 | + self.vm.add_drive(blockdev_target_img, 'node-name=target', | ||
27 | + interface="none") | ||
68 | if iotests.qemu_default_machine == 'pc': | 28 | if iotests.qemu_default_machine == 'pc': |
69 | self.vm.add_drive(None, 'media=cdrom', 'ide') | 29 | self.vm.add_drive(None, 'media=cdrom', 'ide') |
70 | self.vm.launch() | 30 | self.vm.launch() |
71 | @@ -XXX,XX +XXX,XX @@ class TestSetSpeed(iotests.QMPTestCase): | 31 | @@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase): |
72 | def setUp(self): | 32 | def test_pause_blockdev_backup(self): |
73 | qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len)) | 33 | self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) |
74 | 34 | ||
75 | - self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) | 35 | + def do_test_resize_blockdev_backup(self, device, node): |
76 | + self.vm = iotests.VM().add_drive(test_img) | 36 | + def pre_finalize(): |
77 | + self.vm.add_drive(blockdev_target_img, interface="none") | 37 | + result = self.vm.qmp('block_resize', device=device, size=65536) |
78 | self.vm.launch() | 38 | + self.assert_qmp(result, 'error/class', 'GenericError') |
79 | 39 | + | |
80 | def tearDown(self): | 40 | + result = self.vm.qmp('block_resize', node_name=node, size=65536) |
81 | @@ -XXX,XX +XXX,XX @@ class TestSingleTransaction(iotests.QMPTestCase): | 41 | + self.assert_qmp(result, 'error/class', 'GenericError') |
82 | def setUp(self): | 42 | + |
83 | qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len)) | 43 | + result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0', |
84 | 44 | + target='drive1', sync='full', auto_finalize=False, | |
85 | - self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) | 45 | + auto_dismiss=False) |
86 | + self.vm = iotests.VM().add_drive(test_img) | 46 | + self.assert_qmp(result, 'return', {}) |
87 | + self.vm.add_drive(blockdev_target_img, interface="none") | 47 | + |
88 | if iotests.qemu_default_machine == 'pc': | 48 | + self.vm.run_job('job0', auto_finalize=False, pre_finalize=pre_finalize) |
89 | self.vm.add_drive(None, 'media=cdrom', 'ide') | 49 | + |
90 | self.vm.launch() | 50 | + def test_source_resize_blockdev_backup(self): |
91 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | 51 | + self.do_test_resize_blockdev_backup('drive0', 'source') |
92 | 52 | + | |
93 | qemu_img('create', '-f', fmt, blockdev_target_img, | 53 | + def test_target_resize_blockdev_backup(self): |
94 | str(TestDriveCompression.image_len), *args) | 54 | + self.do_test_resize_blockdev_backup('drive1', 'target') |
95 | - self.vm.add_drive(blockdev_target_img, format=fmt) | 55 | + |
96 | + self.vm.add_drive(blockdev_target_img, format=fmt, interface="none") | 56 | + def do_test_target_size(self, size): |
97 | 57 | + result = self.vm.qmp('block_resize', device='drive1', size=size) | |
98 | self.vm.launch() | 58 | + self.assert_qmp(result, 'return', {}) |
99 | 59 | + | |
60 | + result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0', | ||
61 | + target='drive1', sync='full') | ||
62 | + self.assert_qmp(result, 'error/class', 'GenericError') | ||
63 | + | ||
64 | + def test_small_target(self): | ||
65 | + self.do_test_target_size(image_len // 2) | ||
66 | + | ||
67 | + def test_large_target(self): | ||
68 | + self.do_test_target_size(image_len * 2) | ||
69 | + | ||
70 | def test_medium_not_found(self): | ||
71 | if iotests.qemu_default_machine != 'pc': | ||
72 | return | ||
73 | diff --git a/tests/qemu-iotests/055.out b/tests/qemu-iotests/055.out | ||
74 | index XXXXXXX..XXXXXXX 100644 | ||
75 | --- a/tests/qemu-iotests/055.out | ||
76 | +++ b/tests/qemu-iotests/055.out | ||
77 | @@ -XXX,XX +XXX,XX @@ | ||
78 | -.................................... | ||
79 | +........................................ | ||
80 | ---------------------------------------------------------------------- | ||
81 | -Ran 36 tests | ||
82 | +Ran 40 tests | ||
83 | |||
84 | OK | ||
100 | -- | 85 | -- |
101 | 1.8.3.1 | 86 | 2.25.3 |
102 | 87 | ||
103 | 88 | diff view generated by jsdifflib |
1 | Management tools need to be able to know about every node in the graph | 1 | 055 uses the backup block job to create a compressed backup of an |
---|---|---|---|
2 | and need a way to address them. Changing the graph structure was okay | 2 | $IMGFMT image with both qcow2 and vmdk targets. However, cluster |
3 | because libvirt doesn't really manage the node level yet, but future | 3 | allocation in vmdk is very slow because it flushes the image file after |
4 | libvirt versions need to deal with both new and old version of qemu. | 4 | each L2 update. |
5 | 5 | ||
6 | This new option to blockdev-commit allows the client to set a node-name | 6 | There is no reason why we need this level of safety in this test, so |
7 | for the automatically inserted filter driver, and at the same time | 7 | let's disable flushes for vmdk. For the blockdev-backup tests this is |
8 | serves as a witness for a future libvirt that this version of qemu does | 8 | achieved by simply adding the cache.no-flush=on to the drive_add() for |
9 | automatically insert a filter driver. | 9 | the target. For drive-backup, the caching flags are copied from the |
10 | source node, so we'll also add the flag to the source node, even though | ||
11 | it is not vmdk. | ||
12 | |||
13 | This can make the test run significantly faster (though it doesn't make | ||
14 | a difference on tmpfs). In my usual setup it goes from ~45s to ~15s. | ||
10 | 15 | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | Acked-by: Fam Zheng <famz@redhat.com> | 17 | Message-Id: <20200505064618.16267-1-kwolf@redhat.com> |
13 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 18 | Reviewed-by: Eric Blake <eblake@redhat.com> |
19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
14 | --- | 20 | --- |
15 | block/commit.c | 5 +++-- | 21 | tests/qemu-iotests/055 | 11 +++++++---- |
16 | block/mirror.c | 3 ++- | 22 | 1 file changed, 7 insertions(+), 4 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 | 23 | ||
24 | diff --git a/block/commit.c b/block/commit.c | 24 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 |
25 | index XXXXXXX..XXXXXXX 100644 | 25 | index XXXXXXX..XXXXXXX 100755 |
26 | --- a/block/commit.c | 26 | --- a/tests/qemu-iotests/055 |
27 | +++ b/block/commit.c | 27 | +++ b/tests/qemu-iotests/055 |
28 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_commit_top = { | 28 | @@ -XXX,XX +XXX,XX @@ class TestSingleTransaction(iotests.QMPTestCase): |
29 | void commit_start(const char *job_id, BlockDriverState *bs, | 29 | |
30 | BlockDriverState *base, BlockDriverState *top, int64_t speed, | 30 | class TestCompressedToQcow2(iotests.QMPTestCase): |
31 | BlockdevOnError on_error, const char *backing_file_str, | 31 | image_len = 64 * 1024 * 1024 # MB |
32 | - Error **errp) | 32 | - target_fmt = {'type': 'qcow2', 'args': ()} |
33 | + const char *filter_node_name, Error **errp) | 33 | + target_fmt = {'type': 'qcow2', 'args': (), 'drive-opts': ''} |
34 | { | 34 | |
35 | CommitBlockJob *s; | 35 | def tearDown(self): |
36 | BlockReopenQueue *reopen_queue = NULL; | 36 | self.vm.shutdown() |
37 | @@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs, | 37 | @@ -XXX,XX +XXX,XX @@ class TestCompressedToQcow2(iotests.QMPTestCase): |
38 | 38 | pass | |
39 | /* Insert commit_top block node above top, so we can block consistent read | 39 | |
40 | * on the backing chain below it */ | 40 | def do_prepare_drives(self, attach_target): |
41 | - commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, 0, errp); | 41 | - self.vm = iotests.VM().add_drive('blkdebug::' + test_img) |
42 | + commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, filter_node_name, 0, | 42 | + self.vm = iotests.VM().add_drive('blkdebug::' + test_img, |
43 | + errp); | 43 | + opts=self.target_fmt['drive-opts']) |
44 | if (commit_top_bs == NULL) { | 44 | |
45 | goto fail; | 45 | qemu_img('create', '-f', self.target_fmt['type'], blockdev_target_img, |
46 | } | 46 | str(self.image_len), *self.target_fmt['args']) |
47 | diff --git a/block/mirror.c b/block/mirror.c | 47 | if attach_target: |
48 | index XXXXXXX..XXXXXXX 100644 | 48 | self.vm.add_drive(blockdev_target_img, |
49 | --- a/block/mirror.c | 49 | img_format=self.target_fmt['type'], |
50 | +++ b/block/mirror.c | 50 | - interface="none") |
51 | @@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs, | 51 | + interface="none", |
52 | void commit_active_start(const char *job_id, BlockDriverState *bs, | 52 | + opts=self.target_fmt['drive-opts']) |
53 | BlockDriverState *base, int creation_flags, | 53 | |
54 | int64_t speed, BlockdevOnError on_error, | 54 | self.vm.launch() |
55 | + const char *filter_node_name, | 55 | |
56 | BlockCompletionFunc *cb, void *opaque, Error **errp, | 56 | @@ -XXX,XX +XXX,XX @@ class TestCompressedToQcow2(iotests.QMPTestCase): |
57 | bool auto_complete) | 57 | |
58 | { | 58 | |
59 | @@ -XXX,XX +XXX,XX @@ void commit_active_start(const char *job_id, BlockDriverState *bs, | 59 | class TestCompressedToVmdk(TestCompressedToQcow2): |
60 | MIRROR_LEAVE_BACKING_CHAIN, | 60 | - target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')} |
61 | on_error, on_error, true, cb, opaque, &local_err, | 61 | + target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized'), |
62 | &commit_active_job_driver, false, base, auto_complete, | 62 | + 'drive-opts': 'cache.no-flush=on'} |
63 | - NULL); | 63 | |
64 | + filter_node_name); | 64 | @iotests.skip_if_unsupported(['vmdk']) |
65 | if (local_err) { | 65 | def setUp(self): |
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 | ||
168 | index XXXXXXX..XXXXXXX 100644 | ||
169 | --- a/qapi/block-core.json | ||
170 | +++ b/qapi/block-core.json | ||
171 | @@ -XXX,XX +XXX,XX @@ | ||
172 | # | ||
173 | # @speed: #optional the maximum speed, in bytes per second | ||
174 | # | ||
175 | +# @filter-node-name: #optional the node name that should be assigned to the | ||
176 | +# filter driver that the commit job inserts into the graph | ||
177 | +# above @top. If this option is not given, a node name is | ||
178 | +# autogenerated. (Since: 2.9) | ||
179 | +# | ||
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 @@ | ||
184 | ## | ||
185 | { 'command': 'block-commit', | ||
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 | -- | 66 | -- |
209 | 1.8.3.1 | 67 | 2.25.3 |
210 | 68 | ||
211 | 69 | diff view generated by jsdifflib |
1 | We want every user to be specific about the permissions it needs, so | 1 | From: Max Reitz <mreitz@redhat.com> |
---|---|---|---|
2 | we'll pass the initial permissions as parameters to blk_new(). A user | ||
3 | only needs to call blk_set_perm() if it wants to change the permissions | ||
4 | after the fact. | ||
5 | 2 | ||
6 | The permissions are stored in the BlockBackend and applied whenever a | 3 | Calling bdrv_getlength() to get the pre-truncate file size will not |
7 | BlockDriverState should be attached in blk_insert_bs(). | 4 | really work on block devices, because they have always the same length, |
5 | and trying to write beyond it will fail with a rather cryptic error | ||
6 | message. | ||
8 | 7 | ||
9 | This does not include actually choosing the right set of permissions | 8 | Instead, we should use qcow2_get_last_cluster() and bdrv_getlength() |
10 | everywhere yet. Instead, the usual FIXME comment is added to each place | 9 | only as a fallback. |
11 | and will be addressed in individual patches. | ||
12 | 10 | ||
11 | Before this patch: | ||
12 | $ truncate -s 1G test.img | ||
13 | $ sudo losetup -f --show test.img | ||
14 | /dev/loop0 | ||
15 | $ sudo qemu-img create -f qcow2 -o preallocation=full /dev/loop0 64M | ||
16 | Formatting '/dev/loop0', fmt=qcow2 size=67108864 cluster_size=65536 | ||
17 | preallocation=full lazy_refcounts=off refcount_bits=16 | ||
18 | qemu-img: /dev/loop0: Could not resize image: Failed to resize refcount | ||
19 | structures: No space left on device | ||
20 | |||
21 | With this patch: | ||
22 | $ sudo qemu-img create -f qcow2 -o preallocation=full /dev/loop0 64M | ||
23 | Formatting '/dev/loop0', fmt=qcow2 size=67108864 cluster_size=65536 | ||
24 | preallocation=full lazy_refcounts=off refcount_bits=16 | ||
25 | qemu-img: /dev/loop0: Could not resize image: Failed to resize | ||
26 | underlying file: Preallocation mode 'full' unsupported for this | ||
27 | non-regular file | ||
28 | |||
29 | So as you can see, it still fails, but now the problem is missing | ||
30 | support on the block device level, so we at least get a better error | ||
31 | message. | ||
32 | |||
33 | Note that we cannot preallocate block devices on truncate by design, | ||
34 | because we do not know what area to preallocate. Their length is always | ||
35 | the same, the truncate operation does not change it. | ||
36 | |||
37 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
38 | Message-Id: <20200505141801.1096763-1-mreitz@redhat.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 39 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
15 | Acked-by: Fam Zheng <famz@redhat.com> | ||
16 | --- | 40 | --- |
17 | block.c | 2 +- | 41 | block/qcow2.c | 10 ++++++++-- |
18 | block/backup.c | 3 ++- | 42 | 1 file changed, 8 insertions(+), 2 deletions(-) |
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 | 43 | ||
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 | 44 | diff --git a/block/qcow2.c b/block/qcow2.c |
167 | index XXXXXXX..XXXXXXX 100644 | 45 | index XXXXXXX..XXXXXXX 100644 |
168 | --- a/block/qcow2.c | 46 | --- a/block/qcow2.c |
169 | +++ b/block/qcow2.c | 47 | +++ b/block/qcow2.c |
170 | @@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, | 48 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, |
171 | } | 49 | { |
172 | 50 | int64_t allocation_start, host_offset, guest_offset; | |
173 | if (new_size) { | 51 | int64_t clusters_allocated; |
174 | - BlockBackend *blk = blk_new(); | 52 | - int64_t old_file_size, new_file_size; |
175 | + BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL); | 53 | + int64_t old_file_size, last_cluster, new_file_size; |
176 | blk_insert_bs(blk, bs); | 54 | uint64_t nb_new_data_clusters, nb_new_l2_tables; |
177 | ret = blk_truncate(blk, new_size); | 55 | |
178 | blk_unref(blk); | 56 | /* With a data file, preallocation means just allocating the metadata |
179 | diff --git a/blockdev.c b/blockdev.c | 57 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, |
180 | index XXXXXXX..XXXXXXX 100644 | 58 | ret = old_file_size; |
181 | --- a/blockdev.c | 59 | goto fail; |
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 | } | 60 | } |
207 | } | 61 | - old_file_size = ROUND_UP(old_file_size, s->cluster_size); |
208 | 62 | + | |
209 | - blk = blk_new(); | 63 | + last_cluster = qcow2_get_last_cluster(bs, old_file_size); |
210 | + /* FIXME Use real permissions */ | 64 | + if (last_cluster >= 0) { |
211 | + blk = blk_new(0, BLK_PERM_ALL); | 65 | + old_file_size = (last_cluster + 1) * s->cluster_size; |
212 | blk_insert_bs(blk, bs); | 66 | + } else { |
213 | 67 | + old_file_size = ROUND_UP(old_file_size, s->cluster_size); | |
214 | job = g_malloc0(driver->instance_size); | 68 | + } |
215 | diff --git a/hmp.c b/hmp.c | 69 | |
216 | index XXXXXXX..XXXXXXX 100644 | 70 | nb_new_data_clusters = DIV_ROUND_UP(offset - old_length, |
217 | --- a/hmp.c | 71 | s->cluster_size); |
218 | +++ b/hmp.c | ||
219 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | ||
220 | if (!blk) { | ||
221 | BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err); | ||
222 | if (bs) { | ||
223 | - blk = local_blk = blk_new(); | ||
224 | + /* FIXME Use real permissions */ | ||
225 | + blk = local_blk = blk_new(0, BLK_PERM_ALL); | ||
226 | blk_insert_bs(blk, bs); | ||
227 | } else { | ||
228 | goto fail; | ||
229 | diff --git a/hw/block/fdc.c b/hw/block/fdc.c | ||
230 | index XXXXXXX..XXXXXXX 100644 | ||
231 | --- a/hw/block/fdc.c | ||
232 | +++ b/hw/block/fdc.c | ||
233 | @@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev) | ||
234 | |||
235 | if (!dev->conf.blk) { | ||
236 | /* Anonymous BlockBackend for an empty drive */ | ||
237 | - dev->conf.blk = blk_new(); | ||
238 | + /* FIXME Use real permissions */ | ||
239 | + dev->conf.blk = blk_new(0, BLK_PERM_ALL); | ||
240 | ret = blk_attach_dev(dev->conf.blk, qdev); | ||
241 | assert(ret == 0); | ||
242 | } | ||
243 | diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c | ||
244 | index XXXXXXX..XXXXXXX 100644 | ||
245 | --- a/hw/core/qdev-properties-system.c | ||
246 | +++ b/hw/core/qdev-properties-system.c | ||
247 | @@ -XXX,XX +XXX,XX @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr, | ||
248 | if (!blk) { | ||
249 | BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL); | ||
250 | if (bs) { | ||
251 | - blk = blk_new(); | ||
252 | + /* FIXME Use real permissions */ | ||
253 | + blk = blk_new(0, BLK_PERM_ALL); | ||
254 | blk_insert_bs(blk, bs); | ||
255 | blk_created = true; | ||
256 | } | ||
257 | diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c | ||
258 | index XXXXXXX..XXXXXXX 100644 | ||
259 | --- a/hw/ide/qdev.c | ||
260 | +++ b/hw/ide/qdev.c | ||
261 | @@ -XXX,XX +XXX,XX @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) | ||
262 | return -1; | ||
263 | } else { | ||
264 | /* Anonymous BlockBackend for an empty drive */ | ||
265 | - dev->conf.blk = blk_new(); | ||
266 | + /* FIXME Use real permissions */ | ||
267 | + dev->conf.blk = blk_new(0, BLK_PERM_ALL); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c | ||
272 | index XXXXXXX..XXXXXXX 100644 | ||
273 | --- a/hw/scsi/scsi-disk.c | ||
274 | +++ b/hw/scsi/scsi-disk.c | ||
275 | @@ -XXX,XX +XXX,XX @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) | ||
276 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); | ||
277 | |||
278 | if (!dev->conf.blk) { | ||
279 | - dev->conf.blk = blk_new(); | ||
280 | + /* FIXME Use real permissions */ | ||
281 | + dev->conf.blk = blk_new(0, BLK_PERM_ALL); | ||
282 | } | ||
283 | |||
284 | s->qdev.blocksize = 2048; | ||
285 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h | ||
286 | index XXXXXXX..XXXXXXX 100644 | ||
287 | --- a/include/sysemu/block-backend.h | ||
288 | +++ b/include/sysemu/block-backend.h | ||
289 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockBackendPublic { | ||
290 | QLIST_ENTRY(BlockBackendPublic) round_robin; | ||
291 | } BlockBackendPublic; | ||
292 | |||
293 | -BlockBackend *blk_new(void); | ||
294 | +BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm); | ||
295 | BlockBackend *blk_new_open(const char *filename, const char *reference, | ||
296 | QDict *options, int flags, Error **errp); | ||
297 | int blk_get_refcnt(BlockBackend *blk); | ||
298 | diff --git a/migration/block.c b/migration/block.c | ||
299 | index XXXXXXX..XXXXXXX 100644 | ||
300 | --- a/migration/block.c | ||
301 | +++ b/migration/block.c | ||
302 | @@ -XXX,XX +XXX,XX @@ static void init_blk_migration(QEMUFile *f) | ||
303 | } | ||
304 | |||
305 | bmds = g_new0(BlkMigDevState, 1); | ||
306 | - bmds->blk = blk_new(); | ||
307 | + /* FIXME Use real permissions */ | ||
308 | + bmds->blk = blk_new(0, BLK_PERM_ALL); | ||
309 | bmds->blk_name = g_strdup(bdrv_get_device_name(bs)); | ||
310 | bmds->bulk_completed = 0; | ||
311 | bmds->total_sectors = sectors; | ||
312 | diff --git a/nbd/server.c b/nbd/server.c | ||
313 | index XXXXXXX..XXXXXXX 100644 | ||
314 | --- a/nbd/server.c | ||
315 | +++ b/nbd/server.c | ||
316 | @@ -XXX,XX +XXX,XX @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, | ||
317 | BlockBackend *blk; | ||
318 | NBDExport *exp = g_malloc0(sizeof(NBDExport)); | ||
319 | |||
320 | - blk = blk_new(); | ||
321 | + /* FIXME Use real permissions */ | ||
322 | + blk = blk_new(0, BLK_PERM_ALL); | ||
323 | blk_insert_bs(blk, bs); | ||
324 | blk_set_enable_write_cache(blk, !writethrough); | ||
325 | |||
326 | diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c | ||
327 | index XXXXXXX..XXXXXXX 100644 | ||
328 | --- a/tests/test-blockjob.c | ||
329 | +++ b/tests/test-blockjob.c | ||
330 | @@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, | ||
331 | * BlockDriverState inserted. */ | ||
332 | static BlockBackend *create_blk(const char *name) | ||
333 | { | ||
334 | - BlockBackend *blk = blk_new(); | ||
335 | + /* FIXME Use real permissions */ | ||
336 | + BlockBackend *blk = blk_new(0, BLK_PERM_ALL); | ||
337 | BlockDriverState *bs; | ||
338 | |||
339 | bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort); | ||
340 | diff --git a/tests/test-throttle.c b/tests/test-throttle.c | ||
341 | index XXXXXXX..XXXXXXX 100644 | ||
342 | --- a/tests/test-throttle.c | ||
343 | +++ b/tests/test-throttle.c | ||
344 | @@ -XXX,XX +XXX,XX @@ static void test_groups(void) | ||
345 | BlockBackend *blk1, *blk2, *blk3; | ||
346 | BlockBackendPublic *blkp1, *blkp2, *blkp3; | ||
347 | |||
348 | - blk1 = blk_new(); | ||
349 | - blk2 = blk_new(); | ||
350 | - blk3 = blk_new(); | ||
351 | + /* FIXME Use real permissions */ | ||
352 | + blk1 = blk_new(0, BLK_PERM_ALL); | ||
353 | + blk2 = blk_new(0, BLK_PERM_ALL); | ||
354 | + blk3 = blk_new(0, BLK_PERM_ALL); | ||
355 | |||
356 | blkp1 = blk_get_public(blk1); | ||
357 | blkp2 = blk_get_public(blk2); | ||
358 | -- | 72 | -- |
359 | 1.8.3.1 | 73 | 2.25.3 |
360 | 74 | ||
361 | 75 | diff view generated by jsdifflib |
1 | Backing files are somewhat special compared to other kinds of children | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | because they are attached and detached using bdrv_set_backing_hd() | ||
3 | rather than the normal set of functions, which does a few more things | ||
4 | like setting backing blockers, toggling the BDRV_O_NO_BACKING flag, | ||
5 | setting parent_bs->backing_file, etc. | ||
6 | 2 | ||
7 | These special features are a reason why change_parent_backing_link() | 3 | block.c already defaults to 0 if we don't provide a callback; there's |
8 | can't handle backing files yet. With abstracting the additional features | 4 | no need to write a callback that always fails. |
9 | into .attach/.detach callbacks, we get a step closer to a function that | ||
10 | can actually deal with this. | ||
11 | 5 | ||
6 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
7 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
8 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
9 | Message-Id: <20200428202905.770727-2-eblake@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | 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 | --- | 11 | --- |
16 | block.c | 95 ++++++++++++++++++++++++++++++----------------- | 12 | block/gluster.c | 14 -------------- |
17 | include/block/block_int.h | 3 ++ | 13 | 1 file changed, 14 deletions(-) |
18 | 2 files changed, 63 insertions(+), 35 deletions(-) | ||
19 | 14 | ||
20 | diff --git a/block.c b/block.c | 15 | diff --git a/block/gluster.c b/block/gluster.c |
21 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
22 | --- a/block.c | 17 | --- a/block/gluster.c |
23 | +++ b/block.c | 18 | +++ b/block/gluster.c |
24 | @@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = { | 19 | @@ -XXX,XX +XXX,XX @@ static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs) |
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 | } | 20 | } |
110 | } | 21 | } |
111 | 22 | ||
112 | @@ -XXX,XX +XXX,XX @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd) | 23 | -static int qemu_gluster_has_zero_init(BlockDriverState *bs) |
113 | } | 24 | -{ |
114 | 25 | - /* GlusterFS volume could be backed by a block device */ | |
115 | if (bs->backing) { | 26 | - return 0; |
116 | - assert(bs->backing_blocker); | 27 | -} |
117 | - bdrv_op_unblock_all(bs->backing->bs, bs->backing_blocker); | 28 | - |
118 | bdrv_unref_child(bs, bs->backing); | 29 | /* |
119 | - } else if (backing_hd) { | 30 | * Find allocation range in @bs around offset @start. |
120 | - error_setg(&bs->backing_blocker, | 31 | * May change underlying file descriptor's file offset. |
121 | - "node is used as backing hd of '%s'", | 32 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster = { |
122 | - bdrv_get_device_or_node_name(bs)); | 33 | .bdrv_co_readv = qemu_gluster_co_readv, |
123 | } | 34 | .bdrv_co_writev = qemu_gluster_co_writev, |
124 | 35 | .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, | |
125 | if (!backing_hd) { | 36 | - .bdrv_has_zero_init = qemu_gluster_has_zero_init, |
126 | - error_free(bs->backing_blocker); | 37 | - .bdrv_has_zero_init_truncate = qemu_gluster_has_zero_init, |
127 | - bs->backing_blocker = NULL; | 38 | #ifdef CONFIG_GLUSTERFS_DISCARD |
128 | bs->backing = NULL; | 39 | .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, |
129 | goto out; | 40 | #endif |
130 | } | 41 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_tcp = { |
131 | /* FIXME Error handling */ | 42 | .bdrv_co_readv = qemu_gluster_co_readv, |
132 | bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing, | 43 | .bdrv_co_writev = qemu_gluster_co_writev, |
133 | &error_abort); | 44 | .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, |
134 | - bs->open_flags &= ~BDRV_O_NO_BACKING; | 45 | - .bdrv_has_zero_init = qemu_gluster_has_zero_init, |
135 | - pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename); | 46 | - .bdrv_has_zero_init_truncate = qemu_gluster_has_zero_init, |
136 | - pstrcpy(bs->backing_format, sizeof(bs->backing_format), | 47 | #ifdef CONFIG_GLUSTERFS_DISCARD |
137 | - backing_hd->drv ? backing_hd->drv->format_name : ""); | 48 | .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, |
138 | 49 | #endif | |
139 | - bdrv_op_block_all(backing_hd, bs->backing_blocker); | 50 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_unix = { |
140 | - /* Otherwise we won't be able to commit or stream */ | 51 | .bdrv_co_readv = qemu_gluster_co_readv, |
141 | - bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, | 52 | .bdrv_co_writev = qemu_gluster_co_writev, |
142 | - bs->backing_blocker); | 53 | .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, |
143 | - bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM, | 54 | - .bdrv_has_zero_init = qemu_gluster_has_zero_init, |
144 | - bs->backing_blocker); | 55 | - .bdrv_has_zero_init_truncate = qemu_gluster_has_zero_init, |
145 | - /* | 56 | #ifdef CONFIG_GLUSTERFS_DISCARD |
146 | - * We do backup in 3 ways: | 57 | .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, |
147 | - * 1. drive backup | 58 | #endif |
148 | - * The target bs is new opened, and the source is top BDS | 59 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_rdma = { |
149 | - * 2. blockdev backup | 60 | .bdrv_co_readv = qemu_gluster_co_readv, |
150 | - * Both the source and the target are top BDSes. | 61 | .bdrv_co_writev = qemu_gluster_co_writev, |
151 | - * 3. internal backup(used for block replication) | 62 | .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, |
152 | - * Both the source and the target are backing file | 63 | - .bdrv_has_zero_init = qemu_gluster_has_zero_init, |
153 | - * | 64 | - .bdrv_has_zero_init_truncate = qemu_gluster_has_zero_init, |
154 | - * In case 1 and 2, neither the source nor the target is the backing file. | 65 | #ifdef CONFIG_GLUSTERFS_DISCARD |
155 | - * In case 3, we will block the top BDS, so there is only one block job | 66 | .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, |
156 | - * for the top BDS and its backing chain. | 67 | #endif |
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 | -- | 68 | -- |
180 | 1.8.3.1 | 69 | 2.25.3 |
181 | 70 | ||
182 | 71 | diff view generated by jsdifflib |
1 | The correct permissions are relatively obvious here (and explained in | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
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 | 2 | ||
3 | When using bdrv_file, .bdrv_has_zero_init_truncate always returns 1; | ||
4 | therefore, we can behave just like file-posix, and always implement | ||
5 | BDRV_REQ_ZERO_WRITE by ignoring it since the OS gives it to us for | ||
6 | free (note that file-posix.c had to use an 'if' because it shared code | ||
7 | between regular files and block devices, but in file-win32.c, | ||
8 | bdrv_host_device uses a separate .bdrv_file_open). | ||
9 | |||
10 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
11 | Message-Id: <20200428202905.770727-3-eblake@redhat.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | 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 | --- | 13 | --- |
11 | block/stream.c | 39 +++++++++++++++++++++++++++------------ | 14 | block/file-win32.c | 3 +++ |
12 | 1 file changed, 27 insertions(+), 12 deletions(-) | 15 | 1 file changed, 3 insertions(+) |
13 | 16 | ||
14 | diff --git a/block/stream.c b/block/stream.c | 17 | diff --git a/block/file-win32.c b/block/file-win32.c |
15 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/block/stream.c | 19 | --- a/block/file-win32.c |
17 | +++ b/block/stream.c | 20 | +++ b/block/file-win32.c |
18 | @@ -XXX,XX +XXX,XX @@ static void stream_complete(BlockJob *job, void *opaque) | 21 | @@ -XXX,XX +XXX,XX @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, |
19 | 22 | win32_aio_attach_aio_context(s->aio, bdrv_get_aio_context(bs)); | |
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 | } | 23 | } |
26 | 24 | ||
27 | @@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs, | 25 | + /* When extending regular files, we get zeros from the OS */ |
28 | BlockDriverState *iter; | 26 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; |
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 | + | 27 | + |
62 | + /* Block all intermediate nodes between bs and base, because they will | 28 | ret = 0; |
63 | + * disappear from the chain after this operation. The streaming job reads | 29 | fail: |
64 | + * every block only once, assuming that it doesn't change, so block writes | 30 | qemu_opts_del(opts); |
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 | -- | 31 | -- |
87 | 1.8.3.1 | 32 | 2.25.3 |
88 | 33 | ||
89 | 34 | diff view generated by jsdifflib |
1 | For meaningful error messages in the permission system, we need to get | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | some human-readable description of the parent of a BdrvChild. | ||
3 | 2 | ||
3 | Our .bdrv_has_zero_init_truncate returns 1 if we detect that the OS | ||
4 | always 0-fills; we can use that same knowledge to implement | ||
5 | BDRV_REQ_ZERO_WRITE by ignoring it when the OS gives it to us for | ||
6 | free. | ||
7 | |||
8 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
9 | Message-Id: <20200428202905.770727-4-eblake@redhat.com> | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | 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 | --- | 11 | --- |
8 | block.c | 9 +++++++++ | 12 | block/nfs.c | 3 +++ |
9 | block/block-backend.c | 21 +++++++++++++++++++++ | 13 | 1 file changed, 3 insertions(+) |
10 | include/block/block_int.h | 6 ++++++ | ||
11 | 3 files changed, 36 insertions(+) | ||
12 | 14 | ||
13 | diff --git a/block.c b/block.c | 15 | diff --git a/block/nfs.c b/block/nfs.c |
14 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block.c | 17 | --- a/block/nfs.c |
16 | +++ b/block.c | 18 | +++ b/block/nfs.c |
17 | @@ -XXX,XX +XXX,XX @@ int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough) | 19 | @@ -XXX,XX +XXX,XX @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, |
20 | } | ||
21 | |||
22 | bs->total_sectors = ret; | ||
23 | + if (client->has_zero_init) { | ||
24 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; | ||
25 | + } | ||
18 | return 0; | 26 | return 0; |
19 | } | 27 | } |
20 | 28 | ||
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 | -- | 29 | -- |
118 | 1.8.3.1 | 30 | 2.25.3 |
119 | 31 | ||
120 | 32 | diff view generated by jsdifflib |
1 | This is probably one of the most interesting conversions to the new | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
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 | 2 | ||
6 | In order to provide the 'consistent read' permission to the parents of | 3 | Our .bdrv_has_zero_init_truncate always returns 1 because rbd always |
7 | the 'top' node of the commit job, a new filter block driver is inserted | 4 | 0-fills; we can use that same knowledge to implement |
8 | above 'top' which doesn't require 'consistent read' on its backing | 5 | BDRV_REQ_ZERO_WRITE by ignoring it. |
9 | chain. Subsequently, the commit job can block 'consistent read' on all | ||
10 | intermediate nodes without causing a conflict. | ||
11 | 6 | ||
7 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
8 | Message-Id: <20200428202905.770727-5-eblake@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | 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 | --- | 10 | --- |
16 | block/commit.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++--------- | 11 | block/rbd.c | 3 +++ |
17 | 1 file changed, 95 insertions(+), 18 deletions(-) | 12 | 1 file changed, 3 insertions(+) |
18 | 13 | ||
19 | diff --git a/block/commit.c b/block/commit.c | 14 | diff --git a/block/rbd.c b/block/rbd.c |
20 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/block/commit.c | 16 | --- a/block/rbd.c |
22 | +++ b/block/commit.c | 17 | +++ b/block/rbd.c |
23 | @@ -XXX,XX +XXX,XX @@ typedef struct CommitBlockJob { | 18 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, |
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 | } | 19 | } |
127 | } | 20 | } |
128 | 21 | ||
129 | + /* Insert commit_top block node above top, so we can block consistent read | 22 | + /* When extending regular files, we get zeros from the OS */ |
130 | + * on the backing chain below it */ | 23 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; |
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 | + | 24 | + |
136 | + bdrv_set_backing_hd(commit_top_bs, top); | 25 | r = 0; |
137 | + bdrv_set_backing_hd(overlay_bs, commit_top_bs); | 26 | goto out; |
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 | 27 | ||
212 | -- | 28 | -- |
213 | 1.8.3.1 | 29 | 2.25.3 |
214 | 30 | ||
215 | 31 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Commit 75cdcd1 neglected to update tests/qemu-iotests/049.out, and | 3 | Our .bdrv_has_zero_init_truncate always returns 1 because sheepdog |
4 | made the error message for negative size worse. Fix that. | 4 | always 0-fills; we can use that same knowledge to implement |
5 | BDRV_REQ_ZERO_WRITE by ignoring it. | ||
5 | 6 | ||
6 | Reported-by: Thomas Huth <thuth@redhat.com> | 7 | Signed-off-by: Eric Blake <eblake@redhat.com> |
7 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 8 | Message-Id: <20200428202905.770727-6-eblake@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> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 10 | --- |
13 | tests/qemu-iotests/049.out | 14 +++++++++----- | 11 | block/sheepdog.c | 1 + |
14 | util/qemu-option.c | 2 +- | 12 | 1 file changed, 1 insertion(+) |
15 | 2 files changed, 10 insertions(+), 6 deletions(-) | ||
16 | 13 | ||
17 | diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out | 14 | diff --git a/block/sheepdog.c b/block/sheepdog.c |
18 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/tests/qemu-iotests/049.out | 16 | --- a/block/sheepdog.c |
20 | +++ b/tests/qemu-iotests/049.out | 17 | +++ b/block/sheepdog.c |
21 | @@ -XXX,XX +XXX,XX @@ qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024 | 18 | @@ -XXX,XX +XXX,XX @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, |
22 | qemu-img: Image size must be less than 8 EiB! | 19 | memcpy(&s->inode, buf, sizeof(s->inode)); |
23 | 20 | ||
24 | qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 | 21 | bs->total_sectors = s->inode.vdi_size / BDRV_SECTOR_SIZE; |
25 | -qemu-img: Parameter 'size' expects a non-negative number below 2^64 | 22 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; |
26 | +qemu-img: Value '-1024' is out of range for parameter 'size' | 23 | pstrcpy(s->name, sizeof(s->name), vdi); |
27 | qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' | 24 | qemu_co_mutex_init(&s->lock); |
28 | 25 | qemu_co_mutex_init(&s->queue_lock); | |
29 | qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k | ||
30 | qemu-img: Image size must be less than 8 EiB! | ||
31 | |||
32 | qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 | ||
33 | -qemu-img: Parameter 'size' expects a non-negative number below 2^64 | ||
34 | +qemu-img: Value '-1k' is out of range for parameter 'size' | ||
35 | qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' | ||
36 | |||
37 | qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte | ||
38 | @@ -XXX,XX +XXX,XX @@ qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes | ||
39 | qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. | ||
40 | |||
41 | qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 | ||
42 | -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 | ||
43 | +qemu-img: Parameter 'size' expects a non-negative number below 2^64 | ||
44 | +Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- | ||
45 | +and exabytes, respectively. | ||
46 | +qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' | ||
47 | |||
48 | qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar | ||
49 | qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for | ||
50 | qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. | ||
51 | |||
52 | qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 | ||
53 | -qemu-img: Parameter 'size' expects a size | ||
54 | -You may use k, M, G or T suffixes for kilobytes, megabytes, gigabytes and terabytes. | ||
55 | +qemu-img: Parameter 'size' expects a non-negative number below 2^64 | ||
56 | +Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- | ||
57 | +and exabytes, respectively. | ||
58 | qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' | ||
59 | |||
60 | == Check correct interpretation of suffixes for cluster size == | ||
61 | diff --git a/util/qemu-option.c b/util/qemu-option.c | ||
62 | index XXXXXXX..XXXXXXX 100644 | ||
63 | --- a/util/qemu-option.c | ||
64 | +++ b/util/qemu-option.c | ||
65 | @@ -XXX,XX +XXX,XX @@ void parse_option_size(const char *name, const char *value, | ||
66 | |||
67 | err = qemu_strtosz(value, NULL, &size); | ||
68 | if (err == -ERANGE) { | ||
69 | - error_setg(errp, "Value '%s' is too large for parameter '%s'", | ||
70 | + error_setg(errp, "Value '%s' is out of range for parameter '%s'", | ||
71 | value, name); | ||
72 | return; | ||
73 | } | ||
74 | -- | 26 | -- |
75 | 1.8.3.1 | 27 | 2.25.3 |
76 | 28 | ||
77 | 29 | 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 | 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 | All callers will have to request permissions for all of their child | ||
2 | nodes. Block drivers that act as simply filters can use the default | ||
3 | implementation of .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/blkdebug.c | 2 ++ | ||
10 | block/blkreplay.c | 1 + | ||
11 | block/blkverify.c | 1 + | ||
12 | block/quorum.c | 2 ++ | ||
13 | block/raw-format.c | 1 + | ||
14 | block/replication.c | 1 + | ||
15 | 6 files changed, 8 insertions(+) | ||
16 | |||
17 | diff --git a/block/blkdebug.c b/block/blkdebug.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/block/blkdebug.c | ||
20 | +++ b/block/blkdebug.c | ||
21 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = { | ||
22 | .bdrv_file_open = blkdebug_open, | ||
23 | .bdrv_close = blkdebug_close, | ||
24 | .bdrv_reopen_prepare = blkdebug_reopen_prepare, | ||
25 | + .bdrv_child_perm = bdrv_filter_default_perms, | ||
26 | + | ||
27 | .bdrv_getlength = blkdebug_getlength, | ||
28 | .bdrv_truncate = blkdebug_truncate, | ||
29 | .bdrv_refresh_filename = blkdebug_refresh_filename, | ||
30 | diff --git a/block/blkreplay.c b/block/blkreplay.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 | ||
44 | --- a/block/blkverify.c | ||
45 | +++ b/block/blkverify.c | ||
46 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkverify = { | ||
47 | .bdrv_parse_filename = blkverify_parse_filename, | ||
48 | .bdrv_file_open = blkverify_open, | ||
49 | .bdrv_close = blkverify_close, | ||
50 | + .bdrv_child_perm = bdrv_filter_default_perms, | ||
51 | .bdrv_getlength = blkverify_getlength, | ||
52 | .bdrv_refresh_filename = blkverify_refresh_filename, | ||
53 | |||
54 | diff --git a/block/quorum.c b/block/quorum.c | ||
55 | index XXXXXXX..XXXXXXX 100644 | ||
56 | --- a/block/quorum.c | ||
57 | +++ b/block/quorum.c | ||
58 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = { | ||
59 | .bdrv_add_child = quorum_add_child, | ||
60 | .bdrv_del_child = quorum_del_child, | ||
61 | |||
62 | + .bdrv_child_perm = bdrv_filter_default_perms, | ||
63 | + | ||
64 | .is_filter = true, | ||
65 | .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter, | ||
66 | }; | ||
67 | diff --git a/block/raw-format.c b/block/raw-format.c | ||
68 | index XXXXXXX..XXXXXXX 100644 | ||
69 | --- a/block/raw-format.c | ||
70 | +++ b/block/raw-format.c | ||
71 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = { | ||
72 | .bdrv_reopen_abort = &raw_reopen_abort, | ||
73 | .bdrv_open = &raw_open, | ||
74 | .bdrv_close = &raw_close, | ||
75 | + .bdrv_child_perm = bdrv_filter_default_perms, | ||
76 | .bdrv_create = &raw_create, | ||
77 | .bdrv_co_preadv = &raw_co_preadv, | ||
78 | .bdrv_co_pwritev = &raw_co_pwritev, | ||
79 | diff --git a/block/replication.c b/block/replication.c | ||
80 | index XXXXXXX..XXXXXXX 100644 | ||
81 | --- a/block/replication.c | ||
82 | +++ b/block/replication.c | ||
83 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_replication = { | ||
84 | |||
85 | .bdrv_open = replication_open, | ||
86 | .bdrv_close = replication_close, | ||
87 | + .bdrv_child_perm = bdrv_filter_default_perms, | ||
88 | |||
89 | .bdrv_getlength = replication_getlength, | ||
90 | .bdrv_co_readv = replication_co_readv, | ||
91 | -- | ||
92 | 1.8.3.1 | ||
93 | |||
94 | 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 |
1 | This is a little simpler than the commit block job because it's | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | synchronous and only commits into the immediate backing file, but | ||
3 | otherwise doing more or less the same. | ||
4 | 2 | ||
3 | Our .bdrv_has_zero_init_truncate can detect when the remote side | ||
4 | always zero fills; we can reuse that same knowledge to implement | ||
5 | BDRV_REQ_ZERO_WRITE by ignoring it when the server gives it to us for | ||
6 | free. | ||
7 | |||
8 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
9 | Message-Id: <20200428202905.770727-7-eblake@redhat.com> | ||
10 | Reviewed-by: Richard W.M. Jones <rjones@redhat.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
7 | Acked-by: Fam Zheng <famz@redhat.com> | ||
8 | --- | 12 | --- |
9 | block/commit.c | 33 +++++++++++++++++++++++++++------ | 13 | block/ssh.c | 4 ++++ |
10 | 1 file changed, 27 insertions(+), 6 deletions(-) | 14 | 1 file changed, 4 insertions(+) |
11 | 15 | ||
12 | diff --git a/block/commit.c b/block/commit.c | 16 | diff --git a/block/ssh.c b/block/ssh.c |
13 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/block/commit.c | 18 | --- a/block/ssh.c |
15 | +++ b/block/commit.c | 19 | +++ b/block/ssh.c |
16 | @@ -XXX,XX +XXX,XX @@ fail: | 20 | @@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, |
17 | int bdrv_commit(BlockDriverState *bs) | 21 | /* Go non-blocking. */ |
18 | { | 22 | ssh_set_blocking(s->session, 0); |
19 | BlockBackend *src, *backing; | 23 | |
20 | + BlockDriverState *backing_file_bs = NULL; | 24 | + if (s->attrs->type == SSH_FILEXFER_TYPE_REGULAR) { |
21 | + BlockDriverState *commit_top_bs = NULL; | 25 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; |
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 | + } | 26 | + } |
47 | + | 27 | + |
48 | + /* Insert commit_top block node above backing, so we can write to it */ | 28 | qapi_free_BlockdevOptionsSsh(opts); |
49 | + backing_file_bs = backing_bs(bs); | 29 | |
50 | + | 30 | return 0; |
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 | -- | 31 | -- |
83 | 1.8.3.1 | 32 | 2.25.3 |
84 | 33 | ||
85 | 34 | diff view generated by jsdifflib |
1 | This makes all device emulations with a qdev drive property request | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
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 | 2 | ||
3 | The parallels driver tries to use truncation for image growth, but can | ||
4 | only do so when reads are guaranteed as zero. Now that we have a way | ||
5 | to request zero contents from truncation, we can defer the decision to | ||
6 | actual allocation attempts rather than up front, reducing the number | ||
7 | of places that still use bdrv_has_zero_init_truncate. | ||
8 | |||
9 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
10 | Message-Id: <20200428202905.770727-8-eblake@redhat.com> | ||
11 | Reviewed-by: Denis V. Lunev <den@openvz.org> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | 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 | --- | 13 | --- |
9 | hw/block/block.c | 22 +++++++++++++++++++++- | 14 | block/parallels.c | 25 ++++++++++++++++--------- |
10 | hw/block/fdc.c | 25 +++++++++++++++++++++++-- | 15 | 1 file changed, 16 insertions(+), 9 deletions(-) |
11 | hw/block/m25p80.c | 8 ++++++++ | ||
12 | hw/block/nand.c | 7 +++++++ | ||
13 | hw/block/nvme.c | 8 +++++++- | ||
14 | hw/block/onenand.c | 7 +++++++ | ||
15 | hw/block/pflash_cfi01.c | 18 ++++++++++++------ | ||
16 | hw/block/pflash_cfi02.c | 19 +++++++++++++------ | ||
17 | hw/block/virtio-blk.c | 8 +++++++- | ||
18 | hw/core/qdev-properties-system.c | 1 - | ||
19 | hw/ide/qdev.c | 8 ++++++-- | ||
20 | hw/nvram/spapr_nvram.c | 8 ++++++++ | ||
21 | hw/scsi/scsi-disk.c | 9 +++++++-- | ||
22 | hw/sd/sd.c | 6 ++++++ | ||
23 | hw/usb/dev-storage.c | 6 +++++- | ||
24 | include/hw/block/block.h | 3 ++- | ||
25 | tests/qemu-iotests/051.pc.out | 6 +++--- | ||
26 | 17 files changed, 142 insertions(+), 27 deletions(-) | ||
27 | 16 | ||
28 | diff --git a/hw/block/block.c b/hw/block/block.c | 17 | diff --git a/block/parallels.c b/block/parallels.c |
29 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
30 | --- a/hw/block/block.c | 19 | --- a/block/parallels.c |
31 | +++ b/hw/block/block.c | 20 | +++ b/block/parallels.c |
32 | @@ -XXX,XX +XXX,XX @@ void blkconf_blocksizes(BlockConf *conf) | 21 | @@ -XXX,XX +XXX,XX @@ static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, |
22 | static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, | ||
23 | int nb_sectors, int *pnum) | ||
24 | { | ||
25 | - int ret; | ||
26 | + int ret = 0; | ||
27 | BDRVParallelsState *s = bs->opaque; | ||
28 | int64_t pos, space, idx, to_allocate, i, len; | ||
29 | |||
30 | @@ -XXX,XX +XXX,XX @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, | ||
33 | } | 31 | } |
34 | } | 32 | if (s->data_end + space > (len >> BDRV_SECTOR_BITS)) { |
35 | 33 | space += s->prealloc_size; | |
36 | -void blkconf_apply_backend_options(BlockConf *conf) | 34 | + /* |
37 | +void blkconf_apply_backend_options(BlockConf *conf, bool readonly, | 35 | + * We require the expanded size to read back as zero. If the |
38 | + bool resizable, Error **errp) | 36 | + * user permitted truncation, we try that; but if it fails, we |
39 | { | 37 | + * force the safer-but-slower fallocate. |
40 | BlockBackend *blk = conf->blk; | 38 | + */ |
41 | BlockdevOnError rerror, werror; | 39 | + if (s->prealloc_mode == PRL_PREALLOC_MODE_TRUNCATE) { |
42 | + uint64_t perm, shared_perm; | 40 | + ret = bdrv_truncate(bs->file, |
43 | bool wce; | 41 | + (s->data_end + space) << BDRV_SECTOR_BITS, |
44 | + int ret; | 42 | + false, PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE, |
45 | + | 43 | + NULL); |
46 | + perm = BLK_PERM_CONSISTENT_READ; | 44 | + if (ret == -ENOTSUP) { |
47 | + if (!readonly) { | 45 | + s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; |
48 | + perm |= BLK_PERM_WRITE; | 46 | + } |
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 | + } | 47 | + } |
93 | + } | 48 | if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { |
94 | 49 | ret = bdrv_pwrite_zeroes(bs->file, | |
95 | drive->media_changed = 1; | 50 | s->data_end << BDRV_SECTOR_BITS, |
96 | drive->media_validated = false; | 51 | space << BDRV_SECTOR_BITS, 0); |
97 | @@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev) | 52 | - } else { |
98 | FloppyDrive *dev = FLOPPY_DRIVE(qdev); | 53 | - ret = bdrv_truncate(bs->file, |
99 | FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus); | 54 | - (s->data_end + space) << BDRV_SECTOR_BITS, |
100 | FDrive *drive; | 55 | - false, PREALLOC_MODE_OFF, 0, NULL); |
101 | + Error *local_err = NULL; | 56 | } |
102 | int ret; | 57 | if (ret < 0) { |
103 | 58 | return ret; | |
104 | if (dev->unit == -1) { | 59 | @@ -XXX,XX +XXX,XX @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, |
105 | @@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev) | 60 | qemu_opt_get_size_del(opts, PARALLELS_OPT_PREALLOC_SIZE, 0); |
106 | 61 | s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS); | |
107 | if (!dev->conf.blk) { | 62 | buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE); |
108 | /* Anonymous BlockBackend for an empty drive */ | 63 | + /* prealloc_mode can be downgraded later during allocate_clusters */ |
109 | - /* FIXME Use real permissions */ | 64 | s->prealloc_mode = qapi_enum_parse(&prealloc_mode_lookup, buf, |
110 | dev->conf.blk = blk_new(0, BLK_PERM_ALL); | 65 | PRL_PREALLOC_MODE_FALLOCATE, |
111 | ret = blk_attach_dev(dev->conf.blk, qdev); | 66 | &local_err); |
112 | assert(ret == 0); | 67 | @@ -XXX,XX +XXX,XX @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, |
113 | @@ -XXX,XX +XXX,XX @@ static int floppy_drive_init(DeviceState *qdev) | 68 | goto fail_options; |
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 | } | 69 | } |
131 | 70 | ||
132 | + drive->conf = &dev->conf; | 71 | - if (!bdrv_has_zero_init_truncate(bs->file->bs)) { |
133 | drive->blk = dev->conf.blk; | 72 | - s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; |
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 | - } | 73 | - } |
270 | - | 74 | - |
271 | /* Default to devices being used at their maximum device width. This was | 75 | if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) { |
272 | * assumed before the device_width support was added. | 76 | s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC); |
273 | */ | 77 | ret = parallels_update_header(bs); |
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 | -- | 78 | -- |
501 | 1.8.3.1 | 79 | 2.25.3 |
502 | 80 | ||
503 | 81 | diff view generated by jsdifflib |
1 | The BlockBackend can now store the permissions that its user requires. | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
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 | 2 | ||
3 | The vhdx driver uses truncation for image growth, with a special case | ||
4 | for blocks that already read as zero but which are only being | ||
5 | partially written. But with a bit of rearranging, it's just as easy | ||
6 | to defer the decision on whether truncation resulted in zeroes to the | ||
7 | actual allocation attempt, reducing the number of places that still | ||
8 | use bdrv_has_zero_init_truncate. | ||
9 | |||
10 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
11 | Message-Id: <20200428202905.770727-9-eblake@redhat.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
8 | Acked-by: Fam Zheng <famz@redhat.com> | ||
9 | --- | 13 | --- |
10 | block/block-backend.c | 27 +++++++++++++++++++++++++++ | 14 | block/vhdx.c | 89 ++++++++++++++++++++++++++++++---------------------- |
11 | include/sysemu/block-backend.h | 2 ++ | 15 | 1 file changed, 51 insertions(+), 38 deletions(-) |
12 | 2 files changed, 29 insertions(+) | ||
13 | 16 | ||
14 | diff --git a/block/block-backend.c b/block/block-backend.c | 17 | diff --git a/block/vhdx.c b/block/vhdx.c |
15 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/block/block-backend.c | 19 | --- a/block/vhdx.c |
17 | +++ b/block/block-backend.c | 20 | +++ b/block/vhdx.c |
18 | @@ -XXX,XX +XXX,XX @@ struct BlockBackend { | 21 | @@ -XXX,XX +XXX,XX @@ exit: |
19 | bool iostatus_enabled; | 22 | /* |
20 | BlockDeviceIoStatus iostatus; | 23 | * Allocate a new payload block at the end of the file. |
21 | 24 | * | |
22 | + uint64_t perm; | 25 | - * Allocation will happen at 1MB alignment inside the file |
23 | + uint64_t shared_perm; | 26 | + * Allocation will happen at 1MB alignment inside the file. |
27 | + * | ||
28 | + * If @need_zero is set on entry but not cleared on return, then truncation | ||
29 | + * could not guarantee that the new portion reads as zero, and the caller | ||
30 | + * will take care of it instead. | ||
31 | * | ||
32 | * Returns the file offset start of the new payload block | ||
33 | */ | ||
34 | static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, | ||
35 | - uint64_t *new_offset) | ||
36 | + uint64_t *new_offset, bool *need_zero) | ||
37 | { | ||
38 | int64_t current_len; | ||
39 | |||
40 | @@ -XXX,XX +XXX,XX @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, | ||
41 | return -EINVAL; | ||
42 | } | ||
43 | |||
44 | + if (*need_zero) { | ||
45 | + int ret; | ||
24 | + | 46 | + |
25 | bool allow_write_beyond_eof; | 47 | + ret = bdrv_truncate(bs->file, *new_offset + s->block_size, false, |
26 | 48 | + PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE, NULL); | |
27 | NotifierList remove_bs_notifiers, insert_bs_notifiers; | 49 | + if (ret != -ENOTSUP) { |
28 | @@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(void) | 50 | + *need_zero = false; |
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; | 51 | + return ret; |
53 | + } | 52 | + } |
54 | + } | 53 | + } |
55 | + | 54 | + |
56 | + blk->perm = perm; | 55 | return bdrv_truncate(bs->file, *new_offset + s->block_size, false, |
57 | + blk->shared_perm = shared_perm; | 56 | PREALLOC_MODE_OFF, 0, NULL); |
57 | } | ||
58 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||
59 | /* in this case, we need to preserve zero writes for | ||
60 | * data that is not part of this write, so we must pad | ||
61 | * the rest of the buffer to zeroes */ | ||
62 | - | ||
63 | - /* if we are on a posix system with ftruncate() that extends | ||
64 | - * a file, then it is zero-filled for us. On Win32, the raw | ||
65 | - * layer uses SetFilePointer and SetFileEnd, which does not | ||
66 | - * zero fill AFAIK */ | ||
67 | - | ||
68 | - /* Queue another write of zero buffers if the underlying file | ||
69 | - * does not zero-fill on file extension */ | ||
70 | - | ||
71 | - if (bdrv_has_zero_init_truncate(bs->file->bs) == 0) { | ||
72 | - use_zero_buffers = true; | ||
73 | - | ||
74 | + use_zero_buffers = true; | ||
75 | + /* fall through */ | ||
76 | + case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ | ||
77 | + case PAYLOAD_BLOCK_UNMAPPED: | ||
78 | + case PAYLOAD_BLOCK_UNMAPPED_v095: | ||
79 | + case PAYLOAD_BLOCK_UNDEFINED: | ||
80 | + bat_prior_offset = sinfo.file_offset; | ||
81 | + ret = vhdx_allocate_block(bs, s, &sinfo.file_offset, | ||
82 | + &use_zero_buffers); | ||
83 | + if (ret < 0) { | ||
84 | + goto exit; | ||
85 | + } | ||
86 | + /* | ||
87 | + * once we support differencing files, this may also be | ||
88 | + * partially present | ||
89 | + */ | ||
90 | + /* update block state to the newly specified state */ | ||
91 | + vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry, | ||
92 | + &bat_entry_offset, | ||
93 | + PAYLOAD_BLOCK_FULLY_PRESENT); | ||
94 | + bat_update = true; | ||
95 | + /* | ||
96 | + * Since we just allocated a block, file_offset is the | ||
97 | + * beginning of the payload block. It needs to be the | ||
98 | + * write address, which includes the offset into the | ||
99 | + * block, unless the entire block needs to read as | ||
100 | + * zeroes but truncation was not able to provide them, | ||
101 | + * in which case we need to fill in the rest. | ||
102 | + */ | ||
103 | + if (!use_zero_buffers) { | ||
104 | + sinfo.file_offset += sinfo.block_offset; | ||
105 | + } else { | ||
106 | /* zero fill the front, if any */ | ||
107 | if (sinfo.block_offset) { | ||
108 | iov1.iov_len = sinfo.block_offset; | ||
109 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||
110 | } | ||
111 | |||
112 | /* our actual data */ | ||
113 | - qemu_iovec_concat(&hd_qiov, qiov, bytes_done, | ||
114 | + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, | ||
115 | sinfo.bytes_avail); | ||
116 | |||
117 | /* zero fill the back, if any */ | ||
118 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||
119 | sectors_to_write += iov2.iov_len >> BDRV_SECTOR_BITS; | ||
120 | } | ||
121 | } | ||
122 | - /* fall through */ | ||
123 | - case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ | ||
124 | - case PAYLOAD_BLOCK_UNMAPPED: | ||
125 | - case PAYLOAD_BLOCK_UNMAPPED_v095: | ||
126 | - case PAYLOAD_BLOCK_UNDEFINED: | ||
127 | - bat_prior_offset = sinfo.file_offset; | ||
128 | - ret = vhdx_allocate_block(bs, s, &sinfo.file_offset); | ||
129 | - if (ret < 0) { | ||
130 | - goto exit; | ||
131 | - } | ||
132 | - /* once we support differencing files, this may also be | ||
133 | - * partially present */ | ||
134 | - /* update block state to the newly specified state */ | ||
135 | - vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry, | ||
136 | - &bat_entry_offset, | ||
137 | - PAYLOAD_BLOCK_FULLY_PRESENT); | ||
138 | - bat_update = true; | ||
139 | - /* since we just allocated a block, file_offset is the | ||
140 | - * beginning of the payload block. It needs to be the | ||
141 | - * write address, which includes the offset into the block */ | ||
142 | - if (!use_zero_buffers) { | ||
143 | - sinfo.file_offset += sinfo.block_offset; | ||
144 | - } | ||
58 | + | 145 | + |
59 | + return 0; | 146 | /* fall through */ |
60 | +} | 147 | case PAYLOAD_BLOCK_FULLY_PRESENT: |
61 | + | 148 | /* if the file offset address is in the header zone, |
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 | -- | 149 | -- |
87 | 1.8.3.1 | 150 | 2.25.3 |
88 | 151 | ||
89 | 152 | diff view generated by jsdifflib |
1 | blk_new_open() is a convenience function that processes flags rather | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | than QDict options as a simple way to just open an image file. | 2 | |
3 | 3 | Now that there are no clients of bdrv_has_zero_init_truncate, none of | |
4 | In order to keep it convenient in the future, it must automatically | 4 | the drivers need to worry about providing it. |
5 | request the necessary permissions. This can easily be inferred from the | 5 | |
6 | flags for read and write, but we need another flag that tells us whether | 6 | What's more, this eliminates a source of some confusion: a literal |
7 | to get the resize permission. | 7 | reading of the documentation as written in ceaca56f and implemented in |
8 | 8 | commit 1dcaf527 claims that a driver which returns 0 for | |
9 | We can't just always request it because that means that no block jobs | 9 | bdrv_has_zero_init_truncate() must not return 1 for |
10 | can run on the resulting BlockBackend (which is something that e.g. | 10 | bdrv_has_zero_init(); this condition was violated for parallels, qcow, |
11 | qemu-img commit wants to do), but we also can't request it never because | 11 | and sometimes for vdi, although in practice it did not matter since |
12 | most of the .bdrv_create() implementations call blk_truncate(). | 12 | those drivers also lacked .bdrv_co_truncate. |
13 | 13 | ||
14 | The solution is to introduce another flag that is passed by all users | 14 | Signed-off-by: Eric Blake <eblake@redhat.com> |
15 | that want to resize the image. | 15 | Message-Id: <20200428202905.770727-10-eblake@redhat.com> |
16 | 16 | Acked-by: Richard W.M. Jones <rjones@redhat.com> | |
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 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 | --- | 18 | --- |
21 | block/parallels.c | 3 ++- | 19 | include/block/block.h | 1 - |
22 | block/qcow.c | 3 ++- | 20 | include/block/block_int.h | 7 ------- |
23 | block/qcow2.c | 6 ++++-- | 21 | block.c | 21 --------------------- |
24 | block/qed.c | 3 ++- | 22 | block/file-posix.c | 1 - |
25 | block/sheepdog.c | 2 +- | 23 | block/file-win32.c | 1 - |
26 | block/vdi.c | 3 ++- | 24 | block/nfs.c | 1 - |
27 | block/vhdx.c | 3 ++- | 25 | block/qcow2.c | 1 - |
28 | block/vmdk.c | 6 ++++-- | 26 | block/qed.c | 1 - |
29 | block/vpc.c | 3 ++- | 27 | block/raw-format.c | 6 ------ |
30 | include/block/block.h | 1 + | 28 | block/rbd.c | 1 - |
31 | qemu-img.c | 2 +- | 29 | block/sheepdog.c | 3 --- |
32 | 11 files changed, 23 insertions(+), 12 deletions(-) | 30 | block/ssh.c | 1 - |
33 | 31 | 12 files changed, 45 deletions(-) | |
34 | diff --git a/block/parallels.c b/block/parallels.c | 32 | |
35 | index XXXXXXX..XXXXXXX 100644 | 33 | diff --git a/include/block/block.h b/include/block/block.h |
36 | --- a/block/parallels.c | 34 | index XXXXXXX..XXXXXXX 100644 |
37 | +++ b/block/parallels.c | 35 | --- a/include/block/block.h |
38 | @@ -XXX,XX +XXX,XX @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) | 36 | +++ b/include/block/block.h |
39 | } | 37 | @@ -XXX,XX +XXX,XX @@ int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); |
40 | 38 | int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); | |
41 | file = blk_new_open(filename, NULL, NULL, | 39 | int bdrv_has_zero_init_1(BlockDriverState *bs); |
42 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | 40 | int bdrv_has_zero_init(BlockDriverState *bs); |
43 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | 41 | -int bdrv_has_zero_init_truncate(BlockDriverState *bs); |
44 | + &local_err); | 42 | bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); |
45 | if (file == NULL) { | 43 | bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); |
46 | error_propagate(errp, local_err); | 44 | int bdrv_block_status(BlockDriverState *bs, int64_t offset, |
47 | return -EIO; | 45 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
48 | diff --git a/block/qcow.c b/block/qcow.c | 46 | index XXXXXXX..XXXXXXX 100644 |
49 | index XXXXXXX..XXXXXXX 100644 | 47 | --- a/include/block/block_int.h |
50 | --- a/block/qcow.c | 48 | +++ b/include/block/block_int.h |
51 | +++ b/block/qcow.c | 49 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { |
52 | @@ -XXX,XX +XXX,XX @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) | 50 | /* |
53 | } | 51 | * Returns 1 if newly created images are guaranteed to contain only |
54 | 52 | * zeros, 0 otherwise. | |
55 | qcow_blk = blk_new_open(filename, NULL, NULL, | 53 | - * Must return 0 if .bdrv_has_zero_init_truncate() returns 0. |
56 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | 54 | */ |
57 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | 55 | int (*bdrv_has_zero_init)(BlockDriverState *bs); |
58 | + &local_err); | 56 | |
59 | if (qcow_blk == NULL) { | 57 | - /* |
60 | error_propagate(errp, local_err); | 58 | - * Returns 1 if new areas added by growing the image with |
61 | ret = -EIO; | 59 | - * PREALLOC_MODE_OFF contain only zeros, 0 otherwise. |
60 | - */ | ||
61 | - int (*bdrv_has_zero_init_truncate)(BlockDriverState *bs); | ||
62 | - | ||
63 | /* Remove fd handlers, timers, and other event loop callbacks so the event | ||
64 | * loop is no longer in use. Called with no in-flight requests and in | ||
65 | * depth-first traversal order with parents before child nodes. | ||
66 | diff --git a/block.c b/block.c | ||
67 | index XXXXXXX..XXXXXXX 100644 | ||
68 | --- a/block.c | ||
69 | +++ b/block.c | ||
70 | @@ -XXX,XX +XXX,XX @@ int bdrv_has_zero_init(BlockDriverState *bs) | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | -int bdrv_has_zero_init_truncate(BlockDriverState *bs) | ||
75 | -{ | ||
76 | - if (!bs->drv) { | ||
77 | - return 0; | ||
78 | - } | ||
79 | - | ||
80 | - if (bs->backing) { | ||
81 | - /* Depends on the backing image length, but better safe than sorry */ | ||
82 | - return 0; | ||
83 | - } | ||
84 | - if (bs->drv->bdrv_has_zero_init_truncate) { | ||
85 | - return bs->drv->bdrv_has_zero_init_truncate(bs); | ||
86 | - } | ||
87 | - if (bs->file && bs->drv->is_filter) { | ||
88 | - return bdrv_has_zero_init_truncate(bs->file->bs); | ||
89 | - } | ||
90 | - | ||
91 | - /* safe default */ | ||
92 | - return 0; | ||
93 | -} | ||
94 | - | ||
95 | bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs) | ||
96 | { | ||
97 | BlockDriverInfo bdi; | ||
98 | diff --git a/block/file-posix.c b/block/file-posix.c | ||
99 | index XXXXXXX..XXXXXXX 100644 | ||
100 | --- a/block/file-posix.c | ||
101 | +++ b/block/file-posix.c | ||
102 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = { | ||
103 | .bdrv_co_create = raw_co_create, | ||
104 | .bdrv_co_create_opts = raw_co_create_opts, | ||
105 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
106 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
107 | .bdrv_co_block_status = raw_co_block_status, | ||
108 | .bdrv_co_invalidate_cache = raw_co_invalidate_cache, | ||
109 | .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, | ||
110 | diff --git a/block/file-win32.c b/block/file-win32.c | ||
111 | index XXXXXXX..XXXXXXX 100644 | ||
112 | --- a/block/file-win32.c | ||
113 | +++ b/block/file-win32.c | ||
114 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = { | ||
115 | .bdrv_close = raw_close, | ||
116 | .bdrv_co_create_opts = raw_co_create_opts, | ||
117 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
118 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
119 | |||
120 | .bdrv_aio_preadv = raw_aio_preadv, | ||
121 | .bdrv_aio_pwritev = raw_aio_pwritev, | ||
122 | diff --git a/block/nfs.c b/block/nfs.c | ||
123 | index XXXXXXX..XXXXXXX 100644 | ||
124 | --- a/block/nfs.c | ||
125 | +++ b/block/nfs.c | ||
126 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nfs = { | ||
127 | .create_opts = &nfs_create_opts, | ||
128 | |||
129 | .bdrv_has_zero_init = nfs_has_zero_init, | ||
130 | - .bdrv_has_zero_init_truncate = nfs_has_zero_init, | ||
131 | .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, | ||
132 | .bdrv_co_truncate = nfs_file_co_truncate, | ||
133 | |||
62 | diff --git a/block/qcow2.c b/block/qcow2.c | 134 | diff --git a/block/qcow2.c b/block/qcow2.c |
63 | index XXXXXXX..XXXXXXX 100644 | 135 | index XXXXXXX..XXXXXXX 100644 |
64 | --- a/block/qcow2.c | 136 | --- a/block/qcow2.c |
65 | +++ b/block/qcow2.c | 137 | +++ b/block/qcow2.c |
66 | @@ -XXX,XX +XXX,XX @@ static int qcow2_create2(const char *filename, int64_t total_size, | 138 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = { |
67 | } | 139 | .bdrv_co_create_opts = qcow2_co_create_opts, |
68 | 140 | .bdrv_co_create = qcow2_co_create, | |
69 | blk = blk_new_open(filename, NULL, NULL, | 141 | .bdrv_has_zero_init = qcow2_has_zero_init, |
70 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | 142 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, |
71 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | 143 | .bdrv_co_block_status = qcow2_co_block_status, |
72 | + &local_err); | 144 | |
73 | if (blk == NULL) { | 145 | .bdrv_co_preadv_part = qcow2_co_preadv_part, |
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 | 146 | diff --git a/block/qed.c b/block/qed.c |
87 | index XXXXXXX..XXXXXXX 100644 | 147 | index XXXXXXX..XXXXXXX 100644 |
88 | --- a/block/qed.c | 148 | --- a/block/qed.c |
89 | +++ b/block/qed.c | 149 | +++ b/block/qed.c |
90 | @@ -XXX,XX +XXX,XX @@ static int qed_create(const char *filename, uint32_t cluster_size, | 150 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = { |
91 | } | 151 | .bdrv_co_create = bdrv_qed_co_create, |
92 | 152 | .bdrv_co_create_opts = bdrv_qed_co_create_opts, | |
93 | blk = blk_new_open(filename, NULL, NULL, | 153 | .bdrv_has_zero_init = bdrv_has_zero_init_1, |
94 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | 154 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, |
95 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | 155 | .bdrv_co_block_status = bdrv_qed_co_block_status, |
96 | + &local_err); | 156 | .bdrv_co_readv = bdrv_qed_co_readv, |
97 | if (blk == NULL) { | 157 | .bdrv_co_writev = bdrv_qed_co_writev, |
98 | error_propagate(errp, local_err); | 158 | diff --git a/block/raw-format.c b/block/raw-format.c |
99 | return -EIO; | 159 | index XXXXXXX..XXXXXXX 100644 |
160 | --- a/block/raw-format.c | ||
161 | +++ b/block/raw-format.c | ||
162 | @@ -XXX,XX +XXX,XX @@ static int raw_has_zero_init(BlockDriverState *bs) | ||
163 | return bdrv_has_zero_init(bs->file->bs); | ||
164 | } | ||
165 | |||
166 | -static int raw_has_zero_init_truncate(BlockDriverState *bs) | ||
167 | -{ | ||
168 | - return bdrv_has_zero_init_truncate(bs->file->bs); | ||
169 | -} | ||
170 | - | ||
171 | static int coroutine_fn raw_co_create_opts(BlockDriver *drv, | ||
172 | const char *filename, | ||
173 | QemuOpts *opts, | ||
174 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = { | ||
175 | .bdrv_co_ioctl = &raw_co_ioctl, | ||
176 | .create_opts = &raw_create_opts, | ||
177 | .bdrv_has_zero_init = &raw_has_zero_init, | ||
178 | - .bdrv_has_zero_init_truncate = &raw_has_zero_init_truncate, | ||
179 | .strong_runtime_opts = raw_strong_runtime_opts, | ||
180 | .mutable_opts = mutable_opts, | ||
181 | }; | ||
182 | diff --git a/block/rbd.c b/block/rbd.c | ||
183 | index XXXXXXX..XXXXXXX 100644 | ||
184 | --- a/block/rbd.c | ||
185 | +++ b/block/rbd.c | ||
186 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = { | ||
187 | .bdrv_co_create = qemu_rbd_co_create, | ||
188 | .bdrv_co_create_opts = qemu_rbd_co_create_opts, | ||
189 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
190 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
191 | .bdrv_get_info = qemu_rbd_getinfo, | ||
192 | .create_opts = &qemu_rbd_create_opts, | ||
193 | .bdrv_getlength = qemu_rbd_getlength, | ||
100 | diff --git a/block/sheepdog.c b/block/sheepdog.c | 194 | diff --git a/block/sheepdog.c b/block/sheepdog.c |
101 | index XXXXXXX..XXXXXXX 100644 | 195 | index XXXXXXX..XXXXXXX 100644 |
102 | --- a/block/sheepdog.c | 196 | --- a/block/sheepdog.c |
103 | +++ b/block/sheepdog.c | 197 | +++ b/block/sheepdog.c |
104 | @@ -XXX,XX +XXX,XX @@ static int sd_prealloc(const char *filename, Error **errp) | 198 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog = { |
105 | int ret; | 199 | .bdrv_co_create = sd_co_create, |
106 | 200 | .bdrv_co_create_opts = sd_co_create_opts, | |
107 | blk = blk_new_open(filename, NULL, NULL, | 201 | .bdrv_has_zero_init = bdrv_has_zero_init_1, |
108 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, errp); | 202 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, |
109 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); | 203 | .bdrv_getlength = sd_getlength, |
110 | if (blk == NULL) { | 204 | .bdrv_get_allocated_file_size = sd_get_allocated_file_size, |
111 | ret = -EIO; | 205 | .bdrv_co_truncate = sd_co_truncate, |
112 | goto out_with_err_set; | 206 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog_tcp = { |
113 | diff --git a/block/vdi.c b/block/vdi.c | 207 | .bdrv_co_create = sd_co_create, |
114 | index XXXXXXX..XXXXXXX 100644 | 208 | .bdrv_co_create_opts = sd_co_create_opts, |
115 | --- a/block/vdi.c | 209 | .bdrv_has_zero_init = bdrv_has_zero_init_1, |
116 | +++ b/block/vdi.c | 210 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, |
117 | @@ -XXX,XX +XXX,XX @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) | 211 | .bdrv_getlength = sd_getlength, |
118 | } | 212 | .bdrv_get_allocated_file_size = sd_get_allocated_file_size, |
119 | 213 | .bdrv_co_truncate = sd_co_truncate, | |
120 | blk = blk_new_open(filename, NULL, NULL, | 214 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog_unix = { |
121 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | 215 | .bdrv_co_create = sd_co_create, |
122 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | 216 | .bdrv_co_create_opts = sd_co_create_opts, |
123 | + &local_err); | 217 | .bdrv_has_zero_init = bdrv_has_zero_init_1, |
124 | if (blk == NULL) { | 218 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, |
125 | error_propagate(errp, local_err); | 219 | .bdrv_getlength = sd_getlength, |
126 | ret = -EIO; | 220 | .bdrv_get_allocated_file_size = sd_get_allocated_file_size, |
127 | diff --git a/block/vhdx.c b/block/vhdx.c | 221 | .bdrv_co_truncate = sd_co_truncate, |
128 | index XXXXXXX..XXXXXXX 100644 | 222 | diff --git a/block/ssh.c b/block/ssh.c |
129 | --- a/block/vhdx.c | 223 | index XXXXXXX..XXXXXXX 100644 |
130 | +++ b/block/vhdx.c | 224 | --- a/block/ssh.c |
131 | @@ -XXX,XX +XXX,XX @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) | 225 | +++ b/block/ssh.c |
132 | } | 226 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_ssh = { |
133 | 227 | .bdrv_co_create_opts = ssh_co_create_opts, | |
134 | blk = blk_new_open(filename, NULL, NULL, | 228 | .bdrv_close = ssh_close, |
135 | - BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); | 229 | .bdrv_has_zero_init = ssh_has_zero_init, |
136 | + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, | 230 | - .bdrv_has_zero_init_truncate = ssh_has_zero_init, |
137 | + &local_err); | 231 | .bdrv_co_readv = ssh_co_readv, |
138 | if (blk == NULL) { | 232 | .bdrv_co_writev = ssh_co_writev, |
139 | error_propagate(errp, local_err); | 233 | .bdrv_getlength = ssh_getlength, |
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 | -- | 234 | -- |
205 | 1.8.3.1 | 235 | 2.25.3 |
206 | 236 | ||
207 | 237 | 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 | 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 | 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 | Now that the backing file child role implements .attach/.detach | ||
2 | callbacks, nothing prevents us from modifying the graph even if that | ||
3 | involves changing backing file links. | ||
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 | 7 +++---- | ||
10 | 1 file changed, 3 insertions(+), 4 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 change_parent_backing_link(BlockDriverState *from, | ||
17 | continue; | ||
18 | } | ||
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 | -- | ||
38 | 1.8.3.1 | ||
39 | |||
40 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | In some cases, we want to remove op blockers on intermediate nodes | ||
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 | 1 | ||
6 | The whole block job lifecycle is a bit messed up and it's hard to | ||
7 | actually do all things in the right order, but I'll leave simplifying | ||
8 | this for another day. | ||
9 | |||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | Acked-by: Fam Zheng <famz@redhat.com> | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | --- | ||
14 | blockjob.c | 20 +++++++++++++------- | ||
15 | include/block/blockjob.h | 9 +++++++++ | ||
16 | 2 files changed, 22 insertions(+), 7 deletions(-) | ||
17 | |||
18 | diff --git a/blockjob.c b/blockjob.c | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/blockjob.c | ||
21 | +++ b/blockjob.c | ||
22 | @@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque) | ||
23 | block_job_unref(job); | ||
24 | } | ||
25 | |||
26 | +void block_job_remove_all_bdrv(BlockJob *job) | ||
27 | +{ | ||
28 | + GSList *l; | ||
29 | + for (l = job->nodes; l; l = l->next) { | ||
30 | + BdrvChild *c = l->data; | ||
31 | + bdrv_op_unblock_all(c->bs, job->blocker); | ||
32 | + bdrv_root_unref_child(c); | ||
33 | + } | ||
34 | + g_slist_free(job->nodes); | ||
35 | + job->nodes = NULL; | ||
36 | +} | ||
37 | + | ||
38 | int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, | ||
39 | uint64_t perm, uint64_t shared_perm, Error **errp) | ||
40 | { | ||
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 | -- | ||
79 | 1.8.3.1 | ||
80 | |||
81 | 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 |