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