1 | The following changes since commit 91fe7a376ad46e3cc5e82d418aad22173c948a3c: | 1 | The following changes since commit 005ad32358f12fe9313a4a01918a55e60d4f39e5: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging (2018-06-15 11:41:44 +0100) | 3 | Merge tag 'pull-tpm-2023-09-12-3' of https://github.com/stefanberger/qemu-tpm into staging (2023-09-13 13:41:57 -0400) |
4 | 4 | ||
5 | are available in the git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | https://repo.or.cz/qemu/kevin.git tags/for-upstream |
8 | 8 | ||
9 | for you to fetch changes up to 6266e900b8083945cb766b45c124fb3c42932cb3: | 9 | for you to fetch changes up to 5d96864b73225ee61b0dad7e928f0cddf14270fc: |
10 | 10 | ||
11 | block: Remove dead deprecation warning code (2018-06-15 14:49:44 +0200) | 11 | block-coroutine-wrapper: use qemu_get_current_aio_context() (2023-09-15 15:49:14 +0200) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches: | 14 | Block layer patches |
15 | 15 | ||
16 | - Fix options that work only with -drive or -blockdev, but not with | 16 | - Graph locking part 4 (node management) |
17 | both, because of QDict type confusion | 17 | - qemu-img map: report compressed data blocks |
18 | - rbd: Add options 'auth-client-required' and 'key-secret' | 18 | - block-backend: process I/O in the current AioContext |
19 | - Remove deprecated -drive options serial/addr/cyls/heads/secs/trans | ||
20 | - rbd, iscsi: Remove deprecated 'filename' option | ||
21 | - Fix 'qemu-img map' crash with unaligned image size | ||
22 | - Improve QMP documentation for jobs | ||
23 | 19 | ||
24 | ---------------------------------------------------------------- | 20 | ---------------------------------------------------------------- |
25 | Eric Blake (2): | 21 | Andrey Drobyshev via (2): |
26 | qemu-img: Fix assert when mapping unaligned raw file | 22 | block: add BDRV_BLOCK_COMPRESSED flag for bdrv_block_status() |
27 | iotests: Add test 221 to catch qemu-img map regression | 23 | qemu-img: map: report compressed data blocks |
28 | 24 | ||
29 | John Snow (2): | 25 | Kevin Wolf (21): |
30 | jobs: fix stale wording | 26 | block: Remove unused BlockReopenQueueEntry.perms_checked |
31 | jobs: fix verb references in docs | 27 | preallocate: Factor out preallocate_truncate_to_real_size() |
28 | preallocate: Don't poll during permission updates | ||
29 | block: Take AioContext lock for bdrv_append() more consistently | ||
30 | block: Introduce bdrv_schedule_unref() | ||
31 | block-coroutine-wrapper: Add no_co_wrapper_bdrv_wrlock functions | ||
32 | block-coroutine-wrapper: Allow arbitrary parameter names | ||
33 | block: Mark bdrv_replace_child_noperm() GRAPH_WRLOCK | ||
34 | block: Mark bdrv_replace_child_tran() GRAPH_WRLOCK | ||
35 | block: Mark bdrv_attach_child_common() GRAPH_WRLOCK | ||
36 | block: Call transaction callbacks with lock held | ||
37 | block: Mark bdrv_attach_child() GRAPH_WRLOCK | ||
38 | block: Mark bdrv_parent_perms_conflict() and callers GRAPH_RDLOCK | ||
39 | block: Mark bdrv_get_cumulative_perm() and callers GRAPH_RDLOCK | ||
40 | block: Mark bdrv_child_perm() GRAPH_RDLOCK | ||
41 | block: Mark bdrv_parent_cb_change_media() GRAPH_RDLOCK | ||
42 | block: Take graph rdlock in bdrv_drop_intermediate() | ||
43 | block: Take graph rdlock in bdrv_change_aio_context() | ||
44 | block: Mark bdrv_root_unref_child() GRAPH_WRLOCK | ||
45 | block: Mark bdrv_unref_child() GRAPH_WRLOCK | ||
46 | block: Mark bdrv_add/del_child() and caller GRAPH_WRLOCK | ||
32 | 47 | ||
33 | Kevin Wolf (4): | 48 | Stefan Hajnoczi (5): |
34 | block: Remove deprecated -drive geometry options | 49 | block: remove AIOCBInfo->get_aio_context() |
35 | block: Remove deprecated -drive option addr | 50 | test-bdrv-drain: avoid race with BH in IOThread drain test |
36 | block: Remove deprecated -drive option serial | 51 | block-backend: process I/O in the current AioContext |
37 | block: Remove dead deprecation warning code | 52 | block-backend: process zoned requests in the current AioContext |
53 | block-coroutine-wrapper: use qemu_get_current_aio_context() | ||
38 | 54 | ||
39 | Markus Armbruster (17): | 55 | qapi/block-core.json | 6 +- |
40 | rbd: Drop deprecated -drive parameter "filename" | 56 | include/block/aio.h | 1 - |
41 | iscsi: Drop deprecated -drive parameter "filename" | 57 | include/block/block-common.h | 7 + |
42 | qobject: Move block-specific qdict code to block-qdict.c | 58 | include/block/block-global-state.h | 32 +- |
43 | block: Fix -blockdev for certain non-string scalars | 59 | include/block/block-io.h | 1 - |
44 | block: Fix -drive for certain non-string scalars | 60 | include/block/block_int-common.h | 34 +- |
45 | block: Clean up a misuse of qobject_to() in .bdrv_co_create_opts() | 61 | include/block/block_int-global-state.h | 14 +- |
46 | block: Factor out qobject_input_visitor_new_flat_confused() | 62 | include/sysemu/block-backend-global-state.h | 4 +- |
47 | block: Make remaining uses of qobject input visitor more robust | 63 | block.c | 348 +++++++--- |
48 | block-qdict: Simplify qdict_flatten_qdict() | 64 | block/blklogwrites.c | 4 + |
49 | block-qdict: Tweak qdict_flatten_qdict(), qdict_flatten_qlist() | 65 | block/blkverify.c | 2 + |
50 | block-qdict: Clean up qdict_crumple() a bit | 66 | block/block-backend.c | 64 +- |
51 | block-qdict: Simplify qdict_is_list() some | 67 | block/copy-before-write.c | 10 +- |
52 | check-block-qdict: Rename qdict_flatten()'s variables for clarity | 68 | block/crypto.c | 6 +- |
53 | check-block-qdict: Cover flattening of empty lists and dictionaries | 69 | block/graph-lock.c | 26 +- |
54 | block: Fix -blockdev / blockdev-add for empty objects and arrays | 70 | block/io.c | 23 +- |
55 | rbd: New parameter auth-client-required | 71 | block/mirror.c | 8 + |
56 | rbd: New parameter key-secret | 72 | block/preallocate.c | 133 ++-- |
57 | 73 | block/qcow.c | 5 +- | |
58 | Max Reitz (1): | 74 | block/qcow2.c | 7 +- |
59 | block: Add block-specific QDict header | 75 | block/quorum.c | 23 +- |
60 | 76 | block/replication.c | 9 + | |
61 | qapi/block-core.json | 19 ++ | 77 | block/snapshot.c | 2 + |
62 | qapi/job.json | 23 +- | 78 | block/stream.c | 20 +- |
63 | include/block/qdict.h | 34 +++ | 79 | block/vmdk.c | 15 + |
64 | include/hw/block/block.h | 1 - | 80 | blockdev.c | 23 +- |
65 | include/qapi/qmp/qdict.h | 17 -- | 81 | blockjob.c | 2 + |
66 | include/sysemu/blockdev.h | 3 - | 82 | hw/nvme/ctrl.c | 7 - |
67 | block.c | 1 + | 83 | qemu-img.c | 8 +- |
68 | block/block-backend.c | 1 - | 84 | softmmu/dma-helpers.c | 8 - |
69 | block/crypto.c | 12 +- | 85 | tests/unit/test-bdrv-drain.c | 31 +- |
70 | block/gluster.c | 1 + | 86 | tests/unit/test-bdrv-graph-mod.c | 20 + |
71 | block/iscsi.c | 24 +- | 87 | tests/unit/test-block-iothread.c | 3 + |
72 | block/nbd.c | 16 +- | 88 | util/thread-pool.c | 8 - |
73 | block/nfs.c | 8 +- | 89 | scripts/block-coroutine-wrapper.py | 24 +- |
74 | block/parallels.c | 11 +- | 90 | tests/qemu-iotests/051.pc.out | 6 +- |
75 | block/qcow.c | 11 +- | 91 | tests/qemu-iotests/122.out | 84 +-- |
76 | block/qcow2.c | 11 +- | 92 | tests/qemu-iotests/146.out | 780 +++++++++++------------ |
77 | block/qed.c | 11 +- | 93 | tests/qemu-iotests/154.out | 194 +++--- |
78 | block/quorum.c | 1 + | 94 | tests/qemu-iotests/179.out | 178 +++--- |
79 | block/rbd.c | 85 +++--- | 95 | tests/qemu-iotests/209.out | 4 +- |
80 | block/sheepdog.c | 23 +- | 96 | tests/qemu-iotests/221.out | 16 +- |
81 | block/snapshot.c | 1 + | 97 | tests/qemu-iotests/223.out | 60 +- |
82 | block/ssh.c | 16 +- | 98 | tests/qemu-iotests/241.out | 10 +- |
83 | block/vdi.c | 8 +- | 99 | tests/qemu-iotests/244.out | 24 +- |
84 | block/vhdx.c | 11 +- | 100 | tests/qemu-iotests/252.out | 10 +- |
85 | block/vpc.c | 11 +- | 101 | tests/qemu-iotests/253.out | 20 +- |
86 | block/vvfat.c | 1 + | 102 | tests/qemu-iotests/274.out | 48 +- |
87 | block/vxhs.c | 1 + | 103 | tests/qemu-iotests/tests/nbd-qemu-allocation.out | 16 +- |
88 | blockdev.c | 111 +------ | 104 | tests/qemu-iotests/tests/qemu-img-bitmaps.out | 24 +- |
89 | device-hotplug.c | 4 - | 105 | 50 files changed, 1376 insertions(+), 1036 deletions(-) |
90 | hw/block/block.c | 27 -- | ||
91 | hw/block/nvme.c | 1 - | ||
92 | hw/block/virtio-blk.c | 1 - | ||
93 | hw/ide/qdev.c | 1 - | ||
94 | hw/scsi/scsi-disk.c | 1 - | ||
95 | hw/usb/dev-storage.c | 1 - | ||
96 | qemu-img.c | 2 +- | ||
97 | qobject/block-qdict.c | 722 +++++++++++++++++++++++++++++++++++++++++++++ | ||
98 | qobject/qdict.c | 628 --------------------------------------- | ||
99 | tests/ahci-test.c | 6 +- | ||
100 | tests/check-block-qdict.c | 690 +++++++++++++++++++++++++++++++++++++++++++ | ||
101 | tests/check-qdict.c | 641 ---------------------------------------- | ||
102 | tests/check-qobject.c | 1 + | ||
103 | tests/hd-geo-test.c | 37 +-- | ||
104 | tests/ide-test.c | 8 +- | ||
105 | tests/test-replication.c | 1 + | ||
106 | util/qemu-config.c | 1 + | ||
107 | MAINTAINERS | 2 + | ||
108 | hmp-commands.hx | 1 - | ||
109 | qemu-doc.texi | 15 - | ||
110 | qemu-options.hx | 14 +- | ||
111 | qobject/Makefile.objs | 1 + | ||
112 | tests/Makefile.include | 4 + | ||
113 | tests/qemu-iotests/221 | 60 ++++ | ||
114 | tests/qemu-iotests/221.out | 16 + | ||
115 | tests/qemu-iotests/group | 1 + | ||
116 | 55 files changed, 1692 insertions(+), 1668 deletions(-) | ||
117 | create mode 100644 include/block/qdict.h | ||
118 | create mode 100644 qobject/block-qdict.c | ||
119 | create mode 100644 tests/check-block-qdict.c | ||
120 | create mode 100755 tests/qemu-iotests/221 | ||
121 | create mode 100644 tests/qemu-iotests/221.out | ||
122 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | This field has been unused since commit 72373e40fbc ('block: | ||
2 | bdrv_reopen_multiple: refresh permissions on updated graph'). | ||
3 | Remove it. | ||
1 | 4 | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
6 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
7 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> | ||
8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
9 | Message-ID: <20230911094620.45040-2-kwolf@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | block.c | 1 - | ||
13 | 1 file changed, 1 deletion(-) | ||
14 | |||
15 | diff --git a/block.c b/block.c | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/block.c | ||
18 | +++ b/block.c | ||
19 | @@ -XXX,XX +XXX,XX @@ static int bdrv_fill_options(QDict **options, const char *filename, | ||
20 | |||
21 | typedef struct BlockReopenQueueEntry { | ||
22 | bool prepared; | ||
23 | - bool perms_checked; | ||
24 | BDRVReopenState state; | ||
25 | QTAILQ_ENTRY(BlockReopenQueueEntry) entry; | ||
26 | } BlockReopenQueueEntry; | ||
27 | -- | ||
28 | 2.41.0 | diff view generated by jsdifflib |
1 | We removed all options from the 'deprecated' array, so the code is dead | 1 | It's essentially the same code in preallocate_check_perm() and |
---|---|---|---|
2 | and can be removed as well. | 2 | preallocate_close(), except that the latter ignores errors. |
3 | 3 | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
5 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | 5 | Reviewed-by: Eric Blake <eblake@redhat.com> |
6 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> | ||
7 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
8 | Message-ID: <20230911094620.45040-3-kwolf@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
6 | --- | 10 | --- |
7 | blockdev.c | 12 ------------ | 11 | block/preallocate.c | 48 +++++++++++++++++++++------------------------ |
8 | 1 file changed, 12 deletions(-) | 12 | 1 file changed, 22 insertions(+), 26 deletions(-) |
9 | 13 | ||
10 | diff --git a/blockdev.c b/blockdev.c | 14 | diff --git a/block/preallocate.c b/block/preallocate.c |
11 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/blockdev.c | 16 | --- a/block/preallocate.c |
13 | +++ b/blockdev.c | 17 | +++ b/block/preallocate.c |
14 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | 18 | @@ -XXX,XX +XXX,XX @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, |
15 | const char *filename; | 19 | return 0; |
16 | Error *local_err = NULL; | 20 | } |
17 | int i; | 21 | |
18 | - const char *deprecated[] = { | 22 | -static void preallocate_close(BlockDriverState *bs) |
19 | - }; | 23 | +static int preallocate_truncate_to_real_size(BlockDriverState *bs, Error **errp) |
20 | 24 | { | |
21 | /* Change legacy command line options into QMP ones */ | 25 | - int ret; |
22 | static const struct { | 26 | BDRVPreallocateState *s = bs->opaque; |
23 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | 27 | - |
24 | goto fail; | 28 | - if (s->data_end < 0) { |
29 | - return; | ||
30 | - } | ||
31 | + int ret; | ||
32 | |||
33 | if (s->file_end < 0) { | ||
34 | s->file_end = bdrv_getlength(bs->file->bs); | ||
35 | if (s->file_end < 0) { | ||
36 | - return; | ||
37 | + error_setg_errno(errp, -s->file_end, "Failed to get file length"); | ||
38 | + return s->file_end; | ||
39 | } | ||
25 | } | 40 | } |
26 | 41 | ||
27 | - /* Other deprecated options */ | 42 | if (s->data_end < s->file_end) { |
28 | - if (!qtest_enabled()) { | 43 | ret = bdrv_truncate(bs->file, s->data_end, true, PREALLOC_MODE_OFF, 0, |
29 | - for (i = 0; i < ARRAY_SIZE(deprecated); i++) { | 44 | NULL); |
30 | - if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) { | 45 | - s->file_end = ret < 0 ? ret : s->data_end; |
31 | - error_report("'%s' is deprecated, please use the corresponding " | 46 | + if (ret < 0) { |
32 | - "option of '-device' instead", deprecated[i]); | 47 | + error_setg_errno(errp, -ret, "Failed to drop preallocation"); |
48 | + s->file_end = ret; | ||
49 | + return ret; | ||
50 | + } | ||
51 | + s->file_end = s->data_end; | ||
52 | + } | ||
53 | + | ||
54 | + return 0; | ||
55 | +} | ||
56 | + | ||
57 | +static void preallocate_close(BlockDriverState *bs) | ||
58 | +{ | ||
59 | + BDRVPreallocateState *s = bs->opaque; | ||
60 | + | ||
61 | + if (s->data_end >= 0) { | ||
62 | + preallocate_truncate_to_real_size(bs, NULL); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | @@ -XXX,XX +XXX,XX @@ static int preallocate_check_perm(BlockDriverState *bs, | ||
67 | * We should truncate in check_perm, as in set_perm bs->file->perm will | ||
68 | * be already changed, and we should not violate it. | ||
69 | */ | ||
70 | - if (s->file_end < 0) { | ||
71 | - s->file_end = bdrv_getlength(bs->file->bs); | ||
72 | - if (s->file_end < 0) { | ||
73 | - error_setg(errp, "Failed to get file length"); | ||
74 | - return s->file_end; | ||
33 | - } | 75 | - } |
34 | - } | 76 | - } |
35 | - } | ||
36 | - | 77 | - |
37 | /* Media type */ | 78 | - if (s->data_end < s->file_end) { |
38 | value = qemu_opt_get(legacy_opts, "media"); | 79 | - int ret = bdrv_truncate(bs->file, s->data_end, true, |
39 | if (value) { | 80 | - PREALLOC_MODE_OFF, 0, NULL); |
81 | - if (ret < 0) { | ||
82 | - error_setg(errp, "Failed to drop preallocation"); | ||
83 | - s->file_end = ret; | ||
84 | - return ret; | ||
85 | - } | ||
86 | - s->file_end = s->data_end; | ||
87 | - } | ||
88 | + return preallocate_truncate_to_real_size(bs, errp); | ||
89 | } | ||
90 | |||
91 | return 0; | ||
40 | -- | 92 | -- |
41 | 2.13.6 | 93 | 2.41.0 |
42 | |||
43 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | When the permission related BlockDriver callbacks are called, we are in |
---|---|---|---|
2 | 2 | the middle of an operation traversing the block graph. Polling in such a | |
3 | -blockdev and blockdev-add silently ignore empty objects and arrays in | 3 | place is a very bad idea because the graph could change in unexpected |
4 | their argument. That's because qmp_blockdev_add() converts the | 4 | ways. In the future, callers will also hold the graph lock, which is |
5 | argument to a flat QDict, and qdict_flatten() eats empty QDict and | 5 | likely to turn polling into a deadlock. |
6 | QList members. For instance, we ignore an empty BlockdevOptions | 6 | |
7 | member @cache. No real harm, as absent means the same as empty there. | 7 | So we need to get rid of calls to functions like bdrv_getlength() or |
8 | 8 | bdrv_truncate() there as these functions poll internally. They are | |
9 | Thus, the flaw puts an artificial restriction on the QAPI schema: we | 9 | currently used so that when no parent has write/resize permissions on |
10 | can't have potentially empty objects and arrays within | 10 | the image any more, the preallocate filter drops the extra preallocated |
11 | BlockdevOptions, except when they're optional and "empty" has the same | 11 | area in the image file and gives up write/resize permissions itself. |
12 | meaning as "absent". | 12 | |
13 | 13 | In order to achieve this without polling in .bdrv_check_perm, don't | |
14 | Our QAPI schema satisfies this restriction (I checked), but it's a | 14 | immediately truncate the image, but only schedule a BH to do so. The |
15 | trap for the unwary, and a temptation to employ awkward workarounds | 15 | filter keeps the write/resize permissions a bit longer now until the BH |
16 | for the wary. Let's get rid of it. | 16 | has executed. |
17 | 17 | ||
18 | Change qdict_flatten() and qdict_crumple() to treat empty dictionaries | 18 | There is one case in which delaying doesn't work: Reopening the image |
19 | and lists exactly like scalars. | 19 | read-only. In this case, bs->file will likely be reopened read-only, |
20 | 20 | too, so keeping write permissions a bit longer on it doesn't work. But | |
21 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 21 | we can already cover this case in preallocate_reopen_prepare() and not |
22 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 22 | rely on the permission updates for it. |
23 | |||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
25 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
26 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
27 | Message-ID: <20230911094620.45040-4-kwolf@redhat.com> | ||
23 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 28 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
24 | --- | 29 | --- |
25 | qobject/block-qdict.c | 54 +++++++++++++++++++++++++++++------------------ | 30 | block/preallocate.c | 89 +++++++++++++++++++++++++++++++++++---------- |
26 | tests/check-block-qdict.c | 38 ++++++++++++++++++++++++++------- | 31 | 1 file changed, 69 insertions(+), 20 deletions(-) |
27 | 2 files changed, 63 insertions(+), 29 deletions(-) | 32 | |
28 | 33 | diff --git a/block/preallocate.c b/block/preallocate.c | |
29 | diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c | ||
30 | index XXXXXXX..XXXXXXX 100644 | 34 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/qobject/block-qdict.c | 35 | --- a/block/preallocate.c |
32 | +++ b/qobject/block-qdict.c | 36 | +++ b/block/preallocate.c |
33 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) | 37 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVPreallocateState { |
34 | { | 38 | * be invalid (< 0) when we don't have both exclusive BLK_PERM_RESIZE and |
35 | QObject *value; | 39 | * BLK_PERM_WRITE permissions on file child. |
36 | const QListEntry *entry; | 40 | */ |
37 | + QDict *dict_val; | 41 | + |
38 | + QList *list_val; | 42 | + /* Gives up the resize permission on children when parents don't need it */ |
39 | char *new_key; | 43 | + QEMUBH *drop_resize_bh; |
40 | int i; | 44 | } BDRVPreallocateState; |
41 | 45 | ||
42 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) | 46 | +static int preallocate_drop_resize(BlockDriverState *bs, Error **errp); |
43 | 47 | +static void preallocate_drop_resize_bh(void *opaque); | |
44 | for (i = 0; entry; entry = qlist_next(entry), i++) { | 48 | + |
45 | value = qlist_entry_obj(entry); | 49 | #define PREALLOCATE_OPT_PREALLOC_ALIGN "prealloc-align" |
46 | + dict_val = qobject_to(QDict, value); | 50 | #define PREALLOCATE_OPT_PREALLOC_SIZE "prealloc-size" |
47 | + list_val = qobject_to(QList, value); | 51 | static QemuOptsList runtime_opts = { |
48 | new_key = g_strdup_printf("%s.%i", prefix, i); | 52 | @@ -XXX,XX +XXX,XX @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, |
53 | * For this to work, mark them invalid. | ||
54 | */ | ||
55 | s->file_end = s->zero_start = s->data_end = -EINVAL; | ||
56 | + s->drop_resize_bh = qemu_bh_new(preallocate_drop_resize_bh, bs); | ||
57 | |||
58 | ret = bdrv_open_file_child(NULL, options, "file", bs, errp); | ||
59 | if (ret < 0) { | ||
60 | @@ -XXX,XX +XXX,XX @@ static void preallocate_close(BlockDriverState *bs) | ||
61 | { | ||
62 | BDRVPreallocateState *s = bs->opaque; | ||
63 | |||
64 | + qemu_bh_cancel(s->drop_resize_bh); | ||
65 | + qemu_bh_delete(s->drop_resize_bh); | ||
66 | + | ||
67 | if (s->data_end >= 0) { | ||
68 | preallocate_truncate_to_real_size(bs, NULL); | ||
69 | } | ||
70 | @@ -XXX,XX +XXX,XX @@ static int preallocate_reopen_prepare(BDRVReopenState *reopen_state, | ||
71 | BlockReopenQueue *queue, Error **errp) | ||
72 | { | ||
73 | PreallocateOpts *opts = g_new0(PreallocateOpts, 1); | ||
74 | + int ret; | ||
75 | |||
76 | if (!preallocate_absorb_opts(opts, reopen_state->options, | ||
77 | reopen_state->bs->file->bs, errp)) { | ||
78 | @@ -XXX,XX +XXX,XX @@ static int preallocate_reopen_prepare(BDRVReopenState *reopen_state, | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | + /* | ||
83 | + * Drop the preallocation already here if reopening read-only. The child | ||
84 | + * might also be reopened read-only and then scheduling a BH during the | ||
85 | + * permission update is too late. | ||
86 | + */ | ||
87 | + if ((reopen_state->flags & BDRV_O_RDWR) == 0) { | ||
88 | + ret = preallocate_drop_resize(reopen_state->bs, errp); | ||
89 | + if (ret < 0) { | ||
90 | + g_free(opts); | ||
91 | + return ret; | ||
92 | + } | ||
93 | + } | ||
94 | + | ||
95 | reopen_state->opaque = opts; | ||
96 | |||
97 | return 0; | ||
98 | @@ -XXX,XX +XXX,XX @@ preallocate_co_getlength(BlockDriverState *bs) | ||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | -static int preallocate_check_perm(BlockDriverState *bs, | ||
103 | - uint64_t perm, uint64_t shared, Error **errp) | ||
104 | +static int preallocate_drop_resize(BlockDriverState *bs, Error **errp) | ||
105 | { | ||
106 | BDRVPreallocateState *s = bs->opaque; | ||
107 | + int ret; | ||
108 | |||
109 | - if (s->data_end >= 0 && !can_write_resize(perm)) { | ||
110 | - /* | ||
111 | - * Lose permissions. | ||
112 | - * We should truncate in check_perm, as in set_perm bs->file->perm will | ||
113 | - * be already changed, and we should not violate it. | ||
114 | - */ | ||
115 | - return preallocate_truncate_to_real_size(bs, errp); | ||
116 | + if (s->data_end < 0) { | ||
117 | + return 0; | ||
118 | + } | ||
119 | + | ||
120 | + /* | ||
121 | + * Before switching children to be read-only, truncate them to remove | ||
122 | + * the preallocation and let them have the real size. | ||
123 | + */ | ||
124 | + ret = preallocate_truncate_to_real_size(bs, errp); | ||
125 | + if (ret < 0) { | ||
126 | + return ret; | ||
127 | } | ||
128 | |||
129 | + /* | ||
130 | + * We'll drop our permissions and will allow other users to take write and | ||
131 | + * resize permissions (see preallocate_child_perm). Anyone will be able to | ||
132 | + * change the child, so mark all states invalid. We'll regain control if a | ||
133 | + * parent requests write access again. | ||
134 | + */ | ||
135 | + s->data_end = s->file_end = s->zero_start = -EINVAL; | ||
136 | + | ||
137 | + bdrv_graph_rdlock_main_loop(); | ||
138 | + bdrv_child_refresh_perms(bs, bs->file, NULL); | ||
139 | + bdrv_graph_rdunlock_main_loop(); | ||
140 | + | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | +static void preallocate_drop_resize_bh(void *opaque) | ||
145 | +{ | ||
146 | + /* | ||
147 | + * In case of errors, we'll simply keep the exclusive lock on the image | ||
148 | + * indefinitely. | ||
149 | + */ | ||
150 | + preallocate_drop_resize(opaque, NULL); | ||
151 | +} | ||
152 | + | ||
153 | static void preallocate_set_perm(BlockDriverState *bs, | ||
154 | uint64_t perm, uint64_t shared) | ||
155 | { | ||
156 | BDRVPreallocateState *s = bs->opaque; | ||
157 | |||
158 | if (can_write_resize(perm)) { | ||
159 | + qemu_bh_cancel(s->drop_resize_bh); | ||
160 | if (s->data_end < 0) { | ||
161 | s->data_end = s->file_end = s->zero_start = | ||
162 | - bdrv_getlength(bs->file->bs); | ||
163 | + bs->file->bs->total_sectors * BDRV_SECTOR_SIZE; | ||
164 | } | ||
165 | } else { | ||
166 | - /* | ||
167 | - * We drop our permissions, as well as allow shared | ||
168 | - * permissions (see preallocate_child_perm), anyone will be able to | ||
169 | - * change the child, so mark all states invalid. We'll regain control if | ||
170 | - * get good permissions back. | ||
171 | - */ | ||
172 | - s->data_end = s->file_end = s->zero_start = -EINVAL; | ||
173 | + qemu_bh_schedule(s->drop_resize_bh); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | @@ -XXX,XX +XXX,XX @@ static void preallocate_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
178 | BdrvChildRole role, BlockReopenQueue *reopen_queue, | ||
179 | uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) | ||
180 | { | ||
181 | + BDRVPreallocateState *s = bs->opaque; | ||
182 | + | ||
183 | bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared); | ||
184 | |||
185 | - if (can_write_resize(perm)) { | ||
186 | - /* This should come by default, but let's enforce: */ | ||
187 | + /* | ||
188 | + * We need exclusive write and resize permissions on the child not only when | ||
189 | + * the parent can write to it, but also after the parent gave up write | ||
190 | + * permissions until preallocate_drop_resize() has completed. | ||
191 | + */ | ||
192 | + if (can_write_resize(perm) || s->data_end != -EINVAL) { | ||
193 | *nperm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; | ||
49 | 194 | ||
50 | /* | 195 | /* |
51 | * Flatten non-empty QDict and QList recursively into @target, | 196 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_preallocate_filter = { |
52 | * copy other objects to @target | 197 | .bdrv_co_flush = preallocate_co_flush, |
53 | */ | 198 | .bdrv_co_truncate = preallocate_co_truncate, |
54 | - if (qobject_type(value) == QTYPE_QDICT) { | 199 | |
55 | - qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); | 200 | - .bdrv_check_perm = preallocate_check_perm, |
56 | - } else if (qobject_type(value) == QTYPE_QLIST) { | 201 | .bdrv_set_perm = preallocate_set_perm, |
57 | - qdict_flatten_qlist(qobject_to(QList, value), target, new_key); | 202 | .bdrv_child_perm = preallocate_child_perm, |
58 | + if (dict_val && qdict_size(dict_val)) { | 203 | |
59 | + qdict_flatten_qdict(dict_val, target, new_key); | ||
60 | + } else if (list_val && !qlist_empty(list_val)) { | ||
61 | + qdict_flatten_qlist(list_val, target, new_key); | ||
62 | } else { | ||
63 | qdict_put_obj(target, new_key, qobject_ref(value)); | ||
64 | } | ||
65 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
66 | { | ||
67 | QObject *value; | ||
68 | const QDictEntry *entry, *next; | ||
69 | + QDict *dict_val; | ||
70 | + QList *list_val; | ||
71 | char *new_key; | ||
72 | |||
73 | entry = qdict_first(qdict); | ||
74 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
75 | while (entry != NULL) { | ||
76 | next = qdict_next(qdict, entry); | ||
77 | value = qdict_entry_value(entry); | ||
78 | + dict_val = qobject_to(QDict, value); | ||
79 | + list_val = qobject_to(QList, value); | ||
80 | new_key = NULL; | ||
81 | |||
82 | if (prefix) { | ||
83 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
84 | * Flatten non-empty QDict and QList recursively into @target, | ||
85 | * copy other objects to @target | ||
86 | */ | ||
87 | - if (qobject_type(value) == QTYPE_QDICT) { | ||
88 | - qdict_flatten_qdict(qobject_to(QDict, value), target, | ||
89 | + if (dict_val && qdict_size(dict_val)) { | ||
90 | + qdict_flatten_qdict(dict_val, target, | ||
91 | new_key ? new_key : entry->key); | ||
92 | qdict_del(qdict, entry->key); | ||
93 | - } else if (qobject_type(value) == QTYPE_QLIST) { | ||
94 | - qdict_flatten_qlist(qobject_to(QList, value), target, | ||
95 | + } else if (list_val && !qlist_empty(list_val)) { | ||
96 | + qdict_flatten_qlist(list_val, target, | ||
97 | new_key ? new_key : entry->key); | ||
98 | qdict_del(qdict, entry->key); | ||
99 | } else if (target != qdict) { | ||
100 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | - * qdict_flatten(): For each nested QDict with key x, all fields with key y | ||
105 | - * are moved to this QDict and their key is renamed to "x.y". For each nested | ||
106 | - * QList with key x, the field at index y is moved to this QDict with the key | ||
107 | - * "x.y" (i.e., the reverse of what qdict_array_split() does). | ||
108 | + * qdict_flatten(): For each nested non-empty QDict with key x, all | ||
109 | + * fields with key y are moved to this QDict and their key is renamed | ||
110 | + * to "x.y". For each nested non-empty QList with key x, the field at | ||
111 | + * index y is moved to this QDict with the key "x.y" (i.e., the | ||
112 | + * reverse of what qdict_array_split() does). | ||
113 | * This operation is applied recursively for nested QDicts and QLists. | ||
114 | */ | ||
115 | void qdict_flatten(QDict *qdict) | ||
116 | @@ -XXX,XX +XXX,XX @@ static int qdict_is_list(QDict *maybe_list, Error **errp) | ||
117 | * @src: the original flat dictionary (only scalar values) to crumple | ||
118 | * | ||
119 | * Takes a flat dictionary whose keys use '.' separator to indicate | ||
120 | - * nesting, and values are scalars, and crumples it into a nested | ||
121 | - * structure. | ||
122 | + * nesting, and values are scalars, empty dictionaries or empty lists, | ||
123 | + * and crumples it into a nested structure. | ||
124 | * | ||
125 | * To include a literal '.' in a key name, it must be escaped as '..' | ||
126 | * | ||
127 | @@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp) | ||
128 | { | ||
129 | const QDictEntry *ent; | ||
130 | QDict *two_level, *multi_level = NULL, *child_dict; | ||
131 | + QDict *dict_val; | ||
132 | + QList *list_val; | ||
133 | QObject *dst = NULL, *child; | ||
134 | size_t i; | ||
135 | char *prefix = NULL; | ||
136 | @@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp) | ||
137 | |||
138 | /* Step 1: split our totally flat dict into a two level dict */ | ||
139 | for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { | ||
140 | - if (qobject_type(ent->value) == QTYPE_QDICT || | ||
141 | - qobject_type(ent->value) == QTYPE_QLIST) { | ||
142 | - error_setg(errp, "Value %s is not a scalar", | ||
143 | - ent->key); | ||
144 | + dict_val = qobject_to(QDict, ent->value); | ||
145 | + list_val = qobject_to(QList, ent->value); | ||
146 | + if ((dict_val && qdict_size(dict_val)) | ||
147 | + || (list_val && !qlist_empty(list_val))) { | ||
148 | + error_setg(errp, "Value %s is not flat", ent->key); | ||
149 | goto error; | ||
150 | } | ||
151 | |||
152 | @@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp) | ||
153 | multi_level = qdict_new(); | ||
154 | for (ent = qdict_first(two_level); ent != NULL; | ||
155 | ent = qdict_next(two_level, ent)) { | ||
156 | - QDict *dict = qobject_to(QDict, ent->value); | ||
157 | - if (dict) { | ||
158 | - child = qdict_crumple(dict, errp); | ||
159 | + dict_val = qobject_to(QDict, ent->value); | ||
160 | + if (dict_val && qdict_size(dict_val)) { | ||
161 | + child = qdict_crumple(dict_val, errp); | ||
162 | if (!child) { | ||
163 | goto error; | ||
164 | } | ||
165 | diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c | ||
166 | index XXXXXXX..XXXXXXX 100644 | ||
167 | --- a/tests/check-block-qdict.c | ||
168 | +++ b/tests/check-block-qdict.c | ||
169 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void) | ||
170 | * "e.1.2.b": 1, | ||
171 | * "f.c": 2, | ||
172 | * "f.d": 3, | ||
173 | - * "g": 4 | ||
174 | + * "g": 4, | ||
175 | + * "y.0": {}, | ||
176 | + * "z.a": [] | ||
177 | * } | ||
178 | - * | ||
179 | - * Note that "y" and "z" get eaten. | ||
180 | */ | ||
181 | |||
182 | qdict_put_int(e_1_2, "a", 0); | ||
183 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void) | ||
184 | g_assert(qdict_get_int(root, "f.c") == 2); | ||
185 | g_assert(qdict_get_int(root, "f.d") == 3); | ||
186 | g_assert(qdict_get_int(root, "g") == 4); | ||
187 | + g_assert(!qdict_size(qdict_get_qdict(root, "y.0"))); | ||
188 | + g_assert(qlist_empty(qdict_get_qlist(root, "z.a"))); | ||
189 | |||
190 | - g_assert(qdict_size(root) == 8); | ||
191 | + g_assert(qdict_size(root) == 10); | ||
192 | |||
193 | qobject_unref(root); | ||
194 | } | ||
195 | @@ -XXX,XX +XXX,XX @@ static void qdict_join_test(void) | ||
196 | static void qdict_crumple_test_recursive(void) | ||
197 | { | ||
198 | QDict *src, *dst, *rule, *vnc, *acl, *listen; | ||
199 | - QList *rules; | ||
200 | + QDict *empty, *empty_dict, *empty_list_0; | ||
201 | + QList *rules, *empty_list, *empty_dict_a; | ||
202 | |||
203 | src = qdict_new(); | ||
204 | qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); | ||
205 | @@ -XXX,XX +XXX,XX @@ static void qdict_crumple_test_recursive(void) | ||
206 | qdict_put_str(src, "vnc.acl.default", "deny"); | ||
207 | qdict_put_str(src, "vnc.acl..name", "acl0"); | ||
208 | qdict_put_str(src, "vnc.acl.rule..name", "acl0"); | ||
209 | + qdict_put(src, "empty.dict.a", qlist_new()); | ||
210 | + qdict_put(src, "empty.list.0", qdict_new()); | ||
211 | |||
212 | dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); | ||
213 | g_assert(dst); | ||
214 | - g_assert_cmpint(qdict_size(dst), ==, 1); | ||
215 | + g_assert_cmpint(qdict_size(dst), ==, 2); | ||
216 | |||
217 | vnc = qdict_get_qdict(dst, "vnc"); | ||
218 | g_assert(vnc); | ||
219 | @@ -XXX,XX +XXX,XX @@ static void qdict_crumple_test_recursive(void) | ||
220 | g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); | ||
221 | g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); | ||
222 | |||
223 | + empty = qdict_get_qdict(dst, "empty"); | ||
224 | + g_assert(empty); | ||
225 | + g_assert_cmpint(qdict_size(empty), ==, 2); | ||
226 | + empty_dict = qdict_get_qdict(empty, "dict"); | ||
227 | + g_assert(empty_dict); | ||
228 | + g_assert_cmpint(qdict_size(empty_dict), ==, 1); | ||
229 | + empty_dict_a = qdict_get_qlist(empty_dict, "a"); | ||
230 | + g_assert(empty_dict_a && qlist_empty(empty_dict_a)); | ||
231 | + empty_list = qdict_get_qlist(empty, "list"); | ||
232 | + g_assert(empty_list); | ||
233 | + g_assert_cmpint(qlist_size(empty_list), ==, 1); | ||
234 | + empty_list_0 = qobject_to(QDict, qlist_pop(empty_list)); | ||
235 | + g_assert(empty_list_0); | ||
236 | + g_assert_cmpint(qdict_size(empty_list_0), ==, 0); | ||
237 | + | ||
238 | qobject_unref(src); | ||
239 | qobject_unref(dst); | ||
240 | } | ||
241 | @@ -XXX,XX +XXX,XX @@ static void qdict_rename_keys_test(void) | ||
242 | |||
243 | static void qdict_crumple_test_bad_inputs(void) | ||
244 | { | ||
245 | - QDict *src; | ||
246 | + QDict *src, *nested; | ||
247 | Error *error = NULL; | ||
248 | |||
249 | src = qdict_new(); | ||
250 | @@ -XXX,XX +XXX,XX @@ static void qdict_crumple_test_bad_inputs(void) | ||
251 | |||
252 | src = qdict_new(); | ||
253 | /* The input should be flat, ie no dicts or lists */ | ||
254 | - qdict_put(src, "rule.a", qdict_new()); | ||
255 | + nested = qdict_new(); | ||
256 | + qdict_put(nested, "x", qdict_new()); | ||
257 | + qdict_put(src, "rule.a", nested); | ||
258 | qdict_put_str(src, "rule.b", "allow"); | ||
259 | |||
260 | g_assert(qdict_crumple(src, &error) == NULL); | ||
261 | -- | 204 | -- |
262 | 2.13.6 | 205 | 2.41.0 |
263 | |||
264 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | The documentation for bdrv_append() says that the caller must hold the |
---|---|---|---|
2 | AioContext lock for bs_top. Change all callers to actually adhere to the | ||
3 | contract. | ||
2 | 4 | ||
3 | Although qemu-img creates aligned files (by rounding up), it | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | must also gracefully handle files that are not sector-aligned. | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
5 | Test that the bug fixed in the previous patch does not recur. | 7 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
6 | 8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | |
7 | It's a bit annoying that we can see the (implicit) hole past | 9 | Message-ID: <20230911094620.45040-5-kwolf@redhat.com> |
8 | the end of the file on to the next sector boundary, so if we | ||
9 | ever reach the point where we report a byte-accurate size rather | ||
10 | than our current behavior of always rounding up, this test will | ||
11 | probably need a slight modification. | ||
12 | |||
13 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
15 | --- | 11 | --- |
16 | tests/qemu-iotests/221 | 60 ++++++++++++++++++++++++++++++++++++++++++++++ | 12 | tests/unit/test-bdrv-drain.c | 3 +++ |
17 | tests/qemu-iotests/221.out | 16 +++++++++++++ | 13 | tests/unit/test-bdrv-graph-mod.c | 6 ++++++ |
18 | tests/qemu-iotests/group | 1 + | 14 | tests/unit/test-block-iothread.c | 3 +++ |
19 | 3 files changed, 77 insertions(+) | 15 | 3 files changed, 12 insertions(+) |
20 | create mode 100755 tests/qemu-iotests/221 | ||
21 | create mode 100644 tests/qemu-iotests/221.out | ||
22 | 16 | ||
23 | diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 | 17 | diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c |
24 | new file mode 100755 | 18 | index XXXXXXX..XXXXXXX 100644 |
25 | index XXXXXXX..XXXXXXX | 19 | --- a/tests/unit/test-bdrv-drain.c |
26 | --- /dev/null | 20 | +++ b/tests/unit/test-bdrv-drain.c |
27 | +++ b/tests/qemu-iotests/221 | 21 | @@ -XXX,XX +XXX,XX @@ static void test_append_to_drained(void) |
28 | @@ -XXX,XX +XXX,XX @@ | 22 | g_assert_cmpint(base_s->drain_count, ==, 1); |
29 | +#!/bin/bash | 23 | g_assert_cmpint(base->in_flight, ==, 0); |
30 | +# | 24 | |
31 | +# Test qemu-img vs. unaligned images | 25 | + aio_context_acquire(qemu_get_aio_context()); |
32 | +# | 26 | bdrv_append(overlay, base, &error_abort); |
33 | +# Copyright (C) 2018 Red Hat, Inc. | 27 | + aio_context_release(qemu_get_aio_context()); |
34 | +# | ||
35 | +# This program is free software; you can redistribute it and/or modify | ||
36 | +# it under the terms of the GNU General Public License as published by | ||
37 | +# the Free Software Foundation; either version 2 of the License, or | ||
38 | +# (at your option) any later version. | ||
39 | +# | ||
40 | +# This program is distributed in the hope that it will be useful, | ||
41 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
42 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
43 | +# GNU General Public License for more details. | ||
44 | +# | ||
45 | +# You should have received a copy of the GNU General Public License | ||
46 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
47 | +# | ||
48 | + | 28 | + |
49 | +seq="$(basename $0)" | 29 | g_assert_cmpint(base->in_flight, ==, 0); |
50 | +echo "QA output created by $seq" | 30 | g_assert_cmpint(overlay->in_flight, ==, 0); |
31 | |||
32 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
35 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
36 | @@ -XXX,XX +XXX,XX @@ static void test_update_perm_tree(void) | ||
37 | bdrv_attach_child(filter, bs, "child", &child_of_bds, | ||
38 | BDRV_CHILD_DATA, &error_abort); | ||
39 | |||
40 | + aio_context_acquire(qemu_get_aio_context()); | ||
41 | ret = bdrv_append(filter, bs, NULL); | ||
42 | g_assert_cmpint(ret, <, 0); | ||
43 | + aio_context_release(qemu_get_aio_context()); | ||
44 | |||
45 | bdrv_unref(filter); | ||
46 | blk_unref(root); | ||
47 | @@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void) | ||
48 | g_assert(target->backing->bs == bs); | ||
49 | bdrv_attach_child(filter, target, "target", &child_of_bds, | ||
50 | BDRV_CHILD_DATA, &error_abort); | ||
51 | + aio_context_acquire(qemu_get_aio_context()); | ||
52 | bdrv_append(filter, bs, &error_abort); | ||
53 | + aio_context_release(qemu_get_aio_context()); | ||
54 | g_assert(target->backing->bs == bs); | ||
55 | |||
56 | bdrv_unref(filter); | ||
57 | @@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void) | ||
58 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | ||
59 | &error_abort); | ||
60 | |||
61 | + aio_context_acquire(qemu_get_aio_context()); | ||
62 | bdrv_append(fl, base, &error_abort); | ||
63 | + aio_context_release(qemu_get_aio_context()); | ||
64 | bdrv_unref(fl); | ||
65 | bdrv_unref(top); | ||
66 | } | ||
67 | diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c | ||
68 | index XXXXXXX..XXXXXXX 100644 | ||
69 | --- a/tests/unit/test-block-iothread.c | ||
70 | +++ b/tests/unit/test-block-iothread.c | ||
71 | @@ -XXX,XX +XXX,XX @@ static void test_propagate_mirror(void) | ||
72 | &error_abort); | ||
73 | |||
74 | /* Start a mirror job */ | ||
75 | + aio_context_acquire(main_ctx); | ||
76 | mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0, | ||
77 | MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, | ||
78 | BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, | ||
79 | false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, | ||
80 | &error_abort); | ||
81 | + aio_context_release(main_ctx); | ||
51 | + | 82 | + |
52 | +here="$PWD" | 83 | WITH_JOB_LOCK_GUARD() { |
53 | +status=1 # failure is the default! | 84 | job = job_get_locked("job0"); |
54 | + | 85 | } |
55 | +_cleanup() | ||
56 | +{ | ||
57 | + _cleanup_test_img | ||
58 | +} | ||
59 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
60 | + | ||
61 | +# get standard environment, filters and checks | ||
62 | +. ./common.rc | ||
63 | +. ./common.filter | ||
64 | + | ||
65 | +_supported_fmt raw | ||
66 | +_supported_proto file | ||
67 | +_supported_os Linux | ||
68 | + | ||
69 | +echo | ||
70 | +echo "=== Check mapping of unaligned raw image ===" | ||
71 | +echo | ||
72 | + | ||
73 | +_make_test_img 43009 # qemu-img create rounds size up | ||
74 | +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map | ||
75 | + | ||
76 | +truncate --size=43009 "$TEST_IMG" # so we resize it and check again | ||
77 | +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map | ||
78 | + | ||
79 | +$QEMU_IO -c 'w 43008 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up | ||
80 | +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map | ||
81 | + | ||
82 | +truncate --size=43009 "$TEST_IMG" # so we resize it and check again | ||
83 | +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map | ||
84 | + | ||
85 | +# success, all done | ||
86 | +echo '*** done' | ||
87 | +rm -f $seq.full | ||
88 | +status=0 | ||
89 | diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out | ||
90 | new file mode 100644 | ||
91 | index XXXXXXX..XXXXXXX | ||
92 | --- /dev/null | ||
93 | +++ b/tests/qemu-iotests/221.out | ||
94 | @@ -XXX,XX +XXX,XX @@ | ||
95 | +QA output created by 221 | ||
96 | + | ||
97 | +=== Check mapping of unaligned raw image === | ||
98 | + | ||
99 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=43009 | ||
100 | +[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] | ||
101 | +[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] | ||
102 | +wrote 1/1 bytes at offset 43008 | ||
103 | +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
104 | +[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, | ||
105 | +{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, | ||
106 | +{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] | ||
107 | +[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, | ||
108 | +{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, | ||
109 | +{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] | ||
110 | +*** done | ||
111 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
112 | index XXXXXXX..XXXXXXX 100644 | ||
113 | --- a/tests/qemu-iotests/group | ||
114 | +++ b/tests/qemu-iotests/group | ||
115 | @@ -XXX,XX +XXX,XX @@ | ||
116 | 217 rw auto quick | ||
117 | 218 rw auto quick | ||
118 | 219 rw auto | ||
119 | +221 rw auto quick | ||
120 | -- | 86 | -- |
121 | 2.13.6 | 87 | 2.41.0 |
122 | |||
123 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | bdrv_unref() is called by a lot of places that need to hold the graph |
---|---|---|---|
2 | lock (it naturally happens in the context of operations that change the | ||
3 | graph). However, bdrv_unref() takes the graph writer lock internally, so | ||
4 | it can't actually be called while already holding a graph lock without | ||
5 | causing a deadlock. | ||
2 | 6 | ||
3 | Pure code motion, except for two brace placements and a comment | 7 | bdrv_unref() also can't just become GRAPH_WRLOCK because it drains the |
4 | tweaked to appease checkpatch. | 8 | node before closing it, and draining requires that the graph is |
9 | unlocked. | ||
5 | 10 | ||
6 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 11 | The solution is to defer deleting the node until we don't hold the lock |
7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 12 | any more and draining is possible again. |
13 | |||
14 | Note that keeping images open for longer than necessary can create | ||
15 | problems, too: You can't open an image again before it is really closed | ||
16 | (if image locking didn't prevent it, it would cause corruption). | ||
17 | Reopening an image immediately happens at least during bdrv_open() and | ||
18 | bdrv_co_create(). | ||
19 | |||
20 | In order to solve this problem, make sure to run the deferred unref in | ||
21 | bdrv_graph_wrunlock(), i.e. the first possible place where we can drain | ||
22 | again. This is also why bdrv_schedule_unref() is marked GRAPH_WRLOCK. | ||
23 | |||
24 | The output of iotest 051 is updated because the additional polling | ||
25 | changes the order of HMP output, resulting in a new "(qemu)" prompt in | ||
26 | the test output that was previously on a separate line and filtered out. | ||
27 | |||
28 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
29 | Message-ID: <20230911094620.45040-6-kwolf@redhat.com> | ||
30 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 31 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 32 | --- |
10 | qobject/block-qdict.c | 640 ++++++++++++++++++++++++++++++++++++++++++++ | 33 | include/block/block-global-state.h | 1 + |
11 | qobject/qdict.c | 629 -------------------------------------------- | 34 | block.c | 17 +++++++++++++++++ |
12 | tests/check-block-qdict.c | 655 ++++++++++++++++++++++++++++++++++++++++++++++ | 35 | block/graph-lock.c | 26 +++++++++++++++++++------- |
13 | tests/check-qdict.c | 642 --------------------------------------------- | 36 | tests/qemu-iotests/051.pc.out | 6 +++--- |
14 | MAINTAINERS | 2 + | 37 | 4 files changed, 40 insertions(+), 10 deletions(-) |
15 | qobject/Makefile.objs | 1 + | ||
16 | tests/Makefile.include | 4 + | ||
17 | 7 files changed, 1302 insertions(+), 1271 deletions(-) | ||
18 | create mode 100644 qobject/block-qdict.c | ||
19 | create mode 100644 tests/check-block-qdict.c | ||
20 | 38 | ||
21 | diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c | 39 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h |
22 | new file mode 100644 | 40 | index XXXXXXX..XXXXXXX 100644 |
23 | index XXXXXXX..XXXXXXX | 41 | --- a/include/block/block-global-state.h |
24 | --- /dev/null | 42 | +++ b/include/block/block-global-state.h |
25 | +++ b/qobject/block-qdict.c | 43 | @@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt, |
26 | @@ -XXX,XX +XXX,XX @@ | 44 | void bdrv_ref(BlockDriverState *bs); |
45 | void no_coroutine_fn bdrv_unref(BlockDriverState *bs); | ||
46 | void coroutine_fn no_co_wrapper bdrv_co_unref(BlockDriverState *bs); | ||
47 | +void GRAPH_WRLOCK bdrv_schedule_unref(BlockDriverState *bs); | ||
48 | void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); | ||
49 | BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
50 | BlockDriverState *child_bs, | ||
51 | diff --git a/block.c b/block.c | ||
52 | index XXXXXXX..XXXXXXX 100644 | ||
53 | --- a/block.c | ||
54 | +++ b/block.c | ||
55 | @@ -XXX,XX +XXX,XX @@ void bdrv_unref(BlockDriverState *bs) | ||
56 | } | ||
57 | } | ||
58 | |||
27 | +/* | 59 | +/* |
28 | + * Special QDict functions used by the block layer | 60 | + * Release a BlockDriverState reference while holding the graph write lock. |
29 | + * | 61 | + * |
30 | + * Copyright (c) 2013-2018 Red Hat, Inc. | 62 | + * Calling bdrv_unref() directly is forbidden while holding the graph lock |
31 | + * | 63 | + * because bdrv_close() both involves polling and taking the graph lock |
32 | + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | 64 | + * internally. bdrv_schedule_unref() instead delays decreasing the refcount and |
33 | + * See the COPYING.LIB file in the top-level directory. | 65 | + * possibly closing @bs until the graph lock is released. |
34 | + */ | 66 | + */ |
35 | + | 67 | +void bdrv_schedule_unref(BlockDriverState *bs) |
36 | +#include "qemu/osdep.h" | ||
37 | +#include "block/qdict.h" | ||
38 | +#include "qapi/qmp/qlist.h" | ||
39 | +#include "qemu/cutils.h" | ||
40 | +#include "qapi/error.h" | ||
41 | + | ||
42 | +/** | ||
43 | + * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the | ||
44 | + * value of 'key' in 'src' is copied there (and the refcount increased | ||
45 | + * accordingly). | ||
46 | + */ | ||
47 | +void qdict_copy_default(QDict *dst, QDict *src, const char *key) | ||
48 | +{ | 68 | +{ |
49 | + QObject *val; | 69 | + if (!bs) { |
50 | + | ||
51 | + if (qdict_haskey(dst, key)) { | ||
52 | + return; | 70 | + return; |
53 | + } | 71 | + } |
54 | + | 72 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), |
55 | + val = qdict_get(src, key); | 73 | + (QEMUBHFunc *) bdrv_unref, bs); |
56 | + if (val) { | ||
57 | + qdict_put_obj(dst, key, qobject_ref(val)); | ||
58 | + } | ||
59 | +} | 74 | +} |
60 | + | 75 | + |
61 | +/** | 76 | struct BdrvOpBlocker { |
62 | + * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a | 77 | Error *reason; |
63 | + * new QString initialised by 'val' is put there. | 78 | QLIST_ENTRY(BdrvOpBlocker) list; |
64 | + */ | 79 | diff --git a/block/graph-lock.c b/block/graph-lock.c |
65 | +void qdict_set_default_str(QDict *dst, const char *key, const char *val) | 80 | index XXXXXXX..XXXXXXX 100644 |
66 | +{ | 81 | --- a/block/graph-lock.c |
67 | + if (qdict_haskey(dst, key)) { | 82 | +++ b/block/graph-lock.c |
68 | + return; | 83 | @@ -XXX,XX +XXX,XX @@ void bdrv_graph_wrlock(BlockDriverState *bs) |
84 | void bdrv_graph_wrunlock(void) | ||
85 | { | ||
86 | GLOBAL_STATE_CODE(); | ||
87 | - QEMU_LOCK_GUARD(&aio_context_list_lock); | ||
88 | assert(qatomic_read(&has_writer)); | ||
89 | |||
90 | + WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) { | ||
91 | + /* | ||
92 | + * No need for memory barriers, this works in pair with | ||
93 | + * the slow path of rdlock() and both take the lock. | ||
94 | + */ | ||
95 | + qatomic_store_release(&has_writer, 0); | ||
96 | + | ||
97 | + /* Wake up all coroutines that are waiting to read the graph */ | ||
98 | + qemu_co_enter_all(&reader_queue, &aio_context_list_lock); | ||
69 | + } | 99 | + } |
70 | + | 100 | + |
71 | + qdict_put_str(dst, key, val); | 101 | /* |
72 | +} | 102 | - * No need for memory barriers, this works in pair with |
73 | + | 103 | - * the slow path of rdlock() and both take the lock. |
74 | +static void qdict_flatten_qdict(QDict *qdict, QDict *target, | 104 | + * Run any BHs that were scheduled during the wrlock section and that |
75 | + const char *prefix); | 105 | + * callers might expect to have finished (in particular, this is important |
76 | + | 106 | + * for bdrv_schedule_unref()). |
77 | +static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) | 107 | + * |
78 | +{ | 108 | + * Do this only after restarting coroutines so that nested event loops in |
79 | + QObject *value; | 109 | + * BHs don't deadlock if their condition relies on the coroutine making |
80 | + const QListEntry *entry; | 110 | + * progress. |
81 | + char *new_key; | 111 | */ |
82 | + int i; | 112 | - qatomic_store_release(&has_writer, 0); |
83 | + | 113 | - |
84 | + /* This function is never called with prefix == NULL, i.e., it is always | 114 | - /* Wake up all coroutine that are waiting to read the graph */ |
85 | + * called from within qdict_flatten_q(list|dict)(). Therefore, it does not | 115 | - qemu_co_enter_all(&reader_queue, &aio_context_list_lock); |
86 | + * need to remove list entries during the iteration (the whole list will be | 116 | + aio_bh_poll(qemu_get_aio_context()); |
87 | + * deleted eventually anyway from qdict_flatten_qdict()). */ | 117 | } |
88 | + assert(prefix); | 118 | |
89 | + | 119 | void coroutine_fn bdrv_graph_co_rdlock(void) |
90 | + entry = qlist_first(qlist); | 120 | diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out |
91 | + | ||
92 | + for (i = 0; entry; entry = qlist_next(entry), i++) { | ||
93 | + value = qlist_entry_obj(entry); | ||
94 | + new_key = g_strdup_printf("%s.%i", prefix, i); | ||
95 | + | ||
96 | + if (qobject_type(value) == QTYPE_QDICT) { | ||
97 | + qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); | ||
98 | + } else if (qobject_type(value) == QTYPE_QLIST) { | ||
99 | + qdict_flatten_qlist(qobject_to(QList, value), target, new_key); | ||
100 | + } else { | ||
101 | + /* All other types are moved to the target unchanged. */ | ||
102 | + qdict_put_obj(target, new_key, qobject_ref(value)); | ||
103 | + } | ||
104 | + | ||
105 | + g_free(new_key); | ||
106 | + } | ||
107 | +} | ||
108 | + | ||
109 | +static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
110 | +{ | ||
111 | + QObject *value; | ||
112 | + const QDictEntry *entry, *next; | ||
113 | + char *new_key; | ||
114 | + bool delete; | ||
115 | + | ||
116 | + entry = qdict_first(qdict); | ||
117 | + | ||
118 | + while (entry != NULL) { | ||
119 | + | ||
120 | + next = qdict_next(qdict, entry); | ||
121 | + value = qdict_entry_value(entry); | ||
122 | + new_key = NULL; | ||
123 | + delete = false; | ||
124 | + | ||
125 | + if (prefix) { | ||
126 | + new_key = g_strdup_printf("%s.%s", prefix, entry->key); | ||
127 | + } | ||
128 | + | ||
129 | + if (qobject_type(value) == QTYPE_QDICT) { | ||
130 | + /* Entries of QDicts are processed recursively, the QDict object | ||
131 | + * itself disappears. */ | ||
132 | + qdict_flatten_qdict(qobject_to(QDict, value), target, | ||
133 | + new_key ? new_key : entry->key); | ||
134 | + delete = true; | ||
135 | + } else if (qobject_type(value) == QTYPE_QLIST) { | ||
136 | + qdict_flatten_qlist(qobject_to(QList, value), target, | ||
137 | + new_key ? new_key : entry->key); | ||
138 | + delete = true; | ||
139 | + } else if (prefix) { | ||
140 | + /* All other objects are moved to the target unchanged. */ | ||
141 | + qdict_put_obj(target, new_key, qobject_ref(value)); | ||
142 | + delete = true; | ||
143 | + } | ||
144 | + | ||
145 | + g_free(new_key); | ||
146 | + | ||
147 | + if (delete) { | ||
148 | + qdict_del(qdict, entry->key); | ||
149 | + | ||
150 | + /* Restart loop after modifying the iterated QDict */ | ||
151 | + entry = qdict_first(qdict); | ||
152 | + continue; | ||
153 | + } | ||
154 | + | ||
155 | + entry = next; | ||
156 | + } | ||
157 | +} | ||
158 | + | ||
159 | +/** | ||
160 | + * qdict_flatten(): For each nested QDict with key x, all fields with key y | ||
161 | + * are moved to this QDict and their key is renamed to "x.y". For each nested | ||
162 | + * QList with key x, the field at index y is moved to this QDict with the key | ||
163 | + * "x.y" (i.e., the reverse of what qdict_array_split() does). | ||
164 | + * This operation is applied recursively for nested QDicts and QLists. | ||
165 | + */ | ||
166 | +void qdict_flatten(QDict *qdict) | ||
167 | +{ | ||
168 | + qdict_flatten_qdict(qdict, qdict, NULL); | ||
169 | +} | ||
170 | + | ||
171 | +/* extract all the src QDict entries starting by start into dst */ | ||
172 | +void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) | ||
173 | + | ||
174 | +{ | ||
175 | + const QDictEntry *entry, *next; | ||
176 | + const char *p; | ||
177 | + | ||
178 | + *dst = qdict_new(); | ||
179 | + entry = qdict_first(src); | ||
180 | + | ||
181 | + while (entry != NULL) { | ||
182 | + next = qdict_next(src, entry); | ||
183 | + if (strstart(entry->key, start, &p)) { | ||
184 | + qdict_put_obj(*dst, p, qobject_ref(entry->value)); | ||
185 | + qdict_del(src, entry->key); | ||
186 | + } | ||
187 | + entry = next; | ||
188 | + } | ||
189 | +} | ||
190 | + | ||
191 | +static int qdict_count_prefixed_entries(const QDict *src, const char *start) | ||
192 | +{ | ||
193 | + const QDictEntry *entry; | ||
194 | + int count = 0; | ||
195 | + | ||
196 | + for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { | ||
197 | + if (strstart(entry->key, start, NULL)) { | ||
198 | + if (count == INT_MAX) { | ||
199 | + return -ERANGE; | ||
200 | + } | ||
201 | + count++; | ||
202 | + } | ||
203 | + } | ||
204 | + | ||
205 | + return count; | ||
206 | +} | ||
207 | + | ||
208 | +/** | ||
209 | + * qdict_array_split(): This function moves array-like elements of a QDict into | ||
210 | + * a new QList. Every entry in the original QDict with a key "%u" or one | ||
211 | + * prefixed "%u.", where %u designates an unsigned integer starting at 0 and | ||
212 | + * incrementally counting up, will be moved to a new QDict at index %u in the | ||
213 | + * output QList with the key prefix removed, if that prefix is "%u.". If the | ||
214 | + * whole key is just "%u", the whole QObject will be moved unchanged without | ||
215 | + * creating a new QDict. The function terminates when there is no entry in the | ||
216 | + * QDict with a prefix directly (incrementally) following the last one; it also | ||
217 | + * returns if there are both entries with "%u" and "%u." for the same index %u. | ||
218 | + * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} | ||
219 | + * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) | ||
220 | + * => [{"a": 42, "b": 23}, {"x": 0}, 66] | ||
221 | + * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) | ||
222 | + */ | ||
223 | +void qdict_array_split(QDict *src, QList **dst) | ||
224 | +{ | ||
225 | + unsigned i; | ||
226 | + | ||
227 | + *dst = qlist_new(); | ||
228 | + | ||
229 | + for (i = 0; i < UINT_MAX; i++) { | ||
230 | + QObject *subqobj; | ||
231 | + bool is_subqdict; | ||
232 | + QDict *subqdict; | ||
233 | + char indexstr[32], prefix[32]; | ||
234 | + size_t snprintf_ret; | ||
235 | + | ||
236 | + snprintf_ret = snprintf(indexstr, 32, "%u", i); | ||
237 | + assert(snprintf_ret < 32); | ||
238 | + | ||
239 | + subqobj = qdict_get(src, indexstr); | ||
240 | + | ||
241 | + snprintf_ret = snprintf(prefix, 32, "%u.", i); | ||
242 | + assert(snprintf_ret < 32); | ||
243 | + | ||
244 | + /* Overflow is the same as positive non-zero results */ | ||
245 | + is_subqdict = qdict_count_prefixed_entries(src, prefix); | ||
246 | + | ||
247 | + /* | ||
248 | + * There may be either a single subordinate object (named | ||
249 | + * "%u") or multiple objects (each with a key prefixed "%u."), | ||
250 | + * but not both. | ||
251 | + */ | ||
252 | + if (!subqobj == !is_subqdict) { | ||
253 | + break; | ||
254 | + } | ||
255 | + | ||
256 | + if (is_subqdict) { | ||
257 | + qdict_extract_subqdict(src, &subqdict, prefix); | ||
258 | + assert(qdict_size(subqdict) > 0); | ||
259 | + } else { | ||
260 | + qobject_ref(subqobj); | ||
261 | + qdict_del(src, indexstr); | ||
262 | + } | ||
263 | + | ||
264 | + qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); | ||
265 | + } | ||
266 | +} | ||
267 | + | ||
268 | +/** | ||
269 | + * qdict_split_flat_key: | ||
270 | + * @key: the key string to split | ||
271 | + * @prefix: non-NULL pointer to hold extracted prefix | ||
272 | + * @suffix: non-NULL pointer to remaining suffix | ||
273 | + * | ||
274 | + * Given a flattened key such as 'foo.0.bar', split it into two parts | ||
275 | + * at the first '.' separator. Allows double dot ('..') to escape the | ||
276 | + * normal separator. | ||
277 | + * | ||
278 | + * e.g. | ||
279 | + * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' | ||
280 | + * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' | ||
281 | + * | ||
282 | + * The '..' sequence will be unescaped in the returned 'prefix' | ||
283 | + * string. The 'suffix' string will be left in escaped format, so it | ||
284 | + * can be fed back into the qdict_split_flat_key() key as the input | ||
285 | + * later. | ||
286 | + * | ||
287 | + * The caller is responsible for freeing the string returned in @prefix | ||
288 | + * using g_free(). | ||
289 | + */ | ||
290 | +static void qdict_split_flat_key(const char *key, char **prefix, | ||
291 | + const char **suffix) | ||
292 | +{ | ||
293 | + const char *separator; | ||
294 | + size_t i, j; | ||
295 | + | ||
296 | + /* Find first '.' separator, but if there is a pair '..' | ||
297 | + * that acts as an escape, so skip over '..' */ | ||
298 | + separator = NULL; | ||
299 | + do { | ||
300 | + if (separator) { | ||
301 | + separator += 2; | ||
302 | + } else { | ||
303 | + separator = key; | ||
304 | + } | ||
305 | + separator = strchr(separator, '.'); | ||
306 | + } while (separator && separator[1] == '.'); | ||
307 | + | ||
308 | + if (separator) { | ||
309 | + *prefix = g_strndup(key, separator - key); | ||
310 | + *suffix = separator + 1; | ||
311 | + } else { | ||
312 | + *prefix = g_strdup(key); | ||
313 | + *suffix = NULL; | ||
314 | + } | ||
315 | + | ||
316 | + /* Unescape the '..' sequence into '.' */ | ||
317 | + for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { | ||
318 | + if ((*prefix)[i] == '.') { | ||
319 | + assert((*prefix)[i + 1] == '.'); | ||
320 | + i++; | ||
321 | + } | ||
322 | + (*prefix)[j] = (*prefix)[i]; | ||
323 | + } | ||
324 | + (*prefix)[j] = '\0'; | ||
325 | +} | ||
326 | + | ||
327 | +/** | ||
328 | + * qdict_is_list: | ||
329 | + * @maybe_list: dict to check if keys represent list elements. | ||
330 | + * | ||
331 | + * Determine whether all keys in @maybe_list are valid list elements. | ||
332 | + * If @maybe_list is non-zero in length and all the keys look like | ||
333 | + * valid list indexes, this will return 1. If @maybe_list is zero | ||
334 | + * length or all keys are non-numeric then it will return 0 to indicate | ||
335 | + * it is a normal qdict. If there is a mix of numeric and non-numeric | ||
336 | + * keys, or the list indexes are non-contiguous, an error is reported. | ||
337 | + * | ||
338 | + * Returns: 1 if a valid list, 0 if a dict, -1 on error | ||
339 | + */ | ||
340 | +static int qdict_is_list(QDict *maybe_list, Error **errp) | ||
341 | +{ | ||
342 | + const QDictEntry *ent; | ||
343 | + ssize_t len = 0; | ||
344 | + ssize_t max = -1; | ||
345 | + int is_list = -1; | ||
346 | + int64_t val; | ||
347 | + | ||
348 | + for (ent = qdict_first(maybe_list); ent != NULL; | ||
349 | + ent = qdict_next(maybe_list, ent)) { | ||
350 | + | ||
351 | + if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { | ||
352 | + if (is_list == -1) { | ||
353 | + is_list = 1; | ||
354 | + } else if (!is_list) { | ||
355 | + error_setg(errp, | ||
356 | + "Cannot mix list and non-list keys"); | ||
357 | + return -1; | ||
358 | + } | ||
359 | + len++; | ||
360 | + if (val > max) { | ||
361 | + max = val; | ||
362 | + } | ||
363 | + } else { | ||
364 | + if (is_list == -1) { | ||
365 | + is_list = 0; | ||
366 | + } else if (is_list) { | ||
367 | + error_setg(errp, | ||
368 | + "Cannot mix list and non-list keys"); | ||
369 | + return -1; | ||
370 | + } | ||
371 | + } | ||
372 | + } | ||
373 | + | ||
374 | + if (is_list == -1) { | ||
375 | + assert(!qdict_size(maybe_list)); | ||
376 | + is_list = 0; | ||
377 | + } | ||
378 | + | ||
379 | + /* NB this isn't a perfect check - e.g. it won't catch | ||
380 | + * a list containing '1', '+1', '01', '3', but that | ||
381 | + * does not matter - we've still proved that the | ||
382 | + * input is a list. It is up the caller to do a | ||
383 | + * stricter check if desired */ | ||
384 | + if (len != (max + 1)) { | ||
385 | + error_setg(errp, "List indices are not contiguous, " | ||
386 | + "saw %zd elements but %zd largest index", | ||
387 | + len, max); | ||
388 | + return -1; | ||
389 | + } | ||
390 | + | ||
391 | + return is_list; | ||
392 | +} | ||
393 | + | ||
394 | +/** | ||
395 | + * qdict_crumple: | ||
396 | + * @src: the original flat dictionary (only scalar values) to crumple | ||
397 | + * | ||
398 | + * Takes a flat dictionary whose keys use '.' separator to indicate | ||
399 | + * nesting, and values are scalars, and crumples it into a nested | ||
400 | + * structure. | ||
401 | + * | ||
402 | + * To include a literal '.' in a key name, it must be escaped as '..' | ||
403 | + * | ||
404 | + * For example, an input of: | ||
405 | + * | ||
406 | + * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', | ||
407 | + * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } | ||
408 | + * | ||
409 | + * will result in an output of: | ||
410 | + * | ||
411 | + * { | ||
412 | + * 'foo': [ | ||
413 | + * { 'bar': 'one', 'wizz': '1' }, | ||
414 | + * { 'bar': 'two', 'wizz': '2' } | ||
415 | + * ], | ||
416 | + * } | ||
417 | + * | ||
418 | + * The following scenarios in the input dict will result in an | ||
419 | + * error being returned: | ||
420 | + * | ||
421 | + * - Any values in @src are non-scalar types | ||
422 | + * - If keys in @src imply that a particular level is both a | ||
423 | + * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". | ||
424 | + * - If keys in @src imply that a particular level is a list, | ||
425 | + * but the indices are non-contiguous. e.g. "foo.0.bar" and | ||
426 | + * "foo.2.bar" without any "foo.1.bar" present. | ||
427 | + * - If keys in @src represent list indexes, but are not in | ||
428 | + * the "%zu" format. e.g. "foo.+0.bar" | ||
429 | + * | ||
430 | + * Returns: either a QDict or QList for the nested data structure, or NULL | ||
431 | + * on error | ||
432 | + */ | ||
433 | +QObject *qdict_crumple(const QDict *src, Error **errp) | ||
434 | +{ | ||
435 | + const QDictEntry *ent; | ||
436 | + QDict *two_level, *multi_level = NULL; | ||
437 | + QObject *dst = NULL, *child; | ||
438 | + size_t i; | ||
439 | + char *prefix = NULL; | ||
440 | + const char *suffix = NULL; | ||
441 | + int is_list; | ||
442 | + | ||
443 | + two_level = qdict_new(); | ||
444 | + | ||
445 | + /* Step 1: split our totally flat dict into a two level dict */ | ||
446 | + for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { | ||
447 | + if (qobject_type(ent->value) == QTYPE_QDICT || | ||
448 | + qobject_type(ent->value) == QTYPE_QLIST) { | ||
449 | + error_setg(errp, "Value %s is not a scalar", | ||
450 | + ent->key); | ||
451 | + goto error; | ||
452 | + } | ||
453 | + | ||
454 | + qdict_split_flat_key(ent->key, &prefix, &suffix); | ||
455 | + | ||
456 | + child = qdict_get(two_level, prefix); | ||
457 | + if (suffix) { | ||
458 | + QDict *child_dict = qobject_to(QDict, child); | ||
459 | + if (!child_dict) { | ||
460 | + if (child) { | ||
461 | + error_setg(errp, "Key %s prefix is already set as a scalar", | ||
462 | + prefix); | ||
463 | + goto error; | ||
464 | + } | ||
465 | + | ||
466 | + child_dict = qdict_new(); | ||
467 | + qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); | ||
468 | + } | ||
469 | + | ||
470 | + qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); | ||
471 | + } else { | ||
472 | + if (child) { | ||
473 | + error_setg(errp, "Key %s prefix is already set as a dict", | ||
474 | + prefix); | ||
475 | + goto error; | ||
476 | + } | ||
477 | + qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); | ||
478 | + } | ||
479 | + | ||
480 | + g_free(prefix); | ||
481 | + prefix = NULL; | ||
482 | + } | ||
483 | + | ||
484 | + /* Step 2: optionally process the two level dict recursively | ||
485 | + * into a multi-level dict */ | ||
486 | + multi_level = qdict_new(); | ||
487 | + for (ent = qdict_first(two_level); ent != NULL; | ||
488 | + ent = qdict_next(two_level, ent)) { | ||
489 | + QDict *dict = qobject_to(QDict, ent->value); | ||
490 | + if (dict) { | ||
491 | + child = qdict_crumple(dict, errp); | ||
492 | + if (!child) { | ||
493 | + goto error; | ||
494 | + } | ||
495 | + | ||
496 | + qdict_put_obj(multi_level, ent->key, child); | ||
497 | + } else { | ||
498 | + qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); | ||
499 | + } | ||
500 | + } | ||
501 | + qobject_unref(two_level); | ||
502 | + two_level = NULL; | ||
503 | + | ||
504 | + /* Step 3: detect if we need to turn our dict into list */ | ||
505 | + is_list = qdict_is_list(multi_level, errp); | ||
506 | + if (is_list < 0) { | ||
507 | + goto error; | ||
508 | + } | ||
509 | + | ||
510 | + if (is_list) { | ||
511 | + dst = QOBJECT(qlist_new()); | ||
512 | + | ||
513 | + for (i = 0; i < qdict_size(multi_level); i++) { | ||
514 | + char *key = g_strdup_printf("%zu", i); | ||
515 | + | ||
516 | + child = qdict_get(multi_level, key); | ||
517 | + g_free(key); | ||
518 | + | ||
519 | + if (!child) { | ||
520 | + error_setg(errp, "Missing list index %zu", i); | ||
521 | + goto error; | ||
522 | + } | ||
523 | + | ||
524 | + qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); | ||
525 | + } | ||
526 | + qobject_unref(multi_level); | ||
527 | + multi_level = NULL; | ||
528 | + } else { | ||
529 | + dst = QOBJECT(multi_level); | ||
530 | + } | ||
531 | + | ||
532 | + return dst; | ||
533 | + | ||
534 | + error: | ||
535 | + g_free(prefix); | ||
536 | + qobject_unref(multi_level); | ||
537 | + qobject_unref(two_level); | ||
538 | + qobject_unref(dst); | ||
539 | + return NULL; | ||
540 | +} | ||
541 | + | ||
542 | +/** | ||
543 | + * qdict_array_entries(): Returns the number of direct array entries if the | ||
544 | + * sub-QDict of src specified by the prefix in subqdict (or src itself for | ||
545 | + * prefix == "") is valid as an array, i.e. the length of the created list if | ||
546 | + * the sub-QDict would become empty after calling qdict_array_split() on it. If | ||
547 | + * the array is not valid, -EINVAL is returned. | ||
548 | + */ | ||
549 | +int qdict_array_entries(QDict *src, const char *subqdict) | ||
550 | +{ | ||
551 | + const QDictEntry *entry; | ||
552 | + unsigned i; | ||
553 | + unsigned entries = 0; | ||
554 | + size_t subqdict_len = strlen(subqdict); | ||
555 | + | ||
556 | + assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); | ||
557 | + | ||
558 | + /* qdict_array_split() loops until UINT_MAX, but as we want to return | ||
559 | + * negative errors, we only have a signed return value here. Any additional | ||
560 | + * entries will lead to -EINVAL. */ | ||
561 | + for (i = 0; i < INT_MAX; i++) { | ||
562 | + QObject *subqobj; | ||
563 | + int subqdict_entries; | ||
564 | + char *prefix = g_strdup_printf("%s%u.", subqdict, i); | ||
565 | + | ||
566 | + subqdict_entries = qdict_count_prefixed_entries(src, prefix); | ||
567 | + | ||
568 | + /* Remove ending "." */ | ||
569 | + prefix[strlen(prefix) - 1] = 0; | ||
570 | + subqobj = qdict_get(src, prefix); | ||
571 | + | ||
572 | + g_free(prefix); | ||
573 | + | ||
574 | + if (subqdict_entries < 0) { | ||
575 | + return subqdict_entries; | ||
576 | + } | ||
577 | + | ||
578 | + /* There may be either a single subordinate object (named "%u") or | ||
579 | + * multiple objects (each with a key prefixed "%u."), but not both. */ | ||
580 | + if (subqobj && subqdict_entries) { | ||
581 | + return -EINVAL; | ||
582 | + } else if (!subqobj && !subqdict_entries) { | ||
583 | + break; | ||
584 | + } | ||
585 | + | ||
586 | + entries += subqdict_entries ? subqdict_entries : 1; | ||
587 | + } | ||
588 | + | ||
589 | + /* Consider everything handled that isn't part of the given sub-QDict */ | ||
590 | + for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { | ||
591 | + if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { | ||
592 | + entries++; | ||
593 | + } | ||
594 | + } | ||
595 | + | ||
596 | + /* Anything left in the sub-QDict that wasn't handled? */ | ||
597 | + if (qdict_size(src) != entries) { | ||
598 | + return -EINVAL; | ||
599 | + } | ||
600 | + | ||
601 | + return i; | ||
602 | +} | ||
603 | + | ||
604 | +/** | ||
605 | + * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all | ||
606 | + * elements from src to dest. | ||
607 | + * | ||
608 | + * If an element from src has a key already present in dest, it will not be | ||
609 | + * moved unless overwrite is true. | ||
610 | + * | ||
611 | + * If overwrite is true, the conflicting values in dest will be discarded and | ||
612 | + * replaced by the corresponding values from src. | ||
613 | + * | ||
614 | + * Therefore, with overwrite being true, the src QDict will always be empty when | ||
615 | + * this function returns. If overwrite is false, the src QDict will be empty | ||
616 | + * iff there were no conflicts. | ||
617 | + */ | ||
618 | +void qdict_join(QDict *dest, QDict *src, bool overwrite) | ||
619 | +{ | ||
620 | + const QDictEntry *entry, *next; | ||
621 | + | ||
622 | + entry = qdict_first(src); | ||
623 | + while (entry) { | ||
624 | + next = qdict_next(src, entry); | ||
625 | + | ||
626 | + if (overwrite || !qdict_haskey(dest, entry->key)) { | ||
627 | + qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); | ||
628 | + qdict_del(src, entry->key); | ||
629 | + } | ||
630 | + | ||
631 | + entry = next; | ||
632 | + } | ||
633 | +} | ||
634 | + | ||
635 | +/** | ||
636 | + * qdict_rename_keys(): Rename keys in qdict according to the replacements | ||
637 | + * specified in the array renames. The array must be terminated by an entry | ||
638 | + * with from = NULL. | ||
639 | + * | ||
640 | + * The renames are performed individually in the order of the array, so entries | ||
641 | + * may be renamed multiple times and may or may not conflict depending on the | ||
642 | + * order of the renames array. | ||
643 | + * | ||
644 | + * Returns true for success, false in error cases. | ||
645 | + */ | ||
646 | +bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) | ||
647 | +{ | ||
648 | + QObject *qobj; | ||
649 | + | ||
650 | + while (renames->from) { | ||
651 | + if (qdict_haskey(qdict, renames->from)) { | ||
652 | + if (qdict_haskey(qdict, renames->to)) { | ||
653 | + error_setg(errp, "'%s' and its alias '%s' can't be used at the " | ||
654 | + "same time", renames->to, renames->from); | ||
655 | + return false; | ||
656 | + } | ||
657 | + | ||
658 | + qobj = qdict_get(qdict, renames->from); | ||
659 | + qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); | ||
660 | + qdict_del(qdict, renames->from); | ||
661 | + } | ||
662 | + | ||
663 | + renames++; | ||
664 | + } | ||
665 | + return true; | ||
666 | +} | ||
667 | diff --git a/qobject/qdict.c b/qobject/qdict.c | ||
668 | index XXXXXXX..XXXXXXX 100644 | 121 | index XXXXXXX..XXXXXXX 100644 |
669 | --- a/qobject/qdict.c | 122 | --- a/tests/qemu-iotests/051.pc.out |
670 | +++ b/qobject/qdict.c | 123 | +++ b/tests/qemu-iotests/051.pc.out |
671 | @@ -XXX,XX +XXX,XX @@ | 124 | @@ -XXX,XX +XXX,XX @@ QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty |
672 | */ | 125 | |
673 | 126 | Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device ide-hd,drive=disk,share-rw=on | |
674 | #include "qemu/osdep.h" | 127 | QEMU X.Y.Z monitor - type 'help' for more information |
675 | -#include "block/qdict.h" | 128 | -QEMU_PROG: -device ide-hd,drive=disk,share-rw=on: Cannot change iothread of active block backend |
676 | #include "qapi/qmp/qnum.h" | 129 | +(qemu) QEMU_PROG: -device ide-hd,drive=disk,share-rw=on: Cannot change iothread of active block backend |
677 | #include "qapi/qmp/qdict.h" | 130 | |
678 | #include "qapi/qmp/qbool.h" | 131 | Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,share-rw=on |
679 | -#include "qapi/qmp/qlist.h" | 132 | QEMU X.Y.Z monitor - type 'help' for more information |
680 | #include "qapi/qmp/qnull.h" | 133 | -QEMU_PROG: -device virtio-blk-pci,drive=disk,share-rw=on: Cannot change iothread of active block backend |
681 | #include "qapi/qmp/qstring.h" | 134 | +(qemu) QEMU_PROG: -device virtio-blk-pci,drive=disk,share-rw=on: Cannot change iothread of active block backend |
682 | -#include "qapi/error.h" | 135 | |
683 | -#include "qemu/queue.h" | 136 | Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device lsi53c895a,id=lsi0 -device scsi-hd,bus=lsi0.0,drive=disk,share-rw=on |
684 | -#include "qemu-common.h" | 137 | QEMU X.Y.Z monitor - type 'help' for more information |
685 | -#include "qemu/cutils.h" | 138 | @@ -XXX,XX +XXX,XX @@ QEMU X.Y.Z monitor - type 'help' for more information |
686 | 139 | ||
687 | /** | 140 | Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on |
688 | * qdict_new(): Create a new QDict | 141 | QEMU X.Y.Z monitor - type 'help' for more information |
689 | @@ -XXX,XX +XXX,XX @@ void qdict_destroy_obj(QObject *obj) | 142 | -QEMU_PROG: -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on: Cannot change iothread of active block backend |
690 | 143 | +(qemu) QEMU_PROG: -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on: Cannot change iothread of active block backend | |
691 | g_free(qdict); | 144 | |
692 | } | 145 | Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-scsi,id=virtio-scsi1,iothread=thread0 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on |
693 | - | 146 | QEMU X.Y.Z monitor - type 'help' for more information |
694 | -/** | ||
695 | - * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the | ||
696 | - * value of 'key' in 'src' is copied there (and the refcount increased | ||
697 | - * accordingly). | ||
698 | - */ | ||
699 | -void qdict_copy_default(QDict *dst, QDict *src, const char *key) | ||
700 | -{ | ||
701 | - QObject *val; | ||
702 | - | ||
703 | - if (qdict_haskey(dst, key)) { | ||
704 | - return; | ||
705 | - } | ||
706 | - | ||
707 | - val = qdict_get(src, key); | ||
708 | - if (val) { | ||
709 | - qdict_put_obj(dst, key, qobject_ref(val)); | ||
710 | - } | ||
711 | -} | ||
712 | - | ||
713 | -/** | ||
714 | - * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a | ||
715 | - * new QString initialised by 'val' is put there. | ||
716 | - */ | ||
717 | -void qdict_set_default_str(QDict *dst, const char *key, const char *val) | ||
718 | -{ | ||
719 | - if (qdict_haskey(dst, key)) { | ||
720 | - return; | ||
721 | - } | ||
722 | - | ||
723 | - qdict_put_str(dst, key, val); | ||
724 | -} | ||
725 | - | ||
726 | -static void qdict_flatten_qdict(QDict *qdict, QDict *target, | ||
727 | - const char *prefix); | ||
728 | - | ||
729 | -static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) | ||
730 | -{ | ||
731 | - QObject *value; | ||
732 | - const QListEntry *entry; | ||
733 | - char *new_key; | ||
734 | - int i; | ||
735 | - | ||
736 | - /* This function is never called with prefix == NULL, i.e., it is always | ||
737 | - * called from within qdict_flatten_q(list|dict)(). Therefore, it does not | ||
738 | - * need to remove list entries during the iteration (the whole list will be | ||
739 | - * deleted eventually anyway from qdict_flatten_qdict()). */ | ||
740 | - assert(prefix); | ||
741 | - | ||
742 | - entry = qlist_first(qlist); | ||
743 | - | ||
744 | - for (i = 0; entry; entry = qlist_next(entry), i++) { | ||
745 | - value = qlist_entry_obj(entry); | ||
746 | - new_key = g_strdup_printf("%s.%i", prefix, i); | ||
747 | - | ||
748 | - if (qobject_type(value) == QTYPE_QDICT) { | ||
749 | - qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); | ||
750 | - } else if (qobject_type(value) == QTYPE_QLIST) { | ||
751 | - qdict_flatten_qlist(qobject_to(QList, value), target, new_key); | ||
752 | - } else { | ||
753 | - /* All other types are moved to the target unchanged. */ | ||
754 | - qdict_put_obj(target, new_key, qobject_ref(value)); | ||
755 | - } | ||
756 | - | ||
757 | - g_free(new_key); | ||
758 | - } | ||
759 | -} | ||
760 | - | ||
761 | -static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
762 | -{ | ||
763 | - QObject *value; | ||
764 | - const QDictEntry *entry, *next; | ||
765 | - char *new_key; | ||
766 | - bool delete; | ||
767 | - | ||
768 | - entry = qdict_first(qdict); | ||
769 | - | ||
770 | - while (entry != NULL) { | ||
771 | - | ||
772 | - next = qdict_next(qdict, entry); | ||
773 | - value = qdict_entry_value(entry); | ||
774 | - new_key = NULL; | ||
775 | - delete = false; | ||
776 | - | ||
777 | - if (prefix) { | ||
778 | - new_key = g_strdup_printf("%s.%s", prefix, entry->key); | ||
779 | - } | ||
780 | - | ||
781 | - if (qobject_type(value) == QTYPE_QDICT) { | ||
782 | - /* Entries of QDicts are processed recursively, the QDict object | ||
783 | - * itself disappears. */ | ||
784 | - qdict_flatten_qdict(qobject_to(QDict, value), target, | ||
785 | - new_key ? new_key : entry->key); | ||
786 | - delete = true; | ||
787 | - } else if (qobject_type(value) == QTYPE_QLIST) { | ||
788 | - qdict_flatten_qlist(qobject_to(QList, value), target, | ||
789 | - new_key ? new_key : entry->key); | ||
790 | - delete = true; | ||
791 | - } else if (prefix) { | ||
792 | - /* All other objects are moved to the target unchanged. */ | ||
793 | - qdict_put_obj(target, new_key, qobject_ref(value)); | ||
794 | - delete = true; | ||
795 | - } | ||
796 | - | ||
797 | - g_free(new_key); | ||
798 | - | ||
799 | - if (delete) { | ||
800 | - qdict_del(qdict, entry->key); | ||
801 | - | ||
802 | - /* Restart loop after modifying the iterated QDict */ | ||
803 | - entry = qdict_first(qdict); | ||
804 | - continue; | ||
805 | - } | ||
806 | - | ||
807 | - entry = next; | ||
808 | - } | ||
809 | -} | ||
810 | - | ||
811 | -/** | ||
812 | - * qdict_flatten(): For each nested QDict with key x, all fields with key y | ||
813 | - * are moved to this QDict and their key is renamed to "x.y". For each nested | ||
814 | - * QList with key x, the field at index y is moved to this QDict with the key | ||
815 | - * "x.y" (i.e., the reverse of what qdict_array_split() does). | ||
816 | - * This operation is applied recursively for nested QDicts and QLists. | ||
817 | - */ | ||
818 | -void qdict_flatten(QDict *qdict) | ||
819 | -{ | ||
820 | - qdict_flatten_qdict(qdict, qdict, NULL); | ||
821 | -} | ||
822 | - | ||
823 | -/* extract all the src QDict entries starting by start into dst */ | ||
824 | -void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) | ||
825 | - | ||
826 | -{ | ||
827 | - const QDictEntry *entry, *next; | ||
828 | - const char *p; | ||
829 | - | ||
830 | - *dst = qdict_new(); | ||
831 | - entry = qdict_first(src); | ||
832 | - | ||
833 | - while (entry != NULL) { | ||
834 | - next = qdict_next(src, entry); | ||
835 | - if (strstart(entry->key, start, &p)) { | ||
836 | - qdict_put_obj(*dst, p, qobject_ref(entry->value)); | ||
837 | - qdict_del(src, entry->key); | ||
838 | - } | ||
839 | - entry = next; | ||
840 | - } | ||
841 | -} | ||
842 | - | ||
843 | -static int qdict_count_prefixed_entries(const QDict *src, const char *start) | ||
844 | -{ | ||
845 | - const QDictEntry *entry; | ||
846 | - int count = 0; | ||
847 | - | ||
848 | - for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { | ||
849 | - if (strstart(entry->key, start, NULL)) { | ||
850 | - if (count == INT_MAX) { | ||
851 | - return -ERANGE; | ||
852 | - } | ||
853 | - count++; | ||
854 | - } | ||
855 | - } | ||
856 | - | ||
857 | - return count; | ||
858 | -} | ||
859 | - | ||
860 | -/** | ||
861 | - * qdict_array_split(): This function moves array-like elements of a QDict into | ||
862 | - * a new QList. Every entry in the original QDict with a key "%u" or one | ||
863 | - * prefixed "%u.", where %u designates an unsigned integer starting at 0 and | ||
864 | - * incrementally counting up, will be moved to a new QDict at index %u in the | ||
865 | - * output QList with the key prefix removed, if that prefix is "%u.". If the | ||
866 | - * whole key is just "%u", the whole QObject will be moved unchanged without | ||
867 | - * creating a new QDict. The function terminates when there is no entry in the | ||
868 | - * QDict with a prefix directly (incrementally) following the last one; it also | ||
869 | - * returns if there are both entries with "%u" and "%u." for the same index %u. | ||
870 | - * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} | ||
871 | - * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) | ||
872 | - * => [{"a": 42, "b": 23}, {"x": 0}, 66] | ||
873 | - * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) | ||
874 | - */ | ||
875 | -void qdict_array_split(QDict *src, QList **dst) | ||
876 | -{ | ||
877 | - unsigned i; | ||
878 | - | ||
879 | - *dst = qlist_new(); | ||
880 | - | ||
881 | - for (i = 0; i < UINT_MAX; i++) { | ||
882 | - QObject *subqobj; | ||
883 | - bool is_subqdict; | ||
884 | - QDict *subqdict; | ||
885 | - char indexstr[32], prefix[32]; | ||
886 | - size_t snprintf_ret; | ||
887 | - | ||
888 | - snprintf_ret = snprintf(indexstr, 32, "%u", i); | ||
889 | - assert(snprintf_ret < 32); | ||
890 | - | ||
891 | - subqobj = qdict_get(src, indexstr); | ||
892 | - | ||
893 | - snprintf_ret = snprintf(prefix, 32, "%u.", i); | ||
894 | - assert(snprintf_ret < 32); | ||
895 | - | ||
896 | - /* Overflow is the same as positive non-zero results */ | ||
897 | - is_subqdict = qdict_count_prefixed_entries(src, prefix); | ||
898 | - | ||
899 | - // There may be either a single subordinate object (named "%u") or | ||
900 | - // multiple objects (each with a key prefixed "%u."), but not both. | ||
901 | - if (!subqobj == !is_subqdict) { | ||
902 | - break; | ||
903 | - } | ||
904 | - | ||
905 | - if (is_subqdict) { | ||
906 | - qdict_extract_subqdict(src, &subqdict, prefix); | ||
907 | - assert(qdict_size(subqdict) > 0); | ||
908 | - } else { | ||
909 | - qobject_ref(subqobj); | ||
910 | - qdict_del(src, indexstr); | ||
911 | - } | ||
912 | - | ||
913 | - qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); | ||
914 | - } | ||
915 | -} | ||
916 | - | ||
917 | -/** | ||
918 | - * qdict_split_flat_key: | ||
919 | - * @key: the key string to split | ||
920 | - * @prefix: non-NULL pointer to hold extracted prefix | ||
921 | - * @suffix: non-NULL pointer to remaining suffix | ||
922 | - * | ||
923 | - * Given a flattened key such as 'foo.0.bar', split it into two parts | ||
924 | - * at the first '.' separator. Allows double dot ('..') to escape the | ||
925 | - * normal separator. | ||
926 | - * | ||
927 | - * e.g. | ||
928 | - * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' | ||
929 | - * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' | ||
930 | - * | ||
931 | - * The '..' sequence will be unescaped in the returned 'prefix' | ||
932 | - * string. The 'suffix' string will be left in escaped format, so it | ||
933 | - * can be fed back into the qdict_split_flat_key() key as the input | ||
934 | - * later. | ||
935 | - * | ||
936 | - * The caller is responsible for freeing the string returned in @prefix | ||
937 | - * using g_free(). | ||
938 | - */ | ||
939 | -static void qdict_split_flat_key(const char *key, char **prefix, | ||
940 | - const char **suffix) | ||
941 | -{ | ||
942 | - const char *separator; | ||
943 | - size_t i, j; | ||
944 | - | ||
945 | - /* Find first '.' separator, but if there is a pair '..' | ||
946 | - * that acts as an escape, so skip over '..' */ | ||
947 | - separator = NULL; | ||
948 | - do { | ||
949 | - if (separator) { | ||
950 | - separator += 2; | ||
951 | - } else { | ||
952 | - separator = key; | ||
953 | - } | ||
954 | - separator = strchr(separator, '.'); | ||
955 | - } while (separator && separator[1] == '.'); | ||
956 | - | ||
957 | - if (separator) { | ||
958 | - *prefix = g_strndup(key, separator - key); | ||
959 | - *suffix = separator + 1; | ||
960 | - } else { | ||
961 | - *prefix = g_strdup(key); | ||
962 | - *suffix = NULL; | ||
963 | - } | ||
964 | - | ||
965 | - /* Unescape the '..' sequence into '.' */ | ||
966 | - for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { | ||
967 | - if ((*prefix)[i] == '.') { | ||
968 | - assert((*prefix)[i + 1] == '.'); | ||
969 | - i++; | ||
970 | - } | ||
971 | - (*prefix)[j] = (*prefix)[i]; | ||
972 | - } | ||
973 | - (*prefix)[j] = '\0'; | ||
974 | -} | ||
975 | - | ||
976 | -/** | ||
977 | - * qdict_is_list: | ||
978 | - * @maybe_list: dict to check if keys represent list elements. | ||
979 | - * | ||
980 | - * Determine whether all keys in @maybe_list are valid list elements. | ||
981 | - * If @maybe_list is non-zero in length and all the keys look like | ||
982 | - * valid list indexes, this will return 1. If @maybe_list is zero | ||
983 | - * length or all keys are non-numeric then it will return 0 to indicate | ||
984 | - * it is a normal qdict. If there is a mix of numeric and non-numeric | ||
985 | - * keys, or the list indexes are non-contiguous, an error is reported. | ||
986 | - * | ||
987 | - * Returns: 1 if a valid list, 0 if a dict, -1 on error | ||
988 | - */ | ||
989 | -static int qdict_is_list(QDict *maybe_list, Error **errp) | ||
990 | -{ | ||
991 | - const QDictEntry *ent; | ||
992 | - ssize_t len = 0; | ||
993 | - ssize_t max = -1; | ||
994 | - int is_list = -1; | ||
995 | - int64_t val; | ||
996 | - | ||
997 | - for (ent = qdict_first(maybe_list); ent != NULL; | ||
998 | - ent = qdict_next(maybe_list, ent)) { | ||
999 | - | ||
1000 | - if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { | ||
1001 | - if (is_list == -1) { | ||
1002 | - is_list = 1; | ||
1003 | - } else if (!is_list) { | ||
1004 | - error_setg(errp, | ||
1005 | - "Cannot mix list and non-list keys"); | ||
1006 | - return -1; | ||
1007 | - } | ||
1008 | - len++; | ||
1009 | - if (val > max) { | ||
1010 | - max = val; | ||
1011 | - } | ||
1012 | - } else { | ||
1013 | - if (is_list == -1) { | ||
1014 | - is_list = 0; | ||
1015 | - } else if (is_list) { | ||
1016 | - error_setg(errp, | ||
1017 | - "Cannot mix list and non-list keys"); | ||
1018 | - return -1; | ||
1019 | - } | ||
1020 | - } | ||
1021 | - } | ||
1022 | - | ||
1023 | - if (is_list == -1) { | ||
1024 | - assert(!qdict_size(maybe_list)); | ||
1025 | - is_list = 0; | ||
1026 | - } | ||
1027 | - | ||
1028 | - /* NB this isn't a perfect check - e.g. it won't catch | ||
1029 | - * a list containing '1', '+1', '01', '3', but that | ||
1030 | - * does not matter - we've still proved that the | ||
1031 | - * input is a list. It is up the caller to do a | ||
1032 | - * stricter check if desired */ | ||
1033 | - if (len != (max + 1)) { | ||
1034 | - error_setg(errp, "List indices are not contiguous, " | ||
1035 | - "saw %zd elements but %zd largest index", | ||
1036 | - len, max); | ||
1037 | - return -1; | ||
1038 | - } | ||
1039 | - | ||
1040 | - return is_list; | ||
1041 | -} | ||
1042 | - | ||
1043 | -/** | ||
1044 | - * qdict_crumple: | ||
1045 | - * @src: the original flat dictionary (only scalar values) to crumple | ||
1046 | - * | ||
1047 | - * Takes a flat dictionary whose keys use '.' separator to indicate | ||
1048 | - * nesting, and values are scalars, and crumples it into a nested | ||
1049 | - * structure. | ||
1050 | - * | ||
1051 | - * To include a literal '.' in a key name, it must be escaped as '..' | ||
1052 | - * | ||
1053 | - * For example, an input of: | ||
1054 | - * | ||
1055 | - * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', | ||
1056 | - * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } | ||
1057 | - * | ||
1058 | - * will result in an output of: | ||
1059 | - * | ||
1060 | - * { | ||
1061 | - * 'foo': [ | ||
1062 | - * { 'bar': 'one', 'wizz': '1' }, | ||
1063 | - * { 'bar': 'two', 'wizz': '2' } | ||
1064 | - * ], | ||
1065 | - * } | ||
1066 | - * | ||
1067 | - * The following scenarios in the input dict will result in an | ||
1068 | - * error being returned: | ||
1069 | - * | ||
1070 | - * - Any values in @src are non-scalar types | ||
1071 | - * - If keys in @src imply that a particular level is both a | ||
1072 | - * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". | ||
1073 | - * - If keys in @src imply that a particular level is a list, | ||
1074 | - * but the indices are non-contiguous. e.g. "foo.0.bar" and | ||
1075 | - * "foo.2.bar" without any "foo.1.bar" present. | ||
1076 | - * - If keys in @src represent list indexes, but are not in | ||
1077 | - * the "%zu" format. e.g. "foo.+0.bar" | ||
1078 | - * | ||
1079 | - * Returns: either a QDict or QList for the nested data structure, or NULL | ||
1080 | - * on error | ||
1081 | - */ | ||
1082 | -QObject *qdict_crumple(const QDict *src, Error **errp) | ||
1083 | -{ | ||
1084 | - const QDictEntry *ent; | ||
1085 | - QDict *two_level, *multi_level = NULL; | ||
1086 | - QObject *dst = NULL, *child; | ||
1087 | - size_t i; | ||
1088 | - char *prefix = NULL; | ||
1089 | - const char *suffix = NULL; | ||
1090 | - int is_list; | ||
1091 | - | ||
1092 | - two_level = qdict_new(); | ||
1093 | - | ||
1094 | - /* Step 1: split our totally flat dict into a two level dict */ | ||
1095 | - for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { | ||
1096 | - if (qobject_type(ent->value) == QTYPE_QDICT || | ||
1097 | - qobject_type(ent->value) == QTYPE_QLIST) { | ||
1098 | - error_setg(errp, "Value %s is not a scalar", | ||
1099 | - ent->key); | ||
1100 | - goto error; | ||
1101 | - } | ||
1102 | - | ||
1103 | - qdict_split_flat_key(ent->key, &prefix, &suffix); | ||
1104 | - | ||
1105 | - child = qdict_get(two_level, prefix); | ||
1106 | - if (suffix) { | ||
1107 | - QDict *child_dict = qobject_to(QDict, child); | ||
1108 | - if (!child_dict) { | ||
1109 | - if (child) { | ||
1110 | - error_setg(errp, "Key %s prefix is already set as a scalar", | ||
1111 | - prefix); | ||
1112 | - goto error; | ||
1113 | - } | ||
1114 | - | ||
1115 | - child_dict = qdict_new(); | ||
1116 | - qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); | ||
1117 | - } | ||
1118 | - | ||
1119 | - qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); | ||
1120 | - } else { | ||
1121 | - if (child) { | ||
1122 | - error_setg(errp, "Key %s prefix is already set as a dict", | ||
1123 | - prefix); | ||
1124 | - goto error; | ||
1125 | - } | ||
1126 | - qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); | ||
1127 | - } | ||
1128 | - | ||
1129 | - g_free(prefix); | ||
1130 | - prefix = NULL; | ||
1131 | - } | ||
1132 | - | ||
1133 | - /* Step 2: optionally process the two level dict recursively | ||
1134 | - * into a multi-level dict */ | ||
1135 | - multi_level = qdict_new(); | ||
1136 | - for (ent = qdict_first(two_level); ent != NULL; | ||
1137 | - ent = qdict_next(two_level, ent)) { | ||
1138 | - QDict *dict = qobject_to(QDict, ent->value); | ||
1139 | - if (dict) { | ||
1140 | - child = qdict_crumple(dict, errp); | ||
1141 | - if (!child) { | ||
1142 | - goto error; | ||
1143 | - } | ||
1144 | - | ||
1145 | - qdict_put_obj(multi_level, ent->key, child); | ||
1146 | - } else { | ||
1147 | - qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); | ||
1148 | - } | ||
1149 | - } | ||
1150 | - qobject_unref(two_level); | ||
1151 | - two_level = NULL; | ||
1152 | - | ||
1153 | - /* Step 3: detect if we need to turn our dict into list */ | ||
1154 | - is_list = qdict_is_list(multi_level, errp); | ||
1155 | - if (is_list < 0) { | ||
1156 | - goto error; | ||
1157 | - } | ||
1158 | - | ||
1159 | - if (is_list) { | ||
1160 | - dst = QOBJECT(qlist_new()); | ||
1161 | - | ||
1162 | - for (i = 0; i < qdict_size(multi_level); i++) { | ||
1163 | - char *key = g_strdup_printf("%zu", i); | ||
1164 | - | ||
1165 | - child = qdict_get(multi_level, key); | ||
1166 | - g_free(key); | ||
1167 | - | ||
1168 | - if (!child) { | ||
1169 | - error_setg(errp, "Missing list index %zu", i); | ||
1170 | - goto error; | ||
1171 | - } | ||
1172 | - | ||
1173 | - qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); | ||
1174 | - } | ||
1175 | - qobject_unref(multi_level); | ||
1176 | - multi_level = NULL; | ||
1177 | - } else { | ||
1178 | - dst = QOBJECT(multi_level); | ||
1179 | - } | ||
1180 | - | ||
1181 | - return dst; | ||
1182 | - | ||
1183 | - error: | ||
1184 | - g_free(prefix); | ||
1185 | - qobject_unref(multi_level); | ||
1186 | - qobject_unref(two_level); | ||
1187 | - qobject_unref(dst); | ||
1188 | - return NULL; | ||
1189 | -} | ||
1190 | - | ||
1191 | -/** | ||
1192 | - * qdict_array_entries(): Returns the number of direct array entries if the | ||
1193 | - * sub-QDict of src specified by the prefix in subqdict (or src itself for | ||
1194 | - * prefix == "") is valid as an array, i.e. the length of the created list if | ||
1195 | - * the sub-QDict would become empty after calling qdict_array_split() on it. If | ||
1196 | - * the array is not valid, -EINVAL is returned. | ||
1197 | - */ | ||
1198 | -int qdict_array_entries(QDict *src, const char *subqdict) | ||
1199 | -{ | ||
1200 | - const QDictEntry *entry; | ||
1201 | - unsigned i; | ||
1202 | - unsigned entries = 0; | ||
1203 | - size_t subqdict_len = strlen(subqdict); | ||
1204 | - | ||
1205 | - assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); | ||
1206 | - | ||
1207 | - /* qdict_array_split() loops until UINT_MAX, but as we want to return | ||
1208 | - * negative errors, we only have a signed return value here. Any additional | ||
1209 | - * entries will lead to -EINVAL. */ | ||
1210 | - for (i = 0; i < INT_MAX; i++) { | ||
1211 | - QObject *subqobj; | ||
1212 | - int subqdict_entries; | ||
1213 | - char *prefix = g_strdup_printf("%s%u.", subqdict, i); | ||
1214 | - | ||
1215 | - subqdict_entries = qdict_count_prefixed_entries(src, prefix); | ||
1216 | - | ||
1217 | - /* Remove ending "." */ | ||
1218 | - prefix[strlen(prefix) - 1] = 0; | ||
1219 | - subqobj = qdict_get(src, prefix); | ||
1220 | - | ||
1221 | - g_free(prefix); | ||
1222 | - | ||
1223 | - if (subqdict_entries < 0) { | ||
1224 | - return subqdict_entries; | ||
1225 | - } | ||
1226 | - | ||
1227 | - /* There may be either a single subordinate object (named "%u") or | ||
1228 | - * multiple objects (each with a key prefixed "%u."), but not both. */ | ||
1229 | - if (subqobj && subqdict_entries) { | ||
1230 | - return -EINVAL; | ||
1231 | - } else if (!subqobj && !subqdict_entries) { | ||
1232 | - break; | ||
1233 | - } | ||
1234 | - | ||
1235 | - entries += subqdict_entries ? subqdict_entries : 1; | ||
1236 | - } | ||
1237 | - | ||
1238 | - /* Consider everything handled that isn't part of the given sub-QDict */ | ||
1239 | - for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { | ||
1240 | - if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { | ||
1241 | - entries++; | ||
1242 | - } | ||
1243 | - } | ||
1244 | - | ||
1245 | - /* Anything left in the sub-QDict that wasn't handled? */ | ||
1246 | - if (qdict_size(src) != entries) { | ||
1247 | - return -EINVAL; | ||
1248 | - } | ||
1249 | - | ||
1250 | - return i; | ||
1251 | -} | ||
1252 | - | ||
1253 | -/** | ||
1254 | - * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all | ||
1255 | - * elements from src to dest. | ||
1256 | - * | ||
1257 | - * If an element from src has a key already present in dest, it will not be | ||
1258 | - * moved unless overwrite is true. | ||
1259 | - * | ||
1260 | - * If overwrite is true, the conflicting values in dest will be discarded and | ||
1261 | - * replaced by the corresponding values from src. | ||
1262 | - * | ||
1263 | - * Therefore, with overwrite being true, the src QDict will always be empty when | ||
1264 | - * this function returns. If overwrite is false, the src QDict will be empty | ||
1265 | - * iff there were no conflicts. | ||
1266 | - */ | ||
1267 | -void qdict_join(QDict *dest, QDict *src, bool overwrite) | ||
1268 | -{ | ||
1269 | - const QDictEntry *entry, *next; | ||
1270 | - | ||
1271 | - entry = qdict_first(src); | ||
1272 | - while (entry) { | ||
1273 | - next = qdict_next(src, entry); | ||
1274 | - | ||
1275 | - if (overwrite || !qdict_haskey(dest, entry->key)) { | ||
1276 | - qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); | ||
1277 | - qdict_del(src, entry->key); | ||
1278 | - } | ||
1279 | - | ||
1280 | - entry = next; | ||
1281 | - } | ||
1282 | -} | ||
1283 | - | ||
1284 | -/** | ||
1285 | - * qdict_rename_keys(): Rename keys in qdict according to the replacements | ||
1286 | - * specified in the array renames. The array must be terminated by an entry | ||
1287 | - * with from = NULL. | ||
1288 | - * | ||
1289 | - * The renames are performed individually in the order of the array, so entries | ||
1290 | - * may be renamed multiple times and may or may not conflict depending on the | ||
1291 | - * order of the renames array. | ||
1292 | - * | ||
1293 | - * Returns true for success, false in error cases. | ||
1294 | - */ | ||
1295 | -bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) | ||
1296 | -{ | ||
1297 | - QObject *qobj; | ||
1298 | - | ||
1299 | - while (renames->from) { | ||
1300 | - if (qdict_haskey(qdict, renames->from)) { | ||
1301 | - if (qdict_haskey(qdict, renames->to)) { | ||
1302 | - error_setg(errp, "'%s' and its alias '%s' can't be used at the " | ||
1303 | - "same time", renames->to, renames->from); | ||
1304 | - return false; | ||
1305 | - } | ||
1306 | - | ||
1307 | - qobj = qdict_get(qdict, renames->from); | ||
1308 | - qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); | ||
1309 | - qdict_del(qdict, renames->from); | ||
1310 | - } | ||
1311 | - | ||
1312 | - renames++; | ||
1313 | - } | ||
1314 | - return true; | ||
1315 | -} | ||
1316 | diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c | ||
1317 | new file mode 100644 | ||
1318 | index XXXXXXX..XXXXXXX | ||
1319 | --- /dev/null | ||
1320 | +++ b/tests/check-block-qdict.c | ||
1321 | @@ -XXX,XX +XXX,XX @@ | ||
1322 | +/* | ||
1323 | + * Unit-tests for Block layer QDict extras | ||
1324 | + * | ||
1325 | + * Copyright (c) 2013-2018 Red Hat, Inc. | ||
1326 | + * | ||
1327 | + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | ||
1328 | + * See the COPYING.LIB file in the top-level directory. | ||
1329 | + */ | ||
1330 | + | ||
1331 | +#include "qemu/osdep.h" | ||
1332 | +#include "block/qdict.h" | ||
1333 | +#include "qapi/qmp/qlist.h" | ||
1334 | +#include "qapi/qmp/qnum.h" | ||
1335 | +#include "qapi/error.h" | ||
1336 | + | ||
1337 | +static void qdict_defaults_test(void) | ||
1338 | +{ | ||
1339 | + QDict *dict, *copy; | ||
1340 | + | ||
1341 | + dict = qdict_new(); | ||
1342 | + copy = qdict_new(); | ||
1343 | + | ||
1344 | + qdict_set_default_str(dict, "foo", "abc"); | ||
1345 | + qdict_set_default_str(dict, "foo", "def"); | ||
1346 | + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); | ||
1347 | + qdict_set_default_str(dict, "bar", "ghi"); | ||
1348 | + | ||
1349 | + qdict_copy_default(copy, dict, "foo"); | ||
1350 | + g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); | ||
1351 | + qdict_set_default_str(copy, "bar", "xyz"); | ||
1352 | + qdict_copy_default(copy, dict, "bar"); | ||
1353 | + g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); | ||
1354 | + | ||
1355 | + qobject_unref(copy); | ||
1356 | + qobject_unref(dict); | ||
1357 | +} | ||
1358 | + | ||
1359 | +static void qdict_flatten_test(void) | ||
1360 | +{ | ||
1361 | + QList *list1 = qlist_new(); | ||
1362 | + QList *list2 = qlist_new(); | ||
1363 | + QDict *dict1 = qdict_new(); | ||
1364 | + QDict *dict2 = qdict_new(); | ||
1365 | + QDict *dict3 = qdict_new(); | ||
1366 | + | ||
1367 | + /* | ||
1368 | + * Test the flattening of | ||
1369 | + * | ||
1370 | + * { | ||
1371 | + * "e": [ | ||
1372 | + * 42, | ||
1373 | + * [ | ||
1374 | + * 23, | ||
1375 | + * 66, | ||
1376 | + * { | ||
1377 | + * "a": 0, | ||
1378 | + * "b": 1 | ||
1379 | + * } | ||
1380 | + * ] | ||
1381 | + * ], | ||
1382 | + * "f": { | ||
1383 | + * "c": 2, | ||
1384 | + * "d": 3, | ||
1385 | + * }, | ||
1386 | + * "g": 4 | ||
1387 | + * } | ||
1388 | + * | ||
1389 | + * to | ||
1390 | + * | ||
1391 | + * { | ||
1392 | + * "e.0": 42, | ||
1393 | + * "e.1.0": 23, | ||
1394 | + * "e.1.1": 66, | ||
1395 | + * "e.1.2.a": 0, | ||
1396 | + * "e.1.2.b": 1, | ||
1397 | + * "f.c": 2, | ||
1398 | + * "f.d": 3, | ||
1399 | + * "g": 4 | ||
1400 | + * } | ||
1401 | + */ | ||
1402 | + | ||
1403 | + qdict_put_int(dict1, "a", 0); | ||
1404 | + qdict_put_int(dict1, "b", 1); | ||
1405 | + | ||
1406 | + qlist_append_int(list1, 23); | ||
1407 | + qlist_append_int(list1, 66); | ||
1408 | + qlist_append(list1, dict1); | ||
1409 | + qlist_append_int(list2, 42); | ||
1410 | + qlist_append(list2, list1); | ||
1411 | + | ||
1412 | + qdict_put_int(dict2, "c", 2); | ||
1413 | + qdict_put_int(dict2, "d", 3); | ||
1414 | + qdict_put(dict3, "e", list2); | ||
1415 | + qdict_put(dict3, "f", dict2); | ||
1416 | + qdict_put_int(dict3, "g", 4); | ||
1417 | + | ||
1418 | + qdict_flatten(dict3); | ||
1419 | + | ||
1420 | + g_assert(qdict_get_int(dict3, "e.0") == 42); | ||
1421 | + g_assert(qdict_get_int(dict3, "e.1.0") == 23); | ||
1422 | + g_assert(qdict_get_int(dict3, "e.1.1") == 66); | ||
1423 | + g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); | ||
1424 | + g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); | ||
1425 | + g_assert(qdict_get_int(dict3, "f.c") == 2); | ||
1426 | + g_assert(qdict_get_int(dict3, "f.d") == 3); | ||
1427 | + g_assert(qdict_get_int(dict3, "g") == 4); | ||
1428 | + | ||
1429 | + g_assert(qdict_size(dict3) == 8); | ||
1430 | + | ||
1431 | + qobject_unref(dict3); | ||
1432 | +} | ||
1433 | + | ||
1434 | +static void qdict_array_split_test(void) | ||
1435 | +{ | ||
1436 | + QDict *test_dict = qdict_new(); | ||
1437 | + QDict *dict1, *dict2; | ||
1438 | + QNum *int1; | ||
1439 | + QList *test_list; | ||
1440 | + | ||
1441 | + /* | ||
1442 | + * Test the split of | ||
1443 | + * | ||
1444 | + * { | ||
1445 | + * "1.x": 0, | ||
1446 | + * "4.y": 1, | ||
1447 | + * "0.a": 42, | ||
1448 | + * "o.o": 7, | ||
1449 | + * "0.b": 23, | ||
1450 | + * "2": 66 | ||
1451 | + * } | ||
1452 | + * | ||
1453 | + * to | ||
1454 | + * | ||
1455 | + * [ | ||
1456 | + * { | ||
1457 | + * "a": 42, | ||
1458 | + * "b": 23 | ||
1459 | + * }, | ||
1460 | + * { | ||
1461 | + * "x": 0 | ||
1462 | + * }, | ||
1463 | + * 66 | ||
1464 | + * ] | ||
1465 | + * | ||
1466 | + * and | ||
1467 | + * | ||
1468 | + * { | ||
1469 | + * "4.y": 1, | ||
1470 | + * "o.o": 7 | ||
1471 | + * } | ||
1472 | + * | ||
1473 | + * (remaining in the old QDict) | ||
1474 | + * | ||
1475 | + * This example is given in the comment of qdict_array_split(). | ||
1476 | + */ | ||
1477 | + | ||
1478 | + qdict_put_int(test_dict, "1.x", 0); | ||
1479 | + qdict_put_int(test_dict, "4.y", 1); | ||
1480 | + qdict_put_int(test_dict, "0.a", 42); | ||
1481 | + qdict_put_int(test_dict, "o.o", 7); | ||
1482 | + qdict_put_int(test_dict, "0.b", 23); | ||
1483 | + qdict_put_int(test_dict, "2", 66); | ||
1484 | + | ||
1485 | + qdict_array_split(test_dict, &test_list); | ||
1486 | + | ||
1487 | + dict1 = qobject_to(QDict, qlist_pop(test_list)); | ||
1488 | + dict2 = qobject_to(QDict, qlist_pop(test_list)); | ||
1489 | + int1 = qobject_to(QNum, qlist_pop(test_list)); | ||
1490 | + | ||
1491 | + g_assert(dict1); | ||
1492 | + g_assert(dict2); | ||
1493 | + g_assert(int1); | ||
1494 | + g_assert(qlist_empty(test_list)); | ||
1495 | + | ||
1496 | + qobject_unref(test_list); | ||
1497 | + | ||
1498 | + g_assert(qdict_get_int(dict1, "a") == 42); | ||
1499 | + g_assert(qdict_get_int(dict1, "b") == 23); | ||
1500 | + | ||
1501 | + g_assert(qdict_size(dict1) == 2); | ||
1502 | + | ||
1503 | + qobject_unref(dict1); | ||
1504 | + | ||
1505 | + g_assert(qdict_get_int(dict2, "x") == 0); | ||
1506 | + | ||
1507 | + g_assert(qdict_size(dict2) == 1); | ||
1508 | + | ||
1509 | + qobject_unref(dict2); | ||
1510 | + | ||
1511 | + g_assert_cmpint(qnum_get_int(int1), ==, 66); | ||
1512 | + | ||
1513 | + qobject_unref(int1); | ||
1514 | + | ||
1515 | + g_assert(qdict_get_int(test_dict, "4.y") == 1); | ||
1516 | + g_assert(qdict_get_int(test_dict, "o.o") == 7); | ||
1517 | + | ||
1518 | + g_assert(qdict_size(test_dict) == 2); | ||
1519 | + | ||
1520 | + qobject_unref(test_dict); | ||
1521 | + | ||
1522 | + /* | ||
1523 | + * Test the split of | ||
1524 | + * | ||
1525 | + * { | ||
1526 | + * "0": 42, | ||
1527 | + * "1": 23, | ||
1528 | + * "1.x": 84 | ||
1529 | + * } | ||
1530 | + * | ||
1531 | + * to | ||
1532 | + * | ||
1533 | + * [ | ||
1534 | + * 42 | ||
1535 | + * ] | ||
1536 | + * | ||
1537 | + * and | ||
1538 | + * | ||
1539 | + * { | ||
1540 | + * "1": 23, | ||
1541 | + * "1.x": 84 | ||
1542 | + * } | ||
1543 | + * | ||
1544 | + * That is, test whether splitting stops if there is both an entry with key | ||
1545 | + * of "%u" and other entries with keys prefixed "%u." for the same index. | ||
1546 | + */ | ||
1547 | + | ||
1548 | + test_dict = qdict_new(); | ||
1549 | + | ||
1550 | + qdict_put_int(test_dict, "0", 42); | ||
1551 | + qdict_put_int(test_dict, "1", 23); | ||
1552 | + qdict_put_int(test_dict, "1.x", 84); | ||
1553 | + | ||
1554 | + qdict_array_split(test_dict, &test_list); | ||
1555 | + | ||
1556 | + int1 = qobject_to(QNum, qlist_pop(test_list)); | ||
1557 | + | ||
1558 | + g_assert(int1); | ||
1559 | + g_assert(qlist_empty(test_list)); | ||
1560 | + | ||
1561 | + qobject_unref(test_list); | ||
1562 | + | ||
1563 | + g_assert_cmpint(qnum_get_int(int1), ==, 42); | ||
1564 | + | ||
1565 | + qobject_unref(int1); | ||
1566 | + | ||
1567 | + g_assert(qdict_get_int(test_dict, "1") == 23); | ||
1568 | + g_assert(qdict_get_int(test_dict, "1.x") == 84); | ||
1569 | + | ||
1570 | + g_assert(qdict_size(test_dict) == 2); | ||
1571 | + | ||
1572 | + qobject_unref(test_dict); | ||
1573 | +} | ||
1574 | + | ||
1575 | +static void qdict_array_entries_test(void) | ||
1576 | +{ | ||
1577 | + QDict *dict = qdict_new(); | ||
1578 | + | ||
1579 | + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); | ||
1580 | + | ||
1581 | + qdict_put_int(dict, "bar", 0); | ||
1582 | + qdict_put_int(dict, "baz.0", 0); | ||
1583 | + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); | ||
1584 | + | ||
1585 | + qdict_put_int(dict, "foo.1", 0); | ||
1586 | + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); | ||
1587 | + qdict_put_int(dict, "foo.0", 0); | ||
1588 | + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); | ||
1589 | + qdict_put_int(dict, "foo.bar", 0); | ||
1590 | + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); | ||
1591 | + qdict_del(dict, "foo.bar"); | ||
1592 | + | ||
1593 | + qdict_put_int(dict, "foo.2.a", 0); | ||
1594 | + qdict_put_int(dict, "foo.2.b", 0); | ||
1595 | + qdict_put_int(dict, "foo.2.c", 0); | ||
1596 | + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); | ||
1597 | + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); | ||
1598 | + | ||
1599 | + qobject_unref(dict); | ||
1600 | + | ||
1601 | + dict = qdict_new(); | ||
1602 | + qdict_put_int(dict, "1", 0); | ||
1603 | + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); | ||
1604 | + qdict_put_int(dict, "0", 0); | ||
1605 | + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); | ||
1606 | + qdict_put_int(dict, "bar", 0); | ||
1607 | + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); | ||
1608 | + qdict_del(dict, "bar"); | ||
1609 | + | ||
1610 | + qdict_put_int(dict, "2.a", 0); | ||
1611 | + qdict_put_int(dict, "2.b", 0); | ||
1612 | + qdict_put_int(dict, "2.c", 0); | ||
1613 | + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); | ||
1614 | + | ||
1615 | + qobject_unref(dict); | ||
1616 | +} | ||
1617 | + | ||
1618 | +static void qdict_join_test(void) | ||
1619 | +{ | ||
1620 | + QDict *dict1, *dict2; | ||
1621 | + bool overwrite = false; | ||
1622 | + int i; | ||
1623 | + | ||
1624 | + dict1 = qdict_new(); | ||
1625 | + dict2 = qdict_new(); | ||
1626 | + | ||
1627 | + /* Test everything once without overwrite and once with */ | ||
1628 | + do { | ||
1629 | + /* Test empty dicts */ | ||
1630 | + qdict_join(dict1, dict2, overwrite); | ||
1631 | + | ||
1632 | + g_assert(qdict_size(dict1) == 0); | ||
1633 | + g_assert(qdict_size(dict2) == 0); | ||
1634 | + | ||
1635 | + /* First iteration: Test movement */ | ||
1636 | + /* Second iteration: Test empty source and non-empty destination */ | ||
1637 | + qdict_put_int(dict2, "foo", 42); | ||
1638 | + | ||
1639 | + for (i = 0; i < 2; i++) { | ||
1640 | + qdict_join(dict1, dict2, overwrite); | ||
1641 | + | ||
1642 | + g_assert(qdict_size(dict1) == 1); | ||
1643 | + g_assert(qdict_size(dict2) == 0); | ||
1644 | + | ||
1645 | + g_assert(qdict_get_int(dict1, "foo") == 42); | ||
1646 | + } | ||
1647 | + | ||
1648 | + /* Test non-empty source and destination without conflict */ | ||
1649 | + qdict_put_int(dict2, "bar", 23); | ||
1650 | + | ||
1651 | + qdict_join(dict1, dict2, overwrite); | ||
1652 | + | ||
1653 | + g_assert(qdict_size(dict1) == 2); | ||
1654 | + g_assert(qdict_size(dict2) == 0); | ||
1655 | + | ||
1656 | + g_assert(qdict_get_int(dict1, "foo") == 42); | ||
1657 | + g_assert(qdict_get_int(dict1, "bar") == 23); | ||
1658 | + | ||
1659 | + /* Test conflict */ | ||
1660 | + qdict_put_int(dict2, "foo", 84); | ||
1661 | + | ||
1662 | + qdict_join(dict1, dict2, overwrite); | ||
1663 | + | ||
1664 | + g_assert(qdict_size(dict1) == 2); | ||
1665 | + g_assert(qdict_size(dict2) == !overwrite); | ||
1666 | + | ||
1667 | + g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); | ||
1668 | + g_assert(qdict_get_int(dict1, "bar") == 23); | ||
1669 | + | ||
1670 | + if (!overwrite) { | ||
1671 | + g_assert(qdict_get_int(dict2, "foo") == 84); | ||
1672 | + } | ||
1673 | + | ||
1674 | + /* Check the references */ | ||
1675 | + g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); | ||
1676 | + g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); | ||
1677 | + | ||
1678 | + if (!overwrite) { | ||
1679 | + g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); | ||
1680 | + } | ||
1681 | + | ||
1682 | + /* Clean up */ | ||
1683 | + qdict_del(dict1, "foo"); | ||
1684 | + qdict_del(dict1, "bar"); | ||
1685 | + | ||
1686 | + if (!overwrite) { | ||
1687 | + qdict_del(dict2, "foo"); | ||
1688 | + } | ||
1689 | + } while (overwrite ^= true); | ||
1690 | + | ||
1691 | + qobject_unref(dict1); | ||
1692 | + qobject_unref(dict2); | ||
1693 | +} | ||
1694 | + | ||
1695 | +static void qdict_crumple_test_recursive(void) | ||
1696 | +{ | ||
1697 | + QDict *src, *dst, *rule, *vnc, *acl, *listen; | ||
1698 | + QList *rules; | ||
1699 | + | ||
1700 | + src = qdict_new(); | ||
1701 | + qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); | ||
1702 | + qdict_put_str(src, "vnc.listen.port", "5901"); | ||
1703 | + qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); | ||
1704 | + qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); | ||
1705 | + qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); | ||
1706 | + qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); | ||
1707 | + qdict_put_str(src, "vnc.acl.default", "deny"); | ||
1708 | + qdict_put_str(src, "vnc.acl..name", "acl0"); | ||
1709 | + qdict_put_str(src, "vnc.acl.rule..name", "acl0"); | ||
1710 | + | ||
1711 | + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); | ||
1712 | + g_assert(dst); | ||
1713 | + g_assert_cmpint(qdict_size(dst), ==, 1); | ||
1714 | + | ||
1715 | + vnc = qdict_get_qdict(dst, "vnc"); | ||
1716 | + g_assert(vnc); | ||
1717 | + g_assert_cmpint(qdict_size(vnc), ==, 3); | ||
1718 | + | ||
1719 | + listen = qdict_get_qdict(vnc, "listen"); | ||
1720 | + g_assert(listen); | ||
1721 | + g_assert_cmpint(qdict_size(listen), ==, 2); | ||
1722 | + g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); | ||
1723 | + g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); | ||
1724 | + | ||
1725 | + acl = qdict_get_qdict(vnc, "acl"); | ||
1726 | + g_assert(acl); | ||
1727 | + g_assert_cmpint(qdict_size(acl), ==, 3); | ||
1728 | + | ||
1729 | + rules = qdict_get_qlist(acl, "rules"); | ||
1730 | + g_assert(rules); | ||
1731 | + g_assert_cmpint(qlist_size(rules), ==, 2); | ||
1732 | + | ||
1733 | + rule = qobject_to(QDict, qlist_pop(rules)); | ||
1734 | + g_assert(rule); | ||
1735 | + g_assert_cmpint(qdict_size(rule), ==, 2); | ||
1736 | + g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); | ||
1737 | + g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); | ||
1738 | + qobject_unref(rule); | ||
1739 | + | ||
1740 | + rule = qobject_to(QDict, qlist_pop(rules)); | ||
1741 | + g_assert(rule); | ||
1742 | + g_assert_cmpint(qdict_size(rule), ==, 2); | ||
1743 | + g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); | ||
1744 | + g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); | ||
1745 | + qobject_unref(rule); | ||
1746 | + | ||
1747 | + /* With recursive crumpling, we should see all names unescaped */ | ||
1748 | + g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); | ||
1749 | + g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); | ||
1750 | + | ||
1751 | + qobject_unref(src); | ||
1752 | + qobject_unref(dst); | ||
1753 | +} | ||
1754 | + | ||
1755 | +static void qdict_crumple_test_empty(void) | ||
1756 | +{ | ||
1757 | + QDict *src, *dst; | ||
1758 | + | ||
1759 | + src = qdict_new(); | ||
1760 | + | ||
1761 | + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); | ||
1762 | + | ||
1763 | + g_assert_cmpint(qdict_size(dst), ==, 0); | ||
1764 | + | ||
1765 | + qobject_unref(src); | ||
1766 | + qobject_unref(dst); | ||
1767 | +} | ||
1768 | + | ||
1769 | +static int qdict_count_entries(QDict *dict) | ||
1770 | +{ | ||
1771 | + const QDictEntry *e; | ||
1772 | + int count = 0; | ||
1773 | + | ||
1774 | + for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { | ||
1775 | + count++; | ||
1776 | + } | ||
1777 | + | ||
1778 | + return count; | ||
1779 | +} | ||
1780 | + | ||
1781 | +static void qdict_rename_keys_test(void) | ||
1782 | +{ | ||
1783 | + QDict *dict = qdict_new(); | ||
1784 | + QDict *copy; | ||
1785 | + QDictRenames *renames; | ||
1786 | + Error *local_err = NULL; | ||
1787 | + | ||
1788 | + qdict_put_str(dict, "abc", "foo"); | ||
1789 | + qdict_put_str(dict, "abcdef", "bar"); | ||
1790 | + qdict_put_int(dict, "number", 42); | ||
1791 | + qdict_put_bool(dict, "flag", true); | ||
1792 | + qdict_put_null(dict, "nothing"); | ||
1793 | + | ||
1794 | + /* Empty rename list */ | ||
1795 | + renames = (QDictRenames[]) { | ||
1796 | + { NULL, "this can be anything" } | ||
1797 | + }; | ||
1798 | + copy = qdict_clone_shallow(dict); | ||
1799 | + qdict_rename_keys(copy, renames, &error_abort); | ||
1800 | + | ||
1801 | + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); | ||
1802 | + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); | ||
1803 | + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); | ||
1804 | + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); | ||
1805 | + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); | ||
1806 | + g_assert_cmpint(qdict_count_entries(copy), ==, 5); | ||
1807 | + | ||
1808 | + qobject_unref(copy); | ||
1809 | + | ||
1810 | + /* Simple rename of all entries */ | ||
1811 | + renames = (QDictRenames[]) { | ||
1812 | + { "abc", "str1" }, | ||
1813 | + { "abcdef", "str2" }, | ||
1814 | + { "number", "int" }, | ||
1815 | + { "flag", "bool" }, | ||
1816 | + { "nothing", "null" }, | ||
1817 | + { NULL , NULL } | ||
1818 | + }; | ||
1819 | + copy = qdict_clone_shallow(dict); | ||
1820 | + qdict_rename_keys(copy, renames, &error_abort); | ||
1821 | + | ||
1822 | + g_assert(!qdict_haskey(copy, "abc")); | ||
1823 | + g_assert(!qdict_haskey(copy, "abcdef")); | ||
1824 | + g_assert(!qdict_haskey(copy, "number")); | ||
1825 | + g_assert(!qdict_haskey(copy, "flag")); | ||
1826 | + g_assert(!qdict_haskey(copy, "nothing")); | ||
1827 | + | ||
1828 | + g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); | ||
1829 | + g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); | ||
1830 | + g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); | ||
1831 | + g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); | ||
1832 | + g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); | ||
1833 | + g_assert_cmpint(qdict_count_entries(copy), ==, 5); | ||
1834 | + | ||
1835 | + qobject_unref(copy); | ||
1836 | + | ||
1837 | + /* Renames are processed top to bottom */ | ||
1838 | + renames = (QDictRenames[]) { | ||
1839 | + { "abc", "tmp" }, | ||
1840 | + { "abcdef", "abc" }, | ||
1841 | + { "number", "abcdef" }, | ||
1842 | + { "flag", "number" }, | ||
1843 | + { "nothing", "flag" }, | ||
1844 | + { "tmp", "nothing" }, | ||
1845 | + { NULL , NULL } | ||
1846 | + }; | ||
1847 | + copy = qdict_clone_shallow(dict); | ||
1848 | + qdict_rename_keys(copy, renames, &error_abort); | ||
1849 | + | ||
1850 | + g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); | ||
1851 | + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); | ||
1852 | + g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); | ||
1853 | + g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); | ||
1854 | + g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); | ||
1855 | + g_assert(!qdict_haskey(copy, "tmp")); | ||
1856 | + g_assert_cmpint(qdict_count_entries(copy), ==, 5); | ||
1857 | + | ||
1858 | + qobject_unref(copy); | ||
1859 | + | ||
1860 | + /* Conflicting rename */ | ||
1861 | + renames = (QDictRenames[]) { | ||
1862 | + { "abcdef", "abc" }, | ||
1863 | + { NULL , NULL } | ||
1864 | + }; | ||
1865 | + copy = qdict_clone_shallow(dict); | ||
1866 | + qdict_rename_keys(copy, renames, &local_err); | ||
1867 | + | ||
1868 | + g_assert(local_err != NULL); | ||
1869 | + error_free(local_err); | ||
1870 | + local_err = NULL; | ||
1871 | + | ||
1872 | + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); | ||
1873 | + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); | ||
1874 | + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); | ||
1875 | + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); | ||
1876 | + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); | ||
1877 | + g_assert_cmpint(qdict_count_entries(copy), ==, 5); | ||
1878 | + | ||
1879 | + qobject_unref(copy); | ||
1880 | + | ||
1881 | + /* Renames in an empty dict */ | ||
1882 | + renames = (QDictRenames[]) { | ||
1883 | + { "abcdef", "abc" }, | ||
1884 | + { NULL , NULL } | ||
1885 | + }; | ||
1886 | + | ||
1887 | + qobject_unref(dict); | ||
1888 | + dict = qdict_new(); | ||
1889 | + | ||
1890 | + qdict_rename_keys(dict, renames, &error_abort); | ||
1891 | + g_assert(qdict_first(dict) == NULL); | ||
1892 | + | ||
1893 | + qobject_unref(dict); | ||
1894 | +} | ||
1895 | + | ||
1896 | +static void qdict_crumple_test_bad_inputs(void) | ||
1897 | +{ | ||
1898 | + QDict *src; | ||
1899 | + Error *error = NULL; | ||
1900 | + | ||
1901 | + src = qdict_new(); | ||
1902 | + /* rule.0 can't be both a string and a dict */ | ||
1903 | + qdict_put_str(src, "rule.0", "fred"); | ||
1904 | + qdict_put_str(src, "rule.0.policy", "allow"); | ||
1905 | + | ||
1906 | + g_assert(qdict_crumple(src, &error) == NULL); | ||
1907 | + g_assert(error != NULL); | ||
1908 | + error_free(error); | ||
1909 | + error = NULL; | ||
1910 | + qobject_unref(src); | ||
1911 | + | ||
1912 | + src = qdict_new(); | ||
1913 | + /* rule can't be both a list and a dict */ | ||
1914 | + qdict_put_str(src, "rule.0", "fred"); | ||
1915 | + qdict_put_str(src, "rule.a", "allow"); | ||
1916 | + | ||
1917 | + g_assert(qdict_crumple(src, &error) == NULL); | ||
1918 | + g_assert(error != NULL); | ||
1919 | + error_free(error); | ||
1920 | + error = NULL; | ||
1921 | + qobject_unref(src); | ||
1922 | + | ||
1923 | + src = qdict_new(); | ||
1924 | + /* The input should be flat, ie no dicts or lists */ | ||
1925 | + qdict_put(src, "rule.a", qdict_new()); | ||
1926 | + qdict_put_str(src, "rule.b", "allow"); | ||
1927 | + | ||
1928 | + g_assert(qdict_crumple(src, &error) == NULL); | ||
1929 | + g_assert(error != NULL); | ||
1930 | + error_free(error); | ||
1931 | + error = NULL; | ||
1932 | + qobject_unref(src); | ||
1933 | + | ||
1934 | + src = qdict_new(); | ||
1935 | + /* List indexes must not have gaps */ | ||
1936 | + qdict_put_str(src, "rule.0", "deny"); | ||
1937 | + qdict_put_str(src, "rule.3", "allow"); | ||
1938 | + | ||
1939 | + g_assert(qdict_crumple(src, &error) == NULL); | ||
1940 | + g_assert(error != NULL); | ||
1941 | + error_free(error); | ||
1942 | + error = NULL; | ||
1943 | + qobject_unref(src); | ||
1944 | + | ||
1945 | + src = qdict_new(); | ||
1946 | + /* List indexes must be in %zu format */ | ||
1947 | + qdict_put_str(src, "rule.0", "deny"); | ||
1948 | + qdict_put_str(src, "rule.+1", "allow"); | ||
1949 | + | ||
1950 | + g_assert(qdict_crumple(src, &error) == NULL); | ||
1951 | + g_assert(error != NULL); | ||
1952 | + error_free(error); | ||
1953 | + error = NULL; | ||
1954 | + qobject_unref(src); | ||
1955 | +} | ||
1956 | + | ||
1957 | +int main(int argc, char **argv) | ||
1958 | +{ | ||
1959 | + g_test_init(&argc, &argv, NULL); | ||
1960 | + | ||
1961 | + g_test_add_func("/public/defaults", qdict_defaults_test); | ||
1962 | + g_test_add_func("/public/flatten", qdict_flatten_test); | ||
1963 | + g_test_add_func("/public/array_split", qdict_array_split_test); | ||
1964 | + g_test_add_func("/public/array_entries", qdict_array_entries_test); | ||
1965 | + g_test_add_func("/public/join", qdict_join_test); | ||
1966 | + g_test_add_func("/public/crumple/recursive", | ||
1967 | + qdict_crumple_test_recursive); | ||
1968 | + g_test_add_func("/public/crumple/empty", | ||
1969 | + qdict_crumple_test_empty); | ||
1970 | + g_test_add_func("/public/crumple/bad_inputs", | ||
1971 | + qdict_crumple_test_bad_inputs); | ||
1972 | + | ||
1973 | + g_test_add_func("/public/rename_keys", qdict_rename_keys_test); | ||
1974 | + | ||
1975 | + return g_test_run(); | ||
1976 | +} | ||
1977 | diff --git a/tests/check-qdict.c b/tests/check-qdict.c | ||
1978 | index XXXXXXX..XXXXXXX 100644 | ||
1979 | --- a/tests/check-qdict.c | ||
1980 | +++ b/tests/check-qdict.c | ||
1981 | @@ -XXX,XX +XXX,XX @@ | ||
1982 | */ | ||
1983 | |||
1984 | #include "qemu/osdep.h" | ||
1985 | -#include "block/qdict.h" | ||
1986 | #include "qapi/qmp/qdict.h" | ||
1987 | -#include "qapi/qmp/qlist.h" | ||
1988 | -#include "qapi/qmp/qnum.h" | ||
1989 | -#include "qapi/qmp/qstring.h" | ||
1990 | -#include "qapi/error.h" | ||
1991 | -#include "qemu-common.h" | ||
1992 | |||
1993 | /* | ||
1994 | * Public Interface test-cases | ||
1995 | @@ -XXX,XX +XXX,XX @@ static void qdict_get_try_str_test(void) | ||
1996 | qobject_unref(tests_dict); | ||
1997 | } | ||
1998 | |||
1999 | -static void qdict_defaults_test(void) | ||
2000 | -{ | ||
2001 | - QDict *dict, *copy; | ||
2002 | - | ||
2003 | - dict = qdict_new(); | ||
2004 | - copy = qdict_new(); | ||
2005 | - | ||
2006 | - qdict_set_default_str(dict, "foo", "abc"); | ||
2007 | - qdict_set_default_str(dict, "foo", "def"); | ||
2008 | - g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); | ||
2009 | - qdict_set_default_str(dict, "bar", "ghi"); | ||
2010 | - | ||
2011 | - qdict_copy_default(copy, dict, "foo"); | ||
2012 | - g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); | ||
2013 | - qdict_set_default_str(copy, "bar", "xyz"); | ||
2014 | - qdict_copy_default(copy, dict, "bar"); | ||
2015 | - g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); | ||
2016 | - | ||
2017 | - qobject_unref(copy); | ||
2018 | - qobject_unref(dict); | ||
2019 | -} | ||
2020 | - | ||
2021 | static void qdict_haskey_not_test(void) | ||
2022 | { | ||
2023 | QDict *tests_dict = qdict_new(); | ||
2024 | @@ -XXX,XX +XXX,XX @@ static void qdict_iterapi_test(void) | ||
2025 | qobject_unref(tests_dict); | ||
2026 | } | ||
2027 | |||
2028 | -static void qdict_flatten_test(void) | ||
2029 | -{ | ||
2030 | - QList *list1 = qlist_new(); | ||
2031 | - QList *list2 = qlist_new(); | ||
2032 | - QDict *dict1 = qdict_new(); | ||
2033 | - QDict *dict2 = qdict_new(); | ||
2034 | - QDict *dict3 = qdict_new(); | ||
2035 | - | ||
2036 | - /* | ||
2037 | - * Test the flattening of | ||
2038 | - * | ||
2039 | - * { | ||
2040 | - * "e": [ | ||
2041 | - * 42, | ||
2042 | - * [ | ||
2043 | - * 23, | ||
2044 | - * 66, | ||
2045 | - * { | ||
2046 | - * "a": 0, | ||
2047 | - * "b": 1 | ||
2048 | - * } | ||
2049 | - * ] | ||
2050 | - * ], | ||
2051 | - * "f": { | ||
2052 | - * "c": 2, | ||
2053 | - * "d": 3, | ||
2054 | - * }, | ||
2055 | - * "g": 4 | ||
2056 | - * } | ||
2057 | - * | ||
2058 | - * to | ||
2059 | - * | ||
2060 | - * { | ||
2061 | - * "e.0": 42, | ||
2062 | - * "e.1.0": 23, | ||
2063 | - * "e.1.1": 66, | ||
2064 | - * "e.1.2.a": 0, | ||
2065 | - * "e.1.2.b": 1, | ||
2066 | - * "f.c": 2, | ||
2067 | - * "f.d": 3, | ||
2068 | - * "g": 4 | ||
2069 | - * } | ||
2070 | - */ | ||
2071 | - | ||
2072 | - qdict_put_int(dict1, "a", 0); | ||
2073 | - qdict_put_int(dict1, "b", 1); | ||
2074 | - | ||
2075 | - qlist_append_int(list1, 23); | ||
2076 | - qlist_append_int(list1, 66); | ||
2077 | - qlist_append(list1, dict1); | ||
2078 | - qlist_append_int(list2, 42); | ||
2079 | - qlist_append(list2, list1); | ||
2080 | - | ||
2081 | - qdict_put_int(dict2, "c", 2); | ||
2082 | - qdict_put_int(dict2, "d", 3); | ||
2083 | - qdict_put(dict3, "e", list2); | ||
2084 | - qdict_put(dict3, "f", dict2); | ||
2085 | - qdict_put_int(dict3, "g", 4); | ||
2086 | - | ||
2087 | - qdict_flatten(dict3); | ||
2088 | - | ||
2089 | - g_assert(qdict_get_int(dict3, "e.0") == 42); | ||
2090 | - g_assert(qdict_get_int(dict3, "e.1.0") == 23); | ||
2091 | - g_assert(qdict_get_int(dict3, "e.1.1") == 66); | ||
2092 | - g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); | ||
2093 | - g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); | ||
2094 | - g_assert(qdict_get_int(dict3, "f.c") == 2); | ||
2095 | - g_assert(qdict_get_int(dict3, "f.d") == 3); | ||
2096 | - g_assert(qdict_get_int(dict3, "g") == 4); | ||
2097 | - | ||
2098 | - g_assert(qdict_size(dict3) == 8); | ||
2099 | - | ||
2100 | - qobject_unref(dict3); | ||
2101 | -} | ||
2102 | - | ||
2103 | -static void qdict_array_split_test(void) | ||
2104 | -{ | ||
2105 | - QDict *test_dict = qdict_new(); | ||
2106 | - QDict *dict1, *dict2; | ||
2107 | - QNum *int1; | ||
2108 | - QList *test_list; | ||
2109 | - | ||
2110 | - /* | ||
2111 | - * Test the split of | ||
2112 | - * | ||
2113 | - * { | ||
2114 | - * "1.x": 0, | ||
2115 | - * "4.y": 1, | ||
2116 | - * "0.a": 42, | ||
2117 | - * "o.o": 7, | ||
2118 | - * "0.b": 23, | ||
2119 | - * "2": 66 | ||
2120 | - * } | ||
2121 | - * | ||
2122 | - * to | ||
2123 | - * | ||
2124 | - * [ | ||
2125 | - * { | ||
2126 | - * "a": 42, | ||
2127 | - * "b": 23 | ||
2128 | - * }, | ||
2129 | - * { | ||
2130 | - * "x": 0 | ||
2131 | - * }, | ||
2132 | - * 66 | ||
2133 | - * ] | ||
2134 | - * | ||
2135 | - * and | ||
2136 | - * | ||
2137 | - * { | ||
2138 | - * "4.y": 1, | ||
2139 | - * "o.o": 7 | ||
2140 | - * } | ||
2141 | - * | ||
2142 | - * (remaining in the old QDict) | ||
2143 | - * | ||
2144 | - * This example is given in the comment of qdict_array_split(). | ||
2145 | - */ | ||
2146 | - | ||
2147 | - qdict_put_int(test_dict, "1.x", 0); | ||
2148 | - qdict_put_int(test_dict, "4.y", 1); | ||
2149 | - qdict_put_int(test_dict, "0.a", 42); | ||
2150 | - qdict_put_int(test_dict, "o.o", 7); | ||
2151 | - qdict_put_int(test_dict, "0.b", 23); | ||
2152 | - qdict_put_int(test_dict, "2", 66); | ||
2153 | - | ||
2154 | - qdict_array_split(test_dict, &test_list); | ||
2155 | - | ||
2156 | - dict1 = qobject_to(QDict, qlist_pop(test_list)); | ||
2157 | - dict2 = qobject_to(QDict, qlist_pop(test_list)); | ||
2158 | - int1 = qobject_to(QNum, qlist_pop(test_list)); | ||
2159 | - | ||
2160 | - g_assert(dict1); | ||
2161 | - g_assert(dict2); | ||
2162 | - g_assert(int1); | ||
2163 | - g_assert(qlist_empty(test_list)); | ||
2164 | - | ||
2165 | - qobject_unref(test_list); | ||
2166 | - | ||
2167 | - g_assert(qdict_get_int(dict1, "a") == 42); | ||
2168 | - g_assert(qdict_get_int(dict1, "b") == 23); | ||
2169 | - | ||
2170 | - g_assert(qdict_size(dict1) == 2); | ||
2171 | - | ||
2172 | - qobject_unref(dict1); | ||
2173 | - | ||
2174 | - g_assert(qdict_get_int(dict2, "x") == 0); | ||
2175 | - | ||
2176 | - g_assert(qdict_size(dict2) == 1); | ||
2177 | - | ||
2178 | - qobject_unref(dict2); | ||
2179 | - | ||
2180 | - g_assert_cmpint(qnum_get_int(int1), ==, 66); | ||
2181 | - | ||
2182 | - qobject_unref(int1); | ||
2183 | - | ||
2184 | - g_assert(qdict_get_int(test_dict, "4.y") == 1); | ||
2185 | - g_assert(qdict_get_int(test_dict, "o.o") == 7); | ||
2186 | - | ||
2187 | - g_assert(qdict_size(test_dict) == 2); | ||
2188 | - | ||
2189 | - qobject_unref(test_dict); | ||
2190 | - | ||
2191 | - /* | ||
2192 | - * Test the split of | ||
2193 | - * | ||
2194 | - * { | ||
2195 | - * "0": 42, | ||
2196 | - * "1": 23, | ||
2197 | - * "1.x": 84 | ||
2198 | - * } | ||
2199 | - * | ||
2200 | - * to | ||
2201 | - * | ||
2202 | - * [ | ||
2203 | - * 42 | ||
2204 | - * ] | ||
2205 | - * | ||
2206 | - * and | ||
2207 | - * | ||
2208 | - * { | ||
2209 | - * "1": 23, | ||
2210 | - * "1.x": 84 | ||
2211 | - * } | ||
2212 | - * | ||
2213 | - * That is, test whether splitting stops if there is both an entry with key | ||
2214 | - * of "%u" and other entries with keys prefixed "%u." for the same index. | ||
2215 | - */ | ||
2216 | - | ||
2217 | - test_dict = qdict_new(); | ||
2218 | - | ||
2219 | - qdict_put_int(test_dict, "0", 42); | ||
2220 | - qdict_put_int(test_dict, "1", 23); | ||
2221 | - qdict_put_int(test_dict, "1.x", 84); | ||
2222 | - | ||
2223 | - qdict_array_split(test_dict, &test_list); | ||
2224 | - | ||
2225 | - int1 = qobject_to(QNum, qlist_pop(test_list)); | ||
2226 | - | ||
2227 | - g_assert(int1); | ||
2228 | - g_assert(qlist_empty(test_list)); | ||
2229 | - | ||
2230 | - qobject_unref(test_list); | ||
2231 | - | ||
2232 | - g_assert_cmpint(qnum_get_int(int1), ==, 42); | ||
2233 | - | ||
2234 | - qobject_unref(int1); | ||
2235 | - | ||
2236 | - g_assert(qdict_get_int(test_dict, "1") == 23); | ||
2237 | - g_assert(qdict_get_int(test_dict, "1.x") == 84); | ||
2238 | - | ||
2239 | - g_assert(qdict_size(test_dict) == 2); | ||
2240 | - | ||
2241 | - qobject_unref(test_dict); | ||
2242 | -} | ||
2243 | - | ||
2244 | -static void qdict_array_entries_test(void) | ||
2245 | -{ | ||
2246 | - QDict *dict = qdict_new(); | ||
2247 | - | ||
2248 | - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); | ||
2249 | - | ||
2250 | - qdict_put_int(dict, "bar", 0); | ||
2251 | - qdict_put_int(dict, "baz.0", 0); | ||
2252 | - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); | ||
2253 | - | ||
2254 | - qdict_put_int(dict, "foo.1", 0); | ||
2255 | - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); | ||
2256 | - qdict_put_int(dict, "foo.0", 0); | ||
2257 | - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); | ||
2258 | - qdict_put_int(dict, "foo.bar", 0); | ||
2259 | - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); | ||
2260 | - qdict_del(dict, "foo.bar"); | ||
2261 | - | ||
2262 | - qdict_put_int(dict, "foo.2.a", 0); | ||
2263 | - qdict_put_int(dict, "foo.2.b", 0); | ||
2264 | - qdict_put_int(dict, "foo.2.c", 0); | ||
2265 | - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); | ||
2266 | - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); | ||
2267 | - | ||
2268 | - qobject_unref(dict); | ||
2269 | - | ||
2270 | - dict = qdict_new(); | ||
2271 | - qdict_put_int(dict, "1", 0); | ||
2272 | - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); | ||
2273 | - qdict_put_int(dict, "0", 0); | ||
2274 | - g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); | ||
2275 | - qdict_put_int(dict, "bar", 0); | ||
2276 | - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); | ||
2277 | - qdict_del(dict, "bar"); | ||
2278 | - | ||
2279 | - qdict_put_int(dict, "2.a", 0); | ||
2280 | - qdict_put_int(dict, "2.b", 0); | ||
2281 | - qdict_put_int(dict, "2.c", 0); | ||
2282 | - g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); | ||
2283 | - | ||
2284 | - qobject_unref(dict); | ||
2285 | -} | ||
2286 | - | ||
2287 | -static void qdict_join_test(void) | ||
2288 | -{ | ||
2289 | - QDict *dict1, *dict2; | ||
2290 | - bool overwrite = false; | ||
2291 | - int i; | ||
2292 | - | ||
2293 | - dict1 = qdict_new(); | ||
2294 | - dict2 = qdict_new(); | ||
2295 | - | ||
2296 | - /* Test everything once without overwrite and once with */ | ||
2297 | - do | ||
2298 | - { | ||
2299 | - /* Test empty dicts */ | ||
2300 | - qdict_join(dict1, dict2, overwrite); | ||
2301 | - | ||
2302 | - g_assert(qdict_size(dict1) == 0); | ||
2303 | - g_assert(qdict_size(dict2) == 0); | ||
2304 | - | ||
2305 | - /* First iteration: Test movement */ | ||
2306 | - /* Second iteration: Test empty source and non-empty destination */ | ||
2307 | - qdict_put_int(dict2, "foo", 42); | ||
2308 | - | ||
2309 | - for (i = 0; i < 2; i++) { | ||
2310 | - qdict_join(dict1, dict2, overwrite); | ||
2311 | - | ||
2312 | - g_assert(qdict_size(dict1) == 1); | ||
2313 | - g_assert(qdict_size(dict2) == 0); | ||
2314 | - | ||
2315 | - g_assert(qdict_get_int(dict1, "foo") == 42); | ||
2316 | - } | ||
2317 | - | ||
2318 | - /* Test non-empty source and destination without conflict */ | ||
2319 | - qdict_put_int(dict2, "bar", 23); | ||
2320 | - | ||
2321 | - qdict_join(dict1, dict2, overwrite); | ||
2322 | - | ||
2323 | - g_assert(qdict_size(dict1) == 2); | ||
2324 | - g_assert(qdict_size(dict2) == 0); | ||
2325 | - | ||
2326 | - g_assert(qdict_get_int(dict1, "foo") == 42); | ||
2327 | - g_assert(qdict_get_int(dict1, "bar") == 23); | ||
2328 | - | ||
2329 | - /* Test conflict */ | ||
2330 | - qdict_put_int(dict2, "foo", 84); | ||
2331 | - | ||
2332 | - qdict_join(dict1, dict2, overwrite); | ||
2333 | - | ||
2334 | - g_assert(qdict_size(dict1) == 2); | ||
2335 | - g_assert(qdict_size(dict2) == !overwrite); | ||
2336 | - | ||
2337 | - g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); | ||
2338 | - g_assert(qdict_get_int(dict1, "bar") == 23); | ||
2339 | - | ||
2340 | - if (!overwrite) { | ||
2341 | - g_assert(qdict_get_int(dict2, "foo") == 84); | ||
2342 | - } | ||
2343 | - | ||
2344 | - /* Check the references */ | ||
2345 | - g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); | ||
2346 | - g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); | ||
2347 | - | ||
2348 | - if (!overwrite) { | ||
2349 | - g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); | ||
2350 | - } | ||
2351 | - | ||
2352 | - /* Clean up */ | ||
2353 | - qdict_del(dict1, "foo"); | ||
2354 | - qdict_del(dict1, "bar"); | ||
2355 | - | ||
2356 | - if (!overwrite) { | ||
2357 | - qdict_del(dict2, "foo"); | ||
2358 | - } | ||
2359 | - } | ||
2360 | - while (overwrite ^= true); | ||
2361 | - | ||
2362 | - qobject_unref(dict1); | ||
2363 | - qobject_unref(dict2); | ||
2364 | -} | ||
2365 | - | ||
2366 | -static void qdict_crumple_test_recursive(void) | ||
2367 | -{ | ||
2368 | - QDict *src, *dst, *rule, *vnc, *acl, *listen; | ||
2369 | - QList *rules; | ||
2370 | - | ||
2371 | - src = qdict_new(); | ||
2372 | - qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); | ||
2373 | - qdict_put_str(src, "vnc.listen.port", "5901"); | ||
2374 | - qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); | ||
2375 | - qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); | ||
2376 | - qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); | ||
2377 | - qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); | ||
2378 | - qdict_put_str(src, "vnc.acl.default", "deny"); | ||
2379 | - qdict_put_str(src, "vnc.acl..name", "acl0"); | ||
2380 | - qdict_put_str(src, "vnc.acl.rule..name", "acl0"); | ||
2381 | - | ||
2382 | - dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); | ||
2383 | - g_assert(dst); | ||
2384 | - g_assert_cmpint(qdict_size(dst), ==, 1); | ||
2385 | - | ||
2386 | - vnc = qdict_get_qdict(dst, "vnc"); | ||
2387 | - g_assert(vnc); | ||
2388 | - g_assert_cmpint(qdict_size(vnc), ==, 3); | ||
2389 | - | ||
2390 | - listen = qdict_get_qdict(vnc, "listen"); | ||
2391 | - g_assert(listen); | ||
2392 | - g_assert_cmpint(qdict_size(listen), ==, 2); | ||
2393 | - g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); | ||
2394 | - g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); | ||
2395 | - | ||
2396 | - acl = qdict_get_qdict(vnc, "acl"); | ||
2397 | - g_assert(acl); | ||
2398 | - g_assert_cmpint(qdict_size(acl), ==, 3); | ||
2399 | - | ||
2400 | - rules = qdict_get_qlist(acl, "rules"); | ||
2401 | - g_assert(rules); | ||
2402 | - g_assert_cmpint(qlist_size(rules), ==, 2); | ||
2403 | - | ||
2404 | - rule = qobject_to(QDict, qlist_pop(rules)); | ||
2405 | - g_assert(rule); | ||
2406 | - g_assert_cmpint(qdict_size(rule), ==, 2); | ||
2407 | - g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); | ||
2408 | - g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); | ||
2409 | - qobject_unref(rule); | ||
2410 | - | ||
2411 | - rule = qobject_to(QDict, qlist_pop(rules)); | ||
2412 | - g_assert(rule); | ||
2413 | - g_assert_cmpint(qdict_size(rule), ==, 2); | ||
2414 | - g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); | ||
2415 | - g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); | ||
2416 | - qobject_unref(rule); | ||
2417 | - | ||
2418 | - /* With recursive crumpling, we should see all names unescaped */ | ||
2419 | - g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); | ||
2420 | - g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); | ||
2421 | - | ||
2422 | - qobject_unref(src); | ||
2423 | - qobject_unref(dst); | ||
2424 | -} | ||
2425 | - | ||
2426 | -static void qdict_crumple_test_empty(void) | ||
2427 | -{ | ||
2428 | - QDict *src, *dst; | ||
2429 | - | ||
2430 | - src = qdict_new(); | ||
2431 | - | ||
2432 | - dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); | ||
2433 | - | ||
2434 | - g_assert_cmpint(qdict_size(dst), ==, 0); | ||
2435 | - | ||
2436 | - qobject_unref(src); | ||
2437 | - qobject_unref(dst); | ||
2438 | -} | ||
2439 | - | ||
2440 | -static int qdict_count_entries(QDict *dict) | ||
2441 | -{ | ||
2442 | - const QDictEntry *e; | ||
2443 | - int count = 0; | ||
2444 | - | ||
2445 | - for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { | ||
2446 | - count++; | ||
2447 | - } | ||
2448 | - | ||
2449 | - return count; | ||
2450 | -} | ||
2451 | - | ||
2452 | -static void qdict_rename_keys_test(void) | ||
2453 | -{ | ||
2454 | - QDict *dict = qdict_new(); | ||
2455 | - QDict *copy; | ||
2456 | - QDictRenames *renames; | ||
2457 | - Error *local_err = NULL; | ||
2458 | - | ||
2459 | - qdict_put_str(dict, "abc", "foo"); | ||
2460 | - qdict_put_str(dict, "abcdef", "bar"); | ||
2461 | - qdict_put_int(dict, "number", 42); | ||
2462 | - qdict_put_bool(dict, "flag", true); | ||
2463 | - qdict_put_null(dict, "nothing"); | ||
2464 | - | ||
2465 | - /* Empty rename list */ | ||
2466 | - renames = (QDictRenames[]) { | ||
2467 | - { NULL, "this can be anything" } | ||
2468 | - }; | ||
2469 | - copy = qdict_clone_shallow(dict); | ||
2470 | - qdict_rename_keys(copy, renames, &error_abort); | ||
2471 | - | ||
2472 | - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); | ||
2473 | - g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); | ||
2474 | - g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); | ||
2475 | - g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); | ||
2476 | - g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); | ||
2477 | - g_assert_cmpint(qdict_count_entries(copy), ==, 5); | ||
2478 | - | ||
2479 | - qobject_unref(copy); | ||
2480 | - | ||
2481 | - /* Simple rename of all entries */ | ||
2482 | - renames = (QDictRenames[]) { | ||
2483 | - { "abc", "str1" }, | ||
2484 | - { "abcdef", "str2" }, | ||
2485 | - { "number", "int" }, | ||
2486 | - { "flag", "bool" }, | ||
2487 | - { "nothing", "null" }, | ||
2488 | - { NULL , NULL } | ||
2489 | - }; | ||
2490 | - copy = qdict_clone_shallow(dict); | ||
2491 | - qdict_rename_keys(copy, renames, &error_abort); | ||
2492 | - | ||
2493 | - g_assert(!qdict_haskey(copy, "abc")); | ||
2494 | - g_assert(!qdict_haskey(copy, "abcdef")); | ||
2495 | - g_assert(!qdict_haskey(copy, "number")); | ||
2496 | - g_assert(!qdict_haskey(copy, "flag")); | ||
2497 | - g_assert(!qdict_haskey(copy, "nothing")); | ||
2498 | - | ||
2499 | - g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); | ||
2500 | - g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); | ||
2501 | - g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); | ||
2502 | - g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); | ||
2503 | - g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); | ||
2504 | - g_assert_cmpint(qdict_count_entries(copy), ==, 5); | ||
2505 | - | ||
2506 | - qobject_unref(copy); | ||
2507 | - | ||
2508 | - /* Renames are processed top to bottom */ | ||
2509 | - renames = (QDictRenames[]) { | ||
2510 | - { "abc", "tmp" }, | ||
2511 | - { "abcdef", "abc" }, | ||
2512 | - { "number", "abcdef" }, | ||
2513 | - { "flag", "number" }, | ||
2514 | - { "nothing", "flag" }, | ||
2515 | - { "tmp", "nothing" }, | ||
2516 | - { NULL , NULL } | ||
2517 | - }; | ||
2518 | - copy = qdict_clone_shallow(dict); | ||
2519 | - qdict_rename_keys(copy, renames, &error_abort); | ||
2520 | - | ||
2521 | - g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); | ||
2522 | - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); | ||
2523 | - g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); | ||
2524 | - g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); | ||
2525 | - g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); | ||
2526 | - g_assert(!qdict_haskey(copy, "tmp")); | ||
2527 | - g_assert_cmpint(qdict_count_entries(copy), ==, 5); | ||
2528 | - | ||
2529 | - qobject_unref(copy); | ||
2530 | - | ||
2531 | - /* Conflicting rename */ | ||
2532 | - renames = (QDictRenames[]) { | ||
2533 | - { "abcdef", "abc" }, | ||
2534 | - { NULL , NULL } | ||
2535 | - }; | ||
2536 | - copy = qdict_clone_shallow(dict); | ||
2537 | - qdict_rename_keys(copy, renames, &local_err); | ||
2538 | - | ||
2539 | - g_assert(local_err != NULL); | ||
2540 | - error_free(local_err); | ||
2541 | - local_err = NULL; | ||
2542 | - | ||
2543 | - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); | ||
2544 | - g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); | ||
2545 | - g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); | ||
2546 | - g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); | ||
2547 | - g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); | ||
2548 | - g_assert_cmpint(qdict_count_entries(copy), ==, 5); | ||
2549 | - | ||
2550 | - qobject_unref(copy); | ||
2551 | - | ||
2552 | - /* Renames in an empty dict */ | ||
2553 | - renames = (QDictRenames[]) { | ||
2554 | - { "abcdef", "abc" }, | ||
2555 | - { NULL , NULL } | ||
2556 | - }; | ||
2557 | - | ||
2558 | - qobject_unref(dict); | ||
2559 | - dict = qdict_new(); | ||
2560 | - | ||
2561 | - qdict_rename_keys(dict, renames, &error_abort); | ||
2562 | - g_assert(qdict_first(dict) == NULL); | ||
2563 | - | ||
2564 | - qobject_unref(dict); | ||
2565 | -} | ||
2566 | - | ||
2567 | -static void qdict_crumple_test_bad_inputs(void) | ||
2568 | -{ | ||
2569 | - QDict *src; | ||
2570 | - Error *error = NULL; | ||
2571 | - | ||
2572 | - src = qdict_new(); | ||
2573 | - /* rule.0 can't be both a string and a dict */ | ||
2574 | - qdict_put_str(src, "rule.0", "fred"); | ||
2575 | - qdict_put_str(src, "rule.0.policy", "allow"); | ||
2576 | - | ||
2577 | - g_assert(qdict_crumple(src, &error) == NULL); | ||
2578 | - g_assert(error != NULL); | ||
2579 | - error_free(error); | ||
2580 | - error = NULL; | ||
2581 | - qobject_unref(src); | ||
2582 | - | ||
2583 | - src = qdict_new(); | ||
2584 | - /* rule can't be both a list and a dict */ | ||
2585 | - qdict_put_str(src, "rule.0", "fred"); | ||
2586 | - qdict_put_str(src, "rule.a", "allow"); | ||
2587 | - | ||
2588 | - g_assert(qdict_crumple(src, &error) == NULL); | ||
2589 | - g_assert(error != NULL); | ||
2590 | - error_free(error); | ||
2591 | - error = NULL; | ||
2592 | - qobject_unref(src); | ||
2593 | - | ||
2594 | - src = qdict_new(); | ||
2595 | - /* The input should be flat, ie no dicts or lists */ | ||
2596 | - qdict_put(src, "rule.a", qdict_new()); | ||
2597 | - qdict_put_str(src, "rule.b", "allow"); | ||
2598 | - | ||
2599 | - g_assert(qdict_crumple(src, &error) == NULL); | ||
2600 | - g_assert(error != NULL); | ||
2601 | - error_free(error); | ||
2602 | - error = NULL; | ||
2603 | - qobject_unref(src); | ||
2604 | - | ||
2605 | - src = qdict_new(); | ||
2606 | - /* List indexes must not have gaps */ | ||
2607 | - qdict_put_str(src, "rule.0", "deny"); | ||
2608 | - qdict_put_str(src, "rule.3", "allow"); | ||
2609 | - | ||
2610 | - g_assert(qdict_crumple(src, &error) == NULL); | ||
2611 | - g_assert(error != NULL); | ||
2612 | - error_free(error); | ||
2613 | - error = NULL; | ||
2614 | - qobject_unref(src); | ||
2615 | - | ||
2616 | - src = qdict_new(); | ||
2617 | - /* List indexes must be in %zu format */ | ||
2618 | - qdict_put_str(src, "rule.0", "deny"); | ||
2619 | - qdict_put_str(src, "rule.+1", "allow"); | ||
2620 | - | ||
2621 | - g_assert(qdict_crumple(src, &error) == NULL); | ||
2622 | - g_assert(error != NULL); | ||
2623 | - error_free(error); | ||
2624 | - error = NULL; | ||
2625 | - qobject_unref(src); | ||
2626 | -} | ||
2627 | - | ||
2628 | /* | ||
2629 | * Errors test-cases | ||
2630 | */ | ||
2631 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
2632 | g_test_add_func("/public/get_try_int", qdict_get_try_int_test); | ||
2633 | g_test_add_func("/public/get_str", qdict_get_str_test); | ||
2634 | g_test_add_func("/public/get_try_str", qdict_get_try_str_test); | ||
2635 | - g_test_add_func("/public/defaults", qdict_defaults_test); | ||
2636 | g_test_add_func("/public/haskey_not", qdict_haskey_not_test); | ||
2637 | g_test_add_func("/public/haskey", qdict_haskey_test); | ||
2638 | g_test_add_func("/public/del", qdict_del_test); | ||
2639 | g_test_add_func("/public/to_qdict", qobject_to_qdict_test); | ||
2640 | g_test_add_func("/public/iterapi", qdict_iterapi_test); | ||
2641 | - g_test_add_func("/public/flatten", qdict_flatten_test); | ||
2642 | - g_test_add_func("/public/array_split", qdict_array_split_test); | ||
2643 | - g_test_add_func("/public/array_entries", qdict_array_entries_test); | ||
2644 | - g_test_add_func("/public/join", qdict_join_test); | ||
2645 | |||
2646 | g_test_add_func("/errors/put_exists", qdict_put_exists_test); | ||
2647 | g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); | ||
2648 | |||
2649 | - g_test_add_func("/public/crumple/recursive", | ||
2650 | - qdict_crumple_test_recursive); | ||
2651 | - g_test_add_func("/public/crumple/empty", | ||
2652 | - qdict_crumple_test_empty); | ||
2653 | - g_test_add_func("/public/crumple/bad_inputs", | ||
2654 | - qdict_crumple_test_bad_inputs); | ||
2655 | - | ||
2656 | - g_test_add_func("/public/rename_keys", qdict_rename_keys_test); | ||
2657 | - | ||
2658 | /* The Big one */ | ||
2659 | if (g_test_slow()) { | ||
2660 | g_test_add_func("/stress/test", qdict_stress_test); | ||
2661 | diff --git a/MAINTAINERS b/MAINTAINERS | ||
2662 | index XXXXXXX..XXXXXXX 100644 | ||
2663 | --- a/MAINTAINERS | ||
2664 | +++ b/MAINTAINERS | ||
2665 | @@ -XXX,XX +XXX,XX @@ F: qemu-img* | ||
2666 | F: qemu-io* | ||
2667 | F: tests/qemu-iotests/ | ||
2668 | F: util/qemu-progress.c | ||
2669 | +F: qobject/block-qdict.c | ||
2670 | +F: test/check-block-qdict.c | ||
2671 | T: git git://repo.or.cz/qemu/kevin.git block | ||
2672 | |||
2673 | Block I/O path | ||
2674 | diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs | ||
2675 | index XXXXXXX..XXXXXXX 100644 | ||
2676 | --- a/qobject/Makefile.objs | ||
2677 | +++ b/qobject/Makefile.objs | ||
2678 | @@ -XXX,XX +XXX,XX @@ | ||
2679 | util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o | ||
2680 | util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o | ||
2681 | +util-obj-y += block-qdict.o | ||
2682 | diff --git a/tests/Makefile.include b/tests/Makefile.include | ||
2683 | index XXXXXXX..XXXXXXX 100644 | ||
2684 | --- a/tests/Makefile.include | ||
2685 | +++ b/tests/Makefile.include | ||
2686 | @@ -XXX,XX +XXX,XX @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ | ||
2687 | |||
2688 | check-unit-y = tests/check-qdict$(EXESUF) | ||
2689 | gcov-files-check-qdict-y = qobject/qdict.c | ||
2690 | +check-unit-y = tests/check-block-qdict$(EXESUF) | ||
2691 | +gcov-files-check-block-qdict-y = qobject/block-qdict.c | ||
2692 | check-unit-y += tests/test-char$(EXESUF) | ||
2693 | gcov-files-check-qdict-y = chardev/char.c | ||
2694 | check-unit-y += tests/check-qnum$(EXESUF) | ||
2695 | @@ -XXX,XX +XXX,XX @@ GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \ | ||
2696 | test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ | ||
2697 | tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \ | ||
2698 | tests/check-qjson.o tests/check-qlit.o \ | ||
2699 | + tests/check-block-qtest.o \ | ||
2700 | tests/test-coroutine.o tests/test-string-output-visitor.o \ | ||
2701 | tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ | ||
2702 | tests/test-clone-visitor.o \ | ||
2703 | @@ -XXX,XX +XXX,XX @@ test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o | ||
2704 | tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y) | ||
2705 | tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) | ||
2706 | tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) | ||
2707 | +tests/check-block-qdict$(EXESUF): tests/check-block-qdict.o $(test-util-obj-y) | ||
2708 | tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) | ||
2709 | tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) | ||
2710 | tests/check-qobject$(EXESUF): tests/check-qobject.o $(test-util-obj-y) | ||
2711 | -- | 147 | -- |
2712 | 2.13.6 | 148 | 2.41.0 |
2713 | |||
2714 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | Add a new wrapper type for GRAPH_WRLOCK functions that should be called |
---|---|---|---|
2 | from coroutine context. | ||
2 | 3 | ||
3 | During the design for manual completion, we decided not to use the | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | "manual" property as a shorthand for both auto-dismiss and auto-finalize. | 5 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
5 | 6 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | |
6 | Fix the wording. | 7 | Message-ID: <20230911094620.45040-7-kwolf@redhat.com> |
7 | |||
8 | Signed-off-by: John Snow <jsnow@redhat.com> | ||
9 | Reviewed-by: Jeff Cody <jcody@redhat.com> | ||
10 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 9 | --- |
13 | qapi/job.json | 11 ++++++----- | 10 | include/block/block-common.h | 4 ++++ |
14 | 1 file changed, 6 insertions(+), 5 deletions(-) | 11 | scripts/block-coroutine-wrapper.py | 11 +++++++++++ |
12 | 2 files changed, 15 insertions(+) | ||
15 | 13 | ||
16 | diff --git a/qapi/job.json b/qapi/job.json | 14 | diff --git a/include/block/block-common.h b/include/block/block-common.h |
17 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/qapi/job.json | 16 | --- a/include/block/block-common.h |
19 | +++ b/qapi/job.json | 17 | +++ b/include/block/block-common.h |
20 | @@ -XXX,XX +XXX,XX @@ | 18 | @@ -XXX,XX +XXX,XX @@ |
21 | # the last job in a transaction. | 19 | * function. The coroutine yields after scheduling the BH and is reentered when |
22 | # | 20 | * the wrapped function returns. |
23 | # @pending: The job has finished its work, but has finalization steps that it | 21 | * |
24 | -# needs to make prior to completing. These changes may require | 22 | + * A no_co_wrapper_bdrv_wrlock function is a no_co_wrapper function that |
25 | -# manual intervention by the management process if manual was set | 23 | + * automatically takes the graph wrlock when calling the wrapped function. |
26 | -# to true. These changes may still fail. | 24 | + * |
27 | +# needs to make prior to completing. These changes will require | 25 | * If the first parameter of the function is a BlockDriverState, BdrvChild or |
28 | +# manual intervention via @job-finalize if auto-finalize was set to | 26 | * BlockBackend pointer, the AioContext lock for it is taken in the wrapper. |
29 | +# false. These pending changes may still fail. | 27 | */ |
30 | # | 28 | #define no_co_wrapper |
31 | # @aborting: The job is in the process of being aborted, and will finish with | 29 | +#define no_co_wrapper_bdrv_wrlock |
32 | # an error. The job will afterwards report that it is @concluded. | 30 | |
33 | # This status may not be visible to the management process. | 31 | #include "block/blockjob.h" |
34 | # | 32 | |
35 | -# @concluded: The job has finished all work. If manual was set to true, the job | 33 | diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py |
36 | -# will remain in the query list until it is dismissed. | 34 | index XXXXXXX..XXXXXXX 100644 |
37 | +# @concluded: The job has finished all work. If auto-dismiss was set to false, | 35 | --- a/scripts/block-coroutine-wrapper.py |
38 | +# the job will remain in the query list until it is dismissed via | 36 | +++ b/scripts/block-coroutine-wrapper.py |
39 | +# @job-dismiss. | 37 | @@ -XXX,XX +XXX,XX @@ def __init__(self, wrapper_type: str, return_type: str, name: str, |
40 | # | 38 | self.args = [ParamDecl(arg.strip()) for arg in args.split(',')] |
41 | # @null: The job is in the process of being dismantled. This state should not | 39 | self.create_only_co = 'mixed' not in variant |
42 | # ever be visible externally. | 40 | self.graph_rdlock = 'bdrv_rdlock' in variant |
41 | + self.graph_wrlock = 'bdrv_wrlock' in variant | ||
42 | |||
43 | self.wrapper_type = wrapper_type | ||
44 | |||
45 | if wrapper_type == 'co': | ||
46 | + if self.graph_wrlock: | ||
47 | + raise ValueError(f"co function can't be wrlock: {self.name}") | ||
48 | subsystem, subname = self.name.split('_', 1) | ||
49 | self.target_name = f'{subsystem}_co_{subname}' | ||
50 | else: | ||
51 | @@ -XXX,XX +XXX,XX @@ def gen_no_co_wrapper(func: FuncDecl) -> str: | ||
52 | name = func.target_name | ||
53 | struct_name = func.struct_name | ||
54 | |||
55 | + graph_lock='' | ||
56 | + graph_unlock='' | ||
57 | + if func.graph_wrlock: | ||
58 | + graph_lock=' bdrv_graph_wrlock(NULL);' | ||
59 | + graph_unlock=' bdrv_graph_wrunlock();' | ||
60 | + | ||
61 | return f"""\ | ||
62 | /* | ||
63 | * Wrappers for {name} | ||
64 | @@ -XXX,XX +XXX,XX @@ def gen_no_co_wrapper(func: FuncDecl) -> str: | ||
65 | {struct_name} *s = opaque; | ||
66 | AioContext *ctx = {func.gen_ctx('s->')}; | ||
67 | |||
68 | +{graph_lock} | ||
69 | aio_context_acquire(ctx); | ||
70 | {func.get_result}{name}({ func.gen_list('s->{name}') }); | ||
71 | aio_context_release(ctx); | ||
72 | +{graph_unlock} | ||
73 | |||
74 | aio_co_wake(s->co); | ||
75 | }} | ||
43 | -- | 76 | -- |
44 | 2.13.6 | 77 | 2.41.0 |
45 | |||
46 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | Don't assume specific parameter names like 'bs' or 'blk' in the |
---|---|---|---|
2 | generated code, but use the actual name. | ||
2 | 3 | ||
3 | When you mix scalar and non-scalar keys, whether you get an "already | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | set as scalar" or an "already set as dict" error depends on qdict | 5 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
5 | iteration order. Neither message makes much sense. Replace by | 6 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
6 | ""Cannot mix scalar and non-scalar keys". This is similar to the | 7 | Message-ID: <20230911094620.45040-8-kwolf@redhat.com> |
7 | message we get for mixing list and non-list keys. | ||
8 | |||
9 | I find qdict_crumple()'s first loop hard to understand. Rearrange it | ||
10 | and add a comment. | ||
11 | |||
12 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | ||
13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
14 | --- | 9 | --- |
15 | qobject/block-qdict.c | 32 ++++++++++++++++---------------- | 10 | scripts/block-coroutine-wrapper.py | 7 ++++--- |
16 | 1 file changed, 16 insertions(+), 16 deletions(-) | 11 | 1 file changed, 4 insertions(+), 3 deletions(-) |
17 | 12 | ||
18 | diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c | 13 | diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py |
19 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/qobject/block-qdict.c | 15 | --- a/scripts/block-coroutine-wrapper.py |
21 | +++ b/qobject/block-qdict.c | 16 | +++ b/scripts/block-coroutine-wrapper.py |
22 | @@ -XXX,XX +XXX,XX @@ static int qdict_is_list(QDict *maybe_list, Error **errp) | 17 | @@ -XXX,XX +XXX,XX @@ def __init__(self, wrapper_type: str, return_type: str, name: str, |
23 | QObject *qdict_crumple(const QDict *src, Error **errp) | 18 | |
24 | { | 19 | def gen_ctx(self, prefix: str = '') -> str: |
25 | const QDictEntry *ent; | 20 | t = self.args[0].type |
26 | - QDict *two_level, *multi_level = NULL; | 21 | + name = self.args[0].name |
27 | + QDict *two_level, *multi_level = NULL, *child_dict; | 22 | if t == 'BlockDriverState *': |
28 | QObject *dst = NULL, *child; | 23 | - return f'bdrv_get_aio_context({prefix}bs)' |
29 | size_t i; | 24 | + return f'bdrv_get_aio_context({prefix}{name})' |
30 | char *prefix = NULL; | 25 | elif t == 'BdrvChild *': |
31 | @@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp) | 26 | - return f'bdrv_get_aio_context({prefix}child->bs)' |
32 | } | 27 | + return f'bdrv_get_aio_context({prefix}{name}->bs)' |
33 | 28 | elif t == 'BlockBackend *': | |
34 | qdict_split_flat_key(ent->key, &prefix, &suffix); | 29 | - return f'blk_get_aio_context({prefix}blk)' |
35 | - | 30 | + return f'blk_get_aio_context({prefix}{name})' |
36 | child = qdict_get(two_level, prefix); | 31 | else: |
37 | + child_dict = qobject_to(QDict, child); | 32 | return 'qemu_get_aio_context()' |
38 | + | ||
39 | + if (child) { | ||
40 | + /* | ||
41 | + * If @child_dict, then all previous keys with this prefix | ||
42 | + * had a suffix. If @suffix, this one has one as well, | ||
43 | + * and we're good, else there's a clash. | ||
44 | + */ | ||
45 | + if (!child_dict || !suffix) { | ||
46 | + error_setg(errp, "Cannot mix scalar and non-scalar keys"); | ||
47 | + goto error; | ||
48 | + } | ||
49 | + } | ||
50 | + | ||
51 | if (suffix) { | ||
52 | - QDict *child_dict = qobject_to(QDict, child); | ||
53 | if (!child_dict) { | ||
54 | - if (child) { | ||
55 | - error_setg(errp, "Key %s prefix is already set as a scalar", | ||
56 | - prefix); | ||
57 | - goto error; | ||
58 | - } | ||
59 | - | ||
60 | child_dict = qdict_new(); | ||
61 | - qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); | ||
62 | + qdict_put(two_level, prefix, child_dict); | ||
63 | } | ||
64 | - | ||
65 | qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); | ||
66 | } else { | ||
67 | - if (child) { | ||
68 | - error_setg(errp, "Key %s prefix is already set as a dict", | ||
69 | - prefix); | ||
70 | - goto error; | ||
71 | - } | ||
72 | qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); | ||
73 | } | ||
74 | 33 | ||
75 | -- | 34 | -- |
76 | 2.13.6 | 35 | 2.41.0 |
77 | |||
78 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | Instead of taking the writer lock internally, require callers to already |
---|---|---|---|
2 | hold it when calling bdrv_replace_child_noperm(). These callers will | ||
3 | typically already hold the graph lock once the locking work is | ||
4 | completed, which means that they can't call functions that take it | ||
5 | internally. | ||
2 | 6 | ||
3 | Configuration flows through the block subsystem in a rather peculiar | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | way. Configuration made with -drive enters it as QemuOpts. | 8 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
5 | Configuration made with -blockdev / blockdev-add enters it as QAPI | 9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
6 | type BlockdevOptions. The block subsystem uses QDict, QemuOpts and | 10 | Message-ID: <20230911094620.45040-9-kwolf@redhat.com> |
7 | QAPI types internally. The precise flow is next to impossible to | ||
8 | explain (I tried for this commit message, but gave up after wasting | ||
9 | several hours). What I can explain is a flaw in the BlockDriver | ||
10 | interface that leads to this bug: | ||
11 | |||
12 | $ qemu-system-x86_64 -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234 | ||
13 | qemu-system-x86_64: -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234: Internal error: parameter user invalid | ||
14 | |||
15 | QMP blockdev-add is broken the same way. | ||
16 | |||
17 | Here's what happens. The block layer passes configuration represented | ||
18 | as flat QDict (with dotted keys) to BlockDriver methods | ||
19 | .bdrv_file_open(). The QDict's members are typed according to the | ||
20 | QAPI schema. | ||
21 | |||
22 | nfs_file_open() converts it to QAPI type BlockdevOptionsNfs, with | ||
23 | qdict_crumple() and a qobject input visitor. | ||
24 | |||
25 | This visitor comes in two flavors. The plain flavor requires scalars | ||
26 | to be typed according to the QAPI schema. That's the case here. The | ||
27 | keyval flavor requires string scalars. That's not the case here. | ||
28 | nfs_file_open() uses the latter, and promptly falls apart for members | ||
29 | @user, @group, @tcp-syn-count, @readahead-size, @page-cache-size, | ||
30 | @debug. | ||
31 | |||
32 | Switching to the plain flavor would fix -blockdev, but break -drive, | ||
33 | because there the scalars arrive in nfs_file_open() as strings. | ||
34 | |||
35 | The proper fix would be to replace the QDict by QAPI type | ||
36 | BlockdevOptions in the BlockDriver interface. Sadly, that's beyond my | ||
37 | reach right now. | ||
38 | |||
39 | Next best would be to fix the block layer to always pass correctly | ||
40 | typed QDicts to the BlockDriver methods. Also beyond my reach. | ||
41 | |||
42 | What I can do is throw another hack onto the pile: have | ||
43 | nfs_file_open() convert all members to string, so use of the keyval | ||
44 | flavor actually works, by replacing qdict_crumple() by new function | ||
45 | qdict_crumple_for_keyval_qiv(). | ||
46 | |||
47 | The pattern "pass result of qdict_crumple() to | ||
48 | qobject_input_visitor_new_keyval()" occurs several times more: | ||
49 | |||
50 | * qemu_rbd_open() | ||
51 | |||
52 | Same issue as nfs_file_open(), but since BlockdevOptionsRbd has only | ||
53 | string members, its only a latent bug. Fix it anyway. | ||
54 | |||
55 | * parallels_co_create_opts(), qcow_co_create_opts(), | ||
56 | qcow2_co_create_opts(), bdrv_qed_co_create_opts(), | ||
57 | sd_co_create_opts(), vhdx_co_create_opts(), vpc_co_create_opts() | ||
58 | |||
59 | These work, because they create the QDict with | ||
60 | qemu_opts_to_qdict_filtered(), which creates only string scalars. | ||
61 | The function sports a TODO comment asking for better typing; that's | ||
62 | going to be fun. Use qdict_crumple_for_keyval_qiv() to be safe. | ||
63 | |||
64 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | ||
65 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
66 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
67 | --- | 12 | --- |
68 | include/block/qdict.h | 1 + | 13 | block.c | 26 +++++++++++++++++++------- |
69 | block/nfs.c | 2 +- | 14 | 1 file changed, 19 insertions(+), 7 deletions(-) |
70 | block/parallels.c | 2 +- | ||
71 | block/qcow.c | 2 +- | ||
72 | block/qcow2.c | 2 +- | ||
73 | block/qed.c | 2 +- | ||
74 | block/rbd.c | 2 +- | ||
75 | block/sheepdog.c | 2 +- | ||
76 | block/vhdx.c | 2 +- | ||
77 | block/vpc.c | 2 +- | ||
78 | qobject/block-qdict.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ | ||
79 | 11 files changed, 67 insertions(+), 9 deletions(-) | ||
80 | 15 | ||
81 | diff --git a/include/block/qdict.h b/include/block/qdict.h | 16 | diff --git a/block.c b/block.c |
82 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
83 | --- a/include/block/qdict.h | 18 | --- a/block.c |
84 | +++ b/include/block/qdict.h | 19 | +++ b/block.c |
85 | @@ -XXX,XX +XXX,XX @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); | 20 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename, |
86 | void qdict_array_split(QDict *src, QList **dst); | 21 | static bool bdrv_recurse_has_child(BlockDriverState *bs, |
87 | int qdict_array_entries(QDict *src, const char *subqdict); | 22 | BlockDriverState *child); |
88 | QObject *qdict_crumple(const QDict *src, Error **errp); | 23 | |
89 | +QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp); | 24 | -static void bdrv_replace_child_noperm(BdrvChild *child, |
90 | void qdict_flatten(QDict *qdict); | 25 | - BlockDriverState *new_bs); |
91 | 26 | +static void GRAPH_WRLOCK | |
92 | typedef struct QDictRenames { | 27 | +bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs); |
93 | diff --git a/block/nfs.c b/block/nfs.c | 28 | + |
94 | index XXXXXXX..XXXXXXX 100644 | 29 | static void bdrv_remove_child(BdrvChild *child, Transaction *tran); |
95 | --- a/block/nfs.c | 30 | |
96 | +++ b/block/nfs.c | 31 | static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, |
97 | @@ -XXX,XX +XXX,XX @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, | 32 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque) |
98 | const QDictEntry *e; | 33 | BlockDriverState *new_bs = s->child->bs; |
99 | Error *local_err = NULL; | 34 | |
100 | 35 | GLOBAL_STATE_CODE(); | |
101 | - crumpled = qdict_crumple(options, errp); | 36 | + bdrv_graph_wrlock(s->old_bs); |
102 | + crumpled = qdict_crumple_for_keyval_qiv(options, errp); | 37 | + |
103 | if (crumpled == NULL) { | 38 | /* old_bs reference is transparently moved from @s to @s->child */ |
104 | return NULL; | 39 | if (!s->child->bs) { |
40 | /* | ||
41 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque) | ||
105 | } | 42 | } |
106 | diff --git a/block/parallels.c b/block/parallels.c | 43 | assert(s->child->quiesced_parent); |
107 | index XXXXXXX..XXXXXXX 100644 | 44 | bdrv_replace_child_noperm(s->child, s->old_bs); |
108 | --- a/block/parallels.c | 45 | + |
109 | +++ b/block/parallels.c | 46 | + bdrv_graph_wrunlock(); |
110 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename, | 47 | bdrv_unref(new_bs); |
111 | qdict_put_str(qdict, "driver", "parallels"); | 48 | } |
112 | qdict_put_str(qdict, "file", bs->node_name); | 49 | |
113 | 50 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, | |
114 | - qobj = qdict_crumple(qdict, errp); | 51 | if (new_bs) { |
115 | + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | 52 | bdrv_ref(new_bs); |
116 | qobject_unref(qdict); | ||
117 | qdict = qobject_to(QDict, qobj); | ||
118 | if (qdict == NULL) { | ||
119 | diff --git a/block/qcow.c b/block/qcow.c | ||
120 | index XXXXXXX..XXXXXXX 100644 | ||
121 | --- a/block/qcow.c | ||
122 | +++ b/block/qcow.c | ||
123 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, | ||
124 | qdict_put_str(qdict, "driver", "qcow"); | ||
125 | qdict_put_str(qdict, "file", bs->node_name); | ||
126 | |||
127 | - qobj = qdict_crumple(qdict, errp); | ||
128 | + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
129 | qobject_unref(qdict); | ||
130 | qdict = qobject_to(QDict, qobj); | ||
131 | if (qdict == NULL) { | ||
132 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
133 | index XXXXXXX..XXXXXXX 100644 | ||
134 | --- a/block/qcow2.c | ||
135 | +++ b/block/qcow2.c | ||
136 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt | ||
137 | qdict_put_str(qdict, "file", bs->node_name); | ||
138 | |||
139 | /* Now get the QAPI type BlockdevCreateOptions */ | ||
140 | - qobj = qdict_crumple(qdict, errp); | ||
141 | + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
142 | qobject_unref(qdict); | ||
143 | qdict = qobject_to(QDict, qobj); | ||
144 | if (qdict == NULL) { | ||
145 | diff --git a/block/qed.c b/block/qed.c | ||
146 | index XXXXXXX..XXXXXXX 100644 | ||
147 | --- a/block/qed.c | ||
148 | +++ b/block/qed.c | ||
149 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, | ||
150 | qdict_put_str(qdict, "driver", "qed"); | ||
151 | qdict_put_str(qdict, "file", bs->node_name); | ||
152 | |||
153 | - qobj = qdict_crumple(qdict, errp); | ||
154 | + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
155 | qobject_unref(qdict); | ||
156 | qdict = qobject_to(QDict, qobj); | ||
157 | if (qdict == NULL) { | ||
158 | diff --git a/block/rbd.c b/block/rbd.c | ||
159 | index XXXXXXX..XXXXXXX 100644 | ||
160 | --- a/block/rbd.c | ||
161 | +++ b/block/rbd.c | ||
162 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||
163 | } | 53 | } |
164 | 54 | + | |
165 | /* Convert the remaining options into a QAPI object */ | 55 | + bdrv_graph_wrlock(new_bs); |
166 | - crumpled = qdict_crumple(options, errp); | 56 | bdrv_replace_child_noperm(child, new_bs); |
167 | + crumpled = qdict_crumple_for_keyval_qiv(options, errp); | 57 | + bdrv_graph_wrunlock(); |
168 | if (crumpled == NULL) { | 58 | /* old_bs reference is transparently moved from @child to @s */ |
169 | r = -EINVAL; | 59 | } |
170 | goto out; | 60 | |
171 | diff --git a/block/sheepdog.c b/block/sheepdog.c | 61 | @@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) |
172 | index XXXXXXX..XXXXXXX 100644 | 62 | * If @new_bs is non-NULL, the parent of @child must already be drained through |
173 | --- a/block/sheepdog.c | 63 | * @child and the caller must hold the AioContext lock for @new_bs. |
174 | +++ b/block/sheepdog.c | 64 | */ |
175 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, | 65 | -static void bdrv_replace_child_noperm(BdrvChild *child, |
66 | - BlockDriverState *new_bs) | ||
67 | +static void GRAPH_WRLOCK | ||
68 | +bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) | ||
69 | { | ||
70 | BlockDriverState *old_bs = child->bs; | ||
71 | int new_bs_quiesce_counter; | ||
72 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, | ||
73 | assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); | ||
176 | } | 74 | } |
177 | 75 | ||
178 | /* Get the QAPI object */ | 76 | - /* TODO Pull this up into the callers to avoid polling here */ |
179 | - crumpled = qdict_crumple(qdict, errp); | 77 | - bdrv_graph_wrlock(new_bs); |
180 | + crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); | 78 | if (old_bs) { |
181 | if (crumpled == NULL) { | 79 | if (child->klass->detach) { |
182 | ret = -EINVAL; | 80 | child->klass->detach(child); |
183 | goto fail; | 81 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, |
184 | diff --git a/block/vhdx.c b/block/vhdx.c | 82 | child->klass->attach(child); |
185 | index XXXXXXX..XXXXXXX 100644 | 83 | } |
186 | --- a/block/vhdx.c | 84 | } |
187 | +++ b/block/vhdx.c | 85 | - bdrv_graph_wrunlock(); |
188 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, | 86 | |
189 | qdict_put_str(qdict, "driver", "vhdx"); | 87 | /* |
190 | qdict_put_str(qdict, "file", bs->node_name); | 88 | * If the parent was drained through this BdrvChild previously, but new_bs |
191 | 89 | @@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque) | |
192 | - qobj = qdict_crumple(qdict, errp); | 90 | BlockDriverState *bs = s->child->bs; |
193 | + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | 91 | |
194 | qobject_unref(qdict); | 92 | GLOBAL_STATE_CODE(); |
195 | qdict = qobject_to(QDict, qobj); | ||
196 | if (qdict == NULL) { | ||
197 | diff --git a/block/vpc.c b/block/vpc.c | ||
198 | index XXXXXXX..XXXXXXX 100644 | ||
199 | --- a/block/vpc.c | ||
200 | +++ b/block/vpc.c | ||
201 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, | ||
202 | qdict_put_str(qdict, "driver", "vpc"); | ||
203 | qdict_put_str(qdict, "file", bs->node_name); | ||
204 | |||
205 | - qobj = qdict_crumple(qdict, errp); | ||
206 | + qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
207 | qobject_unref(qdict); | ||
208 | qdict = qobject_to(QDict, qobj); | ||
209 | if (qdict == NULL) { | ||
210 | diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c | ||
211 | index XXXXXXX..XXXXXXX 100644 | ||
212 | --- a/qobject/block-qdict.c | ||
213 | +++ b/qobject/block-qdict.c | ||
214 | @@ -XXX,XX +XXX,XX @@ | ||
215 | |||
216 | #include "qemu/osdep.h" | ||
217 | #include "block/qdict.h" | ||
218 | +#include "qapi/qmp/qbool.h" | ||
219 | #include "qapi/qmp/qlist.h" | ||
220 | +#include "qapi/qmp/qnum.h" | ||
221 | +#include "qapi/qmp/qstring.h" | ||
222 | #include "qemu/cutils.h" | ||
223 | #include "qapi/error.h" | ||
224 | |||
225 | @@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp) | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | + * qdict_crumple_for_keyval_qiv: | ||
230 | + * @src: the flat dictionary (only scalar values) to crumple | ||
231 | + * @errp: location to store error | ||
232 | + * | ||
233 | + * Like qdict_crumple(), but additionally transforms scalar values so | ||
234 | + * the result can be passed to qobject_input_visitor_new_keyval(). | ||
235 | + * | ||
236 | + * The block subsystem uses this function to prepare its flat QDict | ||
237 | + * with possibly confused scalar types for a visit. It should not be | ||
238 | + * used for anything else, and it should go away once the block | ||
239 | + * subsystem has been cleaned up. | ||
240 | + */ | ||
241 | +QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) | ||
242 | +{ | ||
243 | + QDict *tmp = NULL; | ||
244 | + char *buf; | ||
245 | + const char *s; | ||
246 | + const QDictEntry *ent; | ||
247 | + QObject *dst; | ||
248 | + | 93 | + |
249 | + for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) { | 94 | + bdrv_graph_wrlock(NULL); |
250 | + buf = NULL; | 95 | bdrv_replace_child_noperm(s->child, NULL); |
251 | + switch (qobject_type(ent->value)) { | 96 | + bdrv_graph_wrunlock(); |
252 | + case QTYPE_QNULL: | 97 | |
253 | + case QTYPE_QSTRING: | 98 | if (bdrv_get_aio_context(bs) != s->old_child_ctx) { |
254 | + continue; | 99 | bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort); |
255 | + case QTYPE_QNUM: | 100 | @@ -XXX,XX +XXX,XX @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, |
256 | + s = buf = qnum_to_string(qobject_to(QNum, ent->value)); | 101 | * a problem, we already did this), but it will still poll until the parent |
257 | + break; | 102 | * is fully quiesced, so it will not be negatively affected either. |
258 | + case QTYPE_QDICT: | 103 | */ |
259 | + case QTYPE_QLIST: | 104 | + bdrv_graph_wrlock(child_bs); |
260 | + /* @src isn't flat; qdict_crumple() will fail */ | 105 | bdrv_parent_drained_begin_single(new_child); |
261 | + continue; | 106 | bdrv_replace_child_noperm(new_child, child_bs); |
262 | + case QTYPE_QBOOL: | 107 | + bdrv_graph_wrunlock(); |
263 | + s = qbool_get_bool(qobject_to(QBool, ent->value)) | 108 | |
264 | + ? "on" : "off"; | 109 | BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1); |
265 | + break; | 110 | *s = (BdrvAttachChildCommonState) { |
266 | + default: | 111 | @@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child) |
267 | + abort(); | 112 | BlockDriverState *child_bs = child->bs; |
268 | + } | 113 | |
269 | + | 114 | GLOBAL_STATE_CODE(); |
270 | + if (!tmp) { | 115 | + bdrv_graph_wrlock(NULL); |
271 | + tmp = qdict_clone_shallow(src); | 116 | bdrv_replace_child_noperm(child, NULL); |
272 | + } | 117 | + bdrv_graph_wrunlock(); |
273 | + qdict_put(tmp, ent->key, qstring_from_str(s)); | 118 | bdrv_child_free(child); |
274 | + g_free(buf); | 119 | |
275 | + } | 120 | if (child_bs) { |
276 | + | ||
277 | + dst = qdict_crumple(tmp ?: src, errp); | ||
278 | + qobject_unref(tmp); | ||
279 | + return dst; | ||
280 | +} | ||
281 | + | ||
282 | +/** | ||
283 | * qdict_array_entries(): Returns the number of direct array entries if the | ||
284 | * sub-QDict of src specified by the prefix in subqdict (or src itself for | ||
285 | * prefix == "") is valid as an array, i.e. the length of the created list if | ||
286 | -- | 121 | -- |
287 | 2.13.6 | 122 | 2.41.0 |
288 | |||
289 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | Instead of taking the writer lock internally, require callers to already |
---|---|---|---|
2 | 2 | hold it when calling bdrv_replace_child_tran(). These callers will | |
3 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 3 | typically already hold the graph lock once the locking work is |
4 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 4 | completed, which means that they can't call functions that take it |
5 | internally. | ||
6 | |||
7 | While a graph lock is held, polling is not allowed. Therefore draining | ||
8 | the necessary nodes can no longer be done in bdrv_remove_child() and | ||
9 | bdrv_replace_node_noperm(), but the callers must already make sure that | ||
10 | they are drained. | ||
11 | |||
12 | Note that the transaction callbacks still take the lock internally, so | ||
13 | tran_finalize() must be called without the lock held. This is because | ||
14 | bdrv_append() also calls bdrv_attach_child_noperm(), which currently | ||
15 | requires to be called unlocked. Once it changes, the transaction | ||
16 | callbacks can be changed, too. | ||
17 | |||
18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> | ||
20 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
21 | Message-ID: <20230911094620.45040-10-kwolf@redhat.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 22 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | --- | 23 | --- |
7 | tests/check-block-qdict.c | 14 +++++++++++++- | 24 | block.c | 78 ++++++++++++++++++++++++++++++++++++--------------------- |
8 | 1 file changed, 13 insertions(+), 1 deletion(-) | 25 | 1 file changed, 50 insertions(+), 28 deletions(-) |
9 | 26 | ||
10 | diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c | 27 | diff --git a/block.c b/block.c |
11 | index XXXXXXX..XXXXXXX 100644 | 28 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/tests/check-block-qdict.c | 29 | --- a/block.c |
13 | +++ b/tests/check-block-qdict.c | 30 | +++ b/block.c |
14 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void) | 31 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_recurse_has_child(BlockDriverState *bs, |
15 | QList *e = qlist_new(); | 32 | static void GRAPH_WRLOCK |
16 | QDict *e_1_2 = qdict_new(); | 33 | bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs); |
17 | QDict *f = qdict_new(); | 34 | |
18 | + QList *y = qlist_new(); | 35 | -static void bdrv_remove_child(BdrvChild *child, Transaction *tran); |
19 | + QDict *z = qdict_new(); | 36 | +static void GRAPH_WRLOCK |
20 | QDict *root = qdict_new(); | 37 | +bdrv_remove_child(BdrvChild *child, Transaction *tran); |
38 | |||
39 | static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, | ||
40 | BlockReopenQueue *queue, | ||
41 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = { | ||
42 | * | ||
43 | * The function doesn't update permissions, caller is responsible for this. | ||
44 | */ | ||
45 | -static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, | ||
46 | - Transaction *tran) | ||
47 | +static void GRAPH_WRLOCK | ||
48 | +bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, | ||
49 | + Transaction *tran) | ||
50 | { | ||
51 | BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1); | ||
52 | |||
53 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, | ||
54 | bdrv_ref(new_bs); | ||
55 | } | ||
56 | |||
57 | - bdrv_graph_wrlock(new_bs); | ||
58 | bdrv_replace_child_noperm(child, new_bs); | ||
59 | - bdrv_graph_wrunlock(); | ||
60 | /* old_bs reference is transparently moved from @child to @s */ | ||
61 | } | ||
62 | |||
63 | @@ -XXX,XX +XXX,XX @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, | ||
64 | } | ||
65 | |||
66 | if (child) { | ||
67 | + bdrv_drained_begin(child->bs); | ||
68 | + bdrv_graph_wrlock(NULL); | ||
69 | + | ||
70 | bdrv_unset_inherits_from(parent_bs, child, tran); | ||
71 | bdrv_remove_child(child, tran); | ||
72 | + | ||
73 | + bdrv_graph_wrunlock(); | ||
74 | + bdrv_drained_end(child->bs); | ||
75 | } | ||
76 | |||
77 | if (!child_bs) { | ||
78 | @@ -XXX,XX +XXX,XX @@ void bdrv_close_all(void) | ||
79 | assert(QTAILQ_EMPTY(&all_bdrv_states)); | ||
80 | } | ||
81 | |||
82 | -static bool should_update_child(BdrvChild *c, BlockDriverState *to) | ||
83 | +static bool GRAPH_RDLOCK should_update_child(BdrvChild *c, BlockDriverState *to) | ||
84 | { | ||
85 | GQueue *queue; | ||
86 | GHashTable *found; | ||
87 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_remove_child_drv = { | ||
88 | .commit = bdrv_remove_child_commit, | ||
89 | }; | ||
90 | |||
91 | -/* Function doesn't update permissions, caller is responsible for this. */ | ||
92 | -static void bdrv_remove_child(BdrvChild *child, Transaction *tran) | ||
93 | +/* | ||
94 | + * Function doesn't update permissions, caller is responsible for this. | ||
95 | + * | ||
96 | + * @child->bs (if non-NULL) must be drained. | ||
97 | + */ | ||
98 | +static void GRAPH_WRLOCK bdrv_remove_child(BdrvChild *child, Transaction *tran) | ||
99 | { | ||
100 | if (!child) { | ||
101 | return; | ||
102 | } | ||
103 | |||
104 | if (child->bs) { | ||
105 | - BlockDriverState *bs = child->bs; | ||
106 | - bdrv_drained_begin(bs); | ||
107 | + assert(child->quiesced_parent); | ||
108 | bdrv_replace_child_tran(child, NULL, tran); | ||
109 | - bdrv_drained_end(bs); | ||
110 | } | ||
111 | |||
112 | tran_add(tran, &bdrv_remove_child_drv, child); | ||
113 | } | ||
114 | |||
115 | -static void undrain_on_clean_cb(void *opaque) | ||
116 | -{ | ||
117 | - bdrv_drained_end(opaque); | ||
118 | -} | ||
119 | - | ||
120 | -static TransactionActionDrv undrain_on_clean = { | ||
121 | - .clean = undrain_on_clean_cb, | ||
122 | -}; | ||
123 | - | ||
124 | -static int bdrv_replace_node_noperm(BlockDriverState *from, | ||
125 | - BlockDriverState *to, | ||
126 | - bool auto_skip, Transaction *tran, | ||
127 | - Error **errp) | ||
128 | +/* | ||
129 | + * Both @from and @to (if non-NULL) must be drained. @to must be kept drained | ||
130 | + * until the transaction is completed. | ||
131 | + */ | ||
132 | +static int GRAPH_WRLOCK | ||
133 | +bdrv_replace_node_noperm(BlockDriverState *from, | ||
134 | + BlockDriverState *to, | ||
135 | + bool auto_skip, Transaction *tran, | ||
136 | + Error **errp) | ||
137 | { | ||
138 | BdrvChild *c, *next; | ||
139 | |||
140 | GLOBAL_STATE_CODE(); | ||
141 | |||
142 | - bdrv_drained_begin(from); | ||
143 | - bdrv_drained_begin(to); | ||
144 | - tran_add(tran, &undrain_on_clean, from); | ||
145 | - tran_add(tran, &undrain_on_clean, to); | ||
146 | + assert(from->quiesce_counter); | ||
147 | + assert(to->quiesce_counter); | ||
148 | |||
149 | QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { | ||
150 | assert(c->bs == from); | ||
151 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, | ||
152 | assert(qemu_get_current_aio_context() == qemu_get_aio_context()); | ||
153 | assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); | ||
154 | bdrv_drained_begin(from); | ||
155 | + bdrv_drained_begin(to); | ||
156 | + | ||
157 | + bdrv_graph_wrlock(to); | ||
21 | 158 | ||
22 | /* | 159 | /* |
23 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void) | 160 | * Do the replacement without permission update. |
24 | * "c": 2, | 161 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, |
25 | * "d": 3, | 162 | } |
26 | * }, | 163 | |
27 | - * "g": 4 | 164 | if (detach_subchain) { |
28 | + * "g": 4, | 165 | + /* to_cow_parent is already drained because from is drained */ |
29 | + * "y": [{}], | 166 | bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran); |
30 | + * "z": {"a": []} | 167 | } |
31 | * } | 168 | |
32 | * | 169 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, |
33 | * to | 170 | ret = 0; |
34 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void) | 171 | |
35 | * "f.d": 3, | 172 | out: |
36 | * "g": 4 | 173 | + bdrv_graph_wrunlock(); |
37 | * } | 174 | tran_finalize(tran, ret); |
38 | + * | 175 | |
39 | + * Note that "y" and "z" get eaten. | 176 | + bdrv_drained_end(to); |
40 | */ | 177 | bdrv_drained_end(from); |
41 | 178 | bdrv_unref(from); | |
42 | qdict_put_int(e_1_2, "a", 0); | 179 | |
43 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void) | 180 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, |
44 | qdict_put_int(f, "c", 2); | 181 | BdrvChild *child; |
45 | qdict_put_int(f, "d", 3); | 182 | Transaction *tran = tran_new(); |
46 | 183 | AioContext *old_context, *new_context = NULL; | |
47 | + qlist_append(y, qdict_new()); | 184 | + bool drained = false; |
48 | + | 185 | |
49 | + qdict_put(z, "a", qlist_new()); | 186 | GLOBAL_STATE_CODE(); |
50 | + | 187 | |
51 | qdict_put(root, "e", e); | 188 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, |
52 | qdict_put(root, "f", f); | 189 | aio_context_acquire(new_context); |
53 | qdict_put_int(root, "g", 4); | 190 | } |
54 | + qdict_put(root, "y", y); | 191 | |
55 | + qdict_put(root, "z", z); | 192 | + bdrv_drained_begin(bs_new); |
56 | 193 | + bdrv_drained_begin(bs_top); | |
57 | qdict_flatten(root); | 194 | + drained = true; |
195 | + | ||
196 | + bdrv_graph_wrlock(bs_new); | ||
197 | ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp); | ||
198 | + bdrv_graph_wrunlock(); | ||
199 | if (ret < 0) { | ||
200 | goto out; | ||
201 | } | ||
202 | @@ -XXX,XX +XXX,XX @@ out: | ||
203 | bdrv_refresh_limits(bs_top, NULL, NULL); | ||
204 | bdrv_graph_rdunlock_main_loop(); | ||
205 | |||
206 | + if (drained) { | ||
207 | + bdrv_drained_end(bs_top); | ||
208 | + bdrv_drained_end(bs_new); | ||
209 | + } | ||
210 | + | ||
211 | if (new_context && old_context != new_context) { | ||
212 | aio_context_release(new_context); | ||
213 | aio_context_acquire(old_context); | ||
214 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, | ||
215 | bdrv_ref(old_bs); | ||
216 | bdrv_drained_begin(old_bs); | ||
217 | bdrv_drained_begin(new_bs); | ||
218 | + bdrv_graph_wrlock(new_bs); | ||
219 | |||
220 | bdrv_replace_child_tran(child, new_bs, tran); | ||
221 | |||
222 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, | ||
223 | refresh_list = g_slist_prepend(refresh_list, new_bs); | ||
224 | |||
225 | ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); | ||
226 | + bdrv_graph_wrunlock(); | ||
227 | |||
228 | tran_finalize(tran, ret); | ||
58 | 229 | ||
59 | -- | 230 | -- |
60 | 2.13.6 | 231 | 2.41.0 |
61 | |||
62 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | Instead of taking the writer lock internally, require callers to already |
---|---|---|---|
2 | hold it when calling bdrv_attach_child_common(). These callers will | ||
3 | typically already hold the graph lock once the locking work is | ||
4 | completed, which means that they can't call functions that take it | ||
5 | internally. | ||
2 | 6 | ||
3 | There's no need to restart the loop. We don't elsewhere, e.g. in | 7 | Note that the transaction callbacks still take the lock internally, so |
4 | qdict_extract_subqdict(), qdict_join() and qemu_opts_absorb_qdict(). | 8 | tran_finalize() must be called without the lock held. This is because |
5 | Simplify accordingly. | 9 | bdrv_append() also calls bdrv_replace_node_noperm(), which currently |
10 | requires the transaction callbacks to be called unlocked. In the next | ||
11 | step, both of them can be switched to locked tran_finalize() calls | ||
12 | together. | ||
6 | 13 | ||
7 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 15 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
16 | Message-ID: <20230911094620.45040-11-kwolf@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 18 | --- |
11 | qobject/block-qdict.c | 18 +++--------------- | 19 | block.c | 133 +++++++++++++++++++++++++++++++------------------ |
12 | 1 file changed, 3 insertions(+), 15 deletions(-) | 20 | block/stream.c | 20 ++++++-- |
21 | 2 files changed, 100 insertions(+), 53 deletions(-) | ||
13 | 22 | ||
14 | diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c | 23 | diff --git a/block.c b/block.c |
15 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/qobject/block-qdict.c | 25 | --- a/block.c |
17 | +++ b/qobject/block-qdict.c | 26 | +++ b/block.c |
18 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | 27 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_attach_child_common_drv = { |
19 | QObject *value; | 28 | * @child_bs can move to a different AioContext in this function. Callers must |
20 | const QDictEntry *entry, *next; | 29 | * make sure that their AioContext locking is still correct after this. |
21 | char *new_key; | 30 | */ |
22 | - bool delete; | 31 | -static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, |
23 | 32 | - const char *child_name, | |
24 | entry = qdict_first(qdict); | 33 | - const BdrvChildClass *child_class, |
25 | 34 | - BdrvChildRole child_role, | |
26 | while (entry != NULL) { | 35 | - uint64_t perm, uint64_t shared_perm, |
36 | - void *opaque, | ||
37 | - Transaction *tran, Error **errp) | ||
38 | +static BdrvChild * GRAPH_WRLOCK | ||
39 | +bdrv_attach_child_common(BlockDriverState *child_bs, | ||
40 | + const char *child_name, | ||
41 | + const BdrvChildClass *child_class, | ||
42 | + BdrvChildRole child_role, | ||
43 | + uint64_t perm, uint64_t shared_perm, | ||
44 | + void *opaque, | ||
45 | + Transaction *tran, Error **errp) | ||
46 | { | ||
47 | BdrvChild *new_child; | ||
48 | AioContext *parent_ctx, *new_child_ctx; | ||
49 | @@ -XXX,XX +XXX,XX @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, | ||
50 | * a problem, we already did this), but it will still poll until the parent | ||
51 | * is fully quiesced, so it will not be negatively affected either. | ||
52 | */ | ||
53 | - bdrv_graph_wrlock(child_bs); | ||
54 | bdrv_parent_drained_begin_single(new_child); | ||
55 | bdrv_replace_child_noperm(new_child, child_bs); | ||
56 | - bdrv_graph_wrunlock(); | ||
57 | |||
58 | BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1); | ||
59 | *s = (BdrvAttachChildCommonState) { | ||
60 | @@ -XXX,XX +XXX,XX @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, | ||
61 | * @child_bs can move to a different AioContext in this function. Callers must | ||
62 | * make sure that their AioContext locking is still correct after this. | ||
63 | */ | ||
64 | -static BdrvChild *bdrv_attach_child_noperm(BlockDriverState *parent_bs, | ||
65 | - BlockDriverState *child_bs, | ||
66 | - const char *child_name, | ||
67 | - const BdrvChildClass *child_class, | ||
68 | - BdrvChildRole child_role, | ||
69 | - Transaction *tran, | ||
70 | - Error **errp) | ||
71 | +static BdrvChild * GRAPH_WRLOCK | ||
72 | +bdrv_attach_child_noperm(BlockDriverState *parent_bs, | ||
73 | + BlockDriverState *child_bs, | ||
74 | + const char *child_name, | ||
75 | + const BdrvChildClass *child_class, | ||
76 | + BdrvChildRole child_role, | ||
77 | + Transaction *tran, | ||
78 | + Error **errp) | ||
79 | { | ||
80 | uint64_t perm, shared_perm; | ||
81 | |||
82 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
83 | |||
84 | GLOBAL_STATE_CODE(); | ||
85 | |||
86 | + bdrv_graph_wrlock(child_bs); | ||
87 | + | ||
88 | child = bdrv_attach_child_common(child_bs, child_name, child_class, | ||
89 | child_role, perm, shared_perm, opaque, | ||
90 | tran, errp); | ||
91 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
92 | ret = bdrv_refresh_perms(child_bs, tran, errp); | ||
93 | |||
94 | out: | ||
95 | + bdrv_graph_wrunlock(); | ||
96 | tran_finalize(tran, ret); | ||
97 | |||
98 | bdrv_unref(child_bs); | ||
99 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
100 | |||
101 | GLOBAL_STATE_CODE(); | ||
102 | |||
103 | + bdrv_graph_wrlock(child_bs); | ||
104 | + | ||
105 | child = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, | ||
106 | child_class, child_role, tran, errp); | ||
107 | if (!child) { | ||
108 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
109 | } | ||
110 | |||
111 | out: | ||
112 | + bdrv_graph_wrunlock(); | ||
113 | tran_finalize(tran, ret); | ||
114 | |||
115 | bdrv_unref(child_bs); | ||
116 | @@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) | ||
117 | * Sets the bs->backing or bs->file link of a BDS. A new reference is created; | ||
118 | * callers which don't need their own reference any more must call bdrv_unref(). | ||
119 | * | ||
120 | + * If the respective child is already present (i.e. we're detaching a node), | ||
121 | + * that child node must be drained. | ||
122 | + * | ||
123 | * Function doesn't update permissions, caller is responsible for this. | ||
124 | * | ||
125 | * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and | ||
126 | * @child_bs can move to a different AioContext in this function. Callers must | ||
127 | * make sure that their AioContext locking is still correct after this. | ||
128 | */ | ||
129 | -static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, | ||
130 | - BlockDriverState *child_bs, | ||
131 | - bool is_backing, | ||
132 | - Transaction *tran, Error **errp) | ||
133 | +static int GRAPH_WRLOCK | ||
134 | +bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, | ||
135 | + BlockDriverState *child_bs, | ||
136 | + bool is_backing, | ||
137 | + Transaction *tran, Error **errp) | ||
138 | { | ||
139 | bool update_inherits_from = | ||
140 | bdrv_inherits_from_recursive(child_bs, parent_bs); | ||
141 | @@ -XXX,XX +XXX,XX @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, | ||
142 | } | ||
143 | |||
144 | if (child) { | ||
145 | - bdrv_drained_begin(child->bs); | ||
146 | - bdrv_graph_wrlock(NULL); | ||
27 | - | 147 | - |
28 | next = qdict_next(qdict, entry); | 148 | + assert(child->bs->quiesce_counter); |
29 | value = qdict_entry_value(entry); | 149 | bdrv_unset_inherits_from(parent_bs, child, tran); |
30 | new_key = NULL; | 150 | bdrv_remove_child(child, tran); |
31 | - delete = false; | ||
32 | |||
33 | if (prefix) { | ||
34 | new_key = g_strdup_printf("%s.%s", prefix, entry->key); | ||
35 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
36 | * itself disappears. */ | ||
37 | qdict_flatten_qdict(qobject_to(QDict, value), target, | ||
38 | new_key ? new_key : entry->key); | ||
39 | - delete = true; | ||
40 | + qdict_del(qdict, entry->key); | ||
41 | } else if (qobject_type(value) == QTYPE_QLIST) { | ||
42 | qdict_flatten_qlist(qobject_to(QList, value), target, | ||
43 | new_key ? new_key : entry->key); | ||
44 | - delete = true; | ||
45 | + qdict_del(qdict, entry->key); | ||
46 | } else if (prefix) { | ||
47 | /* All other objects are moved to the target unchanged. */ | ||
48 | qdict_put_obj(target, new_key, qobject_ref(value)); | ||
49 | - delete = true; | ||
50 | - } | ||
51 | - | 151 | - |
52 | - g_free(new_key); | 152 | - bdrv_graph_wrunlock(); |
153 | - bdrv_drained_end(child->bs); | ||
154 | } | ||
155 | |||
156 | if (!child_bs) { | ||
157 | @@ -XXX,XX +XXX,XX @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, | ||
158 | } | ||
159 | |||
160 | out: | ||
161 | - bdrv_graph_rdlock_main_loop(); | ||
162 | bdrv_refresh_limits(parent_bs, tran, NULL); | ||
163 | - bdrv_graph_rdunlock_main_loop(); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | @@ -XXX,XX +XXX,XX @@ out: | ||
168 | * The caller must hold the AioContext lock for @backing_hd. Both @bs and | ||
169 | * @backing_hd can move to a different AioContext in this function. Callers must | ||
170 | * make sure that their AioContext locking is still correct after this. | ||
171 | + * | ||
172 | + * If a backing child is already present (i.e. we're detaching a node), that | ||
173 | + * child node must be drained. | ||
174 | */ | ||
175 | -static int bdrv_set_backing_noperm(BlockDriverState *bs, | ||
176 | - BlockDriverState *backing_hd, | ||
177 | - Transaction *tran, Error **errp) | ||
178 | +static int GRAPH_WRLOCK | ||
179 | +bdrv_set_backing_noperm(BlockDriverState *bs, | ||
180 | + BlockDriverState *backing_hd, | ||
181 | + Transaction *tran, Error **errp) | ||
182 | { | ||
183 | GLOBAL_STATE_CODE(); | ||
184 | return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); | ||
185 | @@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, | ||
186 | |||
187 | GLOBAL_STATE_CODE(); | ||
188 | assert(bs->quiesce_counter > 0); | ||
189 | + if (bs->backing) { | ||
190 | + assert(bs->backing->bs->quiesce_counter > 0); | ||
191 | + } | ||
192 | + bdrv_graph_wrlock(backing_hd); | ||
193 | |||
194 | ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp); | ||
195 | if (ret < 0) { | ||
196 | @@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, | ||
197 | |||
198 | ret = bdrv_refresh_perms(bs, tran, errp); | ||
199 | out: | ||
200 | + bdrv_graph_wrunlock(); | ||
201 | tran_finalize(tran, ret); | ||
202 | return ret; | ||
203 | } | ||
204 | @@ -XXX,XX +XXX,XX @@ out: | ||
205 | int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, | ||
206 | Error **errp) | ||
207 | { | ||
208 | + BlockDriverState *drain_bs = bs->backing ? bs->backing->bs : bs; | ||
209 | int ret; | ||
210 | GLOBAL_STATE_CODE(); | ||
211 | |||
212 | - bdrv_drained_begin(bs); | ||
213 | + bdrv_ref(drain_bs); | ||
214 | + bdrv_drained_begin(drain_bs); | ||
215 | ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); | ||
216 | - bdrv_drained_end(bs); | ||
217 | + bdrv_drained_end(drain_bs); | ||
218 | + bdrv_unref(drain_bs); | ||
219 | |||
220 | return ret; | ||
221 | } | ||
222 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
223 | |||
224 | abort: | ||
225 | tran_abort(tran); | ||
226 | + | ||
227 | QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { | ||
228 | if (bs_entry->prepared) { | ||
229 | ctx = bdrv_get_aio_context(bs_entry->state.bs); | ||
230 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, | ||
231 | reopen_state->old_file_bs = old_child_bs; | ||
232 | } | ||
233 | |||
234 | + if (old_child_bs) { | ||
235 | + bdrv_ref(old_child_bs); | ||
236 | + bdrv_drained_begin(old_child_bs); | ||
237 | + } | ||
238 | + | ||
239 | old_ctx = bdrv_get_aio_context(bs); | ||
240 | ctx = bdrv_get_aio_context(new_child_bs); | ||
241 | if (old_ctx != ctx) { | ||
242 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, | ||
243 | aio_context_acquire(ctx); | ||
244 | } | ||
245 | |||
246 | + bdrv_graph_wrlock(new_child_bs); | ||
247 | + | ||
248 | ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, | ||
249 | tran, errp); | ||
250 | |||
251 | + bdrv_graph_wrunlock(); | ||
252 | + | ||
253 | if (old_ctx != ctx) { | ||
254 | aio_context_release(ctx); | ||
255 | aio_context_acquire(old_ctx); | ||
256 | } | ||
257 | |||
258 | + if (old_child_bs) { | ||
259 | + bdrv_drained_end(old_child_bs); | ||
260 | + bdrv_unref(old_child_bs); | ||
261 | + } | ||
262 | + | ||
263 | return ret; | ||
264 | } | ||
265 | |||
266 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
267 | BdrvChild *child; | ||
268 | Transaction *tran = tran_new(); | ||
269 | AioContext *old_context, *new_context = NULL; | ||
270 | - bool drained = false; | ||
271 | |||
272 | GLOBAL_STATE_CODE(); | ||
273 | |||
274 | assert(!bs_new->backing); | ||
275 | |||
276 | old_context = bdrv_get_aio_context(bs_top); | ||
277 | + bdrv_drained_begin(bs_top); | ||
278 | + | ||
279 | + /* | ||
280 | + * bdrv_drained_begin() requires that only the AioContext of the drained | ||
281 | + * node is locked, and at this point it can still differ from the AioContext | ||
282 | + * of bs_top. | ||
283 | + */ | ||
284 | + new_context = bdrv_get_aio_context(bs_new); | ||
285 | + aio_context_release(old_context); | ||
286 | + aio_context_acquire(new_context); | ||
287 | + bdrv_drained_begin(bs_new); | ||
288 | + aio_context_release(new_context); | ||
289 | + aio_context_acquire(old_context); | ||
290 | + new_context = NULL; | ||
291 | + | ||
292 | + bdrv_graph_wrlock(bs_top); | ||
293 | |||
294 | child = bdrv_attach_child_noperm(bs_new, bs_top, "backing", | ||
295 | &child_of_bds, bdrv_backing_role(bs_new), | ||
296 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | - * bdrv_attach_child_noperm could change the AioContext of bs_top. | ||
301 | - * bdrv_replace_node_noperm calls bdrv_drained_begin, so let's temporarily | ||
302 | - * hold the new AioContext, since bdrv_drained_begin calls BDRV_POLL_WHILE | ||
303 | - * that assumes the new lock is taken. | ||
304 | + * bdrv_attach_child_noperm could change the AioContext of bs_top and | ||
305 | + * bs_new, but at least they are in the same AioContext now. This is the | ||
306 | + * AioContext that we need to lock for the rest of the function. | ||
307 | */ | ||
308 | new_context = bdrv_get_aio_context(bs_top); | ||
309 | |||
310 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
311 | aio_context_acquire(new_context); | ||
312 | } | ||
313 | |||
314 | - bdrv_drained_begin(bs_new); | ||
315 | - bdrv_drained_begin(bs_top); | ||
316 | - drained = true; | ||
53 | - | 317 | - |
54 | - if (delete) { | 318 | - bdrv_graph_wrlock(bs_new); |
55 | qdict_del(qdict, entry->key); | 319 | ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp); |
56 | - | 320 | - bdrv_graph_wrunlock(); |
57 | - /* Restart loop after modifying the iterated QDict */ | 321 | if (ret < 0) { |
58 | - entry = qdict_first(qdict); | 322 | goto out; |
59 | - continue; | 323 | } |
60 | } | 324 | |
61 | 325 | ret = bdrv_refresh_perms(bs_new, tran, errp); | |
62 | + g_free(new_key); | 326 | out: |
63 | entry = next; | 327 | + bdrv_graph_wrunlock(); |
64 | } | 328 | tran_finalize(tran, ret); |
329 | |||
330 | bdrv_graph_rdlock_main_loop(); | ||
331 | bdrv_refresh_limits(bs_top, NULL, NULL); | ||
332 | bdrv_graph_rdunlock_main_loop(); | ||
333 | |||
334 | - if (drained) { | ||
335 | - bdrv_drained_end(bs_top); | ||
336 | - bdrv_drained_end(bs_new); | ||
337 | - } | ||
338 | + bdrv_drained_end(bs_top); | ||
339 | + bdrv_drained_end(bs_new); | ||
340 | |||
341 | if (new_context && old_context != new_context) { | ||
342 | aio_context_release(new_context); | ||
343 | diff --git a/block/stream.c b/block/stream.c | ||
344 | index XXXXXXX..XXXXXXX 100644 | ||
345 | --- a/block/stream.c | ||
346 | +++ b/block/stream.c | ||
347 | @@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job) | ||
348 | { | ||
349 | StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); | ||
350 | BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs); | ||
351 | + BlockDriverState *unfiltered_bs_cow = bdrv_cow_bs(unfiltered_bs); | ||
352 | BlockDriverState *base; | ||
353 | BlockDriverState *unfiltered_base; | ||
354 | Error *local_err = NULL; | ||
355 | @@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job) | ||
356 | s->cor_filter_bs = NULL; | ||
357 | |||
358 | /* | ||
359 | - * bdrv_set_backing_hd() requires that unfiltered_bs is drained. Drain | ||
360 | - * already here and use bdrv_set_backing_hd_drained() instead because | ||
361 | - * the polling during drained_begin() might change the graph, and if we do | ||
362 | - * this only later, we may end up working with the wrong base node (or it | ||
363 | - * might even have gone away by the time we want to use it). | ||
364 | + * bdrv_set_backing_hd() requires that the unfiltered_bs and the COW child | ||
365 | + * of unfiltered_bs is drained. Drain already here and use | ||
366 | + * bdrv_set_backing_hd_drained() instead because the polling during | ||
367 | + * drained_begin() might change the graph, and if we do this only later, we | ||
368 | + * may end up working with the wrong base node (or it might even have gone | ||
369 | + * away by the time we want to use it). | ||
370 | */ | ||
371 | bdrv_drained_begin(unfiltered_bs); | ||
372 | + if (unfiltered_bs_cow) { | ||
373 | + bdrv_ref(unfiltered_bs_cow); | ||
374 | + bdrv_drained_begin(unfiltered_bs_cow); | ||
375 | + } | ||
376 | |||
377 | base = bdrv_filter_or_cow_bs(s->above_base); | ||
378 | unfiltered_base = bdrv_skip_filters(base); | ||
379 | @@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job) | ||
380 | } | ||
381 | |||
382 | out: | ||
383 | + if (unfiltered_bs_cow) { | ||
384 | + bdrv_drained_end(unfiltered_bs_cow); | ||
385 | + bdrv_unref(unfiltered_bs_cow); | ||
386 | + } | ||
387 | bdrv_drained_end(unfiltered_bs); | ||
388 | return ret; | ||
65 | } | 389 | } |
66 | -- | 390 | -- |
67 | 2.13.6 | 391 | 2.41.0 |
68 | |||
69 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | In previous patches, we changed some transactionable functions to be |
---|---|---|---|
2 | 2 | marked as GRAPH_WRLOCK, but required that tran_finalize() is still | |
3 | The previous commit fixed -blockdev breakage due to misuse of the | 3 | called without the lock. This was because all callbacks that can be in |
4 | qobject input visitor's keyval flavor in bdrv_file_open(). The commit | 4 | the same transaction need to follow the same convention. |
5 | message explain why using the plain flavor would be just as wrong; it | 5 | |
6 | would break -drive. Turns out we break it in three places: | 6 | Now that we don't have conflicting requirements any more, we can switch |
7 | nbd_open(), sd_open() and ssh_file_open(). They are even marked | 7 | all of the transaction callbacks to be declared GRAPH_WRLOCK, too, and |
8 | FIXME. Example breakage: | 8 | call tran_finalize() with the lock held. |
9 | 9 | ||
10 | $ qemu-system-x86 -drive node-name=n1,driver=nbd,server.type=inet,server.host=localhost,server.port=1234,server.numeric=off | 10 | Document for each of these transactionable functions that the lock needs |
11 | qemu-system-x86: -drive node-name=n1,driver=nbd,server.type=inet,server.host=localhost,server.port=1234,server.numeric=off: Invalid parameter type for 'numeric', expected: boolean | 11 | to be held when completing the transaction, and make sure that all |
12 | 12 | callers down to the place where the transaction is finalised actually | |
13 | Fix it the same way: replace qdict_crumple() by | 13 | have the writer lock. |
14 | qdict_crumple_for_keyval_qiv(), and switch from plain to the keyval | 14 | |
15 | flavor. | 15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
16 | 16 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | |
17 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 17 | Message-ID: <20230911094620.45040-12-kwolf@redhat.com> |
18 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
20 | --- | 19 | --- |
21 | block/nbd.c | 12 ++---------- | 20 | block.c | 61 +++++++++++++++++++++++++++++++++++++++++---------------- |
22 | block/sheepdog.c | 12 ++---------- | 21 | 1 file changed, 44 insertions(+), 17 deletions(-) |
23 | block/ssh.c | 12 ++---------- | 22 | |
24 | 3 files changed, 6 insertions(+), 30 deletions(-) | 23 | diff --git a/block.c b/block.c |
25 | |||
26 | diff --git a/block/nbd.c b/block/nbd.c | ||
27 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
28 | --- a/block/nbd.c | 25 | --- a/block.c |
29 | +++ b/block/nbd.c | 26 | +++ b/block.c |
30 | @@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | 27 | @@ -XXX,XX +XXX,XX @@ typedef struct BdrvReplaceChildState { |
31 | goto done; | 28 | BlockDriverState *old_bs; |
29 | } BdrvReplaceChildState; | ||
30 | |||
31 | -static void bdrv_replace_child_commit(void *opaque) | ||
32 | +static void GRAPH_WRLOCK bdrv_replace_child_commit(void *opaque) | ||
33 | { | ||
34 | BdrvReplaceChildState *s = opaque; | ||
35 | GLOBAL_STATE_CODE(); | ||
36 | |||
37 | - bdrv_unref(s->old_bs); | ||
38 | + bdrv_schedule_unref(s->old_bs); | ||
39 | } | ||
40 | |||
41 | -static void bdrv_replace_child_abort(void *opaque) | ||
42 | +static void GRAPH_WRLOCK bdrv_replace_child_abort(void *opaque) | ||
43 | { | ||
44 | BdrvReplaceChildState *s = opaque; | ||
45 | BlockDriverState *new_bs = s->child->bs; | ||
46 | |||
47 | GLOBAL_STATE_CODE(); | ||
48 | - bdrv_graph_wrlock(s->old_bs); | ||
49 | + assert_bdrv_graph_writable(); | ||
50 | |||
51 | /* old_bs reference is transparently moved from @s to @s->child */ | ||
52 | if (!s->child->bs) { | ||
53 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque) | ||
54 | assert(s->child->quiesced_parent); | ||
55 | bdrv_replace_child_noperm(s->child, s->old_bs); | ||
56 | |||
57 | - bdrv_graph_wrunlock(); | ||
58 | bdrv_unref(new_bs); | ||
59 | } | ||
60 | |||
61 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = { | ||
62 | * Both @child->bs and @new_bs (if non-NULL) must be drained. @new_bs must be | ||
63 | * kept drained until the transaction is completed. | ||
64 | * | ||
65 | + * After calling this function, the transaction @tran may only be completed | ||
66 | + * while holding a writer lock for the graph. | ||
67 | + * | ||
68 | * The function doesn't update permissions, caller is responsible for this. | ||
69 | */ | ||
70 | static void GRAPH_WRLOCK | ||
71 | @@ -XXX,XX +XXX,XX @@ typedef struct BdrvAttachChildCommonState { | ||
72 | AioContext *old_child_ctx; | ||
73 | } BdrvAttachChildCommonState; | ||
74 | |||
75 | -static void bdrv_attach_child_common_abort(void *opaque) | ||
76 | +static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque) | ||
77 | { | ||
78 | BdrvAttachChildCommonState *s = opaque; | ||
79 | BlockDriverState *bs = s->child->bs; | ||
80 | |||
81 | GLOBAL_STATE_CODE(); | ||
82 | + assert_bdrv_graph_writable(); | ||
83 | |||
84 | - bdrv_graph_wrlock(NULL); | ||
85 | bdrv_replace_child_noperm(s->child, NULL); | ||
86 | - bdrv_graph_wrunlock(); | ||
87 | |||
88 | if (bdrv_get_aio_context(bs) != s->old_child_ctx) { | ||
89 | bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort); | ||
90 | @@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque) | ||
91 | tran_commit(tran); | ||
32 | } | 92 | } |
33 | 93 | ||
34 | - crumpled_addr = qdict_crumple(addr, errp); | 94 | - bdrv_unref(bs); |
35 | + crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp); | 95 | + bdrv_schedule_unref(bs); |
36 | if (!crumpled_addr) { | 96 | bdrv_child_free(s->child); |
37 | goto done; | 97 | } |
98 | |||
99 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_attach_child_common_drv = { | ||
100 | * | ||
101 | * Function doesn't update permissions, caller is responsible for this. | ||
102 | * | ||
103 | + * After calling this function, the transaction @tran may only be completed | ||
104 | + * while holding a writer lock for the graph. | ||
105 | + * | ||
106 | * Returns new created child. | ||
107 | * | ||
108 | * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and | ||
109 | @@ -XXX,XX +XXX,XX @@ bdrv_attach_child_common(BlockDriverState *child_bs, | ||
110 | * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and | ||
111 | * @child_bs can move to a different AioContext in this function. Callers must | ||
112 | * make sure that their AioContext locking is still correct after this. | ||
113 | + * | ||
114 | + * After calling this function, the transaction @tran may only be completed | ||
115 | + * while holding a writer lock for the graph. | ||
116 | */ | ||
117 | static BdrvChild * GRAPH_WRLOCK | ||
118 | bdrv_attach_child_noperm(BlockDriverState *parent_bs, | ||
119 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
120 | ret = bdrv_refresh_perms(child_bs, tran, errp); | ||
121 | |||
122 | out: | ||
123 | - bdrv_graph_wrunlock(); | ||
124 | tran_finalize(tran, ret); | ||
125 | + bdrv_graph_wrunlock(); | ||
126 | |||
127 | bdrv_unref(child_bs); | ||
128 | |||
129 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, | ||
38 | } | 130 | } |
39 | 131 | ||
40 | - /* | 132 | out: |
41 | - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive | 133 | - bdrv_graph_wrunlock(); |
42 | - * server.type=inet. .to doesn't matter, it's ignored anyway. | 134 | tran_finalize(tran, ret); |
43 | - * That's because when @options come from -blockdev or | 135 | + bdrv_graph_wrunlock(); |
44 | - * blockdev_add, members are typed according to the QAPI schema, | 136 | |
45 | - * but when they come from -drive, they're all QString. The | 137 | bdrv_unref(child_bs); |
46 | - * visitor expects the former. | 138 | |
47 | - */ | 139 | @@ -XXX,XX +XXX,XX @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) |
48 | - iv = qobject_input_visitor_new(crumpled_addr); | 140 | * The caller must hold the AioContext lock for @child_bs. Both @parent_bs and |
49 | + iv = qobject_input_visitor_new_keyval(crumpled_addr); | 141 | * @child_bs can move to a different AioContext in this function. Callers must |
50 | visit_type_SocketAddress(iv, NULL, &saddr, &local_err); | 142 | * make sure that their AioContext locking is still correct after this. |
51 | if (local_err) { | 143 | + * |
52 | error_propagate(errp, local_err); | 144 | + * After calling this function, the transaction @tran may only be completed |
53 | diff --git a/block/sheepdog.c b/block/sheepdog.c | 145 | + * while holding a writer lock for the graph. |
54 | index XXXXXXX..XXXXXXX 100644 | 146 | */ |
55 | --- a/block/sheepdog.c | 147 | static int GRAPH_WRLOCK |
56 | +++ b/block/sheepdog.c | 148 | bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, |
57 | @@ -XXX,XX +XXX,XX @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) | 149 | @@ -XXX,XX +XXX,XX @@ out: |
58 | 150 | * | |
59 | qdict_extract_subqdict(options, &server, "server."); | 151 | * If a backing child is already present (i.e. we're detaching a node), that |
60 | 152 | * child node must be drained. | |
61 | - crumpled_server = qdict_crumple(server, errp); | 153 | + * |
62 | + crumpled_server = qdict_crumple_for_keyval_qiv(server, errp); | 154 | + * After calling this function, the transaction @tran may only be completed |
63 | if (!crumpled_server) { | 155 | + * while holding a writer lock for the graph. |
64 | goto done; | 156 | */ |
157 | static int GRAPH_WRLOCK | ||
158 | bdrv_set_backing_noperm(BlockDriverState *bs, | ||
159 | @@ -XXX,XX +XXX,XX @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, | ||
160 | |||
161 | ret = bdrv_refresh_perms(bs, tran, errp); | ||
162 | out: | ||
163 | - bdrv_graph_wrunlock(); | ||
164 | tran_finalize(tran, ret); | ||
165 | + bdrv_graph_wrunlock(); | ||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
170 | aio_context_release(ctx); | ||
65 | } | 171 | } |
66 | 172 | ||
67 | - /* | 173 | + bdrv_graph_wrlock(NULL); |
68 | - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive | 174 | tran_commit(tran); |
69 | - * server.type=inet. .to doesn't matter, it's ignored anyway. | 175 | + bdrv_graph_wrunlock(); |
70 | - * That's because when @options come from -blockdev or | 176 | |
71 | - * blockdev_add, members are typed according to the QAPI schema, | 177 | QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { |
72 | - * but when they come from -drive, they're all QString. The | 178 | BlockDriverState *bs = bs_entry->state.bs; |
73 | - * visitor expects the former. | 179 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) |
74 | - */ | 180 | goto cleanup; |
75 | - iv = qobject_input_visitor_new(crumpled_server); | 181 | |
76 | + iv = qobject_input_visitor_new_keyval(crumpled_server); | 182 | abort: |
77 | visit_type_SocketAddress(iv, NULL, &saddr, &local_err); | 183 | + bdrv_graph_wrlock(NULL); |
78 | if (local_err) { | 184 | tran_abort(tran); |
79 | error_propagate(errp, local_err); | 185 | + bdrv_graph_wrunlock(); |
80 | diff --git a/block/ssh.c b/block/ssh.c | 186 | |
81 | index XXXXXXX..XXXXXXX 100644 | 187 | QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { |
82 | --- a/block/ssh.c | 188 | if (bs_entry->prepared) { |
83 | +++ b/block/ssh.c | 189 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, |
84 | @@ -XXX,XX +XXX,XX @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) | 190 | * true and reopen_state->new_backing_bs contains a pointer to the new |
85 | } | 191 | * backing BlockDriverState (or NULL). |
86 | 192 | * | |
87 | /* Create the QAPI object */ | 193 | + * After calling this function, the transaction @tran may only be completed |
88 | - crumpled = qdict_crumple(options, errp); | 194 | + * while holding a writer lock for the graph. |
89 | + crumpled = qdict_crumple_for_keyval_qiv(options, errp); | 195 | + * |
90 | if (crumpled == NULL) { | 196 | * Return 0 on success, otherwise return < 0 and set @errp. |
91 | goto fail; | 197 | * |
92 | } | 198 | * The caller must hold the AioContext lock of @reopen_state->bs. |
93 | 199 | @@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, | |
94 | - /* | 200 | * commit() for any other BDS that have been left in a prepare() state |
95 | - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive. | 201 | * |
96 | - * .to doesn't matter, it's ignored anyway. | 202 | * The caller must hold the AioContext lock of @reopen_state->bs. |
97 | - * That's because when @options come from -blockdev or | 203 | + * |
98 | - * blockdev_add, members are typed according to the QAPI schema, | 204 | + * After calling this function, the transaction @change_child_tran may only be |
99 | - * but when they come from -drive, they're all QString. The | 205 | + * completed while holding a writer lock for the graph. |
100 | - * visitor expects the former. | 206 | */ |
101 | - */ | 207 | static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, |
102 | - v = qobject_input_visitor_new(crumpled); | 208 | BlockReopenQueue *queue, |
103 | + v = qobject_input_visitor_new_keyval(crumpled); | 209 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_remove_child_drv = { |
104 | visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); | 210 | * Function doesn't update permissions, caller is responsible for this. |
105 | visit_free(v); | 211 | * |
106 | qobject_unref(crumpled); | 212 | * @child->bs (if non-NULL) must be drained. |
213 | + * | ||
214 | + * After calling this function, the transaction @tran may only be completed | ||
215 | + * while holding a writer lock for the graph. | ||
216 | */ | ||
217 | static void GRAPH_WRLOCK bdrv_remove_child(BdrvChild *child, Transaction *tran) | ||
218 | { | ||
219 | @@ -XXX,XX +XXX,XX @@ static void GRAPH_WRLOCK bdrv_remove_child(BdrvChild *child, Transaction *tran) | ||
220 | /* | ||
221 | * Both @from and @to (if non-NULL) must be drained. @to must be kept drained | ||
222 | * until the transaction is completed. | ||
223 | + * | ||
224 | + * After calling this function, the transaction @tran may only be completed | ||
225 | + * while holding a writer lock for the graph. | ||
226 | */ | ||
227 | static int GRAPH_WRLOCK | ||
228 | bdrv_replace_node_noperm(BlockDriverState *from, | ||
229 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, | ||
230 | ret = 0; | ||
231 | |||
232 | out: | ||
233 | - bdrv_graph_wrunlock(); | ||
234 | tran_finalize(tran, ret); | ||
235 | + bdrv_graph_wrunlock(); | ||
236 | |||
237 | bdrv_drained_end(to); | ||
238 | bdrv_drained_end(from); | ||
239 | @@ -XXX,XX +XXX,XX @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, | ||
240 | |||
241 | ret = bdrv_refresh_perms(bs_new, tran, errp); | ||
242 | out: | ||
243 | - bdrv_graph_wrunlock(); | ||
244 | tran_finalize(tran, ret); | ||
245 | |||
246 | - bdrv_graph_rdlock_main_loop(); | ||
247 | bdrv_refresh_limits(bs_top, NULL, NULL); | ||
248 | - bdrv_graph_rdunlock_main_loop(); | ||
249 | + bdrv_graph_wrunlock(); | ||
250 | |||
251 | bdrv_drained_end(bs_top); | ||
252 | bdrv_drained_end(bs_new); | ||
253 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, | ||
254 | refresh_list = g_slist_prepend(refresh_list, new_bs); | ||
255 | |||
256 | ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); | ||
257 | - bdrv_graph_wrunlock(); | ||
258 | |||
259 | tran_finalize(tran, ret); | ||
260 | |||
261 | + bdrv_graph_wrunlock(); | ||
262 | bdrv_drained_end(old_bs); | ||
263 | bdrv_drained_end(new_bs); | ||
264 | bdrv_unref(old_bs); | ||
107 | -- | 265 | -- |
108 | 2.13.6 | 266 | 2.41.0 |
109 | |||
110 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | Instead of taking the writer lock internally, require callers to already |
---|---|---|---|
2 | hold it when calling bdrv_attach_child_common(). These callers will | ||
3 | typically already hold the graph lock once the locking work is | ||
4 | completed, which means that they can't call functions that take it | ||
5 | internally. | ||
2 | 6 | ||
3 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 8 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
10 | Message-ID: <20230911094620.45040-13-kwolf@redhat.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | --- | 12 | --- |
7 | include/block/qdict.h | 3 ++- | 13 | include/block/block-global-state.h | 14 ++++++++------ |
8 | block/nbd.c | 7 ++----- | 14 | block.c | 7 +++---- |
9 | block/nfs.c | 7 ++----- | 15 | block/quorum.c | 2 ++ |
10 | block/parallels.c | 7 ++----- | 16 | block/replication.c | 6 ++++++ |
11 | block/qcow.c | 7 ++----- | 17 | tests/unit/test-bdrv-drain.c | 14 ++++++++++++++ |
12 | block/qcow2.c | 7 ++----- | 18 | tests/unit/test-bdrv-graph-mod.c | 10 ++++++++++ |
13 | block/qed.c | 7 ++----- | 19 | 6 files changed, 43 insertions(+), 10 deletions(-) |
14 | block/rbd.c | 7 ++----- | ||
15 | block/sheepdog.c | 14 ++++---------- | ||
16 | block/ssh.c | 7 ++----- | ||
17 | block/vhdx.c | 7 ++----- | ||
18 | block/vpc.c | 7 ++----- | ||
19 | qobject/block-qdict.c | 28 +++++++++++++++++++++++++++- | ||
20 | 13 files changed, 53 insertions(+), 62 deletions(-) | ||
21 | 20 | ||
22 | diff --git a/include/block/qdict.h b/include/block/qdict.h | 21 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h |
23 | index XXXXXXX..XXXXXXX 100644 | 22 | index XXXXXXX..XXXXXXX 100644 |
24 | --- a/include/block/qdict.h | 23 | --- a/include/block/block-global-state.h |
25 | +++ b/include/block/qdict.h | 24 | +++ b/include/block/block-global-state.h |
26 | @@ -XXX,XX +XXX,XX @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); | 25 | @@ -XXX,XX +XXX,XX @@ void no_coroutine_fn bdrv_unref(BlockDriverState *bs); |
27 | void qdict_array_split(QDict *src, QList **dst); | 26 | void coroutine_fn no_co_wrapper bdrv_co_unref(BlockDriverState *bs); |
28 | int qdict_array_entries(QDict *src, const char *subqdict); | 27 | void GRAPH_WRLOCK bdrv_schedule_unref(BlockDriverState *bs); |
29 | QObject *qdict_crumple(const QDict *src, Error **errp); | 28 | void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); |
30 | -QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp); | 29 | -BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, |
31 | void qdict_flatten(QDict *qdict); | 30 | - BlockDriverState *child_bs, |
32 | 31 | - const char *child_name, | |
33 | typedef struct QDictRenames { | 32 | - const BdrvChildClass *child_class, |
34 | @@ -XXX,XX +XXX,XX @@ typedef struct QDictRenames { | 33 | - BdrvChildRole child_role, |
35 | } QDictRenames; | 34 | - Error **errp); |
36 | bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); | 35 | + |
37 | 36 | +BdrvChild * GRAPH_WRLOCK | |
38 | +Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, | 37 | +bdrv_attach_child(BlockDriverState *parent_bs, |
39 | + Error **errp); | 38 | + BlockDriverState *child_bs, |
40 | #endif | 39 | + const char *child_name, |
41 | diff --git a/block/nbd.c b/block/nbd.c | 40 | + const BdrvChildClass *child_class, |
42 | index XXXXXXX..XXXXXXX 100644 | 41 | + BdrvChildRole child_role, |
43 | --- a/block/nbd.c | 42 | + Error **errp); |
44 | +++ b/block/nbd.c | 43 | |
45 | @@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | 44 | bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); |
46 | { | 45 | void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason); |
47 | SocketAddress *saddr = NULL; | 46 | diff --git a/block.c b/block.c |
48 | QDict *addr = NULL; | 47 | index XXXXXXX..XXXXXXX 100644 |
49 | - QObject *crumpled_addr = NULL; | 48 | --- a/block.c |
50 | Visitor *iv = NULL; | 49 | +++ b/block.c |
51 | Error *local_err = NULL; | 50 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, |
52 | 51 | ||
53 | @@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | 52 | GLOBAL_STATE_CODE(); |
54 | goto done; | 53 | |
55 | } | 54 | - bdrv_graph_wrlock(child_bs); |
56 | 55 | - | |
57 | - crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp); | 56 | child = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, |
58 | - if (!crumpled_addr) { | 57 | child_class, child_role, tran, errp); |
59 | + iv = qobject_input_visitor_new_flat_confused(addr, errp); | 58 | if (!child) { |
60 | + if (!iv) { | 59 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, |
61 | goto done; | 60 | |
62 | } | 61 | out: |
63 | 62 | tran_finalize(tran, ret); | |
64 | - iv = qobject_input_visitor_new_keyval(crumpled_addr); | 63 | - bdrv_graph_wrunlock(); |
65 | visit_type_SocketAddress(iv, NULL, &saddr, &local_err); | 64 | |
66 | if (local_err) { | 65 | - bdrv_unref(child_bs); |
67 | error_propagate(errp, local_err); | 66 | + bdrv_schedule_unref(child_bs); |
68 | @@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, | 67 | |
69 | 68 | return ret < 0 ? NULL : child; | |
70 | done: | ||
71 | qobject_unref(addr); | ||
72 | - qobject_unref(crumpled_addr); | ||
73 | visit_free(iv); | ||
74 | return saddr; | ||
75 | } | 69 | } |
76 | diff --git a/block/nfs.c b/block/nfs.c | 70 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_open_child(const char *filename, |
77 | index XXXXXXX..XXXXXXX 100644 | ||
78 | --- a/block/nfs.c | ||
79 | +++ b/block/nfs.c | ||
80 | @@ -XXX,XX +XXX,XX @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, | ||
81 | Error **errp) | ||
82 | { | ||
83 | BlockdevOptionsNfs *opts = NULL; | ||
84 | - QObject *crumpled = NULL; | ||
85 | Visitor *v; | ||
86 | const QDictEntry *e; | ||
87 | Error *local_err = NULL; | ||
88 | |||
89 | - crumpled = qdict_crumple_for_keyval_qiv(options, errp); | ||
90 | - if (crumpled == NULL) { | ||
91 | + v = qobject_input_visitor_new_flat_confused(options, errp); | ||
92 | + if (!v) { | ||
93 | return NULL; | 71 | return NULL; |
94 | } | 72 | } |
95 | 73 | ||
96 | - v = qobject_input_visitor_new_keyval(crumpled); | 74 | + bdrv_graph_wrlock(NULL); |
97 | visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); | 75 | ctx = bdrv_get_aio_context(bs); |
98 | visit_free(v); | 76 | aio_context_acquire(ctx); |
99 | - qobject_unref(crumpled); | 77 | child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, |
100 | 78 | errp); | |
101 | if (local_err) { | 79 | aio_context_release(ctx); |
102 | error_propagate(errp, local_err); | 80 | + bdrv_graph_wrunlock(); |
103 | diff --git a/block/parallels.c b/block/parallels.c | 81 | |
104 | index XXXXXXX..XXXXXXX 100644 | 82 | return child; |
105 | --- a/block/parallels.c | 83 | } |
106 | +++ b/block/parallels.c | 84 | diff --git a/block/quorum.c b/block/quorum.c |
107 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename, | 85 | index XXXXXXX..XXXXXXX 100644 |
108 | Error *local_err = NULL; | 86 | --- a/block/quorum.c |
109 | BlockDriverState *bs = NULL; | 87 | +++ b/block/quorum.c |
110 | QDict *qdict; | 88 | @@ -XXX,XX +XXX,XX @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, |
111 | - QObject *qobj; | 89 | /* We can safely add the child now */ |
112 | Visitor *v; | 90 | bdrv_ref(child_bs); |
113 | int ret; | 91 | |
114 | 92 | + bdrv_graph_wrlock(child_bs); | |
115 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename, | 93 | child = bdrv_attach_child(bs, child_bs, indexstr, &child_of_bds, |
116 | qdict_put_str(qdict, "driver", "parallels"); | 94 | BDRV_CHILD_DATA, errp); |
117 | qdict_put_str(qdict, "file", bs->node_name); | 95 | + bdrv_graph_wrunlock(); |
118 | 96 | if (child == NULL) { | |
119 | - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | 97 | s->next_child_index--; |
120 | - if (!qobj) { | 98 | goto out; |
121 | + v = qobject_input_visitor_new_flat_confused(qdict, errp); | 99 | diff --git a/block/replication.c b/block/replication.c |
122 | + if (!v) { | 100 | index XXXXXXX..XXXXXXX 100644 |
123 | ret = -EINVAL; | 101 | --- a/block/replication.c |
124 | goto done; | 102 | +++ b/block/replication.c |
103 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
104 | return; | ||
105 | } | ||
106 | |||
107 | + bdrv_graph_wrlock(bs); | ||
108 | + | ||
109 | bdrv_ref(hidden_disk->bs); | ||
110 | s->hidden_disk = bdrv_attach_child(bs, hidden_disk->bs, "hidden disk", | ||
111 | &child_of_bds, BDRV_CHILD_DATA, | ||
112 | &local_err); | ||
113 | if (local_err) { | ||
114 | error_propagate(errp, local_err); | ||
115 | + bdrv_graph_wrunlock(); | ||
116 | aio_context_release(aio_context); | ||
117 | return; | ||
118 | } | ||
119 | @@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, | ||
120 | BDRV_CHILD_DATA, &local_err); | ||
121 | if (local_err) { | ||
122 | error_propagate(errp, local_err); | ||
123 | + bdrv_graph_wrunlock(); | ||
124 | aio_context_release(aio_context); | ||
125 | return; | ||
126 | } | ||
127 | |||
128 | + bdrv_graph_wrunlock(); | ||
129 | + | ||
130 | /* start backup job now */ | ||
131 | error_setg(&s->blocker, | ||
132 | "Block device is in use by internal backup job"); | ||
133 | diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c | ||
134 | index XXXXXXX..XXXXXXX 100644 | ||
135 | --- a/tests/unit/test-bdrv-drain.c | ||
136 | +++ b/tests/unit/test-bdrv-drain.c | ||
137 | @@ -XXX,XX +XXX,XX @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, | ||
138 | |||
139 | null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, | ||
140 | &error_abort); | ||
141 | + bdrv_graph_wrlock(NULL); | ||
142 | bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, | ||
143 | BDRV_CHILD_DATA, &error_abort); | ||
144 | + bdrv_graph_wrunlock(); | ||
145 | |||
146 | /* This child will be the one to pass to requests through to, and | ||
147 | * it will stall until a drain occurs */ | ||
148 | @@ -XXX,XX +XXX,XX @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, | ||
149 | &error_abort); | ||
150 | child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; | ||
151 | /* Takes our reference to child_bs */ | ||
152 | + bdrv_graph_wrlock(NULL); | ||
153 | tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", | ||
154 | &child_of_bds, | ||
155 | BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, | ||
156 | &error_abort); | ||
157 | + bdrv_graph_wrunlock(); | ||
158 | |||
159 | /* This child is just there to be deleted | ||
160 | * (for detach_instead_of_delete == true) */ | ||
161 | null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, | ||
162 | &error_abort); | ||
163 | + bdrv_graph_wrlock(NULL); | ||
164 | bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, | ||
165 | &error_abort); | ||
166 | + bdrv_graph_wrunlock(); | ||
167 | |||
168 | blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); | ||
169 | blk_insert_bs(blk, bs, &error_abort); | ||
170 | @@ -XXX,XX +XXX,XX @@ static void detach_indirect_bh(void *opaque) | ||
171 | bdrv_unref_child(data->parent_b, data->child_b); | ||
172 | |||
173 | bdrv_ref(data->c); | ||
174 | + bdrv_graph_wrlock(NULL); | ||
175 | data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", | ||
176 | &child_of_bds, BDRV_CHILD_DATA, | ||
177 | &error_abort); | ||
178 | + bdrv_graph_wrunlock(); | ||
179 | } | ||
180 | |||
181 | static void detach_by_parent_aio_cb(void *opaque, int ret) | ||
182 | @@ -XXX,XX +XXX,XX @@ static void test_detach_indirect(bool by_parent_cb) | ||
183 | /* Set child relationships */ | ||
184 | bdrv_ref(b); | ||
185 | bdrv_ref(a); | ||
186 | + bdrv_graph_wrlock(NULL); | ||
187 | child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, | ||
188 | BDRV_CHILD_DATA, &error_abort); | ||
189 | child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_of_bds, | ||
190 | @@ -XXX,XX +XXX,XX @@ static void test_detach_indirect(bool by_parent_cb) | ||
191 | bdrv_attach_child(parent_a, a, "PA-A", | ||
192 | by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, | ||
193 | BDRV_CHILD_DATA, &error_abort); | ||
194 | + bdrv_graph_wrunlock(); | ||
195 | |||
196 | g_assert_cmpint(parent_a->refcnt, ==, 1); | ||
197 | g_assert_cmpint(parent_b->refcnt, ==, 1); | ||
198 | @@ -XXX,XX +XXX,XX @@ static void test_drop_intermediate_poll(void) | ||
199 | * Establish the chain last, so the chain links are the first | ||
200 | * elements in the BDS.parents lists | ||
201 | */ | ||
202 | + bdrv_graph_wrlock(NULL); | ||
203 | for (i = 0; i < 3; i++) { | ||
204 | if (i) { | ||
205 | /* Takes the reference to chain[i - 1] */ | ||
206 | @@ -XXX,XX +XXX,XX @@ static void test_drop_intermediate_poll(void) | ||
207 | &chain_child_class, BDRV_CHILD_COW, &error_abort); | ||
208 | } | ||
125 | } | 209 | } |
126 | 210 | + bdrv_graph_wrunlock(); | |
127 | - v = qobject_input_visitor_new_keyval(qobj); | 211 | |
128 | - qobject_unref(qobj); | 212 | job = block_job_create("job", &test_simple_job_driver, NULL, job_node, |
129 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | 213 | 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); |
130 | visit_free(v); | 214 | @@ -XXX,XX +XXX,XX @@ static void do_test_replace_child_mid_drain(int old_drain_count, |
131 | 215 | new_child_bs->total_sectors = 1; | |
132 | diff --git a/block/qcow.c b/block/qcow.c | 216 | |
133 | index XXXXXXX..XXXXXXX 100644 | 217 | bdrv_ref(old_child_bs); |
134 | --- a/block/qcow.c | 218 | + bdrv_graph_wrlock(NULL); |
135 | +++ b/block/qcow.c | 219 | bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds, |
136 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, | 220 | BDRV_CHILD_COW, &error_abort); |
137 | BlockdevCreateOptions *create_options = NULL; | 221 | + bdrv_graph_wrunlock(); |
138 | BlockDriverState *bs = NULL; | 222 | parent_s->setup_completed = true; |
139 | QDict *qdict; | 223 | |
140 | - QObject *qobj; | 224 | for (i = 0; i < old_drain_count; i++) { |
141 | Visitor *v; | 225 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c |
142 | const char *val; | 226 | index XXXXXXX..XXXXXXX 100644 |
143 | Error *local_err = NULL; | 227 | --- a/tests/unit/test-bdrv-graph-mod.c |
144 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, | 228 | +++ b/tests/unit/test-bdrv-graph-mod.c |
145 | qdict_put_str(qdict, "driver", "qcow"); | 229 | @@ -XXX,XX +XXX,XX @@ static void test_update_perm_tree(void) |
146 | qdict_put_str(qdict, "file", bs->node_name); | 230 | |
147 | 231 | blk_insert_bs(root, bs, &error_abort); | |
148 | - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | 232 | |
149 | - if (!qobj) { | 233 | + bdrv_graph_wrlock(NULL); |
150 | + v = qobject_input_visitor_new_flat_confused(qdict, errp); | 234 | bdrv_attach_child(filter, bs, "child", &child_of_bds, |
151 | + if (!v) { | 235 | BDRV_CHILD_DATA, &error_abort); |
152 | ret = -EINVAL; | 236 | + bdrv_graph_wrunlock(); |
153 | goto fail; | 237 | |
154 | } | 238 | aio_context_acquire(qemu_get_aio_context()); |
155 | 239 | ret = bdrv_append(filter, bs, NULL); | |
156 | - v = qobject_input_visitor_new_keyval(qobj); | 240 | @@ -XXX,XX +XXX,XX @@ static void test_should_update_child(void) |
157 | - qobject_unref(qobj); | 241 | bdrv_set_backing_hd(target, bs, &error_abort); |
158 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | 242 | |
159 | visit_free(v); | 243 | g_assert(target->backing->bs == bs); |
160 | 244 | + bdrv_graph_wrlock(NULL); | |
161 | diff --git a/block/qcow2.c b/block/qcow2.c | 245 | bdrv_attach_child(filter, target, "target", &child_of_bds, |
162 | index XXXXXXX..XXXXXXX 100644 | 246 | BDRV_CHILD_DATA, &error_abort); |
163 | --- a/block/qcow2.c | 247 | + bdrv_graph_wrunlock(); |
164 | +++ b/block/qcow2.c | 248 | aio_context_acquire(qemu_get_aio_context()); |
165 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt | 249 | bdrv_append(filter, bs, &error_abort); |
166 | { | 250 | aio_context_release(qemu_get_aio_context()); |
167 | BlockdevCreateOptions *create_options = NULL; | 251 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_exclusive_write(void) |
168 | QDict *qdict; | 252 | */ |
169 | - QObject *qobj; | 253 | bdrv_ref(base); |
170 | Visitor *v; | 254 | |
171 | BlockDriverState *bs = NULL; | 255 | + bdrv_graph_wrlock(NULL); |
172 | Error *local_err = NULL; | 256 | bdrv_attach_child(top, fl1, "backing", &child_of_bds, |
173 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt | 257 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, |
174 | qdict_put_str(qdict, "file", bs->node_name); | 258 | &error_abort); |
175 | 259 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_exclusive_write(void) | |
176 | /* Now get the QAPI type BlockdevCreateOptions */ | 260 | bdrv_attach_child(fl2, base, "backing", &child_of_bds, |
177 | - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | 261 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, |
178 | - if (!qobj) { | 262 | &error_abort); |
179 | + v = qobject_input_visitor_new_flat_confused(qdict, errp); | 263 | + bdrv_graph_wrunlock(); |
180 | + if (!v) { | 264 | |
181 | ret = -EINVAL; | 265 | bdrv_replace_node(fl1, fl2, &error_abort); |
182 | goto finish; | 266 | |
183 | } | 267 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void) |
184 | 268 | */ | |
185 | - v = qobject_input_visitor_new_keyval(qobj); | 269 | bdrv_ref(base); |
186 | - qobject_unref(qobj); | 270 | |
187 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | 271 | + bdrv_graph_wrlock(NULL); |
188 | visit_free(v); | 272 | bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA, |
189 | 273 | &error_abort); | |
190 | diff --git a/block/qed.c b/block/qed.c | 274 | c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds, |
191 | index XXXXXXX..XXXXXXX 100644 | 275 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void) |
192 | --- a/block/qed.c | 276 | bdrv_attach_child(fl2, base, "backing", &child_of_bds, |
193 | +++ b/block/qed.c | 277 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, |
194 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, | 278 | &error_abort); |
195 | { | 279 | + bdrv_graph_wrunlock(); |
196 | BlockdevCreateOptions *create_options = NULL; | 280 | |
197 | QDict *qdict; | 281 | /* Select fl1 as first child to be active */ |
198 | - QObject *qobj; | 282 | s->selected = c_fl1; |
199 | Visitor *v; | 283 | @@ -XXX,XX +XXX,XX @@ static void test_append_greedy_filter(void) |
200 | BlockDriverState *bs = NULL; | 284 | BlockDriverState *base = no_perm_node("base"); |
201 | Error *local_err = NULL; | 285 | BlockDriverState *fl = exclusive_writer_node("fl1"); |
202 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, | 286 | |
203 | qdict_put_str(qdict, "driver", "qed"); | 287 | + bdrv_graph_wrlock(NULL); |
204 | qdict_put_str(qdict, "file", bs->node_name); | 288 | bdrv_attach_child(top, base, "backing", &child_of_bds, |
205 | 289 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | |
206 | - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | 290 | &error_abort); |
207 | - if (!qobj) { | 291 | + bdrv_graph_wrunlock(); |
208 | + v = qobject_input_visitor_new_flat_confused(qdict, errp); | 292 | |
209 | + if (!v) { | 293 | aio_context_acquire(qemu_get_aio_context()); |
210 | ret = -EINVAL; | 294 | bdrv_append(fl, base, &error_abort); |
211 | goto fail; | ||
212 | } | ||
213 | |||
214 | - v = qobject_input_visitor_new_keyval(qobj); | ||
215 | - qobject_unref(qobj); | ||
216 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||
217 | visit_free(v); | ||
218 | |||
219 | diff --git a/block/rbd.c b/block/rbd.c | ||
220 | index XXXXXXX..XXXXXXX 100644 | ||
221 | --- a/block/rbd.c | ||
222 | +++ b/block/rbd.c | ||
223 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||
224 | BDRVRBDState *s = bs->opaque; | ||
225 | BlockdevOptionsRbd *opts = NULL; | ||
226 | Visitor *v; | ||
227 | - QObject *crumpled = NULL; | ||
228 | const QDictEntry *e; | ||
229 | Error *local_err = NULL; | ||
230 | char *keypairs, *secretid; | ||
231 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | ||
232 | } | ||
233 | |||
234 | /* Convert the remaining options into a QAPI object */ | ||
235 | - crumpled = qdict_crumple_for_keyval_qiv(options, errp); | ||
236 | - if (crumpled == NULL) { | ||
237 | + v = qobject_input_visitor_new_flat_confused(options, errp); | ||
238 | + if (!v) { | ||
239 | r = -EINVAL; | ||
240 | goto out; | ||
241 | } | ||
242 | |||
243 | - v = qobject_input_visitor_new_keyval(crumpled); | ||
244 | visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); | ||
245 | visit_free(v); | ||
246 | - qobject_unref(crumpled); | ||
247 | |||
248 | if (local_err) { | ||
249 | error_propagate(errp, local_err); | ||
250 | diff --git a/block/sheepdog.c b/block/sheepdog.c | ||
251 | index XXXXXXX..XXXXXXX 100644 | ||
252 | --- a/block/sheepdog.c | ||
253 | +++ b/block/sheepdog.c | ||
254 | @@ -XXX,XX +XXX,XX @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s, | ||
255 | static SocketAddress *sd_server_config(QDict *options, Error **errp) | ||
256 | { | ||
257 | QDict *server = NULL; | ||
258 | - QObject *crumpled_server = NULL; | ||
259 | Visitor *iv = NULL; | ||
260 | SocketAddress *saddr = NULL; | ||
261 | Error *local_err = NULL; | ||
262 | |||
263 | qdict_extract_subqdict(options, &server, "server."); | ||
264 | |||
265 | - crumpled_server = qdict_crumple_for_keyval_qiv(server, errp); | ||
266 | - if (!crumpled_server) { | ||
267 | + iv = qobject_input_visitor_new_flat_confused(server, errp); | ||
268 | + if (!iv) { | ||
269 | goto done; | ||
270 | } | ||
271 | |||
272 | - iv = qobject_input_visitor_new_keyval(crumpled_server); | ||
273 | visit_type_SocketAddress(iv, NULL, &saddr, &local_err); | ||
274 | if (local_err) { | ||
275 | error_propagate(errp, local_err); | ||
276 | @@ -XXX,XX +XXX,XX @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) | ||
277 | |||
278 | done: | ||
279 | visit_free(iv); | ||
280 | - qobject_unref(crumpled_server); | ||
281 | qobject_unref(server); | ||
282 | return saddr; | ||
283 | } | ||
284 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, | ||
285 | { | ||
286 | BlockdevCreateOptions *create_options = NULL; | ||
287 | QDict *qdict, *location_qdict; | ||
288 | - QObject *crumpled; | ||
289 | Visitor *v; | ||
290 | char *redundancy; | ||
291 | Error *local_err = NULL; | ||
292 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, | ||
293 | } | ||
294 | |||
295 | /* Get the QAPI object */ | ||
296 | - crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
297 | - if (crumpled == NULL) { | ||
298 | + v = qobject_input_visitor_new_flat_confused(qdict, errp); | ||
299 | + if (!v) { | ||
300 | ret = -EINVAL; | ||
301 | goto fail; | ||
302 | } | ||
303 | |||
304 | - v = qobject_input_visitor_new_keyval(crumpled); | ||
305 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||
306 | visit_free(v); | ||
307 | - qobject_unref(crumpled); | ||
308 | |||
309 | if (local_err) { | ||
310 | error_propagate(errp, local_err); | ||
311 | diff --git a/block/ssh.c b/block/ssh.c | ||
312 | index XXXXXXX..XXXXXXX 100644 | ||
313 | --- a/block/ssh.c | ||
314 | +++ b/block/ssh.c | ||
315 | @@ -XXX,XX +XXX,XX @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) | ||
316 | BlockdevOptionsSsh *result = NULL; | ||
317 | QemuOpts *opts = NULL; | ||
318 | Error *local_err = NULL; | ||
319 | - QObject *crumpled; | ||
320 | const QDictEntry *e; | ||
321 | Visitor *v; | ||
322 | |||
323 | @@ -XXX,XX +XXX,XX @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) | ||
324 | } | ||
325 | |||
326 | /* Create the QAPI object */ | ||
327 | - crumpled = qdict_crumple_for_keyval_qiv(options, errp); | ||
328 | - if (crumpled == NULL) { | ||
329 | + v = qobject_input_visitor_new_flat_confused(options, errp); | ||
330 | + if (!v) { | ||
331 | goto fail; | ||
332 | } | ||
333 | |||
334 | - v = qobject_input_visitor_new_keyval(crumpled); | ||
335 | visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); | ||
336 | visit_free(v); | ||
337 | - qobject_unref(crumpled); | ||
338 | |||
339 | if (local_err) { | ||
340 | error_propagate(errp, local_err); | ||
341 | diff --git a/block/vhdx.c b/block/vhdx.c | ||
342 | index XXXXXXX..XXXXXXX 100644 | ||
343 | --- a/block/vhdx.c | ||
344 | +++ b/block/vhdx.c | ||
345 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, | ||
346 | { | ||
347 | BlockdevCreateOptions *create_options = NULL; | ||
348 | QDict *qdict; | ||
349 | - QObject *qobj; | ||
350 | Visitor *v; | ||
351 | BlockDriverState *bs = NULL; | ||
352 | Error *local_err = NULL; | ||
353 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, | ||
354 | qdict_put_str(qdict, "driver", "vhdx"); | ||
355 | qdict_put_str(qdict, "file", bs->node_name); | ||
356 | |||
357 | - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
358 | - if (!qobj) { | ||
359 | + v = qobject_input_visitor_new_flat_confused(qdict, errp); | ||
360 | + if (!v) { | ||
361 | ret = -EINVAL; | ||
362 | goto fail; | ||
363 | } | ||
364 | |||
365 | - v = qobject_input_visitor_new_keyval(qobj); | ||
366 | - qobject_unref(qobj); | ||
367 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||
368 | visit_free(v); | ||
369 | |||
370 | diff --git a/block/vpc.c b/block/vpc.c | ||
371 | index XXXXXXX..XXXXXXX 100644 | ||
372 | --- a/block/vpc.c | ||
373 | +++ b/block/vpc.c | ||
374 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, | ||
375 | { | ||
376 | BlockdevCreateOptions *create_options = NULL; | ||
377 | QDict *qdict; | ||
378 | - QObject *qobj; | ||
379 | Visitor *v; | ||
380 | BlockDriverState *bs = NULL; | ||
381 | Error *local_err = NULL; | ||
382 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, | ||
383 | qdict_put_str(qdict, "driver", "vpc"); | ||
384 | qdict_put_str(qdict, "file", bs->node_name); | ||
385 | |||
386 | - qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
387 | - if (!qobj) { | ||
388 | + v = qobject_input_visitor_new_flat_confused(qdict, errp); | ||
389 | + if (!v) { | ||
390 | ret = -EINVAL; | ||
391 | goto fail; | ||
392 | } | ||
393 | |||
394 | - v = qobject_input_visitor_new_keyval(qobj); | ||
395 | - qobject_unref(qobj); | ||
396 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||
397 | visit_free(v); | ||
398 | |||
399 | diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c | ||
400 | index XXXXXXX..XXXXXXX 100644 | ||
401 | --- a/qobject/block-qdict.c | ||
402 | +++ b/qobject/block-qdict.c | ||
403 | @@ -XXX,XX +XXX,XX @@ | ||
404 | #include "qapi/qmp/qlist.h" | ||
405 | #include "qapi/qmp/qnum.h" | ||
406 | #include "qapi/qmp/qstring.h" | ||
407 | +#include "qapi/qobject-input-visitor.h" | ||
408 | #include "qemu/cutils.h" | ||
409 | #include "qapi/error.h" | ||
410 | |||
411 | @@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp) | ||
412 | * used for anything else, and it should go away once the block | ||
413 | * subsystem has been cleaned up. | ||
414 | */ | ||
415 | -QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) | ||
416 | +static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) | ||
417 | { | ||
418 | QDict *tmp = NULL; | ||
419 | char *buf; | ||
420 | @@ -XXX,XX +XXX,XX @@ bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) | ||
421 | } | ||
422 | return true; | ||
423 | } | ||
424 | + | ||
425 | +/* | ||
426 | + * Create a QObject input visitor for flat @qdict with possibly | ||
427 | + * confused scalar types. | ||
428 | + * | ||
429 | + * The block subsystem uses this function to visit its flat QDict with | ||
430 | + * possibly confused scalar types. It should not be used for anything | ||
431 | + * else, and it should go away once the block subsystem has been | ||
432 | + * cleaned up. | ||
433 | + */ | ||
434 | +Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, | ||
435 | + Error **errp) | ||
436 | +{ | ||
437 | + QObject *crumpled; | ||
438 | + Visitor *v; | ||
439 | + | ||
440 | + crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
441 | + if (!crumpled) { | ||
442 | + return NULL; | ||
443 | + } | ||
444 | + | ||
445 | + v = qobject_input_visitor_new_keyval(crumpled); | ||
446 | + qobject_unref(crumpled); | ||
447 | + return v; | ||
448 | +} | ||
449 | -- | 295 | -- |
450 | 2.13.6 | 296 | 2.41.0 |
451 | |||
452 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | The function reads the parents list, so it needs to hold the graph lock. |
---|---|---|---|
2 | 2 | ||
3 | Remaining uses of qobject_input_visitor_new_keyval() in the block | 3 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | subsystem: | 4 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
5 | 5 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | |
6 | * block_crypto_open_opts_init() | 6 | Message-ID: <20230911094620.45040-14-kwolf@redhat.com> |
7 | Currently doesn't visit any non-string scalars, thus safe. It's | ||
8 | called from | ||
9 | - block_crypto_open_luks() | ||
10 | Creates the QDict with qemu_opts_to_qdict_filtered(), which | ||
11 | creates only string scalars, but has a TODO asking for other types. | ||
12 | - qcow_open() | ||
13 | - qcow2_open(), qcow2_co_invalidate_cache(), qcow2_reopen_prepare() | ||
14 | |||
15 | * block_crypto_create_opts_init(), called from | ||
16 | - block_crypto_co_create_opts_luks() | ||
17 | Also creates the QDict with qemu_opts_to_qdict_filtered(). | ||
18 | |||
19 | * vdi_co_create_opts() | ||
20 | Also creates the QDict with qemu_opts_to_qdict_filtered(). | ||
21 | |||
22 | Replace these uses by qobject_input_visitor_new_flat_confused() for | ||
23 | robustness. This adds crumpling. Right now, that's a no-op, but if | ||
24 | we ever extend these things in non-flat ways, crumpling will be | ||
25 | needed. | ||
26 | |||
27 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | ||
28 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
29 | --- | 8 | --- |
30 | block/crypto.c | 12 +++++++++--- | 9 | include/block/block_int-common.h | 6 ++--- |
31 | block/vdi.c | 8 ++++++-- | 10 | include/block/block_int-global-state.h | 8 +++--- |
32 | 2 files changed, 15 insertions(+), 5 deletions(-) | 11 | include/sysemu/block-backend-global-state.h | 4 +-- |
12 | block.c | 28 +++++++++++++-------- | ||
13 | block/block-backend.c | 26 ++++++++++++++----- | ||
14 | block/crypto.c | 6 +++-- | ||
15 | block/mirror.c | 8 ++++++ | ||
16 | block/vmdk.c | 2 ++ | ||
17 | tests/unit/test-bdrv-graph-mod.c | 4 +++ | ||
18 | 9 files changed, 66 insertions(+), 26 deletions(-) | ||
33 | 19 | ||
20 | diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/include/block/block_int-common.h | ||
23 | +++ b/include/block/block_int-common.h | ||
24 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | ||
25 | */ | ||
26 | void (*bdrv_cancel_in_flight)(BlockDriverState *bs); | ||
27 | |||
28 | - int (*bdrv_inactivate)(BlockDriverState *bs); | ||
29 | + int GRAPH_RDLOCK_PTR (*bdrv_inactivate)(BlockDriverState *bs); | ||
30 | |||
31 | int (*bdrv_snapshot_create)(BlockDriverState *bs, | ||
32 | QEMUSnapshotInfo *sn_info); | ||
33 | @@ -XXX,XX +XXX,XX @@ struct BdrvChildClass { | ||
34 | * when migration is completing) and it can start/stop requesting | ||
35 | * permissions and doing I/O on it. | ||
36 | */ | ||
37 | - void (*activate)(BdrvChild *child, Error **errp); | ||
38 | - int (*inactivate)(BdrvChild *child); | ||
39 | + void GRAPH_RDLOCK_PTR (*activate)(BdrvChild *child, Error **errp); | ||
40 | + int GRAPH_RDLOCK_PTR (*inactivate)(BdrvChild *child); | ||
41 | |||
42 | void GRAPH_WRLOCK_PTR (*attach)(BdrvChild *child); | ||
43 | void GRAPH_WRLOCK_PTR (*detach)(BdrvChild *child); | ||
44 | diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h | ||
45 | index XXXXXXX..XXXXXXX 100644 | ||
46 | --- a/include/block/block_int-global-state.h | ||
47 | +++ b/include/block/block_int-global-state.h | ||
48 | @@ -XXX,XX +XXX,XX @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
49 | * bdrv_child_refresh_perms() instead and make the parent's | ||
50 | * .bdrv_child_perm() implementation return the correct values. | ||
51 | */ | ||
52 | -int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, | ||
53 | - Error **errp); | ||
54 | +int GRAPH_RDLOCK | ||
55 | +bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, | ||
56 | + Error **errp); | ||
57 | |||
58 | /** | ||
59 | * Calls bs->drv->bdrv_child_perm() and updates the child's permission | ||
60 | @@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, | ||
61 | * values than before, but which will not result in the block layer | ||
62 | * automatically refreshing the permissions. | ||
63 | */ | ||
64 | -int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); | ||
65 | +int GRAPH_RDLOCK | ||
66 | +bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); | ||
67 | |||
68 | bool GRAPH_RDLOCK bdrv_recurse_can_replace(BlockDriverState *bs, | ||
69 | BlockDriverState *to_replace); | ||
70 | diff --git a/include/sysemu/block-backend-global-state.h b/include/sysemu/block-backend-global-state.h | ||
71 | index XXXXXXX..XXXXXXX 100644 | ||
72 | --- a/include/sysemu/block-backend-global-state.h | ||
73 | +++ b/include/sysemu/block-backend-global-state.h | ||
74 | @@ -XXX,XX +XXX,XX @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); | ||
75 | int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp); | ||
76 | bool bdrv_has_blk(BlockDriverState *bs); | ||
77 | bool bdrv_is_root_node(BlockDriverState *bs); | ||
78 | -int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
79 | - Error **errp); | ||
80 | +int GRAPH_UNLOCKED blk_set_perm(BlockBackend *blk, uint64_t perm, | ||
81 | + uint64_t shared_perm, Error **errp); | ||
82 | void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm); | ||
83 | |||
84 | void blk_iostatus_enable(BlockBackend *blk); | ||
85 | diff --git a/block.c b/block.c | ||
86 | index XXXXXXX..XXXXXXX 100644 | ||
87 | --- a/block.c | ||
88 | +++ b/block.c | ||
89 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) | ||
90 | return false; | ||
91 | } | ||
92 | |||
93 | -static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) | ||
94 | +static bool GRAPH_RDLOCK | ||
95 | +bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) | ||
96 | { | ||
97 | BdrvChild *a, *b; | ||
98 | GLOBAL_STATE_CODE(); | ||
99 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, | ||
100 | * simplest way to satisfy this criteria: use only result of | ||
101 | * bdrv_topological_dfs() or NULL as @list parameter. | ||
102 | */ | ||
103 | -static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found, | ||
104 | - BlockDriverState *bs) | ||
105 | +static GSList * GRAPH_RDLOCK | ||
106 | +bdrv_topological_dfs(GSList *list, GHashTable *found, BlockDriverState *bs) | ||
107 | { | ||
108 | BdrvChild *child; | ||
109 | g_autoptr(GHashTable) local_found = NULL; | ||
110 | @@ -XXX,XX +XXX,XX @@ static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
111 | * @list is a product of bdrv_topological_dfs() (may be called several times) - | ||
112 | * a topologically sorted subgraph. | ||
113 | */ | ||
114 | -static int bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
115 | - Transaction *tran, Error **errp) | ||
116 | +static int GRAPH_RDLOCK | ||
117 | +bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, | ||
118 | + Error **errp) | ||
119 | { | ||
120 | int ret; | ||
121 | BlockDriverState *bs; | ||
122 | @@ -XXX,XX +XXX,XX @@ static int bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
123 | * topologically sorted. It's not a problem if some node occurs in the @list | ||
124 | * several times. | ||
125 | */ | ||
126 | -static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, | ||
127 | - Transaction *tran, Error **errp) | ||
128 | +static int GRAPH_RDLOCK | ||
129 | +bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, | ||
130 | + Error **errp) | ||
131 | { | ||
132 | g_autoptr(GHashTable) found = g_hash_table_new(NULL, NULL); | ||
133 | g_autoptr(GSList) refresh_list = NULL; | ||
134 | @@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm) | ||
135 | |||
136 | |||
137 | /* @tran is allowed to be NULL. In this case no rollback is possible */ | ||
138 | -static int bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran, | ||
139 | - Error **errp) | ||
140 | +static int GRAPH_RDLOCK | ||
141 | +bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran, Error **errp) | ||
142 | { | ||
143 | int ret; | ||
144 | Transaction *local_tran = NULL; | ||
145 | @@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child) | ||
146 | GLOBAL_STATE_CODE(); | ||
147 | bdrv_graph_wrlock(NULL); | ||
148 | bdrv_replace_child_noperm(child, NULL); | ||
149 | - bdrv_graph_wrunlock(); | ||
150 | bdrv_child_free(child); | ||
151 | |||
152 | if (child_bs) { | ||
153 | @@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child) | ||
154 | NULL); | ||
155 | } | ||
156 | |||
157 | + bdrv_graph_wrunlock(); | ||
158 | bdrv_unref(child_bs); | ||
159 | } | ||
160 | |||
161 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) | ||
162 | * reconfiguring the fd and that's why it does it in raw_check_perm(), not | ||
163 | * in raw_reopen_prepare() which is called with "old" permissions. | ||
164 | */ | ||
165 | + bdrv_graph_rdlock_main_loop(); | ||
166 | ret = bdrv_list_refresh_perms(refresh_list, bs_queue, tran, errp); | ||
167 | + bdrv_graph_rdunlock_main_loop(); | ||
168 | + | ||
169 | if (ret < 0) { | ||
170 | goto abort; | ||
171 | } | ||
172 | @@ -XXX,XX +XXX,XX @@ int bdrv_activate(BlockDriverState *bs, Error **errp) | ||
173 | BdrvDirtyBitmap *bm; | ||
174 | |||
175 | GLOBAL_STATE_CODE(); | ||
176 | + GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
177 | |||
178 | if (!bs->drv) { | ||
179 | return -ENOMEDIUM; | ||
180 | @@ -XXX,XX +XXX,XX @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) | ||
181 | uint64_t cumulative_perms, cumulative_shared_perms; | ||
182 | |||
183 | GLOBAL_STATE_CODE(); | ||
184 | + GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
185 | |||
186 | if (!bs->drv) { | ||
187 | return -ENOMEDIUM; | ||
188 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
189 | index XXXXXXX..XXXXXXX 100644 | ||
190 | --- a/block/block-backend.c | ||
191 | +++ b/block/block-backend.c | ||
192 | @@ -XXX,XX +XXX,XX @@ static QTAILQ_HEAD(, BlockBackend) block_backends = | ||
193 | static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = | ||
194 | QTAILQ_HEAD_INITIALIZER(monitor_block_backends); | ||
195 | |||
196 | +static int coroutine_mixed_fn GRAPH_RDLOCK | ||
197 | +blk_set_perm_locked(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
198 | + Error **errp); | ||
199 | + | ||
200 | static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format, | ||
201 | int *child_flags, QDict *child_options, | ||
202 | int parent_flags, QDict *parent_options) | ||
203 | @@ -XXX,XX +XXX,XX @@ static void blk_vm_state_changed(void *opaque, bool running, RunState state) | ||
204 | * | ||
205 | * If an error is returned, the VM cannot be allowed to be resumed. | ||
206 | */ | ||
207 | -static void blk_root_activate(BdrvChild *child, Error **errp) | ||
208 | +static void GRAPH_RDLOCK blk_root_activate(BdrvChild *child, Error **errp) | ||
209 | { | ||
210 | BlockBackend *blk = child->opaque; | ||
211 | Error *local_err = NULL; | ||
212 | @@ -XXX,XX +XXX,XX @@ static void blk_root_activate(BdrvChild *child, Error **errp) | ||
213 | */ | ||
214 | saved_shared_perm = blk->shared_perm; | ||
215 | |||
216 | - blk_set_perm(blk, blk->perm, BLK_PERM_ALL, &local_err); | ||
217 | + blk_set_perm_locked(blk, blk->perm, BLK_PERM_ALL, &local_err); | ||
218 | if (local_err) { | ||
219 | error_propagate(errp, local_err); | ||
220 | blk->disable_perm = true; | ||
221 | @@ -XXX,XX +XXX,XX @@ static void blk_root_activate(BdrvChild *child, Error **errp) | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | - blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err); | ||
226 | + blk_set_perm_locked(blk, blk->perm, blk->shared_perm, &local_err); | ||
227 | if (local_err) { | ||
228 | error_propagate(errp, local_err); | ||
229 | blk->disable_perm = true; | ||
230 | @@ -XXX,XX +XXX,XX @@ static bool blk_can_inactivate(BlockBackend *blk) | ||
231 | return blk->force_allow_inactivate; | ||
232 | } | ||
233 | |||
234 | -static int blk_root_inactivate(BdrvChild *child) | ||
235 | +static int GRAPH_RDLOCK blk_root_inactivate(BdrvChild *child) | ||
236 | { | ||
237 | BlockBackend *blk = child->opaque; | ||
238 | |||
239 | @@ -XXX,XX +XXX,XX @@ int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp) | ||
240 | /* | ||
241 | * Sets the permission bitmasks that the user of the BlockBackend needs. | ||
242 | */ | ||
243 | -int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
244 | - Error **errp) | ||
245 | +static int coroutine_mixed_fn GRAPH_RDLOCK | ||
246 | +blk_set_perm_locked(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
247 | + Error **errp) | ||
248 | { | ||
249 | int ret; | ||
250 | GLOBAL_STATE_CODE(); | ||
251 | @@ -XXX,XX +XXX,XX @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | +int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, | ||
256 | + Error **errp) | ||
257 | +{ | ||
258 | + GLOBAL_STATE_CODE(); | ||
259 | + GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
260 | + | ||
261 | + return blk_set_perm_locked(blk, perm, shared_perm, errp); | ||
262 | +} | ||
263 | + | ||
264 | void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) | ||
265 | { | ||
266 | GLOBAL_STATE_CODE(); | ||
34 | diff --git a/block/crypto.c b/block/crypto.c | 267 | diff --git a/block/crypto.c b/block/crypto.c |
35 | index XXXXXXX..XXXXXXX 100644 | 268 | index XXXXXXX..XXXXXXX 100644 |
36 | --- a/block/crypto.c | 269 | --- a/block/crypto.c |
37 | +++ b/block/crypto.c | 270 | +++ b/block/crypto.c |
38 | @@ -XXX,XX +XXX,XX @@ | 271 | @@ -XXX,XX +XXX,XX @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) |
39 | #include "qemu/osdep.h" | 272 | return spec_info; |
40 | 273 | } | |
41 | #include "block/block_int.h" | 274 | |
42 | +#include "block/qdict.h" | 275 | -static int |
43 | #include "sysemu/block-backend.h" | 276 | +static int GRAPH_RDLOCK |
44 | #include "crypto/block.h" | 277 | block_crypto_amend_prepare(BlockDriverState *bs, Error **errp) |
45 | #include "qapi/opts-visitor.h" | 278 | { |
46 | #include "qapi/qapi-visit-crypto.h" | 279 | BlockCrypto *crypto = bs->opaque; |
47 | -#include "qapi/qmp/qdict.h" | 280 | @@ -XXX,XX +XXX,XX @@ block_crypto_amend_prepare(BlockDriverState *bs, Error **errp) |
48 | #include "qapi/qobject-input-visitor.h" | 281 | return ret; |
49 | #include "qapi/error.h" | 282 | } |
50 | #include "qemu/option.h" | 283 | |
51 | @@ -XXX,XX +XXX,XX @@ block_crypto_open_opts_init(QCryptoBlockFormat format, | 284 | -static void |
52 | ret = g_new0(QCryptoBlockOpenOptions, 1); | 285 | +static void GRAPH_RDLOCK |
53 | ret->format = format; | 286 | block_crypto_amend_cleanup(BlockDriverState *bs) |
54 | 287 | { | |
55 | - v = qobject_input_visitor_new_keyval(QOBJECT(opts)); | 288 | BlockCrypto *crypto = bs->opaque; |
56 | + v = qobject_input_visitor_new_flat_confused(opts, &local_err); | 289 | @@ -XXX,XX +XXX,XX @@ block_crypto_amend_options_luks(BlockDriverState *bs, |
57 | + if (local_err) { | 290 | QCryptoBlockAmendOptions *amend_options = NULL; |
58 | + goto out; | 291 | int ret = -EINVAL; |
59 | + } | 292 | |
60 | 293 | + assume_graph_lock(); /* FIXME */ | |
61 | visit_start_struct(v, NULL, NULL, 0, &local_err); | 294 | + |
62 | if (local_err) { | 295 | assert(crypto); |
63 | @@ -XXX,XX +XXX,XX @@ block_crypto_create_opts_init(QCryptoBlockFormat format, | 296 | assert(crypto->block); |
64 | ret = g_new0(QCryptoBlockCreateOptions, 1); | 297 | |
65 | ret->format = format; | 298 | diff --git a/block/mirror.c b/block/mirror.c |
66 | 299 | index XXXXXXX..XXXXXXX 100644 | |
67 | - v = qobject_input_visitor_new_keyval(QOBJECT(opts)); | 300 | --- a/block/mirror.c |
68 | + v = qobject_input_visitor_new_flat_confused(opts, &local_err); | 301 | +++ b/block/mirror.c |
69 | + if (local_err) { | 302 | @@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job) |
70 | + goto out; | 303 | * mirror_top_bs from now on, so keep it drained. */ |
71 | + } | 304 | bdrv_drained_begin(mirror_top_bs); |
72 | 305 | bs_opaque->stop = true; | |
73 | visit_start_struct(v, NULL, NULL, 0, &local_err); | 306 | + |
74 | if (local_err) { | 307 | + bdrv_graph_rdlock_main_loop(); |
75 | diff --git a/block/vdi.c b/block/vdi.c | 308 | bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, |
76 | index XXXXXXX..XXXXXXX 100644 | 309 | &error_abort); |
77 | --- a/block/vdi.c | 310 | + bdrv_graph_rdunlock_main_loop(); |
78 | +++ b/block/vdi.c | 311 | + |
79 | @@ -XXX,XX +XXX,XX @@ | 312 | if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { |
80 | 313 | BlockDriverState *backing = s->is_none_mode ? src : s->base; | |
81 | #include "qemu/osdep.h" | 314 | BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs); |
82 | #include "qapi/error.h" | 315 | @@ -XXX,XX +XXX,XX @@ static BlockJob *mirror_start_job( |
83 | -#include "qapi/qmp/qdict.h" | 316 | uint64_t target_perms, target_shared_perms; |
84 | #include "qapi/qobject-input-visitor.h" | 317 | int ret; |
85 | #include "qapi/qapi-visit-block-core.h" | 318 | |
86 | #include "block/block_int.h" | 319 | + GLOBAL_STATE_CODE(); |
87 | +#include "block/qdict.h" | 320 | + |
88 | #include "sysemu/block-backend.h" | 321 | if (granularity == 0) { |
89 | #include "qemu/module.h" | 322 | granularity = bdrv_get_default_bitmap_granularity(target); |
90 | #include "qemu/option.h" | 323 | } |
91 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, | 324 | @@ -XXX,XX +XXX,XX @@ fail: |
92 | } | 325 | } |
93 | 326 | ||
94 | /* Get the QAPI object */ | 327 | bs_opaque->stop = true; |
95 | - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | 328 | + bdrv_graph_rdlock_main_loop(); |
96 | + v = qobject_input_visitor_new_flat_confused(qdict, errp); | 329 | bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, |
97 | + if (!v) { | 330 | &error_abort); |
98 | + ret = -EINVAL; | 331 | + bdrv_graph_rdunlock_main_loop(); |
99 | + goto done; | 332 | bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); |
100 | + } | 333 | |
101 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | 334 | bdrv_unref(mirror_top_bs); |
102 | visit_free(v); | 335 | diff --git a/block/vmdk.c b/block/vmdk.c |
336 | index XXXXXXX..XXXXXXX 100644 | ||
337 | --- a/block/vmdk.c | ||
338 | +++ b/block/vmdk.c | ||
339 | @@ -XXX,XX +XXX,XX @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, | ||
340 | BDRVVmdkState *s = bs->opaque; | ||
341 | uint32_t magic; | ||
342 | |||
343 | + GRAPH_RDLOCK_GUARD_MAINLOOP(); | ||
344 | + | ||
345 | ret = bdrv_open_file_child(NULL, options, "file", bs, errp); | ||
346 | if (ret < 0) { | ||
347 | return ret; | ||
348 | diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c | ||
349 | index XXXXXXX..XXXXXXX 100644 | ||
350 | --- a/tests/unit/test-bdrv-graph-mod.c | ||
351 | +++ b/tests/unit/test-bdrv-graph-mod.c | ||
352 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void) | ||
353 | |||
354 | /* Select fl1 as first child to be active */ | ||
355 | s->selected = c_fl1; | ||
356 | + | ||
357 | + bdrv_graph_rdlock_main_loop(); | ||
358 | + | ||
359 | bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort); | ||
360 | |||
361 | assert(c_fl1->perm & BLK_PERM_WRITE); | ||
362 | @@ -XXX,XX +XXX,XX @@ static void test_parallel_perm_update(void) | ||
363 | assert(c_fl1->perm & BLK_PERM_WRITE); | ||
364 | assert(!(c_fl2->perm & BLK_PERM_WRITE)); | ||
365 | |||
366 | + bdrv_graph_rdunlock_main_loop(); | ||
367 | bdrv_unref(top); | ||
368 | } | ||
103 | 369 | ||
104 | -- | 370 | -- |
105 | 2.13.6 | 371 | 2.41.0 |
106 | |||
107 | diff view generated by jsdifflib |
1 | The -drive option addr was deprecated in QEMU 2.10. It's time to remove | 1 | The function reads the parents list, so it needs to hold the graph lock. |
---|---|---|---|
2 | it. | 2 | |
3 | This happens to result in BlockDriver.bdrv_set_perm() to be called with | ||
4 | the graph lock held. For consistency, make it the same for all of the | ||
5 | BlockDriver callbacks for updating permissions and annotate the function | ||
6 | pointers with GRAPH_RDLOCK_PTR. | ||
3 | 7 | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
5 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | 9 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
6 | Reviewed-by: Jeff Cody <jcody@redhat.com> | 10 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
11 | Message-ID: <20230911094620.45040-15-kwolf@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
7 | --- | 13 | --- |
8 | include/sysemu/blockdev.h | 1 - | 14 | include/block/block_int-common.h | 9 ++++--- |
9 | blockdev.c | 17 +---------------- | 15 | include/block/block_int-global-state.h | 4 +-- |
10 | device-hotplug.c | 4 ---- | 16 | block.c | 35 ++++++++++++++++++++------ |
11 | qemu-doc.texi | 5 ----- | 17 | blockdev.c | 6 +++++ |
12 | qemu-options.hx | 5 +---- | 18 | 4 files changed, 40 insertions(+), 14 deletions(-) |
13 | 5 files changed, 2 insertions(+), 30 deletions(-) | ||
14 | 19 | ||
15 | diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h | 20 | diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h |
16 | index XXXXXXX..XXXXXXX 100644 | 21 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/include/sysemu/blockdev.h | 22 | --- a/include/block/block_int-common.h |
18 | +++ b/include/sysemu/blockdev.h | 23 | +++ b/include/block/block_int-common.h |
19 | @@ -XXX,XX +XXX,XX @@ typedef enum { | 24 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { |
20 | } BlockInterfaceType; | 25 | * If both conditions are met, 0 is returned. Otherwise, -errno is returned |
21 | 26 | * and errp is set to an error describing the conflict. | |
22 | struct DriveInfo { | 27 | */ |
23 | - const char *devaddr; | 28 | - int (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm, |
24 | BlockInterfaceType type; | 29 | - uint64_t shared, Error **errp); |
25 | int bus; | 30 | + int GRAPH_RDLOCK_PTR (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm, |
26 | int unit; | 31 | + uint64_t shared, Error **errp); |
32 | |||
33 | /** | ||
34 | * Called to inform the driver that the set of cumulative set of used | ||
35 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | ||
36 | * This function is only invoked after bdrv_check_perm(), so block drivers | ||
37 | * may rely on preparations made in their .bdrv_check_perm implementation. | ||
38 | */ | ||
39 | - void (*bdrv_set_perm)(BlockDriverState *bs, uint64_t perm, uint64_t shared); | ||
40 | + void GRAPH_RDLOCK_PTR (*bdrv_set_perm)( | ||
41 | + BlockDriverState *bs, uint64_t perm, uint64_t shared); | ||
42 | |||
43 | /* | ||
44 | * Called to inform the driver that after a previous bdrv_check_perm() | ||
45 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | ||
46 | * This function can be called even for nodes that never saw a | ||
47 | * bdrv_check_perm() call. It is a no-op then. | ||
48 | */ | ||
49 | - void (*bdrv_abort_perm_update)(BlockDriverState *bs); | ||
50 | + void GRAPH_RDLOCK_PTR (*bdrv_abort_perm_update)(BlockDriverState *bs); | ||
51 | |||
52 | /** | ||
53 | * Returns in @nperm and @nshared the permissions that the driver for @bs | ||
54 | diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h | ||
55 | index XXXXXXX..XXXXXXX 100644 | ||
56 | --- a/include/block/block_int-global-state.h | ||
57 | +++ b/include/block/block_int-global-state.h | ||
58 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
59 | void *opaque, Error **errp); | ||
60 | void bdrv_root_unref_child(BdrvChild *child); | ||
61 | |||
62 | -void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
63 | - uint64_t *shared_perm); | ||
64 | +void GRAPH_RDLOCK bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
65 | + uint64_t *shared_perm); | ||
66 | |||
67 | /** | ||
68 | * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use | ||
69 | diff --git a/block.c b/block.c | ||
70 | index XXXXXXX..XXXXXXX 100644 | ||
71 | --- a/block.c | ||
72 | +++ b/block.c | ||
73 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, | ||
74 | tran_add(tran, &bdrv_child_set_pem_drv, s); | ||
75 | } | ||
76 | |||
77 | -static void bdrv_drv_set_perm_commit(void *opaque) | ||
78 | +static void GRAPH_RDLOCK bdrv_drv_set_perm_commit(void *opaque) | ||
79 | { | ||
80 | BlockDriverState *bs = opaque; | ||
81 | uint64_t cumulative_perms, cumulative_shared_perms; | ||
82 | @@ -XXX,XX +XXX,XX @@ static void bdrv_drv_set_perm_commit(void *opaque) | ||
83 | } | ||
84 | } | ||
85 | |||
86 | -static void bdrv_drv_set_perm_abort(void *opaque) | ||
87 | +static void GRAPH_RDLOCK bdrv_drv_set_perm_abort(void *opaque) | ||
88 | { | ||
89 | BlockDriverState *bs = opaque; | ||
90 | GLOBAL_STATE_CODE(); | ||
91 | @@ -XXX,XX +XXX,XX @@ TransactionActionDrv bdrv_drv_set_perm_drv = { | ||
92 | .commit = bdrv_drv_set_perm_commit, | ||
93 | }; | ||
94 | |||
95 | -static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, | ||
96 | - uint64_t shared_perm, Transaction *tran, | ||
97 | - Error **errp) | ||
98 | +/* | ||
99 | + * After calling this function, the transaction @tran may only be completed | ||
100 | + * while holding a reader lock for the graph. | ||
101 | + */ | ||
102 | +static int GRAPH_RDLOCK | ||
103 | +bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, | ||
104 | + Transaction *tran, Error **errp) | ||
105 | { | ||
106 | GLOBAL_STATE_CODE(); | ||
107 | if (!bs->drv) { | ||
108 | @@ -XXX,XX +XXX,XX @@ bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, | ||
109 | /* | ||
110 | * Refresh permissions in @bs subtree. The function is intended to be called | ||
111 | * after some graph modification that was done without permission update. | ||
112 | + * | ||
113 | + * After calling this function, the transaction @tran may only be completed | ||
114 | + * while holding a reader lock for the graph. | ||
115 | */ | ||
116 | -static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
117 | - Transaction *tran, Error **errp) | ||
118 | +static int GRAPH_RDLOCK | ||
119 | +bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
120 | + Transaction *tran, Error **errp) | ||
121 | { | ||
122 | BlockDriver *drv = bs->drv; | ||
123 | BdrvChild *c; | ||
124 | @@ -XXX,XX +XXX,XX @@ static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, | ||
125 | /* | ||
126 | * @list is a product of bdrv_topological_dfs() (may be called several times) - | ||
127 | * a topologically sorted subgraph. | ||
128 | + * | ||
129 | + * After calling this function, the transaction @tran may only be completed | ||
130 | + * while holding a reader lock for the graph. | ||
131 | */ | ||
132 | static int GRAPH_RDLOCK | ||
133 | bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, | ||
134 | @@ -XXX,XX +XXX,XX @@ bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, | ||
135 | * @list is any list of nodes. List is completed by all subtrees and | ||
136 | * topologically sorted. It's not a problem if some node occurs in the @list | ||
137 | * several times. | ||
138 | + * | ||
139 | + * After calling this function, the transaction @tran may only be completed | ||
140 | + * while holding a reader lock for the graph. | ||
141 | */ | ||
142 | static int GRAPH_RDLOCK | ||
143 | bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, | ||
144 | @@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm) | ||
145 | } | ||
146 | |||
147 | |||
148 | -/* @tran is allowed to be NULL. In this case no rollback is possible */ | ||
149 | +/* | ||
150 | + * @tran is allowed to be NULL. In this case no rollback is possible. | ||
151 | + * | ||
152 | + * After calling this function, the transaction @tran may only be completed | ||
153 | + * while holding a reader lock for the graph. | ||
154 | + */ | ||
155 | static int GRAPH_RDLOCK | ||
156 | bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran, Error **errp) | ||
157 | { | ||
27 | diff --git a/blockdev.c b/blockdev.c | 158 | diff --git a/blockdev.c b/blockdev.c |
28 | index XXXXXXX..XXXXXXX 100644 | 159 | index XXXXXXX..XXXXXXX 100644 |
29 | --- a/blockdev.c | 160 | --- a/blockdev.c |
30 | +++ b/blockdev.c | 161 | +++ b/blockdev.c |
31 | @@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = { | 162 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_action(TransactionAction *action, |
32 | .type = QEMU_OPT_STRING, | 163 | AioContext *aio_context; |
33 | .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", | 164 | uint64_t perm, shared; |
34 | },{ | 165 | |
35 | - .name = "addr", | 166 | + /* TODO We'll eventually have to take a writer lock in this function */ |
36 | - .type = QEMU_OPT_STRING, | 167 | + GRAPH_RDLOCK_GUARD_MAINLOOP(); |
37 | - .help = "pci address (virtio only)", | 168 | + |
38 | - },{ | 169 | tran_add(tran, &external_snapshot_drv, state); |
39 | .name = "serial", | 170 | |
40 | .type = QEMU_OPT_STRING, | 171 | /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar |
41 | .help = "disk serial number", | 172 | @@ -XXX,XX +XXX,XX @@ void qmp_block_commit(const char *job_id, const char *device, |
42 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | 173 | int job_flags = JOB_DEFAULT; |
43 | DriveMediaType media = MEDIA_DISK; | 174 | uint64_t top_perm, top_shared; |
44 | BlockInterfaceType type; | 175 | |
45 | int max_devs, bus_id, unit_id, index; | 176 | + /* TODO We'll eventually have to take a writer lock in this function */ |
46 | - const char *devaddr; | 177 | + GRAPH_RDLOCK_GUARD_MAINLOOP(); |
47 | const char *werror, *rerror; | 178 | + |
48 | bool read_only = false; | 179 | if (!has_speed) { |
49 | bool copy_on_read; | 180 | speed = 0; |
50 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
51 | Error *local_err = NULL; | ||
52 | int i; | ||
53 | const char *deprecated[] = { | ||
54 | - "serial", "addr" | ||
55 | + "serial" | ||
56 | }; | ||
57 | |||
58 | /* Change legacy command line options into QMP ones */ | ||
59 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
60 | } | 181 | } |
61 | |||
62 | /* Add virtio block device */ | ||
63 | - devaddr = qemu_opt_get(legacy_opts, "addr"); | ||
64 | - if (devaddr && type != IF_VIRTIO) { | ||
65 | - error_report("addr is not supported by this bus type"); | ||
66 | - goto fail; | ||
67 | - } | ||
68 | - | ||
69 | if (type == IF_VIRTIO) { | ||
70 | QemuOpts *devopts; | ||
71 | devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, | ||
72 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
73 | } | ||
74 | qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), | ||
75 | &error_abort); | ||
76 | - if (devaddr) { | ||
77 | - qemu_opt_set(devopts, "addr", devaddr, &error_abort); | ||
78 | - } | ||
79 | } | ||
80 | |||
81 | filename = qemu_opt_get(legacy_opts, "file"); | ||
82 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
83 | dinfo->type = type; | ||
84 | dinfo->bus = bus_id; | ||
85 | dinfo->unit = unit_id; | ||
86 | - dinfo->devaddr = devaddr; | ||
87 | dinfo->serial = g_strdup(serial); | ||
88 | |||
89 | blk_set_legacy_dinfo(blk, dinfo); | ||
90 | diff --git a/device-hotplug.c b/device-hotplug.c | ||
91 | index XXXXXXX..XXXXXXX 100644 | ||
92 | --- a/device-hotplug.c | ||
93 | +++ b/device-hotplug.c | ||
94 | @@ -XXX,XX +XXX,XX @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) | ||
95 | if (!dinfo) { | ||
96 | goto err; | ||
97 | } | ||
98 | - if (dinfo->devaddr) { | ||
99 | - monitor_printf(mon, "Parameter addr not supported\n"); | ||
100 | - goto err; | ||
101 | - } | ||
102 | |||
103 | switch (dinfo->type) { | ||
104 | case IF_NONE: | ||
105 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
106 | index XXXXXXX..XXXXXXX 100644 | ||
107 | --- a/qemu-doc.texi | ||
108 | +++ b/qemu-doc.texi | ||
109 | @@ -XXX,XX +XXX,XX @@ provided per NIC. | ||
110 | The drive serial argument is replaced by the the serial argument | ||
111 | that can be specified with the ``-device'' parameter. | ||
112 | |||
113 | -@subsection -drive addr=... (since 2.10.0) | ||
114 | - | ||
115 | -The drive addr argument is replaced by the the addr argument | ||
116 | -that can be specified with the ``-device'' parameter. | ||
117 | - | ||
118 | @subsection -usbdevice (since 2.10.0) | ||
119 | |||
120 | The ``-usbdevice DEV'' argument is now a synonym for setting | ||
121 | diff --git a/qemu-options.hx b/qemu-options.hx | ||
122 | index XXXXXXX..XXXXXXX 100644 | ||
123 | --- a/qemu-options.hx | ||
124 | +++ b/qemu-options.hx | ||
125 | @@ -XXX,XX +XXX,XX @@ ETEXI | ||
126 | DEF("drive", HAS_ARG, QEMU_OPTION_drive, | ||
127 | "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" | ||
128 | " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" | ||
129 | - " [,snapshot=on|off][,serial=s][,addr=A][,rerror=ignore|stop|report]\n" | ||
130 | + " [,snapshot=on|off][,serial=s][,rerror=ignore|stop|report]\n" | ||
131 | " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" | ||
132 | " [,readonly=on|off][,copy-on-read=on|off]\n" | ||
133 | " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" | ||
134 | @@ -XXX,XX +XXX,XX @@ an untrusted format header. | ||
135 | This option specifies the serial number to assign to the device. This | ||
136 | parameter is deprecated, use the corresponding parameter of @code{-device} | ||
137 | instead. | ||
138 | -@item addr=@var{addr} | ||
139 | -Specify the controller's PCI address (if=virtio only). This parameter is | ||
140 | -deprecated, use the corresponding parameter of @code{-device} instead. | ||
141 | @item werror=@var{action},rerror=@var{action} | ||
142 | Specify which @var{action} to take on write and read errors. Valid actions are: | ||
143 | "ignore" (ignore the error and try to continue), "stop" (pause QEMU), | ||
144 | -- | 182 | -- |
145 | 2.13.6 | 183 | 2.41.0 |
146 | |||
147 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | This adds GRAPH_RDLOCK annotations to declare that callers of |
---|---|---|---|
2 | bdrv_child_perm() need to hold a reader lock for the graph because | ||
3 | some implementations access the children list of a node. | ||
2 | 4 | ||
3 | Parameter "filename" is deprecated since commit 5c3ad1a6a8f, v2.10.0. | 5 | The callers of bdrv_child_perm() conveniently already hold the lock. |
4 | Time to get rid of it. | ||
5 | 6 | ||
6 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 8 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
10 | Message-ID: <20230911094620.45040-16-kwolf@redhat.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 12 | --- |
10 | block/iscsi.c | 23 ++--------------------- | 13 | include/block/block_int-common.h | 10 +++++----- |
11 | 1 file changed, 2 insertions(+), 21 deletions(-) | 14 | block.c | 11 ++++++----- |
15 | block/copy-before-write.c | 10 +++++----- | ||
16 | 3 files changed, 16 insertions(+), 15 deletions(-) | ||
12 | 17 | ||
13 | diff --git a/block/iscsi.c b/block/iscsi.c | 18 | diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h |
14 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block/iscsi.c | 20 | --- a/include/block/block_int-common.h |
16 | +++ b/block/iscsi.c | 21 | +++ b/include/block/block_int-common.h |
17 | @@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = { | 22 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { |
18 | .name = "timeout", | 23 | * permissions, but those that will be needed after applying the |
19 | .type = QEMU_OPT_NUMBER, | 24 | * @reopen_queue. |
20 | }, | 25 | */ |
21 | - { | 26 | - void (*bdrv_child_perm)(BlockDriverState *bs, BdrvChild *c, |
22 | - .name = "filename", | 27 | - BdrvChildRole role, |
23 | - .type = QEMU_OPT_STRING, | 28 | - BlockReopenQueue *reopen_queue, |
24 | - }, | 29 | - uint64_t parent_perm, uint64_t parent_shared, |
25 | { /* end of list */ } | 30 | - uint64_t *nperm, uint64_t *nshared); |
26 | }, | 31 | + void GRAPH_RDLOCK_PTR (*bdrv_child_perm)( |
27 | }; | 32 | + BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, |
28 | @@ -XXX,XX +XXX,XX @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | 33 | + BlockReopenQueue *reopen_queue, |
29 | char *initiator_name = NULL; | 34 | + uint64_t parent_perm, uint64_t parent_shared, |
30 | QemuOpts *opts; | 35 | + uint64_t *nperm, uint64_t *nshared); |
31 | Error *local_err = NULL; | 36 | |
32 | - const char *transport_name, *portal, *target, *filename; | 37 | /** |
33 | + const char *transport_name, *portal, *target; | 38 | * Register/unregister a buffer for I/O. For example, when the driver is |
34 | #if LIBISCSI_API_VERSION >= (20160603) | 39 | diff --git a/block.c b/block.c |
35 | enum iscsi_transport_type transport; | 40 | index XXXXXXX..XXXXXXX 100644 |
36 | #endif | 41 | --- a/block.c |
37 | int i, ret = 0, timeout = 0, lun; | 42 | +++ b/block.c |
38 | 43 | @@ -XXX,XX +XXX,XX @@ bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) | |
39 | - /* If we are given a filename, parse the filename, with precedence given to | 44 | return false; |
40 | - * filename encoded options */ | ||
41 | - filename = qdict_get_try_str(options, "filename"); | ||
42 | - if (filename) { | ||
43 | - warn_report("'filename' option specified. " | ||
44 | - "This is an unsupported option, and may be deprecated " | ||
45 | - "in the future"); | ||
46 | - iscsi_parse_filename(filename, options, &local_err); | ||
47 | - if (local_err) { | ||
48 | - ret = -EINVAL; | ||
49 | - error_propagate(errp, local_err); | ||
50 | - goto exit; | ||
51 | - } | ||
52 | - } | ||
53 | - | ||
54 | opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); | ||
55 | qemu_opts_absorb_qdict(opts, options, &local_err); | ||
56 | if (local_err) { | ||
57 | @@ -XXX,XX +XXX,XX @@ out: | ||
58 | } | ||
59 | memset(iscsilun, 0, sizeof(IscsiLun)); | ||
60 | } | ||
61 | -exit: | ||
62 | + | ||
63 | return ret; | ||
64 | } | 45 | } |
65 | 46 | ||
47 | -static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, | ||
48 | - BdrvChild *c, BdrvChildRole role, | ||
49 | - BlockReopenQueue *reopen_queue, | ||
50 | - uint64_t parent_perm, uint64_t parent_shared, | ||
51 | - uint64_t *nperm, uint64_t *nshared) | ||
52 | +static void GRAPH_RDLOCK | ||
53 | +bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, | ||
54 | + BdrvChild *c, BdrvChildRole role, | ||
55 | + BlockReopenQueue *reopen_queue, | ||
56 | + uint64_t parent_perm, uint64_t parent_shared, | ||
57 | + uint64_t *nperm, uint64_t *nshared) | ||
58 | { | ||
59 | assert(bs->drv && bs->drv->bdrv_child_perm); | ||
60 | GLOBAL_STATE_CODE(); | ||
61 | diff --git a/block/copy-before-write.c b/block/copy-before-write.c | ||
62 | index XXXXXXX..XXXXXXX 100644 | ||
63 | --- a/block/copy-before-write.c | ||
64 | +++ b/block/copy-before-write.c | ||
65 | @@ -XXX,XX +XXX,XX @@ static void cbw_refresh_filename(BlockDriverState *bs) | ||
66 | bs->file->bs->filename); | ||
67 | } | ||
68 | |||
69 | -static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
70 | - BdrvChildRole role, | ||
71 | - BlockReopenQueue *reopen_queue, | ||
72 | - uint64_t perm, uint64_t shared, | ||
73 | - uint64_t *nperm, uint64_t *nshared) | ||
74 | +static void GRAPH_RDLOCK | ||
75 | +cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, | ||
76 | + BlockReopenQueue *reopen_queue, | ||
77 | + uint64_t perm, uint64_t shared, | ||
78 | + uint64_t *nperm, uint64_t *nshared) | ||
79 | { | ||
80 | if (!(role & BDRV_CHILD_FILTERED)) { | ||
81 | /* | ||
66 | -- | 82 | -- |
67 | 2.13.6 | 83 | 2.41.0 |
68 | |||
69 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | The function reads the parents list, so it needs to hold the graph lock. |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 3 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 4 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
5 | Message-ID: <20230911094620.45040-18-kwolf@redhat.com> | ||
6 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | --- | 8 | --- |
7 | tests/check-block-qdict.c | 57 ++++++++++++++++++++++++----------------------- | 9 | block.c | 2 ++ |
8 | 1 file changed, 29 insertions(+), 28 deletions(-) | 10 | 1 file changed, 2 insertions(+) |
9 | 11 | ||
10 | diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c | 12 | diff --git a/block.c b/block.c |
11 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/tests/check-block-qdict.c | 14 | --- a/block.c |
13 | +++ b/tests/check-block-qdict.c | 15 | +++ b/block.c |
14 | @@ -XXX,XX +XXX,XX @@ static void qdict_defaults_test(void) | 16 | @@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, |
15 | 17 | backing_file_str = base->filename; | |
16 | static void qdict_flatten_test(void) | 18 | } |
17 | { | 19 | |
18 | - QList *list1 = qlist_new(); | 20 | + bdrv_graph_rdlock_main_loop(); |
19 | - QList *list2 = qlist_new(); | 21 | QLIST_FOREACH(c, &top->parents, next_parent) { |
20 | - QDict *dict1 = qdict_new(); | 22 | updated_children = g_slist_prepend(updated_children, c); |
21 | - QDict *dict2 = qdict_new(); | 23 | } |
22 | - QDict *dict3 = qdict_new(); | 24 | + bdrv_graph_rdunlock_main_loop(); |
23 | + QList *e_1 = qlist_new(); | ||
24 | + QList *e = qlist_new(); | ||
25 | + QDict *e_1_2 = qdict_new(); | ||
26 | + QDict *f = qdict_new(); | ||
27 | + QDict *root = qdict_new(); | ||
28 | 25 | ||
29 | /* | 26 | /* |
30 | * Test the flattening of | 27 | * It seems correct to pass detach_subchain=true here, but it triggers |
31 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void) | ||
32 | * } | ||
33 | */ | ||
34 | |||
35 | - qdict_put_int(dict1, "a", 0); | ||
36 | - qdict_put_int(dict1, "b", 1); | ||
37 | + qdict_put_int(e_1_2, "a", 0); | ||
38 | + qdict_put_int(e_1_2, "b", 1); | ||
39 | |||
40 | - qlist_append_int(list1, 23); | ||
41 | - qlist_append_int(list1, 66); | ||
42 | - qlist_append(list1, dict1); | ||
43 | - qlist_append_int(list2, 42); | ||
44 | - qlist_append(list2, list1); | ||
45 | + qlist_append_int(e_1, 23); | ||
46 | + qlist_append_int(e_1, 66); | ||
47 | + qlist_append(e_1, e_1_2); | ||
48 | + qlist_append_int(e, 42); | ||
49 | + qlist_append(e, e_1); | ||
50 | |||
51 | - qdict_put_int(dict2, "c", 2); | ||
52 | - qdict_put_int(dict2, "d", 3); | ||
53 | - qdict_put(dict3, "e", list2); | ||
54 | - qdict_put(dict3, "f", dict2); | ||
55 | - qdict_put_int(dict3, "g", 4); | ||
56 | + qdict_put_int(f, "c", 2); | ||
57 | + qdict_put_int(f, "d", 3); | ||
58 | |||
59 | - qdict_flatten(dict3); | ||
60 | + qdict_put(root, "e", e); | ||
61 | + qdict_put(root, "f", f); | ||
62 | + qdict_put_int(root, "g", 4); | ||
63 | |||
64 | - g_assert(qdict_get_int(dict3, "e.0") == 42); | ||
65 | - g_assert(qdict_get_int(dict3, "e.1.0") == 23); | ||
66 | - g_assert(qdict_get_int(dict3, "e.1.1") == 66); | ||
67 | - g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); | ||
68 | - g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); | ||
69 | - g_assert(qdict_get_int(dict3, "f.c") == 2); | ||
70 | - g_assert(qdict_get_int(dict3, "f.d") == 3); | ||
71 | - g_assert(qdict_get_int(dict3, "g") == 4); | ||
72 | + qdict_flatten(root); | ||
73 | |||
74 | - g_assert(qdict_size(dict3) == 8); | ||
75 | + g_assert(qdict_get_int(root, "e.0") == 42); | ||
76 | + g_assert(qdict_get_int(root, "e.1.0") == 23); | ||
77 | + g_assert(qdict_get_int(root, "e.1.1") == 66); | ||
78 | + g_assert(qdict_get_int(root, "e.1.2.a") == 0); | ||
79 | + g_assert(qdict_get_int(root, "e.1.2.b") == 1); | ||
80 | + g_assert(qdict_get_int(root, "f.c") == 2); | ||
81 | + g_assert(qdict_get_int(root, "f.d") == 3); | ||
82 | + g_assert(qdict_get_int(root, "g") == 4); | ||
83 | |||
84 | - qobject_unref(dict3); | ||
85 | + g_assert(qdict_size(root) == 8); | ||
86 | + | ||
87 | + qobject_unref(root); | ||
88 | } | ||
89 | |||
90 | static void qdict_array_split_test(void) | ||
91 | -- | 28 | -- |
92 | 2.13.6 | 29 | 2.41.0 |
93 | |||
94 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | The function reads the parents list, so it needs to hold the graph lock. |
---|---|---|---|
2 | 2 | ||
3 | Parameter auth-client-required lets you configure authentication | 3 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | methods. We tried to provide that in v2.9.0, but backed out due to | 4 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
5 | interface design doubts (commit 464444fcc16). | 5 | Message-ID: <20230911094620.45040-19-kwolf@redhat.com> |
6 | 6 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | |
7 | This commit is similar to what we backed out, but simpler: we use a | ||
8 | list of enumeration values instead of a list of objects with a member | ||
9 | of enumeration type. | ||
10 | |||
11 | Let's review our reasons for backing out the first try, as stated in | ||
12 | the commit message: | ||
13 | |||
14 | * The implementation uses deprecated rados_conf_set() key | ||
15 | "auth_supported". No biggie. | ||
16 | |||
17 | Fixed: we use "auth-client-required". | ||
18 | |||
19 | * The implementation makes -drive silently ignore invalid parameters | ||
20 | "auth" and "auth-supported.*.X" where X isn't "auth". Fixable (in | ||
21 | fact I'm going to fix similar bugs around parameter server), so | ||
22 | again no biggie. | ||
23 | |||
24 | That fix is commit 2836284db60. This commit doesn't bring the bugs | ||
25 | back. | ||
26 | |||
27 | * BlockdevOptionsRbd member @password-secret applies only to | ||
28 | authentication method cephx. Should it be a variant member of | ||
29 | RbdAuthMethod? | ||
30 | |||
31 | We've had time to ponder, and we decided to stick to the way Ceph | ||
32 | configuration works: the key configured separately, and silently | ||
33 | ignored if the authentication method doesn't use it. | ||
34 | |||
35 | * BlockdevOptionsRbd member @user could apply to both methods cephx | ||
36 | and none, but I'm not sure it's actually used with none. If it | ||
37 | isn't, should it be a variant member of RbdAuthMethod? | ||
38 | |||
39 | Likewise. | ||
40 | |||
41 | * The client offers a *set* of authentication methods, not a list. | ||
42 | Should the methods be optional members of BlockdevOptionsRbd instead | ||
43 | of members of list @auth-supported? The latter begs the question | ||
44 | what multiple entries for the same method mean. Trivial question | ||
45 | now that RbdAuthMethod contains nothing but @type, but less so when | ||
46 | RbdAuthMethod acquires other members, such the ones discussed above. | ||
47 | |||
48 | Again, we decided to stick to the way Ceph configuration works, except | ||
49 | we make auth-client-required a list of enumeration values instead of a | ||
50 | string containing keywords separated by delimiters. | ||
51 | |||
52 | * How BlockdevOptionsRbd member @auth-supported interacts with | ||
53 | settings from a configuration file specified with @conf is | ||
54 | undocumented. I suspect it's untested, too. | ||
55 | |||
56 | Not actually true, the documentation for @conf says "Values in the | ||
57 | configuration file will be overridden by options specified via QAPI", | ||
58 | and we've tested this. | ||
59 | |||
60 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | ||
61 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
62 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
63 | --- | 8 | --- |
64 | qapi/block-core.json | 13 +++++++++++++ | 9 | block.c | 4 ++++ |
65 | block/rbd.c | 42 ++++++++++++++++++++++++++++++++---------- | 10 | 1 file changed, 4 insertions(+) |
66 | 2 files changed, 45 insertions(+), 10 deletions(-) | ||
67 | 11 | ||
68 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 12 | diff --git a/block.c b/block.c |
69 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
70 | --- a/qapi/block-core.json | 14 | --- a/block.c |
71 | +++ b/qapi/block-core.json | 15 | +++ b/block.c |
72 | @@ -XXX,XX +XXX,XX @@ | 16 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, |
73 | 17 | return true; | |
74 | |||
75 | ## | ||
76 | +# @RbdAuthMode: | ||
77 | +# | ||
78 | +# Since: 3.0 | ||
79 | +## | ||
80 | +{ 'enum': 'RbdAuthMode', | ||
81 | + 'data': [ 'cephx', 'none' ] } | ||
82 | + | ||
83 | +## | ||
84 | # @BlockdevOptionsRbd: | ||
85 | # | ||
86 | # @pool: Ceph pool name. | ||
87 | @@ -XXX,XX +XXX,XX @@ | ||
88 | # | ||
89 | # @user: Ceph id name. | ||
90 | # | ||
91 | +# @auth-client-required: Acceptable authentication modes. | ||
92 | +# This maps to Ceph configuration option | ||
93 | +# "auth_client_required". (Since 3.0) | ||
94 | +# | ||
95 | # @server: Monitor host address and port. This maps | ||
96 | # to the "mon_host" Ceph option. | ||
97 | # | ||
98 | @@ -XXX,XX +XXX,XX @@ | ||
99 | '*conf': 'str', | ||
100 | '*snapshot': 'str', | ||
101 | '*user': 'str', | ||
102 | + '*auth-client-required': ['RbdAuthMode'], | ||
103 | '*server': ['InetSocketAddressBase'] } } | ||
104 | |||
105 | ## | ||
106 | diff --git a/block/rbd.c b/block/rbd.c | ||
107 | index XXXXXXX..XXXXXXX 100644 | ||
108 | --- a/block/rbd.c | ||
109 | +++ b/block/rbd.c | ||
110 | @@ -XXX,XX +XXX,XX @@ static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) | ||
111 | |||
112 | |||
113 | static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, | ||
114 | + BlockdevOptionsRbd *opts, | ||
115 | Error **errp) | ||
116 | { | ||
117 | - if (secretid == 0) { | ||
118 | - return 0; | ||
119 | - } | ||
120 | + char *acr; | ||
121 | + int r; | ||
122 | + GString *accu; | ||
123 | + RbdAuthModeList *auth; | ||
124 | + | ||
125 | + if (secretid) { | ||
126 | + gchar *secret = qcrypto_secret_lookup_as_base64(secretid, | ||
127 | + errp); | ||
128 | + if (!secret) { | ||
129 | + return -1; | ||
130 | + } | ||
131 | |||
132 | - gchar *secret = qcrypto_secret_lookup_as_base64(secretid, | ||
133 | - errp); | ||
134 | - if (!secret) { | ||
135 | - return -1; | ||
136 | + rados_conf_set(cluster, "key", secret); | ||
137 | + g_free(secret); | ||
138 | } | 18 | } |
139 | 19 | ||
140 | - rados_conf_set(cluster, "key", secret); | 20 | + bdrv_graph_rdlock_main_loop(); |
141 | - g_free(secret); | 21 | QLIST_FOREACH(c, &bs->parents, next_parent) { |
142 | + if (opts->has_auth_client_required) { | 22 | if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) { |
143 | + accu = g_string_new(""); | 23 | + bdrv_graph_rdunlock_main_loop(); |
144 | + for (auth = opts->auth_client_required; auth; auth = auth->next) { | 24 | return false; |
145 | + if (accu->str[0]) { | ||
146 | + g_string_append_c(accu, ';'); | ||
147 | + } | ||
148 | + g_string_append(accu, RbdAuthMode_str(auth->value)); | ||
149 | + } | ||
150 | + acr = g_string_free(accu, FALSE); | ||
151 | + r = rados_conf_set(cluster, "auth_client_required", acr); | ||
152 | + g_free(acr); | ||
153 | + if (r < 0) { | ||
154 | + error_setg_errno(errp, -r, | ||
155 | + "Could not set 'auth_client_required'"); | ||
156 | + return r; | ||
157 | + } | ||
158 | + } | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, | ||
163 | } | 25 | } |
164 | } | 26 | } |
165 | 27 | ||
166 | - if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) { | 28 | QLIST_FOREACH(c, &bs->children, next) { |
167 | + if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) { | 29 | if (!bdrv_child_change_aio_context(c, ctx, visited, tran, errp)) { |
168 | r = -EIO; | 30 | + bdrv_graph_rdunlock_main_loop(); |
169 | goto failed_shutdown; | 31 | return false; |
32 | } | ||
170 | } | 33 | } |
34 | + bdrv_graph_rdunlock_main_loop(); | ||
35 | |||
36 | state = g_new(BdrvStateSetAioContext, 1); | ||
37 | *state = (BdrvStateSetAioContext) { | ||
171 | -- | 38 | -- |
172 | 2.13.6 | 39 | 2.41.0 |
173 | |||
174 | diff view generated by jsdifflib |
1 | The -drive option serial was deprecated in QEMU 2.10. It's time to | 1 | Instead of taking the writer lock internally, require callers to already |
---|---|---|---|
2 | remove it. | 2 | hold it when calling bdrv_root_unref_child(). These callers will |
3 | 3 | typically already hold the graph lock once the locking work is | |
4 | Tests need to be updated to set the serial number with -global instead | 4 | completed, which means that they can't call functions that take it |
5 | of using the -drive option. | 5 | internally. |
6 | 6 | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | 8 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
9 | Reviewed-by: Jeff Cody <jcody@redhat.com> | 9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
10 | Message-ID: <20230911094620.45040-20-kwolf@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | 12 | --- |
11 | include/hw/block/block.h | 1 - | 13 | include/block/block_int-global-state.h | 2 +- |
12 | include/sysemu/blockdev.h | 1 - | 14 | block.c | 6 +++--- |
13 | block/block-backend.c | 1 - | 15 | block/block-backend.c | 3 +++ |
14 | blockdev.c | 10 ---------- | 16 | blockjob.c | 2 ++ |
15 | hw/block/block.c | 13 ------------- | 17 | 4 files changed, 9 insertions(+), 4 deletions(-) |
16 | hw/block/nvme.c | 1 - | ||
17 | hw/block/virtio-blk.c | 1 - | ||
18 | hw/ide/qdev.c | 1 - | ||
19 | hw/scsi/scsi-disk.c | 1 - | ||
20 | hw/usb/dev-storage.c | 1 - | ||
21 | tests/ahci-test.c | 6 +++--- | ||
22 | tests/ide-test.c | 8 ++++---- | ||
23 | qemu-doc.texi | 5 ----- | ||
24 | qemu-options.hx | 6 +----- | ||
25 | 14 files changed, 8 insertions(+), 48 deletions(-) | ||
26 | 18 | ||
27 | diff --git a/include/hw/block/block.h b/include/hw/block/block.h | 19 | diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h |
28 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
29 | --- a/include/hw/block/block.h | 21 | --- a/include/block/block_int-global-state.h |
30 | +++ b/include/hw/block/block.h | 22 | +++ b/include/block/block_int-global-state.h |
31 | @@ -XXX,XX +XXX,XX @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) | 23 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, |
32 | 24 | BdrvChildRole child_role, | |
33 | /* Configuration helpers */ | 25 | uint64_t perm, uint64_t shared_perm, |
34 | 26 | void *opaque, Error **errp); | |
35 | -void blkconf_serial(BlockConf *conf, char **serial); | 27 | -void bdrv_root_unref_child(BdrvChild *child); |
36 | bool blkconf_geometry(BlockConf *conf, int *trans, | 28 | +void GRAPH_WRLOCK bdrv_root_unref_child(BdrvChild *child); |
37 | unsigned cyls_max, unsigned heads_max, unsigned secs_max, | 29 | |
38 | Error **errp); | 30 | void GRAPH_RDLOCK bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, |
39 | diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h | 31 | uint64_t *shared_perm); |
32 | diff --git a/block.c b/block.c | ||
40 | index XXXXXXX..XXXXXXX 100644 | 33 | index XXXXXXX..XXXXXXX 100644 |
41 | --- a/include/sysemu/blockdev.h | 34 | --- a/block.c |
42 | +++ b/include/sysemu/blockdev.h | 35 | +++ b/block.c |
43 | @@ -XXX,XX +XXX,XX @@ struct DriveInfo { | 36 | @@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child) |
44 | bool is_default; /* Added by default_drive() ? */ | 37 | BlockDriverState *child_bs = child->bs; |
45 | int media_cd; | 38 | |
46 | QemuOpts *opts; | 39 | GLOBAL_STATE_CODE(); |
47 | - char *serial; | 40 | - bdrv_graph_wrlock(NULL); |
48 | QTAILQ_ENTRY(DriveInfo) next; | 41 | bdrv_replace_child_noperm(child, NULL); |
49 | }; | 42 | bdrv_child_free(child); |
43 | |||
44 | @@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child) | ||
45 | NULL); | ||
46 | } | ||
47 | |||
48 | - bdrv_graph_wrunlock(); | ||
49 | - bdrv_unref(child_bs); | ||
50 | + bdrv_schedule_unref(child_bs); | ||
51 | } | ||
52 | |||
53 | typedef struct BdrvSetInheritsFrom { | ||
54 | @@ -XXX,XX +XXX,XX @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) | ||
55 | return; | ||
56 | } | ||
57 | |||
58 | + bdrv_graph_wrlock(NULL); | ||
59 | bdrv_unset_inherits_from(parent, child, NULL); | ||
60 | bdrv_root_unref_child(child); | ||
61 | + bdrv_graph_wrunlock(); | ||
62 | } | ||
63 | |||
50 | 64 | ||
51 | diff --git a/block/block-backend.c b/block/block-backend.c | 65 | diff --git a/block/block-backend.c b/block/block-backend.c |
52 | index XXXXXXX..XXXXXXX 100644 | 66 | index XXXXXXX..XXXXXXX 100644 |
53 | --- a/block/block-backend.c | 67 | --- a/block/block-backend.c |
54 | +++ b/block/block-backend.c | 68 | +++ b/block/block-backend.c |
55 | @@ -XXX,XX +XXX,XX @@ static void drive_info_del(DriveInfo *dinfo) | 69 | @@ -XXX,XX +XXX,XX @@ void blk_remove_bs(BlockBackend *blk) |
56 | return; | 70 | blk_drain(blk); |
71 | root = blk->root; | ||
72 | blk->root = NULL; | ||
73 | + | ||
74 | + bdrv_graph_wrlock(NULL); | ||
75 | bdrv_root_unref_child(root); | ||
76 | + bdrv_graph_wrunlock(); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | diff --git a/blockjob.c b/blockjob.c | ||
81 | index XXXXXXX..XXXXXXX 100644 | ||
82 | --- a/blockjob.c | ||
83 | +++ b/blockjob.c | ||
84 | @@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job) | ||
85 | * one to make sure that such a concurrent access does not attempt | ||
86 | * to process an already freed BdrvChild. | ||
87 | */ | ||
88 | + bdrv_graph_wrlock(NULL); | ||
89 | while (job->nodes) { | ||
90 | GSList *l = job->nodes; | ||
91 | BdrvChild *c = l->data; | ||
92 | @@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job) | ||
93 | |||
94 | g_slist_free_1(l); | ||
57 | } | 95 | } |
58 | qemu_opts_del(dinfo->opts); | 96 | + bdrv_graph_wrunlock(); |
59 | - g_free(dinfo->serial); | ||
60 | g_free(dinfo); | ||
61 | } | 97 | } |
62 | 98 | ||
63 | diff --git a/blockdev.c b/blockdev.c | 99 | bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) |
64 | index XXXXXXX..XXXXXXX 100644 | ||
65 | --- a/blockdev.c | ||
66 | +++ b/blockdev.c | ||
67 | @@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = { | ||
68 | .type = QEMU_OPT_STRING, | ||
69 | .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", | ||
70 | },{ | ||
71 | - .name = "serial", | ||
72 | - .type = QEMU_OPT_STRING, | ||
73 | - .help = "disk serial number", | ||
74 | - },{ | ||
75 | .name = "file", | ||
76 | .type = QEMU_OPT_STRING, | ||
77 | .help = "file name", | ||
78 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
79 | const char *werror, *rerror; | ||
80 | bool read_only = false; | ||
81 | bool copy_on_read; | ||
82 | - const char *serial; | ||
83 | const char *filename; | ||
84 | Error *local_err = NULL; | ||
85 | int i; | ||
86 | const char *deprecated[] = { | ||
87 | - "serial" | ||
88 | }; | ||
89 | |||
90 | /* Change legacy command line options into QMP ones */ | ||
91 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
92 | goto fail; | ||
93 | } | ||
94 | |||
95 | - /* Serial number */ | ||
96 | - serial = qemu_opt_get(legacy_opts, "serial"); | ||
97 | - | ||
98 | /* no id supplied -> create one */ | ||
99 | if (qemu_opts_id(all_opts) == NULL) { | ||
100 | char *new_id; | ||
101 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
102 | dinfo->type = type; | ||
103 | dinfo->bus = bus_id; | ||
104 | dinfo->unit = unit_id; | ||
105 | - dinfo->serial = g_strdup(serial); | ||
106 | |||
107 | blk_set_legacy_dinfo(blk, dinfo); | ||
108 | |||
109 | diff --git a/hw/block/block.c b/hw/block/block.c | ||
110 | index XXXXXXX..XXXXXXX 100644 | ||
111 | --- a/hw/block/block.c | ||
112 | +++ b/hw/block/block.c | ||
113 | @@ -XXX,XX +XXX,XX @@ | ||
114 | #include "qapi/qapi-types-block.h" | ||
115 | #include "qemu/error-report.h" | ||
116 | |||
117 | -void blkconf_serial(BlockConf *conf, char **serial) | ||
118 | -{ | ||
119 | - DriveInfo *dinfo; | ||
120 | - | ||
121 | - if (!*serial) { | ||
122 | - /* try to fall back to value set with legacy -drive serial=... */ | ||
123 | - dinfo = blk_legacy_dinfo(conf->blk); | ||
124 | - if (dinfo) { | ||
125 | - *serial = g_strdup(dinfo->serial); | ||
126 | - } | ||
127 | - } | ||
128 | -} | ||
129 | - | ||
130 | void blkconf_blocksizes(BlockConf *conf) | ||
131 | { | ||
132 | BlockBackend *blk = conf->blk; | ||
133 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | ||
134 | index XXXXXXX..XXXXXXX 100644 | ||
135 | --- a/hw/block/nvme.c | ||
136 | +++ b/hw/block/nvme.c | ||
137 | @@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | - blkconf_serial(&n->conf, &n->serial); | ||
142 | if (!n->serial) { | ||
143 | error_setg(errp, "serial property not set"); | ||
144 | return; | ||
145 | diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c | ||
146 | index XXXXXXX..XXXXXXX 100644 | ||
147 | --- a/hw/block/virtio-blk.c | ||
148 | +++ b/hw/block/virtio-blk.c | ||
149 | @@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | - blkconf_serial(&conf->conf, &conf->serial); | ||
154 | if (!blkconf_apply_backend_options(&conf->conf, | ||
155 | blk_is_read_only(conf->conf.blk), true, | ||
156 | errp)) { | ||
157 | diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c | ||
158 | index XXXXXXX..XXXXXXX 100644 | ||
159 | --- a/hw/ide/qdev.c | ||
160 | +++ b/hw/ide/qdev.c | ||
161 | @@ -XXX,XX +XXX,XX @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) | ||
162 | return; | ||
163 | } | ||
164 | |||
165 | - blkconf_serial(&dev->conf, &dev->serial); | ||
166 | if (kind != IDE_CD) { | ||
167 | if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, | ||
168 | errp)) { | ||
169 | diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c | ||
170 | index XXXXXXX..XXXXXXX 100644 | ||
171 | --- a/hw/scsi/scsi-disk.c | ||
172 | +++ b/hw/scsi/scsi-disk.c | ||
173 | @@ -XXX,XX +XXX,XX @@ static void scsi_realize(SCSIDevice *dev, Error **errp) | ||
174 | return; | ||
175 | } | ||
176 | |||
177 | - blkconf_serial(&s->qdev.conf, &s->serial); | ||
178 | blkconf_blocksizes(&s->qdev.conf); | ||
179 | |||
180 | if (s->qdev.conf.logical_block_size > | ||
181 | diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c | ||
182 | index XXXXXXX..XXXXXXX 100644 | ||
183 | --- a/hw/usb/dev-storage.c | ||
184 | +++ b/hw/usb/dev-storage.c | ||
185 | @@ -XXX,XX +XXX,XX @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) | ||
186 | return; | ||
187 | } | ||
188 | |||
189 | - blkconf_serial(&s->conf, &dev->serial); | ||
190 | blkconf_blocksizes(&s->conf); | ||
191 | if (!blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true, | ||
192 | errp)) { | ||
193 | diff --git a/tests/ahci-test.c b/tests/ahci-test.c | ||
194 | index XXXXXXX..XXXXXXX 100644 | ||
195 | --- a/tests/ahci-test.c | ||
196 | +++ b/tests/ahci-test.c | ||
197 | @@ -XXX,XX +XXX,XX @@ static AHCIQState *ahci_boot(const char *cli, ...) | ||
198 | s = ahci_vboot(cli, ap); | ||
199 | va_end(ap); | ||
200 | } else { | ||
201 | - cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" | ||
202 | - ",format=%s" | ||
203 | + cli = "-drive if=none,id=drive0,file=%s,cache=writeback,format=%s" | ||
204 | " -M q35 " | ||
205 | "-device ide-hd,drive=drive0 " | ||
206 | + "-global ide-hd.serial=%s " | ||
207 | "-global ide-hd.ver=%s"; | ||
208 | - s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version"); | ||
209 | + s = ahci_boot(cli, tmp_path, imgfmt, "testdisk", "version"); | ||
210 | } | ||
211 | |||
212 | return s; | ||
213 | diff --git a/tests/ide-test.c b/tests/ide-test.c | ||
214 | index XXXXXXX..XXXXXXX 100644 | ||
215 | --- a/tests/ide-test.c | ||
216 | +++ b/tests/ide-test.c | ||
217 | @@ -XXX,XX +XXX,XX @@ static void test_bmdma_no_busmaster(void) | ||
218 | static void test_bmdma_setup(void) | ||
219 | { | ||
220 | ide_test_start( | ||
221 | - "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " | ||
222 | - "-global ide-hd.ver=%s", | ||
223 | + "-drive file=%s,if=ide,cache=writeback,format=raw " | ||
224 | + "-global ide-hd.serial=%s -global ide-hd.ver=%s", | ||
225 | tmp_path, "testdisk", "version"); | ||
226 | qtest_irq_intercept_in(global_qtest, "ioapic"); | ||
227 | } | ||
228 | @@ -XXX,XX +XXX,XX @@ static void test_identify(void) | ||
229 | int ret; | ||
230 | |||
231 | ide_test_start( | ||
232 | - "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " | ||
233 | - "-global ide-hd.ver=%s", | ||
234 | + "-drive file=%s,if=ide,cache=writeback,format=raw " | ||
235 | + "-global ide-hd.serial=%s -global ide-hd.ver=%s", | ||
236 | tmp_path, "testdisk", "version"); | ||
237 | |||
238 | dev = get_pci_device(&bmdma_bar, &ide_bar); | ||
239 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
240 | index XXXXXXX..XXXXXXX 100644 | ||
241 | --- a/qemu-doc.texi | ||
242 | +++ b/qemu-doc.texi | ||
243 | @@ -XXX,XX +XXX,XX @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' | ||
244 | (for embedded NICs). The new syntax allows different settings to be | ||
245 | provided per NIC. | ||
246 | |||
247 | -@subsection -drive serial=... (since 2.10.0) | ||
248 | - | ||
249 | -The drive serial argument is replaced by the the serial argument | ||
250 | -that can be specified with the ``-device'' parameter. | ||
251 | - | ||
252 | @subsection -usbdevice (since 2.10.0) | ||
253 | |||
254 | The ``-usbdevice DEV'' argument is now a synonym for setting | ||
255 | diff --git a/qemu-options.hx b/qemu-options.hx | ||
256 | index XXXXXXX..XXXXXXX 100644 | ||
257 | --- a/qemu-options.hx | ||
258 | +++ b/qemu-options.hx | ||
259 | @@ -XXX,XX +XXX,XX @@ ETEXI | ||
260 | DEF("drive", HAS_ARG, QEMU_OPTION_drive, | ||
261 | "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" | ||
262 | " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" | ||
263 | - " [,snapshot=on|off][,serial=s][,rerror=ignore|stop|report]\n" | ||
264 | + " [,snapshot=on|off][,rerror=ignore|stop|report]\n" | ||
265 | " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" | ||
266 | " [,readonly=on|off][,copy-on-read=on|off]\n" | ||
267 | " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" | ||
268 | @@ -XXX,XX +XXX,XX @@ The default mode is @option{cache=writeback}. | ||
269 | Specify which disk @var{format} will be used rather than detecting | ||
270 | the format. Can be used to specify format=raw to avoid interpreting | ||
271 | an untrusted format header. | ||
272 | -@item serial=@var{serial} | ||
273 | -This option specifies the serial number to assign to the device. This | ||
274 | -parameter is deprecated, use the corresponding parameter of @code{-device} | ||
275 | -instead. | ||
276 | @item werror=@var{action},rerror=@var{action} | ||
277 | Specify which @var{action} to take on write and read errors. Valid actions are: | ||
278 | "ignore" (ignore the error and try to continue), "stop" (pause QEMU), | ||
279 | -- | 100 | -- |
280 | 2.13.6 | 101 | 2.41.0 |
281 | |||
282 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | Instead of taking the writer lock internally, require callers to already |
---|---|---|---|
2 | hold it when calling bdrv_unref_child(). These callers will typically | ||
3 | already hold the graph lock once the locking work is completed, which | ||
4 | means that they can't call functions that take it internally. | ||
2 | 5 | ||
3 | There are numerous QDict functions that have been introduced for and are | 6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | used only by the block layer. Move their declarations into an own | 7 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
5 | header file to reflect that. | 8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
6 | 9 | Message-ID: <20230911094620.45040-21-kwolf@redhat.com> | |
7 | While qdict_extract_subqdict() is in fact used outside of the block | ||
8 | layer (in util/qemu-config.c), it is still a function related very | ||
9 | closely to how the block layer works with nested QDicts, namely by | ||
10 | sometimes flattening them. Therefore, its declaration is put into this | ||
11 | header as well and util/qemu-config.c includes it with a comment stating | ||
12 | exactly which function it needs. | ||
13 | |||
14 | Suggested-by: Markus Armbruster <armbru@redhat.com> | ||
15 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
16 | Message-Id: <20180509165530.29561-7-mreitz@redhat.com> | ||
17 | [Copyright note tweaked, superfluous includes dropped] | ||
18 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | ||
19 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
21 | --- | 11 | --- |
22 | include/block/qdict.h | 32 ++++++++++++++++++++++++++++++++ | 12 | include/block/block-global-state.h | 7 ++++++- |
23 | include/qapi/qmp/qdict.h | 17 ----------------- | 13 | block.c | 11 +++++++---- |
24 | block.c | 1 + | 14 | block/blklogwrites.c | 4 ++++ |
25 | block/gluster.c | 1 + | 15 | block/blkverify.c | 2 ++ |
26 | block/iscsi.c | 1 + | 16 | block/qcow2.c | 4 +++- |
27 | block/nbd.c | 1 + | 17 | block/quorum.c | 6 ++++++ |
28 | block/nfs.c | 1 + | 18 | block/replication.c | 3 +++ |
29 | block/parallels.c | 1 + | 19 | block/snapshot.c | 2 ++ |
30 | block/qcow.c | 1 + | 20 | block/vmdk.c | 11 +++++++++++ |
31 | block/qcow2.c | 1 + | 21 | tests/unit/test-bdrv-drain.c | 8 ++++++-- |
32 | block/qed.c | 1 + | 22 | 10 files changed, 50 insertions(+), 8 deletions(-) |
33 | block/quorum.c | 1 + | ||
34 | block/rbd.c | 1 + | ||
35 | block/sheepdog.c | 1 + | ||
36 | block/snapshot.c | 1 + | ||
37 | block/ssh.c | 1 + | ||
38 | block/vhdx.c | 1 + | ||
39 | block/vpc.c | 1 + | ||
40 | block/vvfat.c | 1 + | ||
41 | block/vxhs.c | 1 + | ||
42 | blockdev.c | 1 + | ||
43 | qobject/qdict.c | 1 + | ||
44 | tests/check-qdict.c | 1 + | ||
45 | tests/check-qobject.c | 1 + | ||
46 | tests/test-replication.c | 1 + | ||
47 | util/qemu-config.c | 1 + | ||
48 | 26 files changed, 56 insertions(+), 17 deletions(-) | ||
49 | create mode 100644 include/block/qdict.h | ||
50 | 23 | ||
51 | diff --git a/include/block/qdict.h b/include/block/qdict.h | 24 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h |
52 | new file mode 100644 | 25 | index XXXXXXX..XXXXXXX 100644 |
53 | index XXXXXXX..XXXXXXX | 26 | --- a/include/block/block-global-state.h |
54 | --- /dev/null | 27 | +++ b/include/block/block-global-state.h |
55 | +++ b/include/block/qdict.h | 28 | @@ -XXX,XX +XXX,XX @@ void bdrv_ref(BlockDriverState *bs); |
56 | @@ -XXX,XX +XXX,XX @@ | 29 | void no_coroutine_fn bdrv_unref(BlockDriverState *bs); |
57 | +/* | 30 | void coroutine_fn no_co_wrapper bdrv_co_unref(BlockDriverState *bs); |
58 | + * Special QDict functions used by the block layer | 31 | void GRAPH_WRLOCK bdrv_schedule_unref(BlockDriverState *bs); |
59 | + * | 32 | -void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); |
60 | + * Copyright (c) 2013-2018 Red Hat, Inc. | 33 | + |
61 | + * | 34 | +void GRAPH_WRLOCK |
62 | + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | 35 | +bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); |
63 | + * See the COPYING.LIB file in the top-level directory. | 36 | + |
64 | + */ | 37 | +void coroutine_fn no_co_wrapper_bdrv_wrlock |
65 | + | 38 | +bdrv_co_unref_child(BlockDriverState *parent, BdrvChild *child); |
66 | +#ifndef BLOCK_QDICT_H | 39 | |
67 | +#define BLOCK_QDICT_H | 40 | BdrvChild * GRAPH_WRLOCK |
68 | + | 41 | bdrv_attach_child(BlockDriverState *parent_bs, |
69 | +#include "qapi/qmp/qdict.h" | ||
70 | + | ||
71 | +void qdict_copy_default(QDict *dst, QDict *src, const char *key); | ||
72 | +void qdict_set_default_str(QDict *dst, const char *key, const char *val); | ||
73 | + | ||
74 | +void qdict_join(QDict *dest, QDict *src, bool overwrite); | ||
75 | + | ||
76 | +void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); | ||
77 | +void qdict_array_split(QDict *src, QList **dst); | ||
78 | +int qdict_array_entries(QDict *src, const char *subqdict); | ||
79 | +QObject *qdict_crumple(const QDict *src, Error **errp); | ||
80 | +void qdict_flatten(QDict *qdict); | ||
81 | + | ||
82 | +typedef struct QDictRenames { | ||
83 | + const char *from; | ||
84 | + const char *to; | ||
85 | +} QDictRenames; | ||
86 | +bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); | ||
87 | + | ||
88 | +#endif | ||
89 | diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h | ||
90 | index XXXXXXX..XXXXXXX 100644 | ||
91 | --- a/include/qapi/qmp/qdict.h | ||
92 | +++ b/include/qapi/qmp/qdict.h | ||
93 | @@ -XXX,XX +XXX,XX @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key, | ||
94 | bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value); | ||
95 | const char *qdict_get_try_str(const QDict *qdict, const char *key); | ||
96 | |||
97 | -void qdict_copy_default(QDict *dst, QDict *src, const char *key); | ||
98 | -void qdict_set_default_str(QDict *dst, const char *key, const char *val); | ||
99 | - | ||
100 | QDict *qdict_clone_shallow(const QDict *src); | ||
101 | -void qdict_flatten(QDict *qdict); | ||
102 | - | ||
103 | -void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); | ||
104 | -void qdict_array_split(QDict *src, QList **dst); | ||
105 | -int qdict_array_entries(QDict *src, const char *subqdict); | ||
106 | -QObject *qdict_crumple(const QDict *src, Error **errp); | ||
107 | - | ||
108 | -void qdict_join(QDict *dest, QDict *src, bool overwrite); | ||
109 | - | ||
110 | -typedef struct QDictRenames { | ||
111 | - const char *from; | ||
112 | - const char *to; | ||
113 | -} QDictRenames; | ||
114 | -bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); | ||
115 | |||
116 | #endif /* QDICT_H */ | ||
117 | diff --git a/block.c b/block.c | 42 | diff --git a/block.c b/block.c |
118 | index XXXXXXX..XXXXXXX 100644 | 43 | index XXXXXXX..XXXXXXX 100644 |
119 | --- a/block.c | 44 | --- a/block.c |
120 | +++ b/block.c | 45 | +++ b/block.c |
121 | @@ -XXX,XX +XXX,XX @@ | 46 | @@ -XXX,XX +XXX,XX @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, |
122 | #include "block/block_int.h" | 47 | open_failed: |
123 | #include "block/blockjob.h" | 48 | bs->drv = NULL; |
124 | #include "block/nbd.h" | 49 | if (bs->file != NULL) { |
125 | +#include "block/qdict.h" | 50 | + bdrv_graph_wrlock(NULL); |
126 | #include "qemu/error-report.h" | 51 | bdrv_unref_child(bs, bs->file); |
127 | #include "module_block.h" | 52 | + bdrv_graph_wrunlock(); |
128 | #include "qemu/module.h" | 53 | assert(!bs->file); |
129 | diff --git a/block/gluster.c b/block/gluster.c | 54 | } |
130 | index XXXXXXX..XXXXXXX 100644 | 55 | g_free(bs->opaque); |
131 | --- a/block/gluster.c | 56 | @@ -XXX,XX +XXX,XX @@ static void bdrv_set_inherits_from(BlockDriverState *bs, |
132 | +++ b/block/gluster.c | 57 | * @root that point to @root, where necessary. |
133 | @@ -XXX,XX +XXX,XX @@ | 58 | * @tran is allowed to be NULL. In this case no rollback is possible |
134 | #include "qemu/osdep.h" | 59 | */ |
135 | #include <glusterfs/api/glfs.h> | 60 | -static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, |
136 | #include "block/block_int.h" | 61 | - Transaction *tran) |
137 | +#include "block/qdict.h" | 62 | +static void GRAPH_WRLOCK |
138 | #include "qapi/error.h" | 63 | +bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, |
139 | #include "qapi/qmp/qdict.h" | 64 | + Transaction *tran) |
140 | #include "qapi/qmp/qerror.h" | 65 | { |
141 | diff --git a/block/iscsi.c b/block/iscsi.c | 66 | BdrvChild *c; |
142 | index XXXXXXX..XXXXXXX 100644 | 67 | |
143 | --- a/block/iscsi.c | 68 | @@ -XXX,XX +XXX,XX @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) |
144 | +++ b/block/iscsi.c | 69 | return; |
145 | @@ -XXX,XX +XXX,XX @@ | 70 | } |
146 | #include "qemu/bitops.h" | 71 | |
147 | #include "qemu/bitmap.h" | 72 | - bdrv_graph_wrlock(NULL); |
148 | #include "block/block_int.h" | 73 | bdrv_unset_inherits_from(parent, child, NULL); |
149 | +#include "block/qdict.h" | 74 | bdrv_root_unref_child(child); |
150 | #include "scsi/constants.h" | 75 | - bdrv_graph_wrunlock(); |
151 | #include "qemu/iov.h" | 76 | } |
152 | #include "qemu/option.h" | 77 | |
153 | diff --git a/block/nbd.c b/block/nbd.c | 78 | |
154 | index XXXXXXX..XXXXXXX 100644 | 79 | @@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs) |
155 | --- a/block/nbd.c | 80 | bs->drv = NULL; |
156 | +++ b/block/nbd.c | 81 | } |
157 | @@ -XXX,XX +XXX,XX @@ | 82 | |
158 | 83 | + bdrv_graph_wrlock(NULL); | |
159 | #include "qemu/osdep.h" | 84 | QLIST_FOREACH_SAFE(child, &bs->children, next, next) { |
160 | #include "nbd-client.h" | 85 | bdrv_unref_child(bs, child); |
161 | +#include "block/qdict.h" | 86 | } |
162 | #include "qapi/error.h" | 87 | + bdrv_graph_wrunlock(); |
163 | #include "qemu/uri.h" | 88 | |
164 | #include "block/block_int.h" | 89 | assert(!bs->backing); |
165 | diff --git a/block/nfs.c b/block/nfs.c | 90 | assert(!bs->file); |
166 | index XXXXXXX..XXXXXXX 100644 | 91 | diff --git a/block/blklogwrites.c b/block/blklogwrites.c |
167 | --- a/block/nfs.c | 92 | index XXXXXXX..XXXXXXX 100644 |
168 | +++ b/block/nfs.c | 93 | --- a/block/blklogwrites.c |
169 | @@ -XXX,XX +XXX,XX @@ | 94 | +++ b/block/blklogwrites.c |
170 | #include "qemu/error-report.h" | 95 | @@ -XXX,XX +XXX,XX @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, |
171 | #include "qapi/error.h" | 96 | ret = 0; |
172 | #include "block/block_int.h" | 97 | fail_log: |
173 | +#include "block/qdict.h" | 98 | if (ret < 0) { |
174 | #include "trace.h" | 99 | + bdrv_graph_wrlock(NULL); |
175 | #include "qemu/iov.h" | 100 | bdrv_unref_child(bs, s->log_file); |
176 | #include "qemu/option.h" | 101 | + bdrv_graph_wrunlock(); |
177 | diff --git a/block/parallels.c b/block/parallels.c | 102 | s->log_file = NULL; |
178 | index XXXXXXX..XXXXXXX 100644 | 103 | } |
179 | --- a/block/parallels.c | 104 | fail: |
180 | +++ b/block/parallels.c | 105 | @@ -XXX,XX +XXX,XX @@ static void blk_log_writes_close(BlockDriverState *bs) |
181 | @@ -XXX,XX +XXX,XX @@ | 106 | { |
182 | #include "qemu/osdep.h" | 107 | BDRVBlkLogWritesState *s = bs->opaque; |
183 | #include "qapi/error.h" | 108 | |
184 | #include "block/block_int.h" | 109 | + bdrv_graph_wrlock(NULL); |
185 | +#include "block/qdict.h" | 110 | bdrv_unref_child(bs, s->log_file); |
186 | #include "sysemu/block-backend.h" | 111 | s->log_file = NULL; |
187 | #include "qemu/module.h" | 112 | + bdrv_graph_wrunlock(); |
188 | #include "qemu/option.h" | 113 | } |
189 | diff --git a/block/qcow.c b/block/qcow.c | 114 | |
190 | index XXXXXXX..XXXXXXX 100644 | 115 | static int64_t coroutine_fn GRAPH_RDLOCK |
191 | --- a/block/qcow.c | 116 | diff --git a/block/blkverify.c b/block/blkverify.c |
192 | +++ b/block/qcow.c | 117 | index XXXXXXX..XXXXXXX 100644 |
193 | @@ -XXX,XX +XXX,XX @@ | 118 | --- a/block/blkverify.c |
194 | #include "qapi/error.h" | 119 | +++ b/block/blkverify.c |
195 | #include "qemu/error-report.h" | 120 | @@ -XXX,XX +XXX,XX @@ static void blkverify_close(BlockDriverState *bs) |
196 | #include "block/block_int.h" | 121 | { |
197 | +#include "block/qdict.h" | 122 | BDRVBlkverifyState *s = bs->opaque; |
198 | #include "sysemu/block-backend.h" | 123 | |
199 | #include "qemu/module.h" | 124 | + bdrv_graph_wrlock(NULL); |
200 | #include "qemu/option.h" | 125 | bdrv_unref_child(bs, s->test_file); |
126 | s->test_file = NULL; | ||
127 | + bdrv_graph_wrunlock(); | ||
128 | } | ||
129 | |||
130 | static int64_t coroutine_fn GRAPH_RDLOCK | ||
201 | diff --git a/block/qcow2.c b/block/qcow2.c | 131 | diff --git a/block/qcow2.c b/block/qcow2.c |
202 | index XXXXXXX..XXXXXXX 100644 | 132 | index XXXXXXX..XXXXXXX 100644 |
203 | --- a/block/qcow2.c | 133 | --- a/block/qcow2.c |
204 | +++ b/block/qcow2.c | 134 | +++ b/block/qcow2.c |
205 | @@ -XXX,XX +XXX,XX @@ | 135 | @@ -XXX,XX +XXX,XX @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, |
206 | 136 | g_free(s->image_data_file); | |
207 | #include "qemu/osdep.h" | 137 | if (open_data_file && has_data_file(bs)) { |
208 | #include "block/block_int.h" | 138 | bdrv_graph_co_rdunlock(); |
209 | +#include "block/qdict.h" | 139 | - bdrv_unref_child(bs, s->data_file); |
210 | #include "sysemu/block-backend.h" | 140 | + bdrv_co_unref_child(bs, s->data_file); |
211 | #include "qemu/module.h" | 141 | bdrv_graph_co_rdlock(); |
212 | #include <zlib.h> | 142 | s->data_file = NULL; |
213 | diff --git a/block/qed.c b/block/qed.c | 143 | } |
214 | index XXXXXXX..XXXXXXX 100644 | 144 | @@ -XXX,XX +XXX,XX @@ static void qcow2_do_close(BlockDriverState *bs, bool close_data_file) |
215 | --- a/block/qed.c | 145 | g_free(s->image_backing_format); |
216 | +++ b/block/qed.c | 146 | |
217 | @@ -XXX,XX +XXX,XX @@ | 147 | if (close_data_file && has_data_file(bs)) { |
218 | */ | 148 | + bdrv_graph_wrlock(NULL); |
219 | 149 | bdrv_unref_child(bs, s->data_file); | |
220 | #include "qemu/osdep.h" | 150 | + bdrv_graph_wrunlock(); |
221 | +#include "block/qdict.h" | 151 | s->data_file = NULL; |
222 | #include "qapi/error.h" | 152 | } |
223 | #include "qemu/timer.h" | 153 | |
224 | #include "qemu/bswap.h" | ||
225 | diff --git a/block/quorum.c b/block/quorum.c | 154 | diff --git a/block/quorum.c b/block/quorum.c |
226 | index XXXXXXX..XXXXXXX 100644 | 155 | index XXXXXXX..XXXXXXX 100644 |
227 | --- a/block/quorum.c | 156 | --- a/block/quorum.c |
228 | +++ b/block/quorum.c | 157 | +++ b/block/quorum.c |
229 | @@ -XXX,XX +XXX,XX @@ | 158 | @@ -XXX,XX +XXX,XX @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, |
230 | #include "qemu/cutils.h" | 159 | |
231 | #include "qemu/option.h" | 160 | close_exit: |
232 | #include "block/block_int.h" | 161 | /* cleanup on error */ |
233 | +#include "block/qdict.h" | 162 | + bdrv_graph_wrlock(NULL); |
234 | #include "qapi/error.h" | 163 | for (i = 0; i < s->num_children; i++) { |
235 | #include "qapi/qapi-events-block.h" | 164 | if (!opened[i]) { |
236 | #include "qapi/qmp/qdict.h" | 165 | continue; |
237 | diff --git a/block/rbd.c b/block/rbd.c | 166 | } |
238 | index XXXXXXX..XXXXXXX 100644 | 167 | bdrv_unref_child(bs, s->children[i]); |
239 | --- a/block/rbd.c | 168 | } |
240 | +++ b/block/rbd.c | 169 | + bdrv_graph_wrunlock(); |
241 | @@ -XXX,XX +XXX,XX @@ | 170 | g_free(s->children); |
242 | #include "qemu/error-report.h" | 171 | g_free(opened); |
243 | #include "qemu/option.h" | 172 | exit: |
244 | #include "block/block_int.h" | 173 | @@ -XXX,XX +XXX,XX @@ static void quorum_close(BlockDriverState *bs) |
245 | +#include "block/qdict.h" | 174 | BDRVQuorumState *s = bs->opaque; |
246 | #include "crypto/secret.h" | 175 | int i; |
247 | #include "qemu/cutils.h" | 176 | |
248 | #include "qapi/qmp/qstring.h" | 177 | + bdrv_graph_wrlock(NULL); |
249 | diff --git a/block/sheepdog.c b/block/sheepdog.c | 178 | for (i = 0; i < s->num_children; i++) { |
250 | index XXXXXXX..XXXXXXX 100644 | 179 | bdrv_unref_child(bs, s->children[i]); |
251 | --- a/block/sheepdog.c | 180 | } |
252 | +++ b/block/sheepdog.c | 181 | + bdrv_graph_wrunlock(); |
253 | @@ -XXX,XX +XXX,XX @@ | 182 | |
254 | #include "qemu/option.h" | 183 | g_free(s->children); |
255 | #include "qemu/sockets.h" | 184 | } |
256 | #include "block/block_int.h" | 185 | @@ -XXX,XX +XXX,XX @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, |
257 | +#include "block/qdict.h" | 186 | memmove(&s->children[i], &s->children[i + 1], |
258 | #include "sysemu/block-backend.h" | 187 | (s->num_children - i - 1) * sizeof(BdrvChild *)); |
259 | #include "qemu/bitops.h" | 188 | s->children = g_renew(BdrvChild *, s->children, --s->num_children); |
260 | #include "qemu/cutils.h" | 189 | + bdrv_graph_wrlock(NULL); |
190 | bdrv_unref_child(bs, child); | ||
191 | + bdrv_graph_wrunlock(); | ||
192 | |||
193 | quorum_refresh_flags(bs); | ||
194 | bdrv_drained_end(bs); | ||
195 | diff --git a/block/replication.c b/block/replication.c | ||
196 | index XXXXXXX..XXXXXXX 100644 | ||
197 | --- a/block/replication.c | ||
198 | +++ b/block/replication.c | ||
199 | @@ -XXX,XX +XXX,XX @@ static void replication_done(void *opaque, int ret) | ||
200 | if (ret == 0) { | ||
201 | s->stage = BLOCK_REPLICATION_DONE; | ||
202 | |||
203 | + bdrv_graph_wrlock(NULL); | ||
204 | bdrv_unref_child(bs, s->secondary_disk); | ||
205 | s->secondary_disk = NULL; | ||
206 | bdrv_unref_child(bs, s->hidden_disk); | ||
207 | s->hidden_disk = NULL; | ||
208 | + bdrv_graph_wrunlock(); | ||
209 | + | ||
210 | s->error = 0; | ||
211 | } else { | ||
212 | s->stage = BLOCK_REPLICATION_FAILOVER_FAILED; | ||
261 | diff --git a/block/snapshot.c b/block/snapshot.c | 213 | diff --git a/block/snapshot.c b/block/snapshot.c |
262 | index XXXXXXX..XXXXXXX 100644 | 214 | index XXXXXXX..XXXXXXX 100644 |
263 | --- a/block/snapshot.c | 215 | --- a/block/snapshot.c |
264 | +++ b/block/snapshot.c | 216 | +++ b/block/snapshot.c |
265 | @@ -XXX,XX +XXX,XX @@ | 217 | @@ -XXX,XX +XXX,XX @@ int bdrv_snapshot_goto(BlockDriverState *bs, |
266 | #include "qemu/osdep.h" | 218 | } |
267 | #include "block/snapshot.h" | 219 | |
268 | #include "block/block_int.h" | 220 | /* .bdrv_open() will re-attach it */ |
269 | +#include "block/qdict.h" | 221 | + bdrv_graph_wrlock(NULL); |
270 | #include "qapi/error.h" | 222 | bdrv_unref_child(bs, fallback); |
271 | #include "qapi/qmp/qdict.h" | 223 | + bdrv_graph_wrunlock(); |
272 | #include "qapi/qmp/qerror.h" | 224 | |
273 | diff --git a/block/ssh.c b/block/ssh.c | 225 | ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); |
274 | index XXXXXXX..XXXXXXX 100644 | 226 | open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); |
275 | --- a/block/ssh.c | 227 | diff --git a/block/vmdk.c b/block/vmdk.c |
276 | +++ b/block/ssh.c | 228 | index XXXXXXX..XXXXXXX 100644 |
277 | @@ -XXX,XX +XXX,XX @@ | 229 | --- a/block/vmdk.c |
278 | #include <libssh2_sftp.h> | 230 | +++ b/block/vmdk.c |
279 | 231 | @@ -XXX,XX +XXX,XX @@ static void vmdk_free_extents(BlockDriverState *bs) | |
280 | #include "block/block_int.h" | 232 | BDRVVmdkState *s = bs->opaque; |
281 | +#include "block/qdict.h" | 233 | VmdkExtent *e; |
282 | #include "qapi/error.h" | 234 | |
283 | #include "qemu/error-report.h" | 235 | + bdrv_graph_wrlock(NULL); |
284 | #include "qemu/option.h" | 236 | for (i = 0; i < s->num_extents; i++) { |
285 | diff --git a/block/vhdx.c b/block/vhdx.c | 237 | e = &s->extents[i]; |
286 | index XXXXXXX..XXXXXXX 100644 | 238 | g_free(e->l1_table); |
287 | --- a/block/vhdx.c | 239 | @@ -XXX,XX +XXX,XX @@ static void vmdk_free_extents(BlockDriverState *bs) |
288 | +++ b/block/vhdx.c | 240 | bdrv_unref_child(bs, e->file); |
289 | @@ -XXX,XX +XXX,XX @@ | 241 | } |
290 | #include "qemu/osdep.h" | 242 | } |
291 | #include "qapi/error.h" | 243 | + bdrv_graph_wrunlock(); |
292 | #include "block/block_int.h" | 244 | + |
293 | +#include "block/qdict.h" | 245 | g_free(s->extents); |
294 | #include "sysemu/block-backend.h" | 246 | } |
295 | #include "qemu/module.h" | 247 | |
296 | #include "qemu/option.h" | 248 | @@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, |
297 | diff --git a/block/vpc.c b/block/vpc.c | 249 | ret = vmdk_add_extent(bs, extent_file, true, sectors, |
298 | index XXXXXXX..XXXXXXX 100644 | 250 | 0, 0, 0, 0, 0, &extent, errp); |
299 | --- a/block/vpc.c | 251 | if (ret < 0) { |
300 | +++ b/block/vpc.c | 252 | + bdrv_graph_wrlock(NULL); |
301 | @@ -XXX,XX +XXX,XX @@ | 253 | bdrv_unref_child(bs, extent_file); |
302 | #include "qemu/osdep.h" | 254 | + bdrv_graph_wrunlock(); |
303 | #include "qapi/error.h" | 255 | goto out; |
304 | #include "block/block_int.h" | 256 | } |
305 | +#include "block/qdict.h" | 257 | extent->flat_start_offset = flat_offset << 9; |
306 | #include "sysemu/block-backend.h" | 258 | @@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, |
307 | #include "qemu/module.h" | 259 | } |
308 | #include "qemu/option.h" | 260 | g_free(buf); |
309 | diff --git a/block/vvfat.c b/block/vvfat.c | 261 | if (ret) { |
310 | index XXXXXXX..XXXXXXX 100644 | 262 | + bdrv_graph_wrlock(NULL); |
311 | --- a/block/vvfat.c | 263 | bdrv_unref_child(bs, extent_file); |
312 | +++ b/block/vvfat.c | 264 | + bdrv_graph_wrunlock(); |
313 | @@ -XXX,XX +XXX,XX @@ | 265 | goto out; |
314 | #include <dirent.h> | 266 | } |
315 | #include "qapi/error.h" | 267 | extent = &s->extents[s->num_extents - 1]; |
316 | #include "block/block_int.h" | 268 | } else if (!strcmp(type, "SESPARSE")) { |
317 | +#include "block/qdict.h" | 269 | ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp); |
318 | #include "qemu/module.h" | 270 | if (ret) { |
319 | #include "qemu/option.h" | 271 | + bdrv_graph_wrlock(NULL); |
320 | #include "qemu/bswap.h" | 272 | bdrv_unref_child(bs, extent_file); |
321 | diff --git a/block/vxhs.c b/block/vxhs.c | 273 | + bdrv_graph_wrunlock(); |
322 | index XXXXXXX..XXXXXXX 100644 | 274 | goto out; |
323 | --- a/block/vxhs.c | 275 | } |
324 | +++ b/block/vxhs.c | 276 | extent = &s->extents[s->num_extents - 1]; |
325 | @@ -XXX,XX +XXX,XX @@ | 277 | } else { |
326 | #include <qnio/qnio_api.h> | 278 | error_setg(errp, "Unsupported extent type '%s'", type); |
327 | #include <sys/param.h> | 279 | + bdrv_graph_wrlock(NULL); |
328 | #include "block/block_int.h" | 280 | bdrv_unref_child(bs, extent_file); |
329 | +#include "block/qdict.h" | 281 | + bdrv_graph_wrunlock(); |
330 | #include "qapi/qmp/qerror.h" | 282 | ret = -ENOTSUP; |
331 | #include "qapi/qmp/qdict.h" | 283 | goto out; |
332 | #include "qapi/qmp/qstring.h" | 284 | } |
333 | diff --git a/blockdev.c b/blockdev.c | 285 | diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c |
334 | index XXXXXXX..XXXXXXX 100644 | 286 | index XXXXXXX..XXXXXXX 100644 |
335 | --- a/blockdev.c | 287 | --- a/tests/unit/test-bdrv-drain.c |
336 | +++ b/blockdev.c | 288 | +++ b/tests/unit/test-bdrv-drain.c |
337 | @@ -XXX,XX +XXX,XX @@ | 289 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVTestTopState { |
338 | #include "sysemu/blockdev.h" | 290 | static void bdrv_test_top_close(BlockDriverState *bs) |
339 | #include "hw/block/block.h" | 291 | { |
340 | #include "block/blockjob.h" | 292 | BdrvChild *c, *next_c; |
341 | +#include "block/qdict.h" | 293 | + |
342 | #include "block/throttle-groups.h" | 294 | + bdrv_graph_wrlock(NULL); |
343 | #include "monitor/monitor.h" | 295 | QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { |
344 | #include "qemu/error-report.h" | 296 | bdrv_unref_child(bs, c); |
345 | diff --git a/qobject/qdict.c b/qobject/qdict.c | 297 | } |
346 | index XXXXXXX..XXXXXXX 100644 | 298 | + bdrv_graph_wrunlock(); |
347 | --- a/qobject/qdict.c | 299 | } |
348 | +++ b/qobject/qdict.c | 300 | |
349 | @@ -XXX,XX +XXX,XX @@ | 301 | static int coroutine_fn GRAPH_RDLOCK |
350 | */ | 302 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) |
351 | 303 | } else { | |
352 | #include "qemu/osdep.h" | 304 | BdrvChild *c, *next_c; |
353 | +#include "block/qdict.h" | 305 | QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { |
354 | #include "qapi/qmp/qnum.h" | 306 | - bdrv_unref_child(bs, c); |
355 | #include "qapi/qmp/qdict.h" | 307 | + bdrv_co_unref_child(bs, c); |
356 | #include "qapi/qmp/qbool.h" | 308 | } |
357 | diff --git a/tests/check-qdict.c b/tests/check-qdict.c | 309 | } |
358 | index XXXXXXX..XXXXXXX 100644 | 310 | |
359 | --- a/tests/check-qdict.c | 311 | @@ -XXX,XX +XXX,XX @@ static void detach_indirect_bh(void *opaque) |
360 | +++ b/tests/check-qdict.c | 312 | struct detach_by_parent_data *data = opaque; |
361 | @@ -XXX,XX +XXX,XX @@ | 313 | |
362 | */ | 314 | bdrv_dec_in_flight(data->child_b->bs); |
363 | 315 | + | |
364 | #include "qemu/osdep.h" | 316 | + bdrv_graph_wrlock(NULL); |
365 | +#include "block/qdict.h" | 317 | bdrv_unref_child(data->parent_b, data->child_b); |
366 | #include "qapi/qmp/qdict.h" | 318 | |
367 | #include "qapi/qmp/qlist.h" | 319 | bdrv_ref(data->c); |
368 | #include "qapi/qmp/qnum.h" | 320 | - bdrv_graph_wrlock(NULL); |
369 | diff --git a/tests/check-qobject.c b/tests/check-qobject.c | 321 | data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", |
370 | index XXXXXXX..XXXXXXX 100644 | 322 | &child_of_bds, BDRV_CHILD_DATA, |
371 | --- a/tests/check-qobject.c | 323 | &error_abort); |
372 | +++ b/tests/check-qobject.c | ||
373 | @@ -XXX,XX +XXX,XX @@ | ||
374 | */ | ||
375 | |||
376 | #include "qemu/osdep.h" | ||
377 | +#include "block/qdict.h" | ||
378 | #include "qapi/qmp/qbool.h" | ||
379 | #include "qapi/qmp/qdict.h" | ||
380 | #include "qapi/qmp/qlist.h" | ||
381 | diff --git a/tests/test-replication.c b/tests/test-replication.c | ||
382 | index XXXXXXX..XXXXXXX 100644 | ||
383 | --- a/tests/test-replication.c | ||
384 | +++ b/tests/test-replication.c | ||
385 | @@ -XXX,XX +XXX,XX @@ | ||
386 | #include "qemu/option.h" | ||
387 | #include "replication.h" | ||
388 | #include "block/block_int.h" | ||
389 | +#include "block/qdict.h" | ||
390 | #include "sysemu/block-backend.h" | ||
391 | |||
392 | #define IMG_SIZE (64 * 1024 * 1024) | ||
393 | diff --git a/util/qemu-config.c b/util/qemu-config.c | ||
394 | index XXXXXXX..XXXXXXX 100644 | ||
395 | --- a/util/qemu-config.c | ||
396 | +++ b/util/qemu-config.c | ||
397 | @@ -XXX,XX +XXX,XX @@ | ||
398 | #include "qemu/osdep.h" | ||
399 | +#include "block/qdict.h" /* for qdict_extract_subqdict() */ | ||
400 | #include "qapi/error.h" | ||
401 | #include "qapi/qapi-commands-misc.h" | ||
402 | #include "qapi/qmp/qdict.h" | ||
403 | -- | 324 | -- |
404 | 2.13.6 | 325 | 2.41.0 |
405 | |||
406 | diff view generated by jsdifflib |
1 | The -drive options cyls, heads, secs and trans were deprecated in | 1 | The functions read the parents list in the generic block layer, so we |
---|---|---|---|
2 | QEMU 2.10. It's time to remove them. | 2 | need to hold the graph lock already there. The BlockDriver |
3 | 3 | implementations actually modify the graph, so it has to be a writer | |
4 | hd-geo-test tested both the old version with geometry options in -drive | 4 | lock. |
5 | and the new one with -device. Therefore the code using -drive doesn't | ||
6 | have to be replaced there, we just need to remove the -drive test cases. | ||
7 | This in turn allows some simplification of the code. | ||
8 | 5 | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | 7 | Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> |
8 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
9 | Message-ID: <20230911094620.45040-22-kwolf@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | 11 | --- |
12 | include/sysemu/blockdev.h | 1 - | 12 | include/block/block-global-state.h | 8 +++++--- |
13 | blockdev.c | 75 +---------------------------------------------- | 13 | include/block/block_int-common.h | 9 +++++---- |
14 | hw/block/block.c | 14 --------- | 14 | block/quorum.c | 23 ++++++----------------- |
15 | tests/hd-geo-test.c | 37 +++++------------------ | 15 | blockdev.c | 17 +++++++++++------ |
16 | hmp-commands.hx | 1 - | 16 | 4 files changed, 27 insertions(+), 30 deletions(-) |
17 | qemu-doc.texi | 5 ---- | ||
18 | qemu-options.hx | 7 +---- | ||
19 | 7 files changed, 9 insertions(+), 131 deletions(-) | ||
20 | 17 | ||
21 | diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h | 18 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h |
22 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/include/sysemu/blockdev.h | 20 | --- a/include/block/block-global-state.h |
24 | +++ b/include/sysemu/blockdev.h | 21 | +++ b/include/block/block-global-state.h |
25 | @@ -XXX,XX +XXX,XX @@ struct DriveInfo { | 22 | @@ -XXX,XX +XXX,XX @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, |
26 | int auto_del; /* see blockdev_mark_auto_del() */ | 23 | int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); |
27 | bool is_default; /* Added by default_drive() ? */ | 24 | int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); |
28 | int media_cd; | 25 | |
29 | - int cyls, heads, secs, trans; | 26 | -void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, |
30 | QemuOpts *opts; | 27 | - Error **errp); |
31 | char *serial; | 28 | -void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); |
32 | QTAILQ_ENTRY(DriveInfo) next; | 29 | +void GRAPH_WRLOCK |
30 | +bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, Error **errp); | ||
31 | + | ||
32 | +void GRAPH_WRLOCK | ||
33 | +bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); | ||
34 | |||
35 | /** | ||
36 | * | ||
37 | diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h | ||
38 | index XXXXXXX..XXXXXXX 100644 | ||
39 | --- a/include/block/block_int-common.h | ||
40 | +++ b/include/block/block_int-common.h | ||
41 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | ||
42 | */ | ||
43 | int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); | ||
44 | |||
45 | - void (*bdrv_add_child)(BlockDriverState *parent, BlockDriverState *child, | ||
46 | - Error **errp); | ||
47 | - void (*bdrv_del_child)(BlockDriverState *parent, BdrvChild *child, | ||
48 | - Error **errp); | ||
49 | + void GRAPH_WRLOCK_PTR (*bdrv_add_child)( | ||
50 | + BlockDriverState *parent, BlockDriverState *child, Error **errp); | ||
51 | + | ||
52 | + void GRAPH_WRLOCK_PTR (*bdrv_del_child)( | ||
53 | + BlockDriverState *parent, BdrvChild *child, Error **errp); | ||
54 | |||
55 | /** | ||
56 | * Informs the block driver that a permission change is intended. The | ||
57 | diff --git a/block/quorum.c b/block/quorum.c | ||
58 | index XXXXXXX..XXXXXXX 100644 | ||
59 | --- a/block/quorum.c | ||
60 | +++ b/block/quorum.c | ||
61 | @@ -XXX,XX +XXX,XX @@ static void quorum_close(BlockDriverState *bs) | ||
62 | g_free(s->children); | ||
63 | } | ||
64 | |||
65 | -static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, | ||
66 | - Error **errp) | ||
67 | +static void GRAPH_WRLOCK | ||
68 | +quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, Error **errp) | ||
69 | { | ||
70 | BDRVQuorumState *s = bs->opaque; | ||
71 | BdrvChild *child; | ||
72 | @@ -XXX,XX +XXX,XX @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, | ||
73 | } | ||
74 | s->next_child_index++; | ||
75 | |||
76 | - bdrv_drained_begin(bs); | ||
77 | - | ||
78 | /* We can safely add the child now */ | ||
79 | bdrv_ref(child_bs); | ||
80 | |||
81 | - bdrv_graph_wrlock(child_bs); | ||
82 | child = bdrv_attach_child(bs, child_bs, indexstr, &child_of_bds, | ||
83 | BDRV_CHILD_DATA, errp); | ||
84 | - bdrv_graph_wrunlock(); | ||
85 | if (child == NULL) { | ||
86 | s->next_child_index--; | ||
87 | - goto out; | ||
88 | + return; | ||
89 | } | ||
90 | s->children = g_renew(BdrvChild *, s->children, s->num_children + 1); | ||
91 | s->children[s->num_children++] = child; | ||
92 | quorum_refresh_flags(bs); | ||
93 | - | ||
94 | -out: | ||
95 | - bdrv_drained_end(bs); | ||
96 | } | ||
97 | |||
98 | -static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, | ||
99 | - Error **errp) | ||
100 | +static void GRAPH_WRLOCK | ||
101 | +quorum_del_child(BlockDriverState *bs, BdrvChild *child, Error **errp) | ||
102 | { | ||
103 | BDRVQuorumState *s = bs->opaque; | ||
104 | char indexstr[INDEXSTR_LEN]; | ||
105 | @@ -XXX,XX +XXX,XX @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, | ||
106 | s->next_child_index--; | ||
107 | } | ||
108 | |||
109 | - bdrv_drained_begin(bs); | ||
110 | - | ||
111 | /* We can safely remove this child now */ | ||
112 | memmove(&s->children[i], &s->children[i + 1], | ||
113 | (s->num_children - i - 1) * sizeof(BdrvChild *)); | ||
114 | s->children = g_renew(BdrvChild *, s->children, --s->num_children); | ||
115 | - bdrv_graph_wrlock(NULL); | ||
116 | + | ||
117 | bdrv_unref_child(bs, child); | ||
118 | - bdrv_graph_wrunlock(); | ||
119 | |||
120 | quorum_refresh_flags(bs); | ||
121 | - bdrv_drained_end(bs); | ||
122 | } | ||
123 | |||
124 | static void quorum_gather_child_options(BlockDriverState *bs, QDict *target, | ||
33 | diff --git a/blockdev.c b/blockdev.c | 125 | diff --git a/blockdev.c b/blockdev.c |
34 | index XXXXXXX..XXXXXXX 100644 | 126 | index XXXXXXX..XXXXXXX 100644 |
35 | --- a/blockdev.c | 127 | --- a/blockdev.c |
36 | +++ b/blockdev.c | 128 | +++ b/blockdev.c |
37 | @@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = { | 129 | @@ -XXX,XX +XXX,XX @@ out: |
38 | .type = QEMU_OPT_STRING, | 130 | aio_context_release(aio_context); |
39 | .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", | 131 | } |
40 | },{ | 132 | |
41 | - .name = "cyls", | 133 | -static BdrvChild *bdrv_find_child(BlockDriverState *parent_bs, |
42 | - .type = QEMU_OPT_NUMBER, | 134 | - const char *child_name) |
43 | - .help = "number of cylinders (ide disk geometry)", | 135 | +static BdrvChild * GRAPH_RDLOCK |
44 | - },{ | 136 | +bdrv_find_child(BlockDriverState *parent_bs, const char *child_name) |
45 | - .name = "heads", | 137 | { |
46 | - .type = QEMU_OPT_NUMBER, | 138 | BdrvChild *child; |
47 | - .help = "number of heads (ide disk geometry)", | 139 | |
48 | - },{ | 140 | @@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_change(const char *parent, const char *child, |
49 | - .name = "secs", | 141 | BlockDriverState *parent_bs, *new_bs = NULL; |
50 | - .type = QEMU_OPT_NUMBER, | 142 | BdrvChild *p_child; |
51 | - .help = "number of sectors (ide disk geometry)", | 143 | |
52 | - },{ | 144 | + bdrv_graph_wrlock(NULL); |
53 | - .name = "trans", | 145 | + |
54 | - .type = QEMU_OPT_STRING, | 146 | parent_bs = bdrv_lookup_bs(parent, parent, errp); |
55 | - .help = "chs translation (auto, lba, none)", | 147 | if (!parent_bs) { |
56 | - },{ | 148 | - return; |
57 | .name = "addr", | 149 | + goto out; |
58 | .type = QEMU_OPT_STRING, | ||
59 | .help = "pci address (virtio only)", | ||
60 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
61 | QemuOpts *legacy_opts; | ||
62 | DriveMediaType media = MEDIA_DISK; | ||
63 | BlockInterfaceType type; | ||
64 | - int cyls, heads, secs, translation; | ||
65 | int max_devs, bus_id, unit_id, index; | ||
66 | const char *devaddr; | ||
67 | const char *werror, *rerror; | ||
68 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
69 | Error *local_err = NULL; | ||
70 | int i; | ||
71 | const char *deprecated[] = { | ||
72 | - "serial", "trans", "secs", "heads", "cyls", "addr" | ||
73 | + "serial", "addr" | ||
74 | }; | ||
75 | |||
76 | /* Change legacy command line options into QMP ones */ | ||
77 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
78 | type = block_default_type; | ||
79 | } | 150 | } |
80 | 151 | ||
81 | - /* Geometry */ | 152 | if (!child == !node) { |
82 | - cyls = qemu_opt_get_number(legacy_opts, "cyls", 0); | 153 | @@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_change(const char *parent, const char *child, |
83 | - heads = qemu_opt_get_number(legacy_opts, "heads", 0); | 154 | } else { |
84 | - secs = qemu_opt_get_number(legacy_opts, "secs", 0); | 155 | error_setg(errp, "Either child or node must be specified"); |
85 | - | 156 | } |
86 | - if (cyls || heads || secs) { | 157 | - return; |
87 | - if (cyls < 1) { | 158 | + goto out; |
88 | - error_report("invalid physical cyls number"); | ||
89 | - goto fail; | ||
90 | - } | ||
91 | - if (heads < 1) { | ||
92 | - error_report("invalid physical heads number"); | ||
93 | - goto fail; | ||
94 | - } | ||
95 | - if (secs < 1) { | ||
96 | - error_report("invalid physical secs number"); | ||
97 | - goto fail; | ||
98 | - } | ||
99 | - } | ||
100 | - | ||
101 | - translation = BIOS_ATA_TRANSLATION_AUTO; | ||
102 | - value = qemu_opt_get(legacy_opts, "trans"); | ||
103 | - if (value != NULL) { | ||
104 | - if (!cyls) { | ||
105 | - error_report("'%s' trans must be used with cyls, heads and secs", | ||
106 | - value); | ||
107 | - goto fail; | ||
108 | - } | ||
109 | - if (!strcmp(value, "none")) { | ||
110 | - translation = BIOS_ATA_TRANSLATION_NONE; | ||
111 | - } else if (!strcmp(value, "lba")) { | ||
112 | - translation = BIOS_ATA_TRANSLATION_LBA; | ||
113 | - } else if (!strcmp(value, "large")) { | ||
114 | - translation = BIOS_ATA_TRANSLATION_LARGE; | ||
115 | - } else if (!strcmp(value, "rechs")) { | ||
116 | - translation = BIOS_ATA_TRANSLATION_RECHS; | ||
117 | - } else if (!strcmp(value, "auto")) { | ||
118 | - translation = BIOS_ATA_TRANSLATION_AUTO; | ||
119 | - } else { | ||
120 | - error_report("'%s' invalid translation type", value); | ||
121 | - goto fail; | ||
122 | - } | ||
123 | - } | ||
124 | - | ||
125 | - if (media == MEDIA_CDROM) { | ||
126 | - if (cyls || secs || heads) { | ||
127 | - error_report("CHS can't be set with media=cdrom"); | ||
128 | - goto fail; | ||
129 | - } | ||
130 | - } | ||
131 | - | ||
132 | /* Device address specified by bus/unit or index. | ||
133 | * If none was specified, try to find the first free one. */ | ||
134 | bus_id = qemu_opt_get_number(legacy_opts, "bus", 0); | ||
135 | @@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) | ||
136 | dinfo = g_malloc0(sizeof(*dinfo)); | ||
137 | dinfo->opts = all_opts; | ||
138 | |||
139 | - dinfo->cyls = cyls; | ||
140 | - dinfo->heads = heads; | ||
141 | - dinfo->secs = secs; | ||
142 | - dinfo->trans = translation; | ||
143 | - | ||
144 | dinfo->type = type; | ||
145 | dinfo->bus = bus_id; | ||
146 | dinfo->unit = unit_id; | ||
147 | diff --git a/hw/block/block.c b/hw/block/block.c | ||
148 | index XXXXXXX..XXXXXXX 100644 | ||
149 | --- a/hw/block/block.c | ||
150 | +++ b/hw/block/block.c | ||
151 | @@ -XXX,XX +XXX,XX @@ bool blkconf_geometry(BlockConf *conf, int *ptrans, | ||
152 | unsigned cyls_max, unsigned heads_max, unsigned secs_max, | ||
153 | Error **errp) | ||
154 | { | ||
155 | - DriveInfo *dinfo; | ||
156 | - | ||
157 | - if (!conf->cyls && !conf->heads && !conf->secs) { | ||
158 | - /* try to fall back to value set with legacy -drive cyls=... */ | ||
159 | - dinfo = blk_legacy_dinfo(conf->blk); | ||
160 | - if (dinfo) { | ||
161 | - conf->cyls = dinfo->cyls; | ||
162 | - conf->heads = dinfo->heads; | ||
163 | - conf->secs = dinfo->secs; | ||
164 | - if (ptrans) { | ||
165 | - *ptrans = dinfo->trans; | ||
166 | - } | ||
167 | - } | ||
168 | - } | ||
169 | if (!conf->cyls && !conf->heads && !conf->secs) { | ||
170 | hd_geometry_guess(conf->blk, | ||
171 | &conf->cyls, &conf->heads, &conf->secs, | ||
172 | diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c | ||
173 | index XXXXXXX..XXXXXXX 100644 | ||
174 | --- a/tests/hd-geo-test.c | ||
175 | +++ b/tests/hd-geo-test.c | ||
176 | @@ -XXX,XX +XXX,XX @@ static void setup_mbr(int img_idx, MBRcontents mbr) | ||
177 | |||
178 | static int setup_ide(int argc, char *argv[], int argv_sz, | ||
179 | int ide_idx, const char *dev, int img_idx, | ||
180 | - MBRcontents mbr, const char *opts) | ||
181 | + MBRcontents mbr) | ||
182 | { | ||
183 | char *s1, *s2, *s3; | ||
184 | |||
185 | @@ -XXX,XX +XXX,XX @@ static int setup_ide(int argc, char *argv[], int argv_sz, | ||
186 | s3 = g_strdup(",media=cdrom"); | ||
187 | } | 159 | } |
188 | argc = append_arg(argc, argv, argv_sz, | 160 | |
189 | - g_strdup_printf("%s%s%s%s", s1, s2, s3, opts)); | 161 | if (child) { |
190 | + g_strdup_printf("%s%s%s", s1, s2, s3)); | 162 | @@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_change(const char *parent, const char *child, |
191 | g_free(s1); | 163 | if (!p_child) { |
192 | g_free(s2); | 164 | error_setg(errp, "Node '%s' does not have child '%s'", |
193 | g_free(s3); | 165 | parent, child); |
194 | @@ -XXX,XX +XXX,XX @@ static void test_ide_mbr(bool use_device, MBRcontents mbr) | 166 | - return; |
195 | for (i = 0; i < backend_last; i++) { | 167 | + goto out; |
196 | cur_ide[i] = &hd_chst[i][mbr]; | 168 | } |
197 | dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL; | 169 | bdrv_del_child(parent_bs, p_child, errp); |
198 | - argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr, ""); | ||
199 | + argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr); | ||
200 | } | 170 | } |
201 | args = g_strjoinv(" ", argv); | 171 | @@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_change(const char *parent, const char *child, |
202 | qtest_start(args); | 172 | new_bs = bdrv_find_node(node); |
203 | @@ -XXX,XX +XXX,XX @@ static void test_ide_drive_user(const char *dev, bool trans) | 173 | if (!new_bs) { |
204 | const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans }; | 174 | error_setg(errp, "Node '%s' not found", node); |
205 | 175 | - return; | |
206 | argc = setup_common(argv, ARGV_SIZE); | 176 | + goto out; |
207 | - opts = g_strdup_printf("%s,%s%scyls=%d,heads=%d,secs=%d", | 177 | } |
208 | - dev ?: "", | 178 | bdrv_add_child(parent_bs, new_bs, errp); |
209 | - trans && dev ? "bios-chs-" : "", | 179 | } |
210 | - trans ? "trans=lba," : "", | 180 | + |
211 | + opts = g_strdup_printf("%s,%scyls=%d,heads=%d,secs=%d", | 181 | +out: |
212 | + dev, trans ? "bios-chs-trans=lba," : "", | 182 | + bdrv_graph_wrunlock(); |
213 | expected_chst.cyls, expected_chst.heads, | ||
214 | expected_chst.secs); | ||
215 | cur_ide[0] = &expected_chst; | ||
216 | - argc = setup_ide(argc, argv, ARGV_SIZE, | ||
217 | - 0, dev ? opts : NULL, backend_small, mbr_chs, | ||
218 | - dev ? "" : opts); | ||
219 | + argc = setup_ide(argc, argv, ARGV_SIZE, 0, opts, backend_small, mbr_chs); | ||
220 | g_free(opts); | ||
221 | args = g_strjoinv(" ", argv); | ||
222 | qtest_start(args); | ||
223 | @@ -XXX,XX +XXX,XX @@ static void test_ide_drive_user(const char *dev, bool trans) | ||
224 | } | 183 | } |
225 | 184 | ||
226 | /* | 185 | BlockJobInfoList *qmp_query_block_jobs(Error **errp) |
227 | - * Test case: IDE device (if=ide) with explicit CHS | ||
228 | - */ | ||
229 | -static void test_ide_drive_user_chs(void) | ||
230 | -{ | ||
231 | - test_ide_drive_user(NULL, false); | ||
232 | -} | ||
233 | - | ||
234 | -/* | ||
235 | - * Test case: IDE device (if=ide) with explicit CHS and translation | ||
236 | - */ | ||
237 | -static void test_ide_drive_user_chst(void) | ||
238 | -{ | ||
239 | - test_ide_drive_user(NULL, true); | ||
240 | -} | ||
241 | - | ||
242 | -/* | ||
243 | * Test case: IDE device (if=none) with explicit CHS | ||
244 | */ | ||
245 | static void test_ide_device_user_chs(void) | ||
246 | @@ -XXX,XX +XXX,XX @@ static void test_ide_drive_cd_0(void) | ||
247 | for (i = 0; i <= backend_empty; i++) { | ||
248 | ide_idx = backend_empty - i; | ||
249 | cur_ide[ide_idx] = &hd_chst[i][mbr_blank]; | ||
250 | - argc = setup_ide(argc, argv, ARGV_SIZE, | ||
251 | - ide_idx, NULL, i, mbr_blank, ""); | ||
252 | + argc = setup_ide(argc, argv, ARGV_SIZE, ide_idx, NULL, i, mbr_blank); | ||
253 | } | ||
254 | args = g_strjoinv(" ", argv); | ||
255 | qtest_start(args); | ||
256 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
257 | qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); | ||
258 | qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); | ||
259 | qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); | ||
260 | - qtest_add_func("hd-geo/ide/drive/user/chs", test_ide_drive_user_chs); | ||
261 | - qtest_add_func("hd-geo/ide/drive/user/chst", test_ide_drive_user_chst); | ||
262 | qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); | ||
263 | qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); | ||
264 | qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); | ||
265 | diff --git a/hmp-commands.hx b/hmp-commands.hx | ||
266 | index XXXXXXX..XXXXXXX 100644 | ||
267 | --- a/hmp-commands.hx | ||
268 | +++ b/hmp-commands.hx | ||
269 | @@ -XXX,XX +XXX,XX @@ ETEXI | ||
270 | .params = "[-n] [[<domain>:]<bus>:]<slot>\n" | ||
271 | "[file=file][,if=type][,bus=n]\n" | ||
272 | "[,unit=m][,media=d][,index=i]\n" | ||
273 | - "[,cyls=c,heads=h,secs=s[,trans=t]]\n" | ||
274 | "[,snapshot=on|off][,cache=on|off]\n" | ||
275 | "[,readonly=on|off][,copy-on-read=on|off]", | ||
276 | .help = "add drive to PCI storage controller", | ||
277 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
278 | index XXXXXXX..XXXXXXX 100644 | ||
279 | --- a/qemu-doc.texi | ||
280 | +++ b/qemu-doc.texi | ||
281 | @@ -XXX,XX +XXX,XX @@ with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' | ||
282 | (for embedded NICs). The new syntax allows different settings to be | ||
283 | provided per NIC. | ||
284 | |||
285 | -@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) | ||
286 | - | ||
287 | -The drive geometry arguments are replaced by the the geometry arguments | ||
288 | -that can be specified with the ``-device'' parameter. | ||
289 | - | ||
290 | @subsection -drive serial=... (since 2.10.0) | ||
291 | |||
292 | The drive serial argument is replaced by the the serial argument | ||
293 | diff --git a/qemu-options.hx b/qemu-options.hx | ||
294 | index XXXXXXX..XXXXXXX 100644 | ||
295 | --- a/qemu-options.hx | ||
296 | +++ b/qemu-options.hx | ||
297 | @@ -XXX,XX +XXX,XX @@ ETEXI | ||
298 | |||
299 | DEF("drive", HAS_ARG, QEMU_OPTION_drive, | ||
300 | "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" | ||
301 | - " [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n" | ||
302 | " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" | ||
303 | - " [,serial=s][,addr=A][,rerror=ignore|stop|report]\n" | ||
304 | + " [,snapshot=on|off][,serial=s][,addr=A][,rerror=ignore|stop|report]\n" | ||
305 | " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" | ||
306 | " [,readonly=on|off][,copy-on-read=on|off]\n" | ||
307 | " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" | ||
308 | @@ -XXX,XX +XXX,XX @@ This option defines where is connected the drive by using an index in the list | ||
309 | of available connectors of a given interface type. | ||
310 | @item media=@var{media} | ||
311 | This option defines the type of the media: disk or cdrom. | ||
312 | -@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}] | ||
313 | -Force disk physical geometry and the optional BIOS translation (trans=none or | ||
314 | -lba). These parameters are deprecated, use the corresponding parameters | ||
315 | -of @code{-device} instead. | ||
316 | @item snapshot=@var{snapshot} | ||
317 | @var{snapshot} is "on" or "off" and controls snapshot mode for the given drive | ||
318 | (see @option{-snapshot}). | ||
319 | -- | 186 | -- |
320 | 2.13.6 | 187 | 2.41.0 |
321 | |||
322 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | From: Andrey Drobyshev via <qemu-block@nongnu.org> |
---|---|---|---|
2 | 2 | ||
3 | The following pattern occurs in the .bdrv_co_create_opts() methods of | 3 | Functions qcow2_get_host_offset(), get_cluster_offset(), |
4 | parallels, qcow, qcow2, qed, vhdx and vpc: | 4 | vmdk_co_block_status() explicitly report compressed cluster types when data |
5 | is compressed. However, this information is never passed further. Let's | ||
6 | make use of it by adding new BDRV_BLOCK_COMPRESSED flag for | ||
7 | bdrv_block_status(), so that caller may know that the data range is | ||
8 | compressed. In particular, we're going to use this flag to tweak | ||
9 | "qemu-img map" output. | ||
5 | 10 | ||
6 | qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | 11 | This new flag is only being utilized by qcow, qcow2 and vmdk formats, as only |
7 | qobject_unref(qdict); | 12 | those support compression. |
8 | qdict = qobject_to(QDict, qobj); | ||
9 | if (qdict == NULL) { | ||
10 | ret = -EINVAL; | ||
11 | goto done; | ||
12 | } | ||
13 | 13 | ||
14 | v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | 14 | Reviewed-by: Denis V. Lunev <den@openvz.org> |
15 | [...] | 15 | Reviewed-by: Hanna Czenczek <hreitz@redhat.com> |
16 | ret = 0; | 16 | Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> |
17 | done: | 17 | Message-ID: <20230907210226.953821-2-andrey.drobyshev@virtuozzo.com> |
18 | qobject_unref(qdict); | ||
19 | [...] | ||
20 | return ret; | ||
21 | |||
22 | If qobject_to() fails, we return failure without setting errp. That's | ||
23 | wrong. As far as I can tell, it cannot fail here. Clean it up | ||
24 | anyway, by removing the useless conversion. | ||
25 | |||
26 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | ||
27 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 18 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
28 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
29 | --- | 20 | --- |
30 | block/parallels.c | 9 ++++----- | 21 | include/block/block-common.h | 3 +++ |
31 | block/qcow.c | 9 ++++----- | 22 | block/qcow.c | 5 ++++- |
32 | block/qcow2.c | 9 ++++----- | 23 | block/qcow2.c | 3 +++ |
33 | block/qed.c | 9 ++++----- | 24 | block/vmdk.c | 2 ++ |
34 | block/vhdx.c | 9 ++++----- | 25 | 4 files changed, 12 insertions(+), 1 deletion(-) |
35 | block/vpc.c | 9 ++++----- | ||
36 | 6 files changed, 24 insertions(+), 30 deletions(-) | ||
37 | 26 | ||
38 | diff --git a/block/parallels.c b/block/parallels.c | 27 | diff --git a/include/block/block-common.h b/include/block/block-common.h |
39 | index XXXXXXX..XXXXXXX 100644 | 28 | index XXXXXXX..XXXXXXX 100644 |
40 | --- a/block/parallels.c | 29 | --- a/include/block/block-common.h |
41 | +++ b/block/parallels.c | 30 | +++ b/include/block/block-common.h |
42 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename, | 31 | @@ -XXX,XX +XXX,XX @@ typedef enum { |
43 | BlockdevCreateOptions *create_options = NULL; | 32 | * layer rather than any backing, set by block layer |
44 | Error *local_err = NULL; | 33 | * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this |
45 | BlockDriverState *bs = NULL; | 34 | * layer, set by block layer |
46 | - QDict *qdict = NULL; | 35 | + * BDRV_BLOCK_COMPRESSED: the underlying data is compressed; only valid for |
47 | + QDict *qdict; | 36 | + * the formats supporting compression: qcow, qcow2 |
48 | QObject *qobj; | 37 | * |
49 | Visitor *v; | 38 | * Internal flags: |
50 | int ret; | 39 | * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request |
51 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename, | 40 | @@ -XXX,XX +XXX,XX @@ typedef enum { |
52 | qdict_put_str(qdict, "file", bs->node_name); | 41 | #define BDRV_BLOCK_ALLOCATED 0x10 |
53 | 42 | #define BDRV_BLOCK_EOF 0x20 | |
54 | qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | 43 | #define BDRV_BLOCK_RECURSE 0x40 |
55 | - qobject_unref(qdict); | 44 | +#define BDRV_BLOCK_COMPRESSED 0x80 |
56 | - qdict = qobject_to(QDict, qobj); | 45 | |
57 | - if (qdict == NULL) { | 46 | typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; |
58 | + if (!qobj) { | ||
59 | ret = -EINVAL; | ||
60 | goto done; | ||
61 | } | ||
62 | |||
63 | - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | ||
64 | + v = qobject_input_visitor_new_keyval(qobj); | ||
65 | + qobject_unref(qobj); | ||
66 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||
67 | visit_free(v); | ||
68 | 47 | ||
69 | diff --git a/block/qcow.c b/block/qcow.c | 48 | diff --git a/block/qcow.c b/block/qcow.c |
70 | index XXXXXXX..XXXXXXX 100644 | 49 | index XXXXXXX..XXXXXXX 100644 |
71 | --- a/block/qcow.c | 50 | --- a/block/qcow.c |
72 | +++ b/block/qcow.c | 51 | +++ b/block/qcow.c |
73 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, | 52 | @@ -XXX,XX +XXX,XX @@ qcow_co_block_status(BlockDriverState *bs, bool want_zero, |
74 | { | 53 | if (!cluster_offset) { |
75 | BlockdevCreateOptions *create_options = NULL; | 54 | return 0; |
76 | BlockDriverState *bs = NULL; | ||
77 | - QDict *qdict = NULL; | ||
78 | + QDict *qdict; | ||
79 | QObject *qobj; | ||
80 | Visitor *v; | ||
81 | const char *val; | ||
82 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, | ||
83 | qdict_put_str(qdict, "file", bs->node_name); | ||
84 | |||
85 | qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
86 | - qobject_unref(qdict); | ||
87 | - qdict = qobject_to(QDict, qobj); | ||
88 | - if (qdict == NULL) { | ||
89 | + if (!qobj) { | ||
90 | ret = -EINVAL; | ||
91 | goto fail; | ||
92 | } | 55 | } |
93 | 56 | - if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypto) { | |
94 | - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | 57 | + if (cluster_offset & QCOW_OFLAG_COMPRESSED) { |
95 | + v = qobject_input_visitor_new_keyval(qobj); | 58 | + return BDRV_BLOCK_DATA | BDRV_BLOCK_COMPRESSED; |
96 | + qobject_unref(qobj); | 59 | + } |
97 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | 60 | + if (s->crypto) { |
98 | visit_free(v); | 61 | return BDRV_BLOCK_DATA; |
99 | 62 | } | |
63 | *map = cluster_offset | index_in_cluster; | ||
100 | diff --git a/block/qcow2.c b/block/qcow2.c | 64 | diff --git a/block/qcow2.c b/block/qcow2.c |
101 | index XXXXXXX..XXXXXXX 100644 | 65 | index XXXXXXX..XXXXXXX 100644 |
102 | --- a/block/qcow2.c | 66 | --- a/block/qcow2.c |
103 | +++ b/block/qcow2.c | 67 | +++ b/block/qcow2.c |
104 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt | 68 | @@ -XXX,XX +XXX,XX @@ qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, |
105 | Error **errp) | 69 | { |
106 | { | 70 | status |= BDRV_BLOCK_RECURSE; |
107 | BlockdevCreateOptions *create_options = NULL; | ||
108 | - QDict *qdict = NULL; | ||
109 | + QDict *qdict; | ||
110 | QObject *qobj; | ||
111 | Visitor *v; | ||
112 | BlockDriverState *bs = NULL; | ||
113 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt | ||
114 | |||
115 | /* Now get the QAPI type BlockdevCreateOptions */ | ||
116 | qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
117 | - qobject_unref(qdict); | ||
118 | - qdict = qobject_to(QDict, qobj); | ||
119 | - if (qdict == NULL) { | ||
120 | + if (!qobj) { | ||
121 | ret = -EINVAL; | ||
122 | goto finish; | ||
123 | } | 71 | } |
124 | 72 | + if (type == QCOW2_SUBCLUSTER_COMPRESSED) { | |
125 | - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | 73 | + status |= BDRV_BLOCK_COMPRESSED; |
126 | + v = qobject_input_visitor_new_keyval(qobj); | 74 | + } |
127 | + qobject_unref(qobj); | 75 | return status; |
128 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | 76 | } |
129 | visit_free(v); | 77 | |
130 | 78 | diff --git a/block/vmdk.c b/block/vmdk.c | |
131 | diff --git a/block/qed.c b/block/qed.c | ||
132 | index XXXXXXX..XXXXXXX 100644 | 79 | index XXXXXXX..XXXXXXX 100644 |
133 | --- a/block/qed.c | 80 | --- a/block/vmdk.c |
134 | +++ b/block/qed.c | 81 | +++ b/block/vmdk.c |
135 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, | 82 | @@ -XXX,XX +XXX,XX @@ vmdk_co_block_status(BlockDriverState *bs, bool want_zero, |
136 | Error **errp) | 83 | if (extent->flat) { |
137 | { | 84 | ret |= BDRV_BLOCK_RECURSE; |
138 | BlockdevCreateOptions *create_options = NULL; | 85 | } |
139 | - QDict *qdict = NULL; | 86 | + } else { |
140 | + QDict *qdict; | 87 | + ret |= BDRV_BLOCK_COMPRESSED; |
141 | QObject *qobj; | 88 | } |
142 | Visitor *v; | 89 | *file = extent->file->bs; |
143 | BlockDriverState *bs = NULL; | 90 | break; |
144 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, | ||
145 | qdict_put_str(qdict, "file", bs->node_name); | ||
146 | |||
147 | qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
148 | - qobject_unref(qdict); | ||
149 | - qdict = qobject_to(QDict, qobj); | ||
150 | - if (qdict == NULL) { | ||
151 | + if (!qobj) { | ||
152 | ret = -EINVAL; | ||
153 | goto fail; | ||
154 | } | ||
155 | |||
156 | - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | ||
157 | + v = qobject_input_visitor_new_keyval(qobj); | ||
158 | + qobject_unref(qobj); | ||
159 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||
160 | visit_free(v); | ||
161 | |||
162 | diff --git a/block/vhdx.c b/block/vhdx.c | ||
163 | index XXXXXXX..XXXXXXX 100644 | ||
164 | --- a/block/vhdx.c | ||
165 | +++ b/block/vhdx.c | ||
166 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, | ||
167 | Error **errp) | ||
168 | { | ||
169 | BlockdevCreateOptions *create_options = NULL; | ||
170 | - QDict *qdict = NULL; | ||
171 | + QDict *qdict; | ||
172 | QObject *qobj; | ||
173 | Visitor *v; | ||
174 | BlockDriverState *bs = NULL; | ||
175 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, | ||
176 | qdict_put_str(qdict, "file", bs->node_name); | ||
177 | |||
178 | qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
179 | - qobject_unref(qdict); | ||
180 | - qdict = qobject_to(QDict, qobj); | ||
181 | - if (qdict == NULL) { | ||
182 | + if (!qobj) { | ||
183 | ret = -EINVAL; | ||
184 | goto fail; | ||
185 | } | ||
186 | |||
187 | - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | ||
188 | + v = qobject_input_visitor_new_keyval(qobj); | ||
189 | + qobject_unref(qobj); | ||
190 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||
191 | visit_free(v); | ||
192 | |||
193 | diff --git a/block/vpc.c b/block/vpc.c | ||
194 | index XXXXXXX..XXXXXXX 100644 | ||
195 | --- a/block/vpc.c | ||
196 | +++ b/block/vpc.c | ||
197 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, | ||
198 | QemuOpts *opts, Error **errp) | ||
199 | { | ||
200 | BlockdevCreateOptions *create_options = NULL; | ||
201 | - QDict *qdict = NULL; | ||
202 | + QDict *qdict; | ||
203 | QObject *qobj; | ||
204 | Visitor *v; | ||
205 | BlockDriverState *bs = NULL; | ||
206 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, | ||
207 | qdict_put_str(qdict, "file", bs->node_name); | ||
208 | |||
209 | qobj = qdict_crumple_for_keyval_qiv(qdict, errp); | ||
210 | - qobject_unref(qdict); | ||
211 | - qdict = qobject_to(QDict, qobj); | ||
212 | - if (qdict == NULL) { | ||
213 | + if (!qobj) { | ||
214 | ret = -EINVAL; | ||
215 | goto fail; | ||
216 | } | ||
217 | |||
218 | - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | ||
219 | + v = qobject_input_visitor_new_keyval(qobj); | ||
220 | + qobject_unref(qobj); | ||
221 | visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); | ||
222 | visit_free(v); | ||
223 | |||
224 | -- | 91 | -- |
225 | 2.13.6 | 92 | 2.41.0 |
226 | |||
227 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | From: Andrey Drobyshev via <qemu-block@nongnu.org> |
---|---|---|---|
2 | 2 | ||
3 | Legacy -drive supports "password-secret" parameter that isn't | 3 | Right now "qemu-img map" reports compressed blocks as containing data |
4 | available with -blockdev / blockdev-add. That's because we backed out | 4 | but having no host offset. This is not very informative. Instead, |
5 | our first try to provide it there due to interface design doubts, in | 5 | let's add another boolean field named "compressed" in case JSON output |
6 | commit 577d8c9a811, v2.9.0. | 6 | mode is specified. This is achieved by utilizing new allocation status |
7 | flag BDRV_BLOCK_COMPRESSED for bdrv_block_status(). | ||
7 | 8 | ||
8 | This is the second try. It brings back the parameter, except it's | 9 | Also update the expected qemu-iotests outputs to contain the new field. |
9 | named "key-secret" now. | ||
10 | 10 | ||
11 | Let's review our reasons for backing out the first try, as stated in | 11 | Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> |
12 | the commit message: | 12 | Message-ID: <20230907210226.953821-3-andrey.drobyshev@virtuozzo.com> |
13 | |||
14 | * BlockdevOptionsRbd member @password-secret isn't actually a | ||
15 | password, it's a key generated by Ceph. | ||
16 | |||
17 | Addressed by the rename. | ||
18 | |||
19 | * We're not sure where member @password-secret belongs (see the | ||
20 | previous commit). | ||
21 | |||
22 | See previous commit. | ||
23 | |||
24 | * How @password-secret interacts with settings from a configuration | ||
25 | file specified with @conf is undocumented. | ||
26 | |||
27 | Not actually true, the documentation for @conf says "Values in the | ||
28 | configuration file will be overridden by options specified via QAPI", | ||
29 | and we've tested this. | ||
30 | |||
31 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | ||
32 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 13 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
33 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
34 | --- | 15 | --- |
35 | qapi/block-core.json | 6 ++++++ | 16 | qapi/block-core.json | 6 +- |
36 | block/rbd.c | 41 +++++++++++++++++++++++++---------------- | 17 | qemu-img.c | 8 +- |
37 | 2 files changed, 31 insertions(+), 16 deletions(-) | 18 | tests/qemu-iotests/122.out | 84 +- |
19 | tests/qemu-iotests/146.out | 780 +++++++++--------- | ||
20 | tests/qemu-iotests/154.out | 194 ++--- | ||
21 | tests/qemu-iotests/179.out | 178 ++-- | ||
22 | tests/qemu-iotests/209.out | 4 +- | ||
23 | tests/qemu-iotests/221.out | 16 +- | ||
24 | tests/qemu-iotests/223.out | 60 +- | ||
25 | tests/qemu-iotests/241.out | 10 +- | ||
26 | tests/qemu-iotests/244.out | 24 +- | ||
27 | tests/qemu-iotests/252.out | 10 +- | ||
28 | tests/qemu-iotests/253.out | 20 +- | ||
29 | tests/qemu-iotests/274.out | 48 +- | ||
30 | .../tests/nbd-qemu-allocation.out | 16 +- | ||
31 | tests/qemu-iotests/tests/qemu-img-bitmaps.out | 24 +- | ||
32 | 16 files changed, 744 insertions(+), 738 deletions(-) | ||
38 | 33 | ||
39 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 34 | diff --git a/qapi/block-core.json b/qapi/block-core.json |
40 | index XXXXXXX..XXXXXXX 100644 | 35 | index XXXXXXX..XXXXXXX 100644 |
41 | --- a/qapi/block-core.json | 36 | --- a/qapi/block-core.json |
42 | +++ b/qapi/block-core.json | 37 | +++ b/qapi/block-core.json |
43 | @@ -XXX,XX +XXX,XX @@ | 38 | @@ -XXX,XX +XXX,XX @@ |
44 | # This maps to Ceph configuration option | ||
45 | # "auth_client_required". (Since 3.0) | ||
46 | # | 39 | # |
47 | +# @key-secret: ID of a QCryptoSecret object providing a key | 40 | # @zero: whether the virtual blocks read as zeroes |
48 | +# for cephx authentication. | 41 | # |
49 | +# This maps to Ceph configuration option | 42 | +# @compressed: true if the data is stored compressed (since 8.2) |
50 | +# "key". (Since 3.0) | ||
51 | +# | 43 | +# |
52 | # @server: Monitor host address and port. This maps | 44 | # @depth: number of layers (0 = top image, 1 = top image's backing |
53 | # to the "mon_host" Ceph option. | 45 | # file, ..., n - 1 = bottom image (where n is the number of images |
54 | # | 46 | # in the chain)) before reaching one for which the range is |
55 | @@ -XXX,XX +XXX,XX @@ | 47 | @@ -XXX,XX +XXX,XX @@ |
56 | '*snapshot': 'str', | ||
57 | '*user': 'str', | ||
58 | '*auth-client-required': ['RbdAuthMode'], | ||
59 | + '*key-secret': 'str', | ||
60 | '*server': ['InetSocketAddressBase'] } } | ||
61 | |||
62 | ## | 48 | ## |
63 | diff --git a/block/rbd.c b/block/rbd.c | 49 | { 'struct': 'MapEntry', |
50 | 'data': {'start': 'int', 'length': 'int', 'data': 'bool', | ||
51 | - 'zero': 'bool', 'depth': 'int', 'present': 'bool', | ||
52 | - '*offset': 'int', '*filename': 'str' } } | ||
53 | + 'zero': 'bool', 'compressed': 'bool', 'depth': 'int', | ||
54 | + 'present': 'bool', '*offset': 'int', '*filename': 'str' } } | ||
55 | |||
56 | ## | ||
57 | # @BlockdevCacheInfo: | ||
58 | diff --git a/qemu-img.c b/qemu-img.c | ||
64 | index XXXXXXX..XXXXXXX 100644 | 59 | index XXXXXXX..XXXXXXX 100644 |
65 | --- a/block/rbd.c | 60 | --- a/qemu-img.c |
66 | +++ b/block/rbd.c | 61 | +++ b/qemu-img.c |
67 | @@ -XXX,XX +XXX,XX @@ static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) | 62 | @@ -XXX,XX +XXX,XX @@ static int dump_map_entry(OutputFormat output_format, MapEntry *e, |
68 | } | 63 | case OFORMAT_JSON: |
69 | 64 | printf("{ \"start\": %"PRId64", \"length\": %"PRId64"," | |
70 | 65 | " \"depth\": %"PRId64", \"present\": %s, \"zero\": %s," | |
71 | -static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, | 66 | - " \"data\": %s", e->start, e->length, e->depth, |
72 | - BlockdevOptionsRbd *opts, | 67 | + " \"data\": %s, \"compressed\": %s", |
73 | +static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts, | 68 | + e->start, e->length, e->depth, |
74 | Error **errp) | 69 | e->present ? "true" : "false", |
75 | { | 70 | e->zero ? "true" : "false", |
76 | - char *acr; | 71 | - e->data ? "true" : "false"); |
77 | + char *key, *acr; | 72 | + e->data ? "true" : "false", |
78 | int r; | 73 | + e->compressed ? "true" : "false"); |
79 | GString *accu; | 74 | if (e->has_offset) { |
80 | RbdAuthModeList *auth; | 75 | printf(", \"offset\": %"PRId64"", e->offset); |
81 | |||
82 | - if (secretid) { | ||
83 | - gchar *secret = qcrypto_secret_lookup_as_base64(secretid, | ||
84 | - errp); | ||
85 | - if (!secret) { | ||
86 | - return -1; | ||
87 | + if (opts->key_secret) { | ||
88 | + key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp); | ||
89 | + if (!key) { | ||
90 | + return -EIO; | ||
91 | + } | ||
92 | + r = rados_conf_set(cluster, "key", key); | ||
93 | + g_free(key); | ||
94 | + if (r < 0) { | ||
95 | + error_setg_errno(errp, -r, "Could not set 'key'"); | ||
96 | + return r; | ||
97 | } | 76 | } |
98 | - | 77 | @@ -XXX,XX +XXX,XX @@ static int get_block_status(BlockDriverState *bs, int64_t offset, |
99 | - rados_conf_set(cluster, "key", secret); | 78 | .length = bytes, |
100 | - g_free(secret); | 79 | .data = !!(ret & BDRV_BLOCK_DATA), |
80 | .zero = !!(ret & BDRV_BLOCK_ZERO), | ||
81 | + .compressed = !!(ret & BDRV_BLOCK_COMPRESSED), | ||
82 | .offset = map, | ||
83 | .has_offset = has_offset, | ||
84 | .depth = depth, | ||
85 | @@ -XXX,XX +XXX,XX @@ static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) | ||
101 | } | 86 | } |
102 | 87 | if (curr->zero != next->zero || | |
103 | if (opts->has_auth_client_required) { | 88 | curr->data != next->data || |
104 | @@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = { | 89 | + curr->compressed != next->compressed || |
105 | }, | 90 | curr->depth != next->depth || |
106 | }; | 91 | curr->present != next->present || |
107 | 92 | !curr->filename != !next->filename || | |
108 | -/* FIXME Deprecate and remove keypairs or make it available in QMP. | 93 | diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out |
109 | - * password_secret should eventually be configurable in opts->location. Support | 94 | index XXXXXXX..XXXXXXX 100644 |
110 | - * for it in .bdrv_open will make it work here as well. */ | 95 | --- a/tests/qemu-iotests/122.out |
111 | +/* FIXME Deprecate and remove keypairs or make it available in QMP. */ | 96 | +++ b/tests/qemu-iotests/122.out |
112 | static int qemu_rbd_do_create(BlockdevCreateOptions *options, | 97 | @@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 4194304 |
113 | const char *keypairs, const char *password_secret, | 98 | 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
114 | Error **errp) | 99 | read 65536/65536 bytes at offset 8388608 |
115 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, | 100 | 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
116 | Error *local_err = NULL; | 101 | -[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, |
117 | int r; | 102 | -{ "start": 65536, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false}, |
118 | 103 | -{ "start": 4194304, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, | |
119 | + if (secretid) { | 104 | -{ "start": 4259840, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false}, |
120 | + if (opts->key_secret) { | 105 | -{ "start": 8388608, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, |
121 | + error_setg(errp, | 106 | -{ "start": 8454144, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false}] |
122 | + "Legacy 'password-secret' clashes with 'key-secret'"); | 107 | +[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, |
123 | + return -EINVAL; | 108 | +{ "start": 65536, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, |
124 | + } | 109 | +{ "start": 4194304, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, |
125 | + opts->key_secret = g_strdup(secretid); | 110 | +{ "start": 4259840, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, |
126 | + opts->has_key_secret = true; | 111 | +{ "start": 8388608, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, |
127 | + } | 112 | +{ "start": 8454144, "length": 4128768, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] |
128 | + | 113 | read 65536/65536 bytes at offset 0 |
129 | mon_host = qemu_rbd_mon_host(opts, &local_err); | 114 | 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
130 | if (local_err) { | 115 | read 65536/65536 bytes at offset 4194304 |
131 | error_propagate(errp, local_err); | 116 | @@ -XXX,XX +XXX,XX @@ wrote 1024/1024 bytes at offset 1046528 |
132 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, | 117 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
133 | } | 118 | wrote 1024/1024 bytes at offset 0 |
134 | } | 119 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
135 | 120 | -[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, | |
136 | - if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) { | 121 | -{ "start": 65536, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false}, |
137 | - r = -EIO; | 122 | -{ "start": 131072, "length": 196608, "depth": 0, "present": true, "zero": false, "data": true}, |
138 | + r = qemu_rbd_set_auth(*cluster, opts, errp); | 123 | -{ "start": 327680, "length": 655360, "depth": 0, "present": false, "zero": true, "data": false}, |
139 | + if (r < 0) { | 124 | -{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true}, |
140 | goto failed_shutdown; | 125 | -{ "start": 1048576, "length": 1046528, "depth": 0, "present": false, "zero": true, "data": false}] |
141 | } | 126 | +[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, |
127 | +{ "start": 65536, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
128 | +{ "start": 131072, "length": 196608, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, | ||
129 | +{ "start": 327680, "length": 655360, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
130 | +{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, | ||
131 | +{ "start": 1048576, "length": 1046528, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
132 | read 16384/16384 bytes at offset 0 | ||
133 | 16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
134 | read 16384/16384 bytes at offset 16384 | ||
135 | @@ -XXX,XX +XXX,XX @@ read 3145728/3145728 bytes at offset 0 | ||
136 | 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
137 | read 63963136/63963136 bytes at offset 3145728 | ||
138 | 61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
139 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
140 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
141 | |||
142 | convert -c -S 0: | ||
143 | read 3145728/3145728 bytes at offset 0 | ||
144 | 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
145 | read 63963136/63963136 bytes at offset 3145728 | ||
146 | 61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
147 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true}] | ||
148 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}] | ||
149 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 | ||
150 | wrote 33554432/33554432 bytes at offset 0 | ||
151 | 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
152 | @@ -XXX,XX +XXX,XX @@ read 30408704/30408704 bytes at offset 3145728 | ||
153 | 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
154 | read 33554432/33554432 bytes at offset 33554432 | ||
155 | 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
156 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
157 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
158 | |||
159 | convert -c -S 0 with source backing file: | ||
160 | read 3145728/3145728 bytes at offset 0 | ||
161 | @@ -XXX,XX +XXX,XX @@ read 30408704/30408704 bytes at offset 3145728 | ||
162 | 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
163 | read 33554432/33554432 bytes at offset 33554432 | ||
164 | 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
165 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true}] | ||
166 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}] | ||
167 | |||
168 | convert -S 0 -B ... | ||
169 | read 3145728/3145728 bytes at offset 0 | ||
170 | @@ -XXX,XX +XXX,XX @@ read 30408704/30408704 bytes at offset 3145728 | ||
171 | 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
172 | read 33554432/33554432 bytes at offset 33554432 | ||
173 | 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
174 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
175 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
176 | |||
177 | convert -c -S 0 -B ... | ||
178 | read 3145728/3145728 bytes at offset 0 | ||
179 | @@ -XXX,XX +XXX,XX @@ read 30408704/30408704 bytes at offset 3145728 | ||
180 | 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
181 | read 33554432/33554432 bytes at offset 33554432 | ||
182 | 32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
183 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true}] | ||
184 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}] | ||
185 | |||
186 | === Non-zero -S === | ||
187 | |||
188 | @@ -XXX,XX +XXX,XX @@ wrote 1024/1024 bytes at offset 66560 | ||
189 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
190 | |||
191 | convert -S 4k | ||
192 | -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
193 | -{ "start": 4096, "length": 4096, "depth": 0, "present": false, "zero": true, "data": false}, | ||
194 | -{ "start": 8192, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
195 | -{ "start": 12288, "length": 4096, "depth": 0, "present": false, "zero": true, "data": false}, | ||
196 | -{ "start": 16384, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
197 | -{ "start": 20480, "length": 67088384, "depth": 0, "present": false, "zero": true, "data": false}] | ||
198 | +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
199 | +{ "start": 4096, "length": 4096, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
200 | +{ "start": 8192, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
201 | +{ "start": 12288, "length": 4096, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
202 | +{ "start": 16384, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
203 | +{ "start": 20480, "length": 67088384, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
204 | |||
205 | convert -c -S 4k | ||
206 | -[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, | ||
207 | -{ "start": 1024, "length": 7168, "depth": 0, "present": false, "zero": true, "data": false}, | ||
208 | -{ "start": 8192, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, | ||
209 | -{ "start": 9216, "length": 8192, "depth": 0, "present": false, "zero": true, "data": false}, | ||
210 | -{ "start": 17408, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, | ||
211 | -{ "start": 18432, "length": 67090432, "depth": 0, "present": false, "zero": true, "data": false}] | ||
212 | +[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, | ||
213 | +{ "start": 1024, "length": 7168, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
214 | +{ "start": 8192, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, | ||
215 | +{ "start": 9216, "length": 8192, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
216 | +{ "start": 17408, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, | ||
217 | +{ "start": 18432, "length": 67090432, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
218 | |||
219 | convert -S 8k | ||
220 | -[{ "start": 0, "length": 24576, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
221 | -{ "start": 24576, "length": 67084288, "depth": 0, "present": false, "zero": true, "data": false}] | ||
222 | +[{ "start": 0, "length": 24576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
223 | +{ "start": 24576, "length": 67084288, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
224 | |||
225 | convert -c -S 8k | ||
226 | -[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, | ||
227 | -{ "start": 1024, "length": 7168, "depth": 0, "present": false, "zero": true, "data": false}, | ||
228 | -{ "start": 8192, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, | ||
229 | -{ "start": 9216, "length": 8192, "depth": 0, "present": false, "zero": true, "data": false}, | ||
230 | -{ "start": 17408, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true}, | ||
231 | -{ "start": 18432, "length": 67090432, "depth": 0, "present": false, "zero": true, "data": false}] | ||
232 | +[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, | ||
233 | +{ "start": 1024, "length": 7168, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
234 | +{ "start": 8192, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, | ||
235 | +{ "start": 9216, "length": 8192, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
236 | +{ "start": 17408, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": true}, | ||
237 | +{ "start": 18432, "length": 67090432, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
238 | |||
239 | === -n to a non-zero image === | ||
240 | |||
241 | @@ -XXX,XX +XXX,XX @@ Images are identical. | ||
242 | |||
243 | Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 | ||
244 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 | ||
245 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false}] | ||
246 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
247 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 | ||
248 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": false, "zero": true, "data": false}] | ||
249 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
250 | |||
251 | === -n to an empty image with a backing file === | ||
252 | |||
253 | Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 | ||
254 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 | ||
255 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
256 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false}] | ||
257 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
258 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
259 | -[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] | ||
260 | +[{ "start": 0, "length": 67108864, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] | ||
261 | |||
262 | === -n -B to an image without a backing file === | ||
263 | |||
264 | diff --git a/tests/qemu-iotests/146.out b/tests/qemu-iotests/146.out | ||
265 | index XXXXXXX..XXXXXXX 100644 | ||
266 | --- a/tests/qemu-iotests/146.out | ||
267 | +++ b/tests/qemu-iotests/146.out | ||
268 | @@ -XXX,XX +XXX,XX @@ QA output created by 146 | ||
269 | |||
270 | === Testing VPC Autodetect === | ||
271 | |||
272 | -[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false}] | ||
273 | +[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
274 | |||
275 | === Testing VPC with current_size force === | ||
276 | |||
277 | -[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false}] | ||
278 | +[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
279 | |||
280 | === Testing VPC with chs force === | ||
281 | |||
282 | -[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false}] | ||
283 | +[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
284 | |||
285 | === Testing Hyper-V Autodetect === | ||
286 | |||
287 | -[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false}] | ||
288 | +[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
289 | |||
290 | === Testing Hyper-V with current_size force === | ||
291 | |||
292 | -[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false}] | ||
293 | +[{ "start": 0, "length": 136365211648, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
294 | |||
295 | === Testing Hyper-V with chs force === | ||
296 | |||
297 | -[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false}] | ||
298 | +[{ "start": 0, "length": 136363130880, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
299 | |||
300 | === Testing d2v Autodetect === | ||
301 | |||
302 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
303 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
304 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
305 | -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
306 | -{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
307 | -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
308 | -{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
309 | -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
310 | -{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
311 | -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
312 | -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
313 | -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
314 | -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
315 | -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
316 | -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
317 | -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
318 | -{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
319 | -{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
320 | -{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
321 | -{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
322 | -{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
323 | -{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
324 | -{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
325 | -{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
326 | -{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
327 | -{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
328 | -{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
329 | -{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
330 | -{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
331 | -{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
332 | -{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
333 | -{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
334 | -{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
335 | -{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
336 | -{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
337 | -{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
338 | -{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
339 | -{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
340 | -{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
341 | -{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
342 | -{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
343 | -{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
344 | -{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
345 | -{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
346 | -{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
347 | -{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
348 | -{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
349 | -{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
350 | -{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
351 | -{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
352 | -{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
353 | -{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
354 | -{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
355 | -{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
356 | -{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
357 | -{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
358 | -{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
359 | -{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
360 | -{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
361 | -{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
362 | -{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
363 | -{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
364 | -{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
365 | -{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
366 | -{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
367 | -{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
368 | -{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
369 | -{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
370 | -{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
371 | -{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
372 | -{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
373 | -{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
374 | -{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
375 | -{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
376 | -{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
377 | -{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
378 | -{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
379 | -{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
380 | -{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
381 | -{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
382 | -{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
383 | -{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
384 | -{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
385 | -{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
386 | -{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
387 | -{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
388 | -{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
389 | -{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
390 | -{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
391 | -{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
392 | -{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
393 | -{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
394 | -{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
395 | -{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
396 | -{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
397 | -{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
398 | -{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
399 | -{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
400 | -{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
401 | -{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
402 | -{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
403 | -{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
404 | -{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
405 | -{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
406 | -{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
407 | -{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
408 | -{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
409 | -{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
410 | -{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
411 | -{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
412 | -{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
413 | -{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
414 | -{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
415 | -{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
416 | -{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
417 | -{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
418 | -{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
419 | -{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
420 | -{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
421 | -{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
422 | -{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
423 | -{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
424 | -{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
425 | -{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
426 | -{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
427 | -{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
428 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
429 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
430 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
431 | +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
432 | +{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
433 | +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
434 | +{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
435 | +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
436 | +{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
437 | +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
438 | +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
439 | +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
440 | +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
441 | +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
442 | +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
443 | +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
444 | +{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
445 | +{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
446 | +{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
447 | +{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
448 | +{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
449 | +{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
450 | +{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
451 | +{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
452 | +{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
453 | +{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
454 | +{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
455 | +{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
456 | +{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
457 | +{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
458 | +{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
459 | +{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
460 | +{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
461 | +{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
462 | +{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
463 | +{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
464 | +{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
465 | +{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
466 | +{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
467 | +{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
468 | +{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
469 | +{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
470 | +{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
471 | +{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
472 | +{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
473 | +{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
474 | +{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
475 | +{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
476 | +{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
477 | +{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
478 | +{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
479 | +{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
480 | +{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
481 | +{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
482 | +{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
483 | +{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
484 | +{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
485 | +{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
486 | +{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
487 | +{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
488 | +{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
489 | +{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
490 | +{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
491 | +{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
492 | +{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
493 | +{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
494 | +{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
495 | +{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
496 | +{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
497 | +{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
498 | +{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
499 | +{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
500 | +{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
501 | +{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
502 | +{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
503 | +{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
504 | +{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
505 | +{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
506 | +{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
507 | +{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
508 | +{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
509 | +{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
510 | +{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
511 | +{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
512 | +{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
513 | +{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
514 | +{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
515 | +{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
516 | +{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
517 | +{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
518 | +{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
519 | +{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
520 | +{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
521 | +{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
522 | +{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
523 | +{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
524 | +{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
525 | +{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
526 | +{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
527 | +{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
528 | +{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
529 | +{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
530 | +{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
531 | +{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
532 | +{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
533 | +{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
534 | +{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
535 | +{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
536 | +{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
537 | +{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
538 | +{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
539 | +{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
540 | +{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
541 | +{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
542 | +{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
543 | +{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
544 | +{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
545 | +{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
546 | +{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
547 | +{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
548 | +{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
549 | +{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
550 | +{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
551 | +{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
552 | +{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
553 | +{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
554 | |||
555 | === Testing d2v with current_size force === | ||
556 | |||
557 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
558 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
559 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
560 | -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
561 | -{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
562 | -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
563 | -{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
564 | -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
565 | -{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
566 | -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
567 | -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
568 | -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
569 | -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
570 | -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
571 | -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
572 | -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
573 | -{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
574 | -{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
575 | -{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
576 | -{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
577 | -{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
578 | -{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
579 | -{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
580 | -{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
581 | -{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
582 | -{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
583 | -{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
584 | -{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
585 | -{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
586 | -{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
587 | -{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
588 | -{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
589 | -{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
590 | -{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
591 | -{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
592 | -{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
593 | -{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
594 | -{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
595 | -{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
596 | -{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
597 | -{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
598 | -{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
599 | -{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
600 | -{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
601 | -{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
602 | -{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
603 | -{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
604 | -{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
605 | -{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
606 | -{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
607 | -{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
608 | -{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
609 | -{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
610 | -{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
611 | -{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
612 | -{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
613 | -{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
614 | -{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
615 | -{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
616 | -{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
617 | -{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
618 | -{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
619 | -{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
620 | -{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
621 | -{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
622 | -{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
623 | -{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
624 | -{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
625 | -{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
626 | -{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
627 | -{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
628 | -{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
629 | -{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
630 | -{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
631 | -{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
632 | -{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
633 | -{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
634 | -{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
635 | -{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
636 | -{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
637 | -{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
638 | -{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
639 | -{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
640 | -{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
641 | -{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
642 | -{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
643 | -{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
644 | -{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
645 | -{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
646 | -{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
647 | -{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
648 | -{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
649 | -{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
650 | -{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
651 | -{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
652 | -{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
653 | -{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
654 | -{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
655 | -{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
656 | -{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
657 | -{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
658 | -{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
659 | -{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
660 | -{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
661 | -{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
662 | -{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
663 | -{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
664 | -{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
665 | -{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
666 | -{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
667 | -{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
668 | -{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
669 | -{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
670 | -{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
671 | -{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
672 | -{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
673 | -{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
674 | -{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
675 | -{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
676 | -{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
677 | -{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
678 | -{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
679 | -{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
680 | -{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
681 | -{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
682 | -{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
683 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
684 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
685 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
686 | +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
687 | +{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
688 | +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
689 | +{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
690 | +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
691 | +{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
692 | +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
693 | +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
694 | +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
695 | +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
696 | +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
697 | +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
698 | +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
699 | +{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
700 | +{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
701 | +{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
702 | +{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
703 | +{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
704 | +{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
705 | +{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
706 | +{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
707 | +{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
708 | +{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
709 | +{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
710 | +{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
711 | +{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
712 | +{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
713 | +{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
714 | +{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
715 | +{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
716 | +{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
717 | +{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
718 | +{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
719 | +{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
720 | +{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
721 | +{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
722 | +{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
723 | +{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
724 | +{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
725 | +{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
726 | +{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
727 | +{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
728 | +{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
729 | +{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
730 | +{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
731 | +{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
732 | +{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
733 | +{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
734 | +{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
735 | +{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
736 | +{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
737 | +{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
738 | +{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
739 | +{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
740 | +{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
741 | +{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
742 | +{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
743 | +{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
744 | +{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
745 | +{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
746 | +{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
747 | +{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
748 | +{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
749 | +{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
750 | +{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
751 | +{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
752 | +{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
753 | +{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
754 | +{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
755 | +{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
756 | +{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
757 | +{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
758 | +{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
759 | +{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
760 | +{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
761 | +{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
762 | +{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
763 | +{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
764 | +{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
765 | +{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
766 | +{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
767 | +{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
768 | +{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
769 | +{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
770 | +{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
771 | +{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
772 | +{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
773 | +{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
774 | +{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
775 | +{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
776 | +{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
777 | +{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
778 | +{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
779 | +{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
780 | +{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
781 | +{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
782 | +{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
783 | +{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
784 | +{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
785 | +{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
786 | +{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
787 | +{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
788 | +{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
789 | +{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
790 | +{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
791 | +{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
792 | +{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
793 | +{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
794 | +{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
795 | +{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
796 | +{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
797 | +{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
798 | +{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
799 | +{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
800 | +{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
801 | +{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
802 | +{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
803 | +{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
804 | +{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
805 | +{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
806 | +{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
807 | +{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
808 | +{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
809 | |||
810 | === Testing d2v with chs force === | ||
811 | |||
812 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
813 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
814 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
815 | -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
816 | -{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
817 | -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
818 | -{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
819 | -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
820 | -{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
821 | -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
822 | -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
823 | -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
824 | -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
825 | -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
826 | -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
827 | -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
828 | -{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
829 | -{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
830 | -{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
831 | -{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
832 | -{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
833 | -{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
834 | -{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
835 | -{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
836 | -{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
837 | -{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
838 | -{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
839 | -{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
840 | -{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
841 | -{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
842 | -{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
843 | -{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
844 | -{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
845 | -{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
846 | -{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
847 | -{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
848 | -{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
849 | -{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
850 | -{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
851 | -{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
852 | -{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
853 | -{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
854 | -{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
855 | -{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
856 | -{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
857 | -{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
858 | -{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
859 | -{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
860 | -{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
861 | -{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
862 | -{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
863 | -{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
864 | -{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
865 | -{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
866 | -{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
867 | -{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
868 | -{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
869 | -{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
870 | -{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
871 | -{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
872 | -{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
873 | -{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
874 | -{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
875 | -{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
876 | -{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
877 | -{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
878 | -{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
879 | -{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
880 | -{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
881 | -{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
882 | -{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
883 | -{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
884 | -{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
885 | -{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
886 | -{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
887 | -{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
888 | -{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
889 | -{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
890 | -{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
891 | -{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
892 | -{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
893 | -{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
894 | -{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
895 | -{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
896 | -{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
897 | -{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
898 | -{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
899 | -{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
900 | -{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
901 | -{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
902 | -{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
903 | -{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
904 | -{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
905 | -{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
906 | -{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
907 | -{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
908 | -{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
909 | -{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
910 | -{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
911 | -{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
912 | -{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
913 | -{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
914 | -{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
915 | -{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
916 | -{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
917 | -{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
918 | -{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
919 | -{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
920 | -{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
921 | -{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
922 | -{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
923 | -{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
924 | -{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
925 | -{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
926 | -{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
927 | -{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
928 | -{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
929 | -{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
930 | -{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
931 | -{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
932 | -{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
933 | -{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
934 | -{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
935 | -{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
936 | -{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
937 | -{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
938 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
939 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
940 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
941 | +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
942 | +{ "start": 8388608, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
943 | +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
944 | +{ "start": 12582912, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
945 | +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
946 | +{ "start": 16777216, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
947 | +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
948 | +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
949 | +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
950 | +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
951 | +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
952 | +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
953 | +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
954 | +{ "start": 33554432, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
955 | +{ "start": 35651584, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
956 | +{ "start": 37748736, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
957 | +{ "start": 39845888, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
958 | +{ "start": 41943040, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
959 | +{ "start": 44040192, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
960 | +{ "start": 46137344, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
961 | +{ "start": 48234496, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
962 | +{ "start": 50331648, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
963 | +{ "start": 52428800, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
964 | +{ "start": 54525952, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
965 | +{ "start": 56623104, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
966 | +{ "start": 58720256, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
967 | +{ "start": 60817408, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
968 | +{ "start": 62914560, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
969 | +{ "start": 65011712, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
970 | +{ "start": 67108864, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
971 | +{ "start": 69206016, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
972 | +{ "start": 71303168, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
973 | +{ "start": 73400320, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
974 | +{ "start": 75497472, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
975 | +{ "start": 77594624, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
976 | +{ "start": 79691776, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
977 | +{ "start": 81788928, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
978 | +{ "start": 83886080, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
979 | +{ "start": 85983232, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
980 | +{ "start": 88080384, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
981 | +{ "start": 90177536, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
982 | +{ "start": 92274688, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
983 | +{ "start": 94371840, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
984 | +{ "start": 96468992, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
985 | +{ "start": 98566144, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
986 | +{ "start": 100663296, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
987 | +{ "start": 102760448, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
988 | +{ "start": 104857600, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
989 | +{ "start": 106954752, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
990 | +{ "start": 109051904, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
991 | +{ "start": 111149056, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
992 | +{ "start": 113246208, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
993 | +{ "start": 115343360, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
994 | +{ "start": 117440512, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
995 | +{ "start": 119537664, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
996 | +{ "start": 121634816, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
997 | +{ "start": 123731968, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
998 | +{ "start": 125829120, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
999 | +{ "start": 127926272, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1000 | +{ "start": 130023424, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1001 | +{ "start": 132120576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1002 | +{ "start": 134217728, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1003 | +{ "start": 136314880, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1004 | +{ "start": 138412032, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1005 | +{ "start": 140509184, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1006 | +{ "start": 142606336, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1007 | +{ "start": 144703488, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1008 | +{ "start": 146800640, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1009 | +{ "start": 148897792, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1010 | +{ "start": 150994944, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1011 | +{ "start": 153092096, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1012 | +{ "start": 155189248, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1013 | +{ "start": 157286400, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1014 | +{ "start": 159383552, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1015 | +{ "start": 161480704, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1016 | +{ "start": 163577856, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1017 | +{ "start": 165675008, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1018 | +{ "start": 167772160, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1019 | +{ "start": 169869312, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1020 | +{ "start": 171966464, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1021 | +{ "start": 174063616, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1022 | +{ "start": 176160768, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1023 | +{ "start": 178257920, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1024 | +{ "start": 180355072, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1025 | +{ "start": 182452224, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1026 | +{ "start": 184549376, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1027 | +{ "start": 186646528, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1028 | +{ "start": 188743680, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1029 | +{ "start": 190840832, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1030 | +{ "start": 192937984, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1031 | +{ "start": 195035136, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1032 | +{ "start": 197132288, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1033 | +{ "start": 199229440, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1034 | +{ "start": 201326592, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1035 | +{ "start": 203423744, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1036 | +{ "start": 205520896, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1037 | +{ "start": 207618048, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1038 | +{ "start": 209715200, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1039 | +{ "start": 211812352, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1040 | +{ "start": 213909504, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1041 | +{ "start": 216006656, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1042 | +{ "start": 218103808, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1043 | +{ "start": 220200960, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1044 | +{ "start": 222298112, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1045 | +{ "start": 224395264, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1046 | +{ "start": 226492416, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1047 | +{ "start": 228589568, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1048 | +{ "start": 230686720, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1049 | +{ "start": 232783872, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1050 | +{ "start": 234881024, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1051 | +{ "start": 236978176, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1052 | +{ "start": 239075328, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1053 | +{ "start": 241172480, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1054 | +{ "start": 243269632, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1055 | +{ "start": 245366784, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1056 | +{ "start": 247463936, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1057 | +{ "start": 249561088, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1058 | +{ "start": 251658240, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1059 | +{ "start": 253755392, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1060 | +{ "start": 255852544, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1061 | +{ "start": 257949696, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1062 | +{ "start": 260046848, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1063 | +{ "start": 262144000, "length": 1310720, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1064 | |||
1065 | === Testing Image create, default === | ||
1066 | |||
1067 | @@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 | ||
1068 | |||
1069 | === Read created image, default opts ==== | ||
1070 | |||
1071 | -[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1072 | +[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1073 | |||
1074 | === Read created image, force_size_calc=chs ==== | ||
1075 | |||
1076 | -[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1077 | +[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1078 | |||
1079 | === Read created image, force_size_calc=current_size ==== | ||
1080 | |||
1081 | -[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1082 | +[{ "start": 0, "length": 4295467008, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1083 | |||
1084 | === Testing Image create, force_size === | ||
1085 | |||
1086 | @@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 | ||
1087 | |||
1088 | === Read created image, default opts ==== | ||
1089 | |||
1090 | -[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1091 | +[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1092 | |||
1093 | === Read created image, force_size_calc=chs ==== | ||
1094 | |||
1095 | -[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1096 | +[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1097 | |||
1098 | === Read created image, force_size_calc=current_size ==== | ||
1099 | |||
1100 | -[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1101 | +[{ "start": 0, "length": 4294967296, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1102 | *** done | ||
1103 | diff --git a/tests/qemu-iotests/154.out b/tests/qemu-iotests/154.out | ||
1104 | index XXXXXXX..XXXXXXX 100644 | ||
1105 | --- a/tests/qemu-iotests/154.out | ||
1106 | +++ b/tests/qemu-iotests/154.out | ||
1107 | @@ -XXX,XX +XXX,XX @@ wrote 2048/2048 bytes at offset 17408 | ||
1108 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1109 | wrote 2048/2048 bytes at offset 27648 | ||
1110 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1111 | -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1112 | -{ "start": 4096, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1113 | -{ "start": 8192, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1114 | -{ "start": 12288, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1115 | -{ "start": 16384, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1116 | -{ "start": 20480, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1117 | -{ "start": 24576, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1118 | -{ "start": 32768, "length": 134184960, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1119 | +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1120 | +{ "start": 4096, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1121 | +{ "start": 8192, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1122 | +{ "start": 12288, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1123 | +{ "start": 16384, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1124 | +{ "start": 20480, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1125 | +{ "start": 24576, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1126 | +{ "start": 32768, "length": 134184960, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1127 | |||
1128 | == backing file contains non-zero data before write_zeroes == | ||
1129 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1130 | @@ -XXX,XX +XXX,XX @@ read 1024/1024 bytes at offset 65536 | ||
1131 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1132 | read 2048/2048 bytes at offset 67584 | ||
1133 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1134 | -[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1135 | -{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1136 | -{ "start": 36864, "length": 28672, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1137 | -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1138 | -{ "start": 69632, "length": 134148096, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1139 | +[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1140 | +{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1141 | +{ "start": 36864, "length": 28672, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1142 | +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1143 | +{ "start": 69632, "length": 134148096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1144 | |||
1145 | == backing file contains non-zero data after write_zeroes == | ||
1146 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1147 | @@ -XXX,XX +XXX,XX @@ read 1024/1024 bytes at offset 44032 | ||
1148 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1149 | read 3072/3072 bytes at offset 40960 | ||
1150 | 3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1151 | -[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1152 | -{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1153 | -{ "start": 36864, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1154 | -{ "start": 40960, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1155 | -{ "start": 45056, "length": 134172672, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1156 | +[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1157 | +{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1158 | +{ "start": 36864, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1159 | +{ "start": 40960, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1160 | +{ "start": 45056, "length": 134172672, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1161 | |||
1162 | == write_zeroes covers non-zero data == | ||
1163 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1164 | @@ -XXX,XX +XXX,XX @@ wrote 2048/2048 bytes at offset 29696 | ||
1165 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1166 | read 4096/4096 bytes at offset 28672 | ||
1167 | 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1168 | -[{ "start": 0, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1169 | -{ "start": 4096, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1170 | -{ "start": 8192, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1171 | -{ "start": 12288, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1172 | -{ "start": 16384, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1173 | -{ "start": 20480, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1174 | -{ "start": 24576, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1175 | -{ "start": 28672, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1176 | -{ "start": 32768, "length": 134184960, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1177 | +[{ "start": 0, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1178 | +{ "start": 4096, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1179 | +{ "start": 8192, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1180 | +{ "start": 12288, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1181 | +{ "start": 16384, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1182 | +{ "start": 20480, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1183 | +{ "start": 24576, "length": 4096, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1184 | +{ "start": 28672, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1185 | +{ "start": 32768, "length": 134184960, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1186 | |||
1187 | == spanning two clusters, non-zero before request == | ||
1188 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1189 | @@ -XXX,XX +XXX,XX @@ read 1024/1024 bytes at offset 67584 | ||
1190 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1191 | read 5120/5120 bytes at offset 68608 | ||
1192 | 5 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1193 | -[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1194 | -{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1195 | -{ "start": 36864, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1196 | -{ "start": 40960, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1197 | -{ "start": 49152, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1198 | -{ "start": 53248, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1199 | -{ "start": 57344, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1200 | -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1201 | -{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1202 | -{ "start": 73728, "length": 134144000, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1203 | +[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1204 | +{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1205 | +{ "start": 36864, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1206 | +{ "start": 40960, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1207 | +{ "start": 49152, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1208 | +{ "start": 53248, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1209 | +{ "start": 57344, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1210 | +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1211 | +{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1212 | +{ "start": 73728, "length": 134144000, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1213 | |||
1214 | == spanning two clusters, non-zero after request == | ||
1215 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1216 | @@ -XXX,XX +XXX,XX @@ read 7168/7168 bytes at offset 65536 | ||
1217 | 7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1218 | read 1024/1024 bytes at offset 72704 | ||
1219 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1220 | -[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1221 | -{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1222 | -{ "start": 36864, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1223 | -{ "start": 40960, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1224 | -{ "start": 49152, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1225 | -{ "start": 53248, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1226 | -{ "start": 57344, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1227 | -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1228 | -{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1229 | -{ "start": 73728, "length": 134144000, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1230 | +[{ "start": 0, "length": 32768, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1231 | +{ "start": 32768, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1232 | +{ "start": 36864, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1233 | +{ "start": 40960, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1234 | +{ "start": 49152, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1235 | +{ "start": 53248, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1236 | +{ "start": 57344, "length": 8192, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1237 | +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1238 | +{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1239 | +{ "start": 73728, "length": 134144000, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1240 | |||
1241 | == spanning two clusters, partially overwriting backing file == | ||
1242 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1243 | @@ -XXX,XX +XXX,XX @@ read 1024/1024 bytes at offset 5120 | ||
1244 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1245 | read 2048/2048 bytes at offset 6144 | ||
1246 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1247 | -[{ "start": 0, "length": 8192, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1248 | -{ "start": 8192, "length": 134209536, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1249 | +[{ "start": 0, "length": 8192, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1250 | +{ "start": 8192, "length": 134209536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1251 | |||
1252 | == spanning multiple clusters, non-zero in first cluster == | ||
1253 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1254 | @@ -XXX,XX +XXX,XX @@ read 2048/2048 bytes at offset 65536 | ||
1255 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1256 | read 10240/10240 bytes at offset 67584 | ||
1257 | 10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1258 | -[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1259 | -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1260 | -{ "start": 69632, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1261 | -{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1262 | +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1263 | +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1264 | +{ "start": 69632, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1265 | +{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1266 | |||
1267 | == spanning multiple clusters, non-zero in intermediate cluster == | ||
1268 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1269 | @@ -XXX,XX +XXX,XX @@ wrote 7168/7168 bytes at offset 67584 | ||
1270 | 7 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1271 | read 12288/12288 bytes at offset 65536 | ||
1272 | 12 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1273 | -[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1274 | -{ "start": 65536, "length": 12288, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1275 | -{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1276 | +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1277 | +{ "start": 65536, "length": 12288, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1278 | +{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1279 | |||
1280 | == spanning multiple clusters, non-zero in final cluster == | ||
1281 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1282 | @@ -XXX,XX +XXX,XX @@ read 10240/10240 bytes at offset 65536 | ||
1283 | 10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1284 | read 2048/2048 bytes at offset 75776 | ||
1285 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1286 | -[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1287 | -{ "start": 65536, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1288 | -{ "start": 73728, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1289 | -{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1290 | +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1291 | +{ "start": 65536, "length": 8192, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1292 | +{ "start": 73728, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1293 | +{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1294 | |||
1295 | == spanning multiple clusters, partially overwriting backing file == | ||
1296 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 | ||
1297 | @@ -XXX,XX +XXX,XX @@ read 2048/2048 bytes at offset 74752 | ||
1298 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1299 | read 1024/1024 bytes at offset 76800 | ||
1300 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1301 | -[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1302 | -{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1303 | -{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1304 | -{ "start": 73728, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1305 | -{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1306 | +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1307 | +{ "start": 65536, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1308 | +{ "start": 69632, "length": 4096, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1309 | +{ "start": 73728, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1310 | +{ "start": 77824, "length": 134139904, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1311 | |||
1312 | == unaligned image tail cluster, no allocation needed == | ||
1313 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 | ||
1314 | wrote 512/512 bytes at offset 134217728 | ||
1315 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1316 | 2048/2048 bytes allocated at offset 128 MiB | ||
1317 | -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1318 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1319 | +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1320 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1321 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 | ||
1322 | wrote 512/512 bytes at offset 134219264 | ||
1323 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1324 | 2048/2048 bytes allocated at offset 128 MiB | ||
1325 | -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1326 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1327 | +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1328 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1329 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 | ||
1330 | wrote 1024/1024 bytes at offset 134218240 | ||
1331 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1332 | 2048/2048 bytes allocated at offset 128 MiB | ||
1333 | -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1334 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1335 | +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1336 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1337 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 | ||
1338 | wrote 2048/2048 bytes at offset 134217728 | ||
1339 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1340 | 2048/2048 bytes allocated at offset 128 MiB | ||
1341 | -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1342 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1343 | +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1344 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1345 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752 | ||
1346 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1347 | wrote 512/512 bytes at offset 134217728 | ||
1348 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1349 | 2048/2048 bytes allocated at offset 128 MiB | ||
1350 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1351 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1352 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1353 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1354 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1355 | wrote 512/512 bytes at offset 134219264 | ||
1356 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1357 | 2048/2048 bytes allocated at offset 128 MiB | ||
1358 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1359 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1360 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1361 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1362 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1363 | wrote 1024/1024 bytes at offset 134218240 | ||
1364 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1365 | 2048/2048 bytes allocated at offset 128 MiB | ||
1366 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1367 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1368 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1369 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1370 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1371 | wrote 2048/2048 bytes at offset 134217728 | ||
1372 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1373 | 2048/2048 bytes allocated at offset 128 MiB | ||
1374 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1375 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1376 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1377 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1378 | wrote 512/512 bytes at offset 134217728 | ||
1379 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1380 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1381 | wrote 512/512 bytes at offset 134217728 | ||
1382 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1383 | 2048/2048 bytes allocated at offset 128 MiB | ||
1384 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1385 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1386 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1387 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1388 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1389 | wrote 512/512 bytes at offset 134219264 | ||
1390 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1391 | 2048/2048 bytes allocated at offset 128 MiB | ||
1392 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1393 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1394 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1395 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1396 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1397 | wrote 1024/1024 bytes at offset 134218240 | ||
1398 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1399 | 2048/2048 bytes allocated at offset 128 MiB | ||
1400 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1401 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1402 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1403 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1404 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1405 | wrote 2048/2048 bytes at offset 134217728 | ||
1406 | 2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1407 | 2048/2048 bytes allocated at offset 128 MiB | ||
1408 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1409 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1410 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1411 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1412 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134218752 | ||
1413 | wrote 1024/1024 bytes at offset 134217728 | ||
1414 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1415 | @@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 134217728 | ||
1416 | read 512/512 bytes at offset 134218240 | ||
1417 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1418 | 1024/1024 bytes allocated at offset 128 MiB | ||
1419 | -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1420 | -{ "start": 134217728, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1421 | +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1422 | +{ "start": 134217728, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1423 | wrote 1024/1024 bytes at offset 134217728 | ||
1424 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1425 | 1024/1024 bytes allocated at offset 128 MiB | ||
1426 | read 1024/1024 bytes at offset 134217728 | ||
1427 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1428 | -[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1429 | -{ "start": 134217728, "length": 1024, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1430 | +[{ "start": 0, "length": 134217728, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1431 | +{ "start": 134217728, "length": 1024, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1432 | |||
1433 | == unaligned image tail cluster, allocation required == | ||
1434 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752 | ||
1435 | @@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 134217728 | ||
1436 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1437 | read 1536/1536 bytes at offset 134218240 | ||
1438 | 1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1439 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1440 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1441 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1442 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1443 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752 | ||
1444 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT | ||
1445 | wrote 512/512 bytes at offset 134218240 | ||
1446 | @@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 134218240 | ||
1447 | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1448 | read 1024/1024 bytes at offset 134218752 | ||
1449 | 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1450 | -[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1451 | -{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1452 | +[{ "start": 0, "length": 134217728, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1453 | +{ "start": 134217728, "length": 2048, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1454 | *** done | ||
1455 | diff --git a/tests/qemu-iotests/179.out b/tests/qemu-iotests/179.out | ||
1456 | index XXXXXXX..XXXXXXX 100644 | ||
1457 | --- a/tests/qemu-iotests/179.out | ||
1458 | +++ b/tests/qemu-iotests/179.out | ||
1459 | @@ -XXX,XX +XXX,XX @@ wrote 2097152/2097152 bytes at offset 6291456 | ||
1460 | 2 MiB (0x200000) bytes not allocated at offset 4 MiB (0x400000) | ||
1461 | 2 MiB (0x200000) bytes allocated at offset 6 MiB (0x600000) | ||
1462 | 56 MiB (0x3800000) bytes not allocated at offset 8 MiB (0x800000) | ||
1463 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1464 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1465 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1466 | -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1467 | -{ "start": 8388608, "length": 58720256, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1468 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1469 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1470 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1471 | +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1472 | +{ "start": 8388608, "length": 58720256, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1473 | wrote 2097150/2097150 bytes at offset 10485761 | ||
1474 | 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1475 | wrote 2097150/2097150 bytes at offset 14680065 | ||
1476 | @@ -XXX,XX +XXX,XX @@ wrote 2097150/2097150 bytes at offset 14680065 | ||
1477 | 2 MiB (0x200000) bytes not allocated at offset 12 MiB (0xc00000) | ||
1478 | 2 MiB (0x200000) bytes allocated at offset 14 MiB (0xe00000) | ||
1479 | 48 MiB (0x3000000) bytes not allocated at offset 16 MiB (0x1000000) | ||
1480 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1481 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1482 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1483 | -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1484 | -{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1485 | -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1486 | -{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1487 | -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1488 | -{ "start": 16777216, "length": 50331648, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1489 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1490 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1491 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1492 | +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1493 | +{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1494 | +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1495 | +{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1496 | +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1497 | +{ "start": 16777216, "length": 50331648, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1498 | wrote 14680064/14680064 bytes at offset 18874368 | ||
1499 | 14 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1500 | wrote 2097152/2097152 bytes at offset 20971520 | ||
1501 | @@ -XXX,XX +XXX,XX @@ wrote 6291456/6291456 bytes at offset 25165824 | ||
1502 | 2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000) | ||
1503 | 14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000) | ||
1504 | 32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000) | ||
1505 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1506 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1507 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1508 | -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1509 | -{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1510 | -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1511 | -{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1512 | -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1513 | -{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1514 | -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1515 | -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1516 | -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1517 | -{ "start": 25165824, "length": 6291456, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1518 | -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1519 | -{ "start": 33554432, "length": 33554432, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1520 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1521 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1522 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1523 | +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1524 | +{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1525 | +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1526 | +{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1527 | +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1528 | +{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1529 | +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1530 | +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1531 | +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1532 | +{ "start": 25165824, "length": 6291456, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1533 | +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1534 | +{ "start": 33554432, "length": 33554432, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1535 | wrote 2097152/2097152 bytes at offset 27262976 | ||
1536 | 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1537 | wrote 2097152/2097152 bytes at offset 29360128 | ||
1538 | @@ -XXX,XX +XXX,XX @@ wrote 2097152/2097152 bytes at offset 29360128 | ||
1539 | 2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000) | ||
1540 | 14 MiB (0xe00000) bytes allocated at offset 18 MiB (0x1200000) | ||
1541 | 32 MiB (0x2000000) bytes not allocated at offset 32 MiB (0x2000000) | ||
1542 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1543 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1544 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1545 | -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1546 | -{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1547 | -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1548 | -{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1549 | -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1550 | -{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1551 | -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1552 | -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1553 | -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1554 | -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1555 | -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1556 | -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1557 | -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1558 | -{ "start": 33554432, "length": 33554432, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1559 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1560 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1561 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1562 | +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1563 | +{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1564 | +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1565 | +{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1566 | +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1567 | +{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1568 | +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1569 | +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1570 | +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1571 | +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1572 | +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1573 | +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1574 | +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1575 | +{ "start": 33554432, "length": 33554432, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1576 | wrote 8388608/8388608 bytes at offset 33554432 | ||
1577 | 8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1578 | wrote 2097152/2097152 bytes at offset 35651584 | ||
1579 | @@ -XXX,XX +XXX,XX @@ wrote 2097152/2097152 bytes at offset 37748736 | ||
1580 | 2 MiB (0x200000) bytes not allocated at offset 16 MiB (0x1000000) | ||
1581 | 22 MiB (0x1600000) bytes allocated at offset 18 MiB (0x1200000) | ||
1582 | 24 MiB (0x1800000) bytes not allocated at offset 40 MiB (0x2800000) | ||
1583 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1584 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1585 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1586 | -{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1587 | -{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1588 | -{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1589 | -{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1590 | -{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1591 | -{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1592 | -{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1593 | -{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1594 | -{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1595 | -{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1596 | -{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1597 | -{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1598 | -{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1599 | -{ "start": 33554432, "length": 8388608, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1600 | -{ "start": 41943040, "length": 25165824, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1601 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1602 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1603 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1604 | +{ "start": 6291456, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1605 | +{ "start": 8388608, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1606 | +{ "start": 10485760, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1607 | +{ "start": 12582912, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1608 | +{ "start": 14680064, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1609 | +{ "start": 16777216, "length": 2097152, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1610 | +{ "start": 18874368, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1611 | +{ "start": 20971520, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1612 | +{ "start": 23068672, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1613 | +{ "start": 25165824, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1614 | +{ "start": 27262976, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1615 | +{ "start": 29360128, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1616 | +{ "start": 31457280, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1617 | +{ "start": 33554432, "length": 8388608, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1618 | +{ "start": 41943040, "length": 25165824, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1619 | wrote 8388608/8388608 bytes at offset 41943040 | ||
1620 | 8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1621 | wrote 8388608/8388608 bytes at offset 50331648 | ||
1622 | @@ -XXX,XX +XXX,XX @@ wrote 2097152/2097152 bytes at offset 62914560 | ||
1623 | 4 MiB (0x400000) bytes not allocated at offset 54 MiB (0x3600000) | ||
1624 | 4 MiB (0x400000) bytes allocated at offset 58 MiB (0x3a00000) | ||
1625 | 2 MiB (0x200000) bytes not allocated at offset 62 MiB (0x3e00000) | ||
1626 | -[{ "start": 0, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1627 | -{ "start": 2097152, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, | ||
1628 | -{ "start": 4194304, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1629 | -{ "start": 6291456, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, | ||
1630 | -{ "start": 8388608, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1631 | -{ "start": 10485760, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, | ||
1632 | -{ "start": 12582912, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1633 | -{ "start": 14680064, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, | ||
1634 | -{ "start": 16777216, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1635 | -{ "start": 18874368, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1636 | -{ "start": 20971520, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, | ||
1637 | -{ "start": 23068672, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1638 | -{ "start": 25165824, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1639 | -{ "start": 27262976, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, | ||
1640 | -{ "start": 29360128, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1641 | -{ "start": 31457280, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1642 | -{ "start": 33554432, "length": 10485760, "depth": 1, "present": true, "zero": true, "data": false}, | ||
1643 | -{ "start": 44040192, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1644 | -{ "start": 48234496, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false}, | ||
1645 | -{ "start": 50331648, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1646 | -{ "start": 52428800, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1647 | -{ "start": 56623104, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1648 | -{ "start": 58720256, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1649 | -{ "start": 60817408, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1650 | -{ "start": 65011712, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1651 | +[{ "start": 0, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1652 | +{ "start": 2097152, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1653 | +{ "start": 4194304, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1654 | +{ "start": 6291456, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1655 | +{ "start": 8388608, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1656 | +{ "start": 10485760, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1657 | +{ "start": 12582912, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1658 | +{ "start": 14680064, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1659 | +{ "start": 16777216, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1660 | +{ "start": 18874368, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1661 | +{ "start": 20971520, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1662 | +{ "start": 23068672, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1663 | +{ "start": 25165824, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1664 | +{ "start": 27262976, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1665 | +{ "start": 29360128, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1666 | +{ "start": 31457280, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1667 | +{ "start": 33554432, "length": 10485760, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1668 | +{ "start": 44040192, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1669 | +{ "start": 48234496, "length": 2097152, "depth": 1, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1670 | +{ "start": 50331648, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1671 | +{ "start": 52428800, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1672 | +{ "start": 56623104, "length": 2097152, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1673 | +{ "start": 58720256, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1674 | +{ "start": 60817408, "length": 4194304, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1675 | +{ "start": 65011712, "length": 2097152, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1676 | No errors were found on the image. | ||
1677 | No errors were found on the image. | ||
1678 | |||
1679 | diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out | ||
1680 | index XXXXXXX..XXXXXXX 100644 | ||
1681 | --- a/tests/qemu-iotests/209.out | ||
1682 | +++ b/tests/qemu-iotests/209.out | ||
1683 | @@ -XXX,XX +XXX,XX @@ | ||
1684 | -[{ "start": 0, "length": 524288, "depth": 0, "present": true, "zero": false, "data": true, "offset": 0}, | ||
1685 | -{ "start": 524288, "length": 524288, "depth": 0, "present": true, "zero": true, "data": false, "offset": 524288}] | ||
1686 | +[{ "start": 0, "length": 524288, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 0}, | ||
1687 | +{ "start": 524288, "length": 524288, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 524288}] | ||
1688 | |||
1689 | done. | ||
1690 | diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out | ||
1691 | index XXXXXXX..XXXXXXX 100644 | ||
1692 | --- a/tests/qemu-iotests/221.out | ||
1693 | +++ b/tests/qemu-iotests/221.out | ||
1694 | @@ -XXX,XX +XXX,XX @@ QA output created by 221 | ||
1695 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65537 | ||
1696 | discard 65537/65537 bytes at offset 0 | ||
1697 | 64.001 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1698 | -[{ "start": 0, "length": 66048, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1699 | -[{ "start": 0, "length": 66048, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1700 | +[{ "start": 0, "length": 66048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1701 | +[{ "start": 0, "length": 66048, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1702 | wrote 1/1 bytes at offset 65536 | ||
1703 | 1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1704 | -[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1705 | -{ "start": 65536, "length": 1, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1706 | -{ "start": 65537, "length": 511, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1707 | -[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1708 | -{ "start": 65536, "length": 1, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1709 | -{ "start": 65537, "length": 511, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1710 | +[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1711 | +{ "start": 65536, "length": 1, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1712 | +{ "start": 65537, "length": 511, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1713 | +[{ "start": 0, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1714 | +{ "start": 65536, "length": 1, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1715 | +{ "start": 65537, "length": 511, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1716 | *** done | ||
1717 | diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out | ||
1718 | index XXXXXXX..XXXXXXX 100644 | ||
1719 | --- a/tests/qemu-iotests/223.out | ||
1720 | +++ b/tests/qemu-iotests/223.out | ||
1721 | @@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 1048576 | ||
1722 | 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1723 | read 2097152/2097152 bytes at offset 2097152 | ||
1724 | 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1725 | -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1726 | -{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1727 | -{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1728 | -[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false}, | ||
1729 | -{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1730 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] | ||
1731 | +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1732 | +{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1733 | +{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1734 | +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
1735 | +{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1736 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] | ||
1737 | |||
1738 | === Contrast to small granularity dirty-bitmap === | ||
1739 | |||
1740 | -[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1741 | -{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false}, | ||
1742 | -{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1743 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] | ||
1744 | +[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1745 | +{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
1746 | +{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1747 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] | ||
1748 | |||
1749 | === Check bitmap taken from another node === | ||
1750 | |||
1751 | -[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1752 | +[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1753 | |||
1754 | === End qemu NBD server === | ||
1755 | |||
1756 | @@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 1048576 | ||
1757 | 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1758 | read 2097152/2097152 bytes at offset 2097152 | ||
1759 | 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1760 | -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1761 | -{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1762 | -{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1763 | -[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false}, | ||
1764 | -{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1765 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] | ||
1766 | +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1767 | +{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1768 | +{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1769 | +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
1770 | +{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1771 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] | ||
1772 | |||
1773 | === Contrast to small granularity dirty-bitmap === | ||
1774 | |||
1775 | -[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1776 | -{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false}, | ||
1777 | -{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1778 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] | ||
1779 | +[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1780 | +{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
1781 | +{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1782 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] | ||
1783 | |||
1784 | === Check bitmap taken from another node === | ||
1785 | |||
1786 | -[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1787 | +[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1788 | |||
1789 | === End qemu NBD server === | ||
1790 | |||
1791 | @@ -XXX,XX +XXX,XX @@ read 2097152/2097152 bytes at offset 2097152 | ||
1792 | |||
1793 | === Use qemu-nbd as server === | ||
1794 | |||
1795 | -[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false}, | ||
1796 | -{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1797 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] | ||
1798 | -[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1799 | -{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false}, | ||
1800 | -{ "start": 1024, "length": 11321, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1801 | -[{ "start": 12345, "length": 2084807, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1802 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}] | ||
1803 | +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
1804 | +{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1805 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] | ||
1806 | +[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1807 | +{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
1808 | +{ "start": 1024, "length": 11321, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1809 | +[{ "start": 12345, "length": 2084807, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1810 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}] | ||
1811 | *** done | ||
1812 | diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out | ||
1813 | index XXXXXXX..XXXXXXX 100644 | ||
1814 | --- a/tests/qemu-iotests/241.out | ||
1815 | +++ b/tests/qemu-iotests/241.out | ||
1816 | @@ -XXX,XX +XXX,XX @@ exports available: 1 | ||
1817 | export: '' | ||
1818 | size: 1024 | ||
1819 | min block: 1 | ||
1820 | -[{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1821 | -{ "start": 1000, "length": 24, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1822 | +[{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1823 | +{ "start": 1000, "length": 24, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1824 | 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) | ||
1825 | |||
1826 | === Exporting unaligned raw image, forced server sector alignment === | ||
1827 | @@ -XXX,XX +XXX,XX @@ exports available: 1 | ||
1828 | export: '' | ||
1829 | size: 1024 | ||
1830 | min block: 512 | ||
1831 | -[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1832 | +[{ "start": 0, "length": 1024, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1833 | 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) | ||
1834 | WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. | ||
1835 | Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. | ||
1836 | @@ -XXX,XX +XXX,XX @@ exports available: 1 | ||
1837 | export: '' | ||
1838 | size: 1024 | ||
1839 | min block: 1 | ||
1840 | -[{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1841 | -{ "start": 1000, "length": 24, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1842 | +[{ "start": 0, "length": 1000, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1843 | +{ "start": 1000, "length": 24, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1844 | 1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) | ||
1845 | *** done | ||
1846 | diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out | ||
1847 | index XXXXXXX..XXXXXXX 100644 | ||
1848 | --- a/tests/qemu-iotests/244.out | ||
1849 | +++ b/tests/qemu-iotests/244.out | ||
1850 | @@ -XXX,XX +XXX,XX @@ wrote 3145728/3145728 bytes at offset 3145728 | ||
1851 | 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1852 | No errors were found on the image. | ||
1853 | |||
1854 | -[{ "start": 0, "length": 1048576, "depth": 0, "present": false, "zero": true, "data": false}, | ||
1855 | -{ "start": 1048576, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "offset": 1048576}, | ||
1856 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1857 | -{ "start": 4194304, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "offset": 4194304}, | ||
1858 | -{ "start": 5242880, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1859 | -{ "start": 6291456, "length": 60817408, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1860 | +[{ "start": 0, "length": 1048576, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1861 | +{ "start": 1048576, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 1048576}, | ||
1862 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1863 | +{ "start": 4194304, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 4194304}, | ||
1864 | +{ "start": 5242880, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1865 | +{ "start": 6291456, "length": 60817408, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1866 | |||
1867 | read 1048576/1048576 bytes at offset 0 | ||
1868 | 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1869 | @@ -XXX,XX +XXX,XX @@ wrote 3145728/3145728 bytes at offset 3145728 | ||
1870 | 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1871 | No errors were found on the image. | ||
1872 | |||
1873 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": 0}, | ||
1874 | -{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1875 | -{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "offset": 4194304}, | ||
1876 | -{ "start": 6291456, "length": 60817408, "depth": 0, "present": true, "zero": false, "data": true, "offset": 6291456}] | ||
1877 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 0}, | ||
1878 | +{ "start": 2097152, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1879 | +{ "start": 4194304, "length": 2097152, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 4194304}, | ||
1880 | +{ "start": 6291456, "length": 60817408, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 6291456}] | ||
1881 | |||
1882 | read 1048576/1048576 bytes at offset 0 | ||
1883 | 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1884 | @@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 0 | ||
1885 | 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1886 | Offset Length Mapped to File | ||
1887 | 0 0x100000 0 TEST_DIR/t.qcow2.data | ||
1888 | -[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "offset": 0}, | ||
1889 | -{ "start": 1048576, "length": 66060288, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1890 | +[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 0}, | ||
1891 | +{ "start": 1048576, "length": 66060288, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1892 | |||
1893 | === Copy offloading === | ||
1894 | |||
1895 | diff --git a/tests/qemu-iotests/252.out b/tests/qemu-iotests/252.out | ||
1896 | index XXXXXXX..XXXXXXX 100644 | ||
1897 | --- a/tests/qemu-iotests/252.out | ||
1898 | +++ b/tests/qemu-iotests/252.out | ||
1899 | @@ -XXX,XX +XXX,XX @@ read 131072/131072 bytes at offset 131072 | ||
1900 | read 131072/131072 bytes at offset 262144 | ||
1901 | 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1902 | |||
1903 | -[{ "start": 0, "length": 262144, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1904 | -{ "start": 262144, "length": 131072, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1905 | +[{ "start": 0, "length": 262144, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1906 | +{ "start": 262144, "length": 131072, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1907 | |||
1908 | read 131072/131072 bytes at offset 0 | ||
1909 | 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1910 | @@ -XXX,XX +XXX,XX @@ read 131072/131072 bytes at offset 131072 | ||
1911 | read 131072/131072 bytes at offset 262144 | ||
1912 | 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1913 | |||
1914 | -[{ "start": 0, "length": 262144, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1915 | -{ "start": 262144, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false}, | ||
1916 | -{ "start": 327680, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false}] | ||
1917 | +[{ "start": 0, "length": 262144, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1918 | +{ "start": 262144, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, | ||
1919 | +{ "start": 327680, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1920 | *** done | ||
1921 | diff --git a/tests/qemu-iotests/253.out b/tests/qemu-iotests/253.out | ||
1922 | index XXXXXXX..XXXXXXX 100644 | ||
1923 | --- a/tests/qemu-iotests/253.out | ||
1924 | +++ b/tests/qemu-iotests/253.out | ||
1925 | @@ -XXX,XX +XXX,XX @@ QA output created by 253 | ||
1926 | === Check mapping of unaligned raw image === | ||
1927 | |||
1928 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048575 | ||
1929 | -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1930 | -{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1931 | -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1932 | -{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
1933 | +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1934 | +{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1935 | +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1936 | +{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
1937 | wrote 65535/65535 bytes at offset 983040 | ||
1938 | 63.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
1939 | -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1940 | -{ "start": 4096, "length": 978944, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1941 | -{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1942 | -[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
1943 | -{ "start": 4096, "length": 978944, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}, | ||
1944 | -{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
1945 | +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1946 | +{ "start": 4096, "length": 978944, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1947 | +{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1948 | +[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
1949 | +{ "start": 4096, "length": 978944, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}, | ||
1950 | +{ "start": 983040, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
1951 | *** done | ||
1952 | diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out | ||
1953 | index XXXXXXX..XXXXXXX 100644 | ||
1954 | --- a/tests/qemu-iotests/274.out | ||
1955 | +++ b/tests/qemu-iotests/274.out | ||
1956 | @@ -XXX,XX +XXX,XX @@ read 1048576/1048576 bytes at offset 1048576 | ||
1957 | 0/1048576 bytes allocated at offset 1 MiB | ||
1958 | |||
1959 | === Checking map === | ||
1960 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] | ||
1961 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] | ||
1962 | |||
1963 | Offset Length Mapped to File | ||
1964 | 0 0x200000 0x50000 TEST_DIR/PID-base | ||
1965 | |||
1966 | -[{ "start": 0, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "offset": 327680}] | ||
1967 | +[{ "start": 0, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] | ||
1968 | |||
1969 | Offset Length Mapped to File | ||
1970 | 0 0x100000 0x50000 TEST_DIR/PID-base | ||
1971 | |||
1972 | -[{ "start": 0, "length": 1048576, "depth": 2, "present": true, "zero": false, "data": true, "offset": 327680}, | ||
1973 | -{ "start": 1048576, "length": 1048576, "depth": 0, "present": false, "zero": true, "data": false}] | ||
1974 | +[{ "start": 0, "length": 1048576, "depth": 2, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}, | ||
1975 | +{ "start": 1048576, "length": 1048576, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] | ||
1976 | |||
1977 | Offset Length Mapped to File | ||
1978 | 0 0x100000 0x50000 TEST_DIR/PID-base | ||
1979 | @@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 5368709120 | ||
1980 | 1 GiB (0x40000000) bytes not allocated at offset 0 bytes (0x0) | ||
1981 | 7 GiB (0x1c0000000) bytes allocated at offset 1 GiB (0x40000000) | ||
1982 | |||
1983 | -[{ "start": 0, "length": 1073741824, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1984 | -{ "start": 1073741824, "length": 7516192768, "depth": 0, "present": true, "zero": true, "data": false}] | ||
1985 | +[{ "start": 0, "length": 1073741824, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
1986 | +{ "start": 1073741824, "length": 7516192768, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
1987 | |||
1988 | === preallocation=metadata === | ||
1989 | wrote 65536/65536 bytes at offset 33285996544 | ||
1990 | @@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 33285996544 | ||
1991 | 30 GiB (0x780000000) bytes not allocated at offset 0 bytes (0x0) | ||
1992 | 3 GiB (0xc0000000) bytes allocated at offset 30 GiB (0x780000000) | ||
1993 | |||
1994 | -[{ "start": 0, "length": 32212254720, "depth": 1, "present": false, "zero": true, "data": false}, | ||
1995 | -{ "start": 32212254720, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 327680}, | ||
1996 | -{ "start": 32749125632, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 537264128}, | ||
1997 | -{ "start": 33285996544, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 1074200576}, | ||
1998 | -{ "start": 33822867456, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 1611137024}, | ||
1999 | -{ "start": 34359738368, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 2148139008}, | ||
2000 | -{ "start": 34896609280, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 2685075456}] | ||
2001 | +[{ "start": 0, "length": 32212254720, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
2002 | +{ "start": 32212254720, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 327680}, | ||
2003 | +{ "start": 32749125632, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 537264128}, | ||
2004 | +{ "start": 33285996544, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 1074200576}, | ||
2005 | +{ "start": 33822867456, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 1611137024}, | ||
2006 | +{ "start": 34359738368, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 2148139008}, | ||
2007 | +{ "start": 34896609280, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": 2685075456}] | ||
2008 | |||
2009 | === preallocation=falloc === | ||
2010 | wrote 65536/65536 bytes at offset 9437184 | ||
2011 | @@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 9437184 | ||
2012 | 5 MiB (0x500000) bytes not allocated at offset 0 bytes (0x0) | ||
2013 | 10 MiB (0xa00000) bytes allocated at offset 5 MiB (0x500000) | ||
2014 | |||
2015 | -[{ "start": 0, "length": 5242880, "depth": 1, "present": false, "zero": true, "data": false}, | ||
2016 | -{ "start": 5242880, "length": 10485760, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] | ||
2017 | +[{ "start": 0, "length": 5242880, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
2018 | +{ "start": 5242880, "length": 10485760, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] | ||
2019 | |||
2020 | === preallocation=full === | ||
2021 | wrote 65536/65536 bytes at offset 11534336 | ||
2022 | @@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 11534336 | ||
2023 | 8 MiB (0x800000) bytes not allocated at offset 0 bytes (0x0) | ||
2024 | 4 MiB (0x400000) bytes allocated at offset 8 MiB (0x800000) | ||
2025 | |||
2026 | -[{ "start": 0, "length": 8388608, "depth": 1, "present": false, "zero": true, "data": false}, | ||
2027 | -{ "start": 8388608, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] | ||
2028 | +[{ "start": 0, "length": 8388608, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
2029 | +{ "start": 8388608, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}] | ||
2030 | |||
2031 | === preallocation=off === | ||
2032 | wrote 65536/65536 bytes at offset 259072 | ||
2033 | @@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 259072 | ||
2034 | 192 KiB (0x30000) bytes not allocated at offset 0 bytes (0x0) | ||
2035 | 320 KiB (0x50000) bytes allocated at offset 192 KiB (0x30000) | ||
2036 | |||
2037 | -[{ "start": 0, "length": 196608, "depth": 1, "present": false, "zero": true, "data": false}, | ||
2038 | -{ "start": 196608, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}, | ||
2039 | -{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}] | ||
2040 | +[{ "start": 0, "length": 196608, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
2041 | +{ "start": 196608, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}, | ||
2042 | +{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
2043 | |||
2044 | === preallocation=off === | ||
2045 | wrote 65536/65536 bytes at offset 344064 | ||
2046 | @@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 344064 | ||
2047 | 256 KiB (0x40000) bytes not allocated at offset 0 bytes (0x0) | ||
2048 | 256 KiB (0x40000) bytes allocated at offset 256 KiB (0x40000) | ||
2049 | |||
2050 | -[{ "start": 0, "length": 262144, "depth": 1, "present": false, "zero": true, "data": false}, | ||
2051 | -{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}] | ||
2052 | +[{ "start": 0, "length": 262144, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
2053 | +{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
2054 | |||
2055 | === preallocation=off === | ||
2056 | wrote 65536/65536 bytes at offset 446464 | ||
2057 | @@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 446464 | ||
2058 | 256 KiB (0x40000) bytes not allocated at offset 0 bytes (0x0) | ||
2059 | 244 KiB (0x3d000) bytes allocated at offset 256 KiB (0x40000) | ||
2060 | |||
2061 | -[{ "start": 0, "length": 262144, "depth": 1, "present": false, "zero": true, "data": false}, | ||
2062 | -{ "start": 262144, "length": 249856, "depth": 0, "present": true, "zero": true, "data": false}] | ||
2063 | +[{ "start": 0, "length": 262144, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, | ||
2064 | +{ "start": 262144, "length": 249856, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}] | ||
2065 | |||
2066 | diff --git a/tests/qemu-iotests/tests/nbd-qemu-allocation.out b/tests/qemu-iotests/tests/nbd-qemu-allocation.out | ||
2067 | index XXXXXXX..XXXXXXX 100644 | ||
2068 | --- a/tests/qemu-iotests/tests/nbd-qemu-allocation.out | ||
2069 | +++ b/tests/qemu-iotests/tests/nbd-qemu-allocation.out | ||
2070 | @@ -XXX,XX +XXX,XX @@ wrote 2097152/2097152 bytes at offset 1048576 | ||
2071 | |||
2072 | === Check allocation over NBD === | ||
2073 | |||
2074 | -[{ "start": 0, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "offset": 327680}, | ||
2075 | -{ "start": 1048576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}, | ||
2076 | -{ "start": 3145728, "length": 1048576, "depth": 1, "present": false, "zero": true, "data": false}] | ||
2077 | +[{ "start": 0, "length": 1048576, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}, | ||
2078 | +{ "start": 1048576, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": 327680}, | ||
2079 | +{ "start": 3145728, "length": 1048576, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] | ||
2080 | exports available: 1 | ||
2081 | export: '' | ||
2082 | size: 4194304 | ||
2083 | @@ -XXX,XX +XXX,XX @@ exports available: 1 | ||
2084 | available meta contexts: 2 | ||
2085 | base:allocation | ||
2086 | qemu:allocation-depth | ||
2087 | -[{ "start": 0, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
2088 | -{ "start": 3145728, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "offset": OFFSET}] | ||
2089 | -[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": true, "offset": OFFSET}, | ||
2090 | -{ "start": 1048576, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}, | ||
2091 | -{ "start": 3145728, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
2092 | +[{ "start": 0, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
2093 | +{ "start": 3145728, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET}] | ||
2094 | +[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": true, "data": true, "compressed": false, "offset": OFFSET}, | ||
2095 | +{ "start": 1048576, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
2096 | +{ "start": 3145728, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
2097 | *** done | ||
2098 | diff --git a/tests/qemu-iotests/tests/qemu-img-bitmaps.out b/tests/qemu-iotests/tests/qemu-img-bitmaps.out | ||
2099 | index XXXXXXX..XXXXXXX 100644 | ||
2100 | --- a/tests/qemu-iotests/tests/qemu-img-bitmaps.out | ||
2101 | +++ b/tests/qemu-iotests/tests/qemu-img-bitmaps.out | ||
2102 | @@ -XXX,XX +XXX,XX @@ Format specific information: | ||
2103 | |||
2104 | === Check bitmap contents === | ||
2105 | |||
2106 | -[{ "start": 0, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
2107 | -{ "start": 3145728, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false}, | ||
2108 | -{ "start": 4194304, "length": 6291456, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
2109 | -[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
2110 | -{ "start": 1048576, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false}, | ||
2111 | -{ "start": 2097152, "length": 8388608, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
2112 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
2113 | -{ "start": 2097152, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false}, | ||
2114 | -{ "start": 3145728, "length": 7340032, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
2115 | -[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}, | ||
2116 | -{ "start": 2097152, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false}, | ||
2117 | -{ "start": 3145728, "length": 7340032, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}] | ||
2118 | +[{ "start": 0, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
2119 | +{ "start": 3145728, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
2120 | +{ "start": 4194304, "length": 6291456, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
2121 | +[{ "start": 0, "length": 1048576, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
2122 | +{ "start": 1048576, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
2123 | +{ "start": 2097152, "length": 8388608, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
2124 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
2125 | +{ "start": 2097152, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
2126 | +{ "start": 3145728, "length": 7340032, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
2127 | +[{ "start": 0, "length": 2097152, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, | ||
2128 | +{ "start": 2097152, "length": 1048576, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}, | ||
2129 | +{ "start": 3145728, "length": 7340032, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}] | ||
2130 | |||
2131 | === Check handling of inconsistent bitmap === | ||
142 | 2132 | ||
143 | -- | 2133 | -- |
144 | 2.13.6 | 2134 | 2.41.0 |
145 | |||
146 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Parameter "filename" is deprecated since commit 91589d9e5ca, v2.10.0. | 3 | The synchronous bdrv_aio_cancel() function needs the acb's AioContext so |
4 | Time to get rid of it. | 4 | it can call aio_poll() to wait for cancellation. |
5 | 5 | ||
6 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 6 | It turns out that all users run under the BQL in the main AioContext, so |
7 | this callback is not needed. | ||
8 | |||
9 | Remove the callback, mark bdrv_aio_cancel() GLOBAL_STATE_CODE just like | ||
10 | its blk_aio_cancel() caller, and poll the main loop AioContext. | ||
11 | |||
12 | The purpose of this cleanup is to identify bdrv_aio_cancel() as an API | ||
13 | that does not work with the multi-queue block layer. | ||
14 | |||
15 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
16 | Message-ID: <20230912231037.826804-2-stefanha@redhat.com> | ||
7 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 17 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
18 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
19 | Reviewed-by: Klaus Jensen <k.jensen@samsung.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | --- | 21 | --- |
10 | block/rbd.c | 16 ---------------- | 22 | include/block/aio.h | 1 - |
11 | 1 file changed, 16 deletions(-) | 23 | include/block/block-global-state.h | 2 ++ |
12 | 24 | include/block/block-io.h | 1 - | |
13 | diff --git a/block/rbd.c b/block/rbd.c | 25 | block/block-backend.c | 17 ----------------- |
14 | index XXXXXXX..XXXXXXX 100644 | 26 | block/io.c | 23 ++++++++--------------- |
15 | --- a/block/rbd.c | 27 | hw/nvme/ctrl.c | 7 ------- |
16 | +++ b/block/rbd.c | 28 | softmmu/dma-helpers.c | 8 -------- |
17 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, | 29 | util/thread-pool.c | 8 -------- |
18 | QObject *crumpled = NULL; | 30 | 8 files changed, 10 insertions(+), 57 deletions(-) |
19 | const QDictEntry *e; | 31 | |
20 | Error *local_err = NULL; | 32 | diff --git a/include/block/aio.h b/include/block/aio.h |
21 | - const char *filename; | 33 | index XXXXXXX..XXXXXXX 100644 |
22 | char *keypairs, *secretid; | 34 | --- a/include/block/aio.h |
23 | int r; | 35 | +++ b/include/block/aio.h |
24 | 36 | @@ -XXX,XX +XXX,XX @@ typedef void BlockCompletionFunc(void *opaque, int ret); | |
25 | - /* If we are given a filename, parse the filename, with precedence given to | 37 | |
26 | - * filename encoded options */ | 38 | typedef struct AIOCBInfo { |
27 | - filename = qdict_get_try_str(options, "filename"); | 39 | void (*cancel_async)(BlockAIOCB *acb); |
28 | - if (filename) { | 40 | - AioContext *(*get_aio_context)(BlockAIOCB *acb); |
29 | - warn_report("'filename' option specified. " | 41 | size_t aiocb_size; |
30 | - "This is an unsupported option, and may be deprecated " | 42 | } AIOCBInfo; |
31 | - "in the future"); | 43 | |
32 | - qemu_rbd_parse_filename(filename, options, &local_err); | 44 | diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h |
33 | - qdict_del(options, "filename"); | 45 | index XXXXXXX..XXXXXXX 100644 |
34 | - if (local_err) { | 46 | --- a/include/block/block-global-state.h |
35 | - error_propagate(errp, local_err); | 47 | +++ b/include/block/block-global-state.h |
36 | - return -EINVAL; | 48 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin_nopoll(void); |
49 | void bdrv_drain_all_end(void); | ||
50 | void bdrv_drain_all(void); | ||
51 | |||
52 | +void bdrv_aio_cancel(BlockAIOCB *acb); | ||
53 | + | ||
54 | int bdrv_has_zero_init_1(BlockDriverState *bs); | ||
55 | int bdrv_has_zero_init(BlockDriverState *bs); | ||
56 | BlockDriverState *bdrv_find_node(const char *node_name); | ||
57 | diff --git a/include/block/block-io.h b/include/block/block-io.h | ||
58 | index XXXXXXX..XXXXXXX 100644 | ||
59 | --- a/include/block/block-io.h | ||
60 | +++ b/include/block/block-io.h | ||
61 | @@ -XXX,XX +XXX,XX @@ bdrv_co_delete_file_noerr(BlockDriverState *bs); | ||
62 | |||
63 | |||
64 | /* async block I/O */ | ||
65 | -void bdrv_aio_cancel(BlockAIOCB *acb); | ||
66 | void bdrv_aio_cancel_async(BlockAIOCB *acb); | ||
67 | |||
68 | /* sg packet commands */ | ||
69 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
70 | index XXXXXXX..XXXXXXX 100644 | ||
71 | --- a/block/block-backend.c | ||
72 | +++ b/block/block-backend.c | ||
73 | @@ -XXX,XX +XXX,XX @@ | ||
74 | |||
75 | #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ | ||
76 | |||
77 | -static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); | ||
78 | - | ||
79 | typedef struct BlockBackendAioNotifier { | ||
80 | void (*attached_aio_context)(AioContext *new_context, void *opaque); | ||
81 | void (*detach_aio_context)(void *opaque); | ||
82 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockBackendAIOCB { | ||
83 | } BlockBackendAIOCB; | ||
84 | |||
85 | static const AIOCBInfo block_backend_aiocb_info = { | ||
86 | - .get_aio_context = blk_aiocb_get_aio_context, | ||
87 | .aiocb_size = sizeof(BlockBackendAIOCB), | ||
88 | }; | ||
89 | |||
90 | @@ -XXX,XX +XXX,XX @@ typedef struct BlkAioEmAIOCB { | ||
91 | bool has_returned; | ||
92 | } BlkAioEmAIOCB; | ||
93 | |||
94 | -static AioContext *blk_aio_em_aiocb_get_aio_context(BlockAIOCB *acb_) | ||
95 | -{ | ||
96 | - BlkAioEmAIOCB *acb = container_of(acb_, BlkAioEmAIOCB, common); | ||
97 | - | ||
98 | - return blk_get_aio_context(acb->rwco.blk); | ||
99 | -} | ||
100 | - | ||
101 | static const AIOCBInfo blk_aio_em_aiocb_info = { | ||
102 | .aiocb_size = sizeof(BlkAioEmAIOCB), | ||
103 | - .get_aio_context = blk_aio_em_aiocb_get_aio_context, | ||
104 | }; | ||
105 | |||
106 | static void blk_aio_complete(BlkAioEmAIOCB *acb) | ||
107 | @@ -XXX,XX +XXX,XX @@ AioContext *blk_get_aio_context(BlockBackend *blk) | ||
108 | return blk->ctx; | ||
109 | } | ||
110 | |||
111 | -static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) | ||
112 | -{ | ||
113 | - BlockBackendAIOCB *blk_acb = DO_UPCAST(BlockBackendAIOCB, common, acb); | ||
114 | - return blk_get_aio_context(blk_acb->blk); | ||
115 | -} | ||
116 | - | ||
117 | int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, | ||
118 | Error **errp) | ||
119 | { | ||
120 | diff --git a/block/io.c b/block/io.c | ||
121 | index XXXXXXX..XXXXXXX 100644 | ||
122 | --- a/block/io.c | ||
123 | +++ b/block/io.c | ||
124 | @@ -XXX,XX +XXX,XX @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, | ||
125 | /**************************************************************/ | ||
126 | /* async I/Os */ | ||
127 | |||
128 | +/** | ||
129 | + * Synchronously cancels an acb. Must be called with the BQL held and the acb | ||
130 | + * must be processed with the BQL held too (IOThreads are not allowed). | ||
131 | + * | ||
132 | + * Use bdrv_aio_cancel_async() instead when possible. | ||
133 | + */ | ||
134 | void bdrv_aio_cancel(BlockAIOCB *acb) | ||
135 | { | ||
136 | - IO_CODE(); | ||
137 | + GLOBAL_STATE_CODE(); | ||
138 | qemu_aio_ref(acb); | ||
139 | bdrv_aio_cancel_async(acb); | ||
140 | - while (acb->refcnt > 1) { | ||
141 | - if (acb->aiocb_info->get_aio_context) { | ||
142 | - aio_poll(acb->aiocb_info->get_aio_context(acb), true); | ||
143 | - } else if (acb->bs) { | ||
144 | - /* qemu_aio_ref and qemu_aio_unref are not thread-safe, so | ||
145 | - * assert that we're not using an I/O thread. Thread-safe | ||
146 | - * code should use bdrv_aio_cancel_async exclusively. | ||
147 | - */ | ||
148 | - assert(bdrv_get_aio_context(acb->bs) == qemu_get_aio_context()); | ||
149 | - aio_poll(bdrv_get_aio_context(acb->bs), true); | ||
150 | - } else { | ||
151 | - abort(); | ||
37 | - } | 152 | - } |
38 | - } | 153 | - } |
39 | - | 154 | + AIO_WAIT_WHILE_UNLOCKED(NULL, acb->refcnt > 1); |
40 | keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); | 155 | qemu_aio_unref(acb); |
41 | if (keypairs) { | 156 | } |
42 | qdict_del(options, "=keyvalue-pairs"); | 157 | |
158 | diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c | ||
159 | index XXXXXXX..XXXXXXX 100644 | ||
160 | --- a/hw/nvme/ctrl.c | ||
161 | +++ b/hw/nvme/ctrl.c | ||
162 | @@ -XXX,XX +XXX,XX @@ static inline bool nvme_is_write(NvmeRequest *req) | ||
163 | rw->opcode == NVME_CMD_WRITE_ZEROES; | ||
164 | } | ||
165 | |||
166 | -static AioContext *nvme_get_aio_context(BlockAIOCB *acb) | ||
167 | -{ | ||
168 | - return qemu_get_aio_context(); | ||
169 | -} | ||
170 | - | ||
171 | static void nvme_misc_cb(void *opaque, int ret) | ||
172 | { | ||
173 | NvmeRequest *req = opaque; | ||
174 | @@ -XXX,XX +XXX,XX @@ static void nvme_flush_cancel(BlockAIOCB *acb) | ||
175 | static const AIOCBInfo nvme_flush_aiocb_info = { | ||
176 | .aiocb_size = sizeof(NvmeFlushAIOCB), | ||
177 | .cancel_async = nvme_flush_cancel, | ||
178 | - .get_aio_context = nvme_get_aio_context, | ||
179 | }; | ||
180 | |||
181 | static void nvme_do_flush(NvmeFlushAIOCB *iocb); | ||
182 | @@ -XXX,XX +XXX,XX @@ static void nvme_format_cancel(BlockAIOCB *aiocb) | ||
183 | static const AIOCBInfo nvme_format_aiocb_info = { | ||
184 | .aiocb_size = sizeof(NvmeFormatAIOCB), | ||
185 | .cancel_async = nvme_format_cancel, | ||
186 | - .get_aio_context = nvme_get_aio_context, | ||
187 | }; | ||
188 | |||
189 | static void nvme_format_set(NvmeNamespace *ns, uint8_t lbaf, uint8_t mset, | ||
190 | diff --git a/softmmu/dma-helpers.c b/softmmu/dma-helpers.c | ||
191 | index XXXXXXX..XXXXXXX 100644 | ||
192 | --- a/softmmu/dma-helpers.c | ||
193 | +++ b/softmmu/dma-helpers.c | ||
194 | @@ -XXX,XX +XXX,XX @@ static void dma_aio_cancel(BlockAIOCB *acb) | ||
195 | } | ||
196 | } | ||
197 | |||
198 | -static AioContext *dma_get_aio_context(BlockAIOCB *acb) | ||
199 | -{ | ||
200 | - DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common); | ||
201 | - | ||
202 | - return dbs->ctx; | ||
203 | -} | ||
204 | - | ||
205 | static const AIOCBInfo dma_aiocb_info = { | ||
206 | .aiocb_size = sizeof(DMAAIOCB), | ||
207 | .cancel_async = dma_aio_cancel, | ||
208 | - .get_aio_context = dma_get_aio_context, | ||
209 | }; | ||
210 | |||
211 | BlockAIOCB *dma_blk_io(AioContext *ctx, | ||
212 | diff --git a/util/thread-pool.c b/util/thread-pool.c | ||
213 | index XXXXXXX..XXXXXXX 100644 | ||
214 | --- a/util/thread-pool.c | ||
215 | +++ b/util/thread-pool.c | ||
216 | @@ -XXX,XX +XXX,XX @@ static void thread_pool_cancel(BlockAIOCB *acb) | ||
217 | |||
218 | } | ||
219 | |||
220 | -static AioContext *thread_pool_get_aio_context(BlockAIOCB *acb) | ||
221 | -{ | ||
222 | - ThreadPoolElement *elem = (ThreadPoolElement *)acb; | ||
223 | - ThreadPool *pool = elem->pool; | ||
224 | - return pool->ctx; | ||
225 | -} | ||
226 | - | ||
227 | static const AIOCBInfo thread_pool_aiocb_info = { | ||
228 | .aiocb_size = sizeof(ThreadPoolElement), | ||
229 | .cancel_async = thread_pool_cancel, | ||
230 | - .get_aio_context = thread_pool_get_aio_context, | ||
231 | }; | ||
232 | |||
233 | BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, | ||
43 | -- | 234 | -- |
44 | 2.13.6 | 235 | 2.41.0 |
45 | |||
46 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | qdict_flatten_qdict() skips copying scalars from @qdict to @target | 3 | This patch fixes a race condition in test-bdrv-drain that is difficult |
4 | when the two are the same. Fair enough, but it uses a non-obvious | 4 | to reproduce. test-bdrv-drain sometimes fails without an error message |
5 | test for "same". Replace it by the obvious one. While there, improve | 5 | on the block pull request sent by Kevin Wolf on Sep 4, 2023. I was able |
6 | comments. | 6 | to reproduce it locally and found that "block-backend: process I/O in |
7 | the current AioContext" (in this patch series) is the first commit where | ||
8 | it reproduces. | ||
7 | 9 | ||
8 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 10 | I do not know why "block-backend: process I/O in the current AioContext" |
11 | exposes this bug. It might be related to the fact that the test's preadv | ||
12 | request runs in the main thread instead of IOThread a after my commit. | ||
13 | That might simply change the timing of the test. | ||
14 | |||
15 | Now on to the race condition in test-bdrv-drain. The main thread | ||
16 | schedules a BH in IOThread a and then drains the BDS: | ||
17 | |||
18 | aio_bh_schedule_oneshot(ctx_a, test_iothread_main_thread_bh, &data); | ||
19 | |||
20 | /* The request is running on the IOThread a. Draining its block device | ||
21 | * will make sure that it has completed as far as the BDS is concerned, | ||
22 | * but the drain in this thread can continue immediately after | ||
23 | * bdrv_dec_in_flight() and aio_ret might be assigned only slightly | ||
24 | * later. */ | ||
25 | do_drain_begin(drain_type, bs); | ||
26 | |||
27 | If the BH completes before do_drain_begin() then there is nothing to | ||
28 | worry about. | ||
29 | |||
30 | If the BH invokes bdrv_flush() before do_drain_begin(), then | ||
31 | do_drain_begin() waits for it to complete. | ||
32 | |||
33 | The problematic case is when do_drain_begin() runs before the BH enters | ||
34 | bdrv_flush(). Then do_drain_begin() misses the BH and the drain | ||
35 | mechanism has failed in quiescing I/O. | ||
36 | |||
37 | Fix this by incrementing the in_flight counter so that do_drain_begin() | ||
38 | waits for test_iothread_main_thread_bh(). | ||
39 | |||
40 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
41 | Message-ID: <20230912231037.826804-3-stefanha@redhat.com> | ||
42 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
9 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 43 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 44 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | --- | 45 | --- |
12 | qobject/block-qdict.c | 14 +++++++++----- | 46 | tests/unit/test-bdrv-drain.c | 8 ++++++++ |
13 | 1 file changed, 9 insertions(+), 5 deletions(-) | 47 | 1 file changed, 8 insertions(+) |
14 | 48 | ||
15 | diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c | 49 | diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c |
16 | index XXXXXXX..XXXXXXX 100644 | 50 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/qobject/block-qdict.c | 51 | --- a/tests/unit/test-bdrv-drain.c |
18 | +++ b/qobject/block-qdict.c | 52 | +++ b/tests/unit/test-bdrv-drain.c |
19 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) | 53 | @@ -XXX,XX +XXX,XX @@ static void test_iothread_main_thread_bh(void *opaque) |
20 | value = qlist_entry_obj(entry); | 54 | * executed during drain, otherwise this would deadlock. */ |
21 | new_key = g_strdup_printf("%s.%i", prefix, i); | 55 | aio_context_acquire(bdrv_get_aio_context(data->bs)); |
56 | bdrv_flush(data->bs); | ||
57 | + bdrv_dec_in_flight(data->bs); /* incremented by test_iothread_common() */ | ||
58 | aio_context_release(bdrv_get_aio_context(data->bs)); | ||
59 | } | ||
60 | |||
61 | @@ -XXX,XX +XXX,XX @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) | ||
62 | aio_context_acquire(ctx_a); | ||
63 | } | ||
22 | 64 | ||
23 | + /* | 65 | + /* |
24 | + * Flatten non-empty QDict and QList recursively into @target, | 66 | + * Increment in_flight so that do_drain_begin() waits for |
25 | + * copy other objects to @target | 67 | + * test_iothread_main_thread_bh(). This prevents the race between |
68 | + * test_iothread_main_thread_bh() in IOThread a and do_drain_begin() in | ||
69 | + * this thread. test_iothread_main_thread_bh() decrements in_flight. | ||
26 | + */ | 70 | + */ |
27 | if (qobject_type(value) == QTYPE_QDICT) { | 71 | + bdrv_inc_in_flight(bs); |
28 | qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); | 72 | aio_bh_schedule_oneshot(ctx_a, test_iothread_main_thread_bh, &data); |
29 | } else if (qobject_type(value) == QTYPE_QLIST) { | 73 | |
30 | qdict_flatten_qlist(qobject_to(QList, value), target, new_key); | 74 | /* The request is running on the IOThread a. Draining its block device |
31 | } else { | ||
32 | - /* All other types are moved to the target unchanged. */ | ||
33 | qdict_put_obj(target, new_key, qobject_ref(value)); | ||
34 | } | ||
35 | |||
36 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
37 | new_key = g_strdup_printf("%s.%s", prefix, entry->key); | ||
38 | } | ||
39 | |||
40 | + /* | ||
41 | + * Flatten non-empty QDict and QList recursively into @target, | ||
42 | + * copy other objects to @target | ||
43 | + */ | ||
44 | if (qobject_type(value) == QTYPE_QDICT) { | ||
45 | - /* Entries of QDicts are processed recursively, the QDict object | ||
46 | - * itself disappears. */ | ||
47 | qdict_flatten_qdict(qobject_to(QDict, value), target, | ||
48 | new_key ? new_key : entry->key); | ||
49 | qdict_del(qdict, entry->key); | ||
50 | @@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) | ||
51 | qdict_flatten_qlist(qobject_to(QList, value), target, | ||
52 | new_key ? new_key : entry->key); | ||
53 | qdict_del(qdict, entry->key); | ||
54 | - } else if (prefix) { | ||
55 | - /* All other objects are moved to the target unchanged. */ | ||
56 | + } else if (target != qdict) { | ||
57 | qdict_put_obj(target, new_key, qobject_ref(value)); | ||
58 | qdict_del(qdict, entry->key); | ||
59 | } | ||
60 | -- | 75 | -- |
61 | 2.13.6 | 76 | 2.41.0 |
62 | |||
63 | diff view generated by jsdifflib |
1 | From: Markus Armbruster <armbru@redhat.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | 3 | Switch blk_aio_*() APIs over to multi-queue by using |
4 | qemu_get_current_aio_context() instead of blk_get_aio_context(). This | ||
5 | change will allow devices to process I/O in multiple IOThreads in the | ||
6 | future. | ||
7 | |||
8 | I audited existing blk_aio_*() callers: | ||
9 | - migration/block.c: blk_mig_lock() protects the data accessed by the | ||
10 | completion callback. | ||
11 | - The remaining emulated devices and exports run with | ||
12 | qemu_get_aio_context() == blk_get_aio_context(). | ||
13 | |||
14 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
15 | Message-ID: <20230912231037.826804-4-stefanha@redhat.com> | ||
4 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | 16 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
17 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | --- | 19 | --- |
7 | qobject/block-qdict.c | 27 +++++++++++---------------- | 20 | block/block-backend.c | 6 +++--- |
8 | 1 file changed, 11 insertions(+), 16 deletions(-) | 21 | 1 file changed, 3 insertions(+), 3 deletions(-) |
9 | 22 | ||
10 | diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c | 23 | diff --git a/block/block-backend.c b/block/block-backend.c |
11 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/qobject/block-qdict.c | 25 | --- a/block/block-backend.c |
13 | +++ b/qobject/block-qdict.c | 26 | +++ b/block/block-backend.c |
14 | @@ -XXX,XX +XXX,XX @@ static int qdict_is_list(QDict *maybe_list, Error **errp) | 27 | @@ -XXX,XX +XXX,XX @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, |
15 | 28 | acb->blk = blk; | |
16 | for (ent = qdict_first(maybe_list); ent != NULL; | 29 | acb->ret = ret; |
17 | ent = qdict_next(maybe_list, ent)) { | 30 | |
18 | + int is_index = !qemu_strtoi64(ent->key, NULL, 10, &val); | 31 | - replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), |
19 | 32 | + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), | |
20 | - if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { | 33 | error_callback_bh, acb); |
21 | - if (is_list == -1) { | 34 | return &acb->common; |
22 | - is_list = 1; | 35 | } |
23 | - } else if (!is_list) { | 36 | @@ -XXX,XX +XXX,XX @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, |
24 | - error_setg(errp, | 37 | acb->has_returned = false; |
25 | - "Cannot mix list and non-list keys"); | 38 | |
26 | - return -1; | 39 | co = qemu_coroutine_create(co_entry, acb); |
27 | - } | 40 | - aio_co_enter(blk_get_aio_context(blk), co); |
28 | + if (is_list == -1) { | 41 | + aio_co_enter(qemu_get_current_aio_context(), co); |
29 | + is_list = is_index; | 42 | |
30 | + } | 43 | acb->has_returned = true; |
31 | + | 44 | if (acb->rwco.ret != NOT_DONE) { |
32 | + if (is_index != is_list) { | 45 | - replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), |
33 | + error_setg(errp, "Cannot mix list and non-list keys"); | 46 | + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), |
34 | + return -1; | 47 | blk_aio_complete_bh, acb); |
35 | + } | ||
36 | + | ||
37 | + if (is_index) { | ||
38 | len++; | ||
39 | if (val > max) { | ||
40 | max = val; | ||
41 | } | ||
42 | - } else { | ||
43 | - if (is_list == -1) { | ||
44 | - is_list = 0; | ||
45 | - } else if (is_list) { | ||
46 | - error_setg(errp, | ||
47 | - "Cannot mix list and non-list keys"); | ||
48 | - return -1; | ||
49 | - } | ||
50 | } | ||
51 | } | 48 | } |
52 | 49 | ||
53 | -- | 50 | -- |
54 | 2.13.6 | 51 | 2.41.0 |
55 | |||
56 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | These point to the job versions now, not the blockjob versions which | 3 | Process zoned requests in the current thread's AioContext instead of in |
4 | don't really exist anymore. | 4 | the BlockBackend's AioContext. |
5 | 5 | ||
6 | Except set-speed, which does. It sticks out like a sore thumb. This | 6 | There is no need to use the BlockBackend's AioContext thanks to CoMutex |
7 | patch doesn't fix that, but it doesn't make it any worse, either. | 7 | bs->wps->colock, which protects zone metadata. |
8 | 8 | ||
9 | Signed-off-by: John Snow <jsnow@redhat.com> | 9 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
10 | Reviewed-by: Jeff Cody <jcody@redhat.com> | 10 | Message-ID: <20230912231037.826804-5-stefanha@redhat.com> |
11 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | 11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
12 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 13 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 14 | --- |
14 | qapi/job.json | 12 ++++++------ | 15 | block/block-backend.c | 12 ++++++------ |
15 | 1 file changed, 6 insertions(+), 6 deletions(-) | 16 | 1 file changed, 6 insertions(+), 6 deletions(-) |
16 | 17 | ||
17 | diff --git a/qapi/job.json b/qapi/job.json | 18 | diff --git a/block/block-backend.c b/block/block-backend.c |
18 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/qapi/job.json | 20 | --- a/block/block-backend.c |
20 | +++ b/qapi/job.json | 21 | +++ b/block/block-backend.c |
21 | @@ -XXX,XX +XXX,XX @@ | 22 | @@ -XXX,XX +XXX,XX @@ BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, |
22 | # | 23 | acb->has_returned = false; |
23 | # Represents command verbs that can be applied to a job. | 24 | |
24 | # | 25 | co = qemu_coroutine_create(blk_aio_zone_report_entry, acb); |
25 | -# @cancel: see @block-job-cancel | 26 | - aio_co_enter(blk_get_aio_context(blk), co); |
26 | +# @cancel: see @job-cancel | 27 | + aio_co_enter(qemu_get_current_aio_context(), co); |
27 | # | 28 | |
28 | -# @pause: see @block-job-pause | 29 | acb->has_returned = true; |
29 | +# @pause: see @job-pause | 30 | if (acb->rwco.ret != NOT_DONE) { |
30 | # | 31 | - replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), |
31 | -# @resume: see @block-job-resume | 32 | + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), |
32 | +# @resume: see @job-resume | 33 | blk_aio_complete_bh, acb); |
33 | # | 34 | } |
34 | # @set-speed: see @block-job-set-speed | 35 | |
35 | # | 36 | @@ -XXX,XX +XXX,XX @@ BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, |
36 | -# @complete: see @block-job-complete | 37 | acb->has_returned = false; |
37 | +# @complete: see @job-complete | 38 | |
38 | # | 39 | co = qemu_coroutine_create(blk_aio_zone_mgmt_entry, acb); |
39 | -# @dismiss: see @block-job-dismiss | 40 | - aio_co_enter(blk_get_aio_context(blk), co); |
40 | +# @dismiss: see @job-dismiss | 41 | + aio_co_enter(qemu_get_current_aio_context(), co); |
41 | # | 42 | |
42 | -# @finalize: see @block-job-finalize | 43 | acb->has_returned = true; |
43 | +# @finalize: see @job-finalize | 44 | if (acb->rwco.ret != NOT_DONE) { |
44 | # | 45 | - replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), |
45 | # Since: 2.12 | 46 | + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), |
46 | ## | 47 | blk_aio_complete_bh, acb); |
48 | } | ||
49 | |||
50 | @@ -XXX,XX +XXX,XX @@ BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, | ||
51 | acb->has_returned = false; | ||
52 | |||
53 | co = qemu_coroutine_create(blk_aio_zone_append_entry, acb); | ||
54 | - aio_co_enter(blk_get_aio_context(blk), co); | ||
55 | + aio_co_enter(qemu_get_current_aio_context(), co); | ||
56 | acb->has_returned = true; | ||
57 | if (acb->rwco.ret != NOT_DONE) { | ||
58 | - replay_bh_schedule_oneshot_event(blk_get_aio_context(blk), | ||
59 | + replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), | ||
60 | blk_aio_complete_bh, acb); | ||
61 | } | ||
62 | |||
47 | -- | 63 | -- |
48 | 2.13.6 | 64 | 2.41.0 |
49 | |||
50 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Commit a290f085 exposed a latent bug in qemu-img map introduced | 3 | Use qemu_get_current_aio_context() in mixed wrappers and coroutine |
4 | during the conversion of block status to be byte-based. Earlier in | 4 | wrappers so that code runs in the caller's AioContext instead of moving |
5 | commit 5e344dd8, the internal interface get_block_status() switched | 5 | to the BlockDriverState's AioContext. This change is necessary for the |
6 | to take byte-based parameters, but still called a sector-based | 6 | multi-queue block layer where any thread can call into the block layer. |
7 | block layer function; as such, rounding was added in the lone | ||
8 | caller to obey the contract. However, commit 237d78f8 changed | ||
9 | get_block_status() to truly be byte-based, at which point rounding | ||
10 | to sector boundaries can result in calling bdrv_block_status() with | ||
11 | 'bytes == 0' (a coding error) when the boundary between data and a | ||
12 | hole falls mid-sector (true for the past-EOF implicit hole present | ||
13 | in POSIX files). Fix things by removing the rounding that is now | ||
14 | no longer necessary. | ||
15 | 7 | ||
16 | See also https://bugzilla.redhat.com/1589738 | 8 | Most wrappers are IO_CODE where it's safe to use the current AioContext |
9 | nowadays. BlockDrivers and the core block layer use their own locks and | ||
10 | no longer depend on the AioContext lock for thread-safety. | ||
17 | 11 | ||
18 | Fixes: 237d78f8 | 12 | The bdrv_create() wrapper invokes GLOBAL_STATE code. Using the current |
19 | Reported-by: Dan Kenigsberg <danken@redhat.com> | 13 | AioContext is safe because this code is only called with the BQL held |
20 | Reported-by: Nir Soffer <nsoffer@redhat.com> | 14 | from the main loop thread. |
21 | Reported-by: Maor Lipchuk <mlipchuk@redhat.com> | 15 | |
22 | CC: qemu-stable@nongnu.org | 16 | The output of qemu-iotests 051 is sensitive to event loop activity. |
23 | Signed-off-by: Eric Blake <eblake@redhat.com> | 17 | Update the output because the monitor BH runs at a different time, |
18 | causing prompts to be printed differently in the output. | ||
19 | |||
20 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
21 | Message-ID: <20230912231037.826804-6-stefanha@redhat.com> | ||
22 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
23 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
25 | --- | 25 | --- |
26 | qemu-img.c | 2 +- | 26 | scripts/block-coroutine-wrapper.py | 6 ++---- |
27 | 1 file changed, 1 insertion(+), 1 deletion(-) | 27 | 1 file changed, 2 insertions(+), 4 deletions(-) |
28 | 28 | ||
29 | diff --git a/qemu-img.c b/qemu-img.c | 29 | diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py |
30 | index XXXXXXX..XXXXXXX 100644 | 30 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/qemu-img.c | 31 | --- a/scripts/block-coroutine-wrapper.py |
32 | +++ b/qemu-img.c | 32 | +++ b/scripts/block-coroutine-wrapper.py |
33 | @@ -XXX,XX +XXX,XX @@ static int img_map(int argc, char **argv) | 33 | @@ -XXX,XX +XXX,XX @@ def __init__(self, wrapper_type: str, return_type: str, name: str, |
34 | int64_t n; | 34 | raise ValueError(f"no_co function can't be rdlock: {self.name}") |
35 | 35 | self.target_name = f'{subsystem}_{subname}' | |
36 | /* Probe up to 1 GiB at a time. */ | 36 | |
37 | - n = QEMU_ALIGN_DOWN(MIN(1 << 30, length - offset), BDRV_SECTOR_SIZE); | 37 | - self.ctx = self.gen_ctx() |
38 | + n = MIN(1 << 30, length - offset); | 38 | - |
39 | ret = get_block_status(bs, offset, n, &next); | 39 | self.get_result = 's->ret = ' |
40 | 40 | self.ret = 'return s.ret;' | |
41 | if (ret < 0) { | 41 | self.co_ret = 'return ' |
42 | @@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str: | ||
43 | {func.co_ret}{name}({ func.gen_list('{name}') }); | ||
44 | }} else {{ | ||
45 | {struct_name} s = {{ | ||
46 | - .poll_state.ctx = {func.ctx}, | ||
47 | + .poll_state.ctx = qemu_get_current_aio_context(), | ||
48 | .poll_state.in_progress = true, | ||
49 | |||
50 | { func.gen_block(' .{name} = {name},') } | ||
51 | @@ -XXX,XX +XXX,XX @@ def create_co_wrapper(func: FuncDecl) -> str: | ||
52 | {func.return_type} {func.name}({ func.gen_list('{decl}') }) | ||
53 | {{ | ||
54 | {struct_name} s = {{ | ||
55 | - .poll_state.ctx = {func.ctx}, | ||
56 | + .poll_state.ctx = qemu_get_current_aio_context(), | ||
57 | .poll_state.in_progress = true, | ||
58 | |||
59 | { func.gen_block(' .{name} = {name},') } | ||
42 | -- | 60 | -- |
43 | 2.13.6 | 61 | 2.41.0 |
44 | |||
45 | diff view generated by jsdifflib |