1
The following changes since commit 91fe7a376ad46e3cc5e82d418aad22173c948a3c:
1
The following changes since commit 2ccad61746ca7de5dd3e25146062264387e43bd4:
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-tcg-20221109' of https://gitlab.com/rth7680/qemu into staging (2022-11-09 13:26:45 -0500)
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 b04af371af685c12970ea93027dc6d8bf86265aa:
10
10
11
block: Remove dead deprecation warning code (2018-06-15 14:49:44 +0200)
11
tests/stream-under-throttle: New test (2022-11-11 13:02:43 +0100)
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
- Fix deadlock in graph modification with iothreads
17
both, because of QDict type confusion
17
- mirror: Fix non-converging cases for active mirror
18
- rbd: Add options 'auth-client-required' and 'key-secret'
18
- qapi: Fix BlockdevOptionsNvmeIoUring @path description
19
- Remove deprecated -drive options serial/addr/cyls/heads/secs/trans
19
- blkio: Set BlockDriver::has_variable_length to false
20
- rbd, iscsi: Remove deprecated 'filename' option
21
- Fix 'qemu-img map' crash with unaligned image size
22
- Improve QMP documentation for jobs
23
20
24
----------------------------------------------------------------
21
----------------------------------------------------------------
25
Eric Blake (2):
22
Alberto Faria (2):
26
qemu-img: Fix assert when mapping unaligned raw file
23
qapi/block-core: Fix BlockdevOptionsNvmeIoUring @path description
27
iotests: Add test 221 to catch qemu-img map regression
24
block/blkio: Set BlockDriver::has_variable_length to false
28
25
29
John Snow (2):
26
Hanna Reitz (9):
30
jobs: fix stale wording
27
block/mirror: Do not wait for active writes
31
jobs: fix verb references in docs
28
block/mirror: Drop mirror_wait_for_any_operation()
29
block/mirror: Fix NULL s->job in active writes
30
iotests/151: Test that active mirror progresses
31
iotests/151: Test active requests on mirror start
32
block: Make bdrv_child_get_parent_aio_context I/O
33
block-backend: Update ctx immediately after root
34
block: Start/end drain on correct AioContext
35
tests/stream-under-throttle: New test
32
36
33
Kevin Wolf (4):
37
qapi/block-core.json | 2 +-
34
block: Remove deprecated -drive geometry options
38
include/block/block-global-state.h | 1 -
35
block: Remove deprecated -drive option addr
39
include/block/block-io.h | 2 +
36
block: Remove deprecated -drive option serial
40
include/block/block_int-common.h | 4 +-
37
block: Remove dead deprecation warning code
41
block.c | 2 +-
38
42
block/blkio.c | 1 -
39
Markus Armbruster (17):
43
block/block-backend.c | 9 +-
40
rbd: Drop deprecated -drive parameter "filename"
44
block/io.c | 6 +-
41
iscsi: Drop deprecated -drive parameter "filename"
45
block/mirror.c | 78 ++++---
42
qobject: Move block-specific qdict code to block-qdict.c
46
blockjob.c | 3 +-
43
block: Fix -blockdev for certain non-string scalars
47
tests/qemu-iotests/151 | 227 ++++++++++++++++++++-
44
block: Fix -drive for certain non-string scalars
48
tests/qemu-iotests/151.out | 4 +-
45
block: Clean up a misuse of qobject_to() in .bdrv_co_create_opts()
49
tests/qemu-iotests/tests/stream-under-throttle | 121 +++++++++++
46
block: Factor out qobject_input_visitor_new_flat_confused()
50
tests/qemu-iotests/tests/stream-under-throttle.out | 5 +
47
block: Make remaining uses of qobject input visitor more robust
51
14 files changed, 424 insertions(+), 41 deletions(-)
48
block-qdict: Simplify qdict_flatten_qdict()
52
create mode 100755 tests/qemu-iotests/tests/stream-under-throttle
49
block-qdict: Tweak qdict_flatten_qdict(), qdict_flatten_qlist()
53
create mode 100644 tests/qemu-iotests/tests/stream-under-throttle.out
50
block-qdict: Clean up qdict_crumple() a bit
51
block-qdict: Simplify qdict_is_list() some
52
check-block-qdict: Rename qdict_flatten()'s variables for clarity
53
check-block-qdict: Cover flattening of empty lists and dictionaries
54
block: Fix -blockdev / blockdev-add for empty objects and arrays
55
rbd: New parameter auth-client-required
56
rbd: New parameter key-secret
57
58
Max Reitz (1):
59
block: Add block-specific QDict header
60
61
qapi/block-core.json | 19 ++
62
qapi/job.json | 23 +-
63
include/block/qdict.h | 34 +++
64
include/hw/block/block.h | 1 -
65
include/qapi/qmp/qdict.h | 17 --
66
include/sysemu/blockdev.h | 3 -
67
block.c | 1 +
68
block/block-backend.c | 1 -
69
block/crypto.c | 12 +-
70
block/gluster.c | 1 +
71
block/iscsi.c | 24 +-
72
block/nbd.c | 16 +-
73
block/nfs.c | 8 +-
74
block/parallels.c | 11 +-
75
block/qcow.c | 11 +-
76
block/qcow2.c | 11 +-
77
block/qed.c | 11 +-
78
block/quorum.c | 1 +
79
block/rbd.c | 85 +++---
80
block/sheepdog.c | 23 +-
81
block/snapshot.c | 1 +
82
block/ssh.c | 16 +-
83
block/vdi.c | 8 +-
84
block/vhdx.c | 11 +-
85
block/vpc.c | 11 +-
86
block/vvfat.c | 1 +
87
block/vxhs.c | 1 +
88
blockdev.c | 111 +------
89
device-hotplug.c | 4 -
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
Deleted patch
1
From: Eric Blake <eblake@redhat.com>
2
1
3
Commit a290f085 exposed a latent bug in qemu-img map introduced
4
during the conversion of block status to be byte-based. Earlier in
5
commit 5e344dd8, the internal interface get_block_status() switched
6
to take byte-based parameters, but still called a sector-based
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
16
See also https://bugzilla.redhat.com/1589738
17
18
Fixes: 237d78f8
19
Reported-by: Dan Kenigsberg <danken@redhat.com>
20
Reported-by: Nir Soffer <nsoffer@redhat.com>
21
Reported-by: Maor Lipchuk <mlipchuk@redhat.com>
22
CC: qemu-stable@nongnu.org
23
Signed-off-by: Eric Blake <eblake@redhat.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
26
qemu-img.c | 2 +-
27
1 file changed, 1 insertion(+), 1 deletion(-)
28
29
diff --git a/qemu-img.c b/qemu-img.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/qemu-img.c
32
+++ b/qemu-img.c
33
@@ -XXX,XX +XXX,XX @@ static int img_map(int argc, char **argv)
34
int64_t n;
35
36
/* Probe up to 1 GiB at a time. */
37
- n = QEMU_ALIGN_DOWN(MIN(1 << 30, length - offset), BDRV_SECTOR_SIZE);
38
+ n = MIN(1 << 30, length - offset);
39
ret = get_block_status(bs, offset, n, &next);
40
41
if (ret < 0) {
42
--
43
2.13.6
44
45
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
Configuration flows through the block subsystem in a rather peculiar
3
Waiting for all active writes to settle before daring to create a
4
way. Configuration made with -drive enters it as QemuOpts.
4
background copying operation means that we will never do background
5
Configuration made with -blockdev / blockdev-add enters it as QAPI
5
operations while the guest does anything (in write-blocking mode), and
6
type BlockdevOptions. The block subsystem uses QDict, QemuOpts and
6
therefore cannot converge. Yes, we also will not diverge, but actually
7
QAPI types internally. The precise flow is next to impossible to
7
converging would be even nicer.
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
8
12
$ qemu-system-x86_64 -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234
9
It is unclear why we did decide to wait for all active writes to settle
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
10
before creating a background operation, but it just does not seem
11
necessary. Active writes will put themselves into the in_flight bitmap
12
and thus properly block actually conflicting background requests.
14
13
15
QMP blockdev-add is broken the same way.
14
It is important for active requests to wait on overlapping background
15
requests, which we do in active_write_prepare(). However, so far it was
16
not documented why it is important. Add such documentation now, and
17
also to the other call of mirror_wait_on_conflicts(), so that it becomes
18
more clear why and when requests need to actively wait for other
19
requests to settle.
16
20
17
Here's what happens. The block layer passes configuration represented
21
Another thing to note is that of course we need to ensure that there are
18
as flat QDict (with dotted keys) to BlockDriver methods
22
no active requests when the job completes, but that is done by virtue of
19
.bdrv_file_open(). The QDict's members are typed according to the
23
the BDS being drained anyway, so there cannot be any active requests at
20
QAPI schema.
24
that point.
21
25
22
nfs_file_open() converts it to QAPI type BlockdevOptionsNfs, with
26
With this change, we will need to explicitly keep track of how many
23
qdict_crumple() and a qobject input visitor.
27
bytes are in flight in active requests so that
28
job_progress_set_remaining() in mirror_run() can set the correct number
29
of remaining bytes.
24
30
25
This visitor comes in two flavors. The plain flavor requires scalars
31
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2123297
26
to be typed according to the QAPI schema. That's the case here. The
32
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
27
keyval flavor requires string scalars. That's not the case here.
33
Message-Id: <20221109165452.67927-2-hreitz@redhat.com>
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>
34
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
66
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
35
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
67
---
36
---
68
include/block/qdict.h | 1 +
37
block/mirror.c | 37 ++++++++++++++++++++++++++++++-------
69
block/nfs.c | 2 +-
38
1 file changed, 30 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
39
81
diff --git a/include/block/qdict.h b/include/block/qdict.h
40
diff --git a/block/mirror.c b/block/mirror.c
82
index XXXXXXX..XXXXXXX 100644
41
index XXXXXXX..XXXXXXX 100644
83
--- a/include/block/qdict.h
42
--- a/block/mirror.c
84
+++ b/include/block/qdict.h
43
+++ b/block/mirror.c
85
@@ -XXX,XX +XXX,XX @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
44
@@ -XXX,XX +XXX,XX @@ typedef struct MirrorBlockJob {
86
void qdict_array_split(QDict *src, QList **dst);
45
int max_iov;
87
int qdict_array_entries(QDict *src, const char *subqdict);
46
bool initial_zeroing_ongoing;
88
QObject *qdict_crumple(const QDict *src, Error **errp);
47
int in_active_write_counter;
89
+QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp);
48
+ int64_t active_write_bytes_in_flight;
90
void qdict_flatten(QDict *qdict);
49
bool prepared;
91
50
bool in_drain;
92
typedef struct QDictRenames {
51
} MirrorBlockJob;
93
diff --git a/block/nfs.c b/block/nfs.c
52
@@ -XXX,XX +XXX,XX @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
94
index XXXXXXX..XXXXXXX 100644
95
--- a/block/nfs.c
96
+++ b/block/nfs.c
97
@@ -XXX,XX +XXX,XX @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
98
const QDictEntry *e;
99
Error *local_err = NULL;
100
101
- crumpled = qdict_crumple(options, errp);
102
+ crumpled = qdict_crumple_for_keyval_qiv(options, errp);
103
if (crumpled == NULL) {
104
return NULL;
105
}
53
}
106
diff --git a/block/parallels.c b/block/parallels.c
54
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
107
index XXXXXXX..XXXXXXX 100644
55
108
--- a/block/parallels.c
56
+ /*
109
+++ b/block/parallels.c
57
+ * Wait for concurrent requests to @offset. The next loop will limit the
110
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
58
+ * copied area based on in_flight_bitmap so we only copy an area that does
111
qdict_put_str(qdict, "driver", "parallels");
59
+ * not overlap with concurrent in-flight requests. Still, we would like to
112
qdict_put_str(qdict, "file", bs->node_name);
60
+ * copy something, so wait until there are at least no more requests to the
113
61
+ * very beginning of the area.
114
- qobj = qdict_crumple(qdict, errp);
62
+ */
115
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
63
mirror_wait_on_conflicts(NULL, s, offset, 1);
116
qobject_unref(qdict);
64
117
qdict = qobject_to(QDict, qobj);
65
job_pause_point(&s->common.job);
118
if (qdict == NULL) {
66
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
119
diff --git a/block/qcow.c b/block/qcow.c
67
int64_t cnt, delta;
120
index XXXXXXX..XXXXXXX 100644
68
bool should_complete;
121
--- a/block/qcow.c
69
122
+++ b/block/qcow.c
70
- /* Do not start passive operations while there are active
123
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
71
- * writes in progress */
124
qdict_put_str(qdict, "driver", "qcow");
72
- while (s->in_active_write_counter) {
125
qdict_put_str(qdict, "file", bs->node_name);
73
- mirror_wait_for_any_operation(s, true);
126
74
- }
127
- qobj = qdict_crumple(qdict, errp);
75
-
128
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
76
if (s->ret < 0) {
129
qobject_unref(qdict);
77
ret = s->ret;
130
qdict = qobject_to(QDict, qobj);
78
goto immediate_exit;
131
if (qdict == NULL) {
79
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
132
diff --git a/block/qcow2.c b/block/qcow2.c
80
/* cnt is the number of dirty bytes remaining and s->bytes_in_flight is
133
index XXXXXXX..XXXXXXX 100644
81
* the number of bytes currently being processed; together those are
134
--- a/block/qcow2.c
82
* the current remaining operation length */
135
+++ b/block/qcow2.c
83
- job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt);
136
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
84
+ job_progress_set_remaining(&s->common.job,
137
qdict_put_str(qdict, "file", bs->node_name);
85
+ s->bytes_in_flight + cnt +
138
86
+ s->active_write_bytes_in_flight);
139
/* Now get the QAPI type BlockdevCreateOptions */
87
140
- qobj = qdict_crumple(qdict, errp);
88
/* Note that even when no rate limit is applied we need to yield
141
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
89
* periodically with no pending I/O so that bdrv_drain_all() returns.
142
qobject_unref(qdict);
90
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
143
qdict = qobject_to(QDict, qobj);
91
144
if (qdict == NULL) {
92
s->in_drain = true;
145
diff --git a/block/qed.c b/block/qed.c
93
bdrv_drained_begin(bs);
146
index XXXXXXX..XXXXXXX 100644
94
+
147
--- a/block/qed.c
95
+ /* Must be zero because we are drained */
148
+++ b/block/qed.c
96
+ assert(s->in_active_write_counter == 0);
149
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
97
+
150
qdict_put_str(qdict, "driver", "qed");
98
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
151
qdict_put_str(qdict, "file", bs->node_name);
99
if (cnt > 0 || mirror_flush(s) < 0) {
152
100
bdrv_drained_end(bs);
153
- qobj = qdict_crumple(qdict, errp);
101
@@ -XXX,XX +XXX,XX @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
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
}
102
}
164
103
165
/* Convert the remaining options into a QAPI object */
104
job_progress_increase_remaining(&job->common.job, bytes);
166
- crumpled = qdict_crumple(options, errp);
105
+ job->active_write_bytes_in_flight += bytes;
167
+ crumpled = qdict_crumple_for_keyval_qiv(options, errp);
106
168
if (crumpled == NULL) {
107
switch (method) {
169
r = -EINVAL;
108
case MIRROR_METHOD_COPY:
170
goto out;
109
@@ -XXX,XX +XXX,XX @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method,
171
diff --git a/block/sheepdog.c b/block/sheepdog.c
110
abort();
172
index XXXXXXX..XXXXXXX 100644
173
--- a/block/sheepdog.c
174
+++ b/block/sheepdog.c
175
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
176
}
111
}
177
112
178
/* Get the QAPI object */
113
+ job->active_write_bytes_in_flight -= bytes;
179
- crumpled = qdict_crumple(qdict, errp);
114
if (ret >= 0) {
180
+ crumpled = qdict_crumple_for_keyval_qiv(qdict, errp);
115
job_progress_update(&job->common.job, bytes);
181
if (crumpled == NULL) {
116
} else {
182
ret = -EINVAL;
117
@@ -XXX,XX +XXX,XX @@ static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s,
183
goto fail;
118
184
diff --git a/block/vhdx.c b/block/vhdx.c
119
s->in_active_write_counter++;
185
index XXXXXXX..XXXXXXX 100644
120
186
--- a/block/vhdx.c
121
+ /*
187
+++ b/block/vhdx.c
122
+ * Wait for concurrent requests affecting the area. If there are already
188
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename,
123
+ * running requests that are copying off now-to-be stale data in the area,
189
qdict_put_str(qdict, "driver", "vhdx");
124
+ * we must wait for them to finish before we begin writing fresh data to the
190
qdict_put_str(qdict, "file", bs->node_name);
125
+ * target so that the write operations appear in the correct order.
191
126
+ * Note that background requests (see mirror_iteration()) in contrast only
192
- qobj = qdict_crumple(qdict, errp);
127
+ * wait for conflicting requests at the start of the dirty area, and then
193
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
128
+ * (based on the in_flight_bitmap) truncate the area to copy so it will not
194
qobject_unref(qdict);
129
+ * conflict with any requests beyond that. For active writes, however, we
195
qdict = qobject_to(QDict, qobj);
130
+ * cannot truncate that area. The request from our parent must be blocked
196
if (qdict == NULL) {
131
+ * until the area is copied in full. Therefore, we must wait for the whole
197
diff --git a/block/vpc.c b/block/vpc.c
132
+ * area to become free of concurrent requests.
198
index XXXXXXX..XXXXXXX 100644
133
+ */
199
--- a/block/vpc.c
134
mirror_wait_on_conflicts(op, s, offset, bytes);
200
+++ b/block/vpc.c
135
201
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
136
bitmap_set(s->in_flight_bitmap, start_chunk, end_chunk - start_chunk);
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
+
249
+ for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) {
250
+ buf = NULL;
251
+ switch (qobject_type(ent->value)) {
252
+ case QTYPE_QNULL:
253
+ case QTYPE_QSTRING:
254
+ continue;
255
+ case QTYPE_QNUM:
256
+ s = buf = qnum_to_string(qobject_to(QNum, ent->value));
257
+ break;
258
+ case QTYPE_QDICT:
259
+ case QTYPE_QLIST:
260
+ /* @src isn't flat; qdict_crumple() will fail */
261
+ continue;
262
+ case QTYPE_QBOOL:
263
+ s = qbool_get_bool(qobject_to(QBool, ent->value))
264
+ ? "on" : "off";
265
+ break;
266
+ default:
267
+ abort();
268
+ }
269
+
270
+ if (!tmp) {
271
+ tmp = qdict_clone_shallow(src);
272
+ }
273
+ qdict_put(tmp, ent->key, qstring_from_str(s));
274
+ g_free(buf);
275
+ }
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
--
137
--
287
2.13.6
138
2.38.1
288
289
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
Legacy -drive supports "password-secret" parameter that isn't
3
mirror_wait_for_free_in_flight_slot() is the only remaining user of
4
available with -blockdev / blockdev-add. That's because we backed out
4
mirror_wait_for_any_operation(), so inline the latter into the former.
5
our first try to provide it there due to interface design doubts, in
6
commit 577d8c9a811, v2.9.0.
7
5
8
This is the second try. It brings back the parameter, except it's
6
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
9
named "key-secret" now.
7
Message-Id: <20221109165452.67927-3-hreitz@redhat.com>
10
11
Let's review our reasons for backing out the first try, as stated in
12
the commit message:
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>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
33
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
34
---
10
---
35
qapi/block-core.json | 6 ++++++
11
block/mirror.c | 21 ++++++++-------------
36
block/rbd.c | 41 +++++++++++++++++++++++++----------------
12
1 file changed, 8 insertions(+), 13 deletions(-)
37
2 files changed, 31 insertions(+), 16 deletions(-)
38
13
39
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/block/mirror.c b/block/mirror.c
40
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
41
--- a/qapi/block-core.json
16
--- a/block/mirror.c
42
+++ b/qapi/block-core.json
17
+++ b/block/mirror.c
43
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset,
44
# This maps to Ceph configuration option
45
# "auth_client_required". (Since 3.0)
46
#
47
+# @key-secret: ID of a QCryptoSecret object providing a key
48
+# for cephx authentication.
49
+# This maps to Ceph configuration option
50
+# "key". (Since 3.0)
51
+#
52
# @server: Monitor host address and port. This maps
53
# to the "mon_host" Ceph option.
54
#
55
@@ -XXX,XX +XXX,XX @@
56
'*snapshot': 'str',
57
'*user': 'str',
58
'*auth-client-required': ['RbdAuthMode'],
59
+ '*key-secret': 'str',
60
'*server': ['InetSocketAddressBase'] } }
61
62
##
63
diff --git a/block/rbd.c b/block/rbd.c
64
index XXXXXXX..XXXXXXX 100644
65
--- a/block/rbd.c
66
+++ b/block/rbd.c
67
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp)
68
}
19
}
69
20
70
21
static inline void coroutine_fn
71
-static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
22
-mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
72
- BlockdevOptionsRbd *opts,
23
+mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)
73
+static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts,
74
Error **errp)
75
{
24
{
76
- char *acr;
25
MirrorOp *op;
77
+ char *key, *acr;
26
78
int r;
27
QTAILQ_FOREACH(op, &s->ops_in_flight, next) {
79
GString *accu;
28
- /* Do not wait on pseudo ops, because it may in turn wait on
80
RbdAuthModeList *auth;
29
+ /*
81
30
+ * Do not wait on pseudo ops, because it may in turn wait on
82
- if (secretid) {
31
* some other operation to start, which may in fact be the
83
- gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
32
* caller of this function. Since there is only one pseudo op
84
- errp);
33
* at any given time, we will always find some real operation
85
- if (!secret) {
34
- * to wait on. */
86
- return -1;
35
- if (!op->is_pseudo_op && op->is_in_flight &&
87
+ if (opts->key_secret) {
36
- op->is_active_write == active)
88
+ key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp);
37
- {
89
+ if (!key) {
38
+ * to wait on.
90
+ return -EIO;
39
+ * Also, do not wait on active operations, because they do not
91
+ }
40
+ * use up in-flight slots.
92
+ r = rados_conf_set(cluster, "key", key);
41
+ */
93
+ g_free(key);
42
+ if (!op->is_pseudo_op && op->is_in_flight && !op->is_active_write) {
94
+ if (r < 0) {
43
qemu_co_queue_wait(&op->waiting_requests, NULL);
95
+ error_setg_errno(errp, -r, "Could not set 'key'");
44
return;
96
+ return r;
97
}
45
}
46
@@ -XXX,XX +XXX,XX @@ mirror_wait_for_any_operation(MirrorBlockJob *s, bool active)
47
abort();
48
}
49
50
-static inline void coroutine_fn
51
-mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)
52
-{
53
- /* Only non-active operations use up in-flight slots */
54
- mirror_wait_for_any_operation(s, false);
55
-}
98
-
56
-
99
- rados_conf_set(cluster, "key", secret);
57
/* Perform a mirror copy operation.
100
- g_free(secret);
58
*
101
}
59
* *op->bytes_handled is set to the number of bytes copied after and
102
103
if (opts->has_auth_client_required) {
104
@@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = {
105
},
106
};
107
108
-/* FIXME Deprecate and remove keypairs or make it available in QMP.
109
- * password_secret should eventually be configurable in opts->location. Support
110
- * for it in .bdrv_open will make it work here as well. */
111
+/* FIXME Deprecate and remove keypairs or make it available in QMP. */
112
static int qemu_rbd_do_create(BlockdevCreateOptions *options,
113
const char *keypairs, const char *password_secret,
114
Error **errp)
115
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
116
Error *local_err = NULL;
117
int r;
118
119
+ if (secretid) {
120
+ if (opts->key_secret) {
121
+ error_setg(errp,
122
+ "Legacy 'password-secret' clashes with 'key-secret'");
123
+ return -EINVAL;
124
+ }
125
+ opts->key_secret = g_strdup(secretid);
126
+ opts->has_key_secret = true;
127
+ }
128
+
129
mon_host = qemu_rbd_mon_host(opts, &local_err);
130
if (local_err) {
131
error_propagate(errp, local_err);
132
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
133
}
134
}
135
136
- if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) {
137
- r = -EIO;
138
+ r = qemu_rbd_set_auth(*cluster, opts, errp);
139
+ if (r < 0) {
140
goto failed_shutdown;
141
}
142
143
--
60
--
144
2.13.6
61
2.38.1
145
146
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
Remaining uses of qobject_input_visitor_new_keyval() in the block
3
There is a small gap in mirror_start_job() before putting the mirror
4
subsystem:
4
filter node into the block graph (bdrv_append() call) and the actual job
5
being created. Before the job is created, MirrorBDSOpaque.job is NULL.
5
6
6
* block_crypto_open_opts_init()
7
It is possible that requests come in when bdrv_drained_end() is called,
7
Currently doesn't visit any non-string scalars, thus safe. It's
8
and those requests would see MirrorBDSOpaque.job == NULL. Have our
8
called from
9
filter node handle that case gracefully.
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
10
15
* block_crypto_create_opts_init(), called from
11
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
16
- block_crypto_co_create_opts_luks()
12
Message-Id: <20221109165452.67927-4-hreitz@redhat.com>
17
Also creates the QDict with qemu_opts_to_qdict_filtered().
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
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>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
29
---
15
---
30
block/crypto.c | 12 +++++++++---
16
block/mirror.c | 20 ++++++++++++--------
31
block/vdi.c | 8 ++++++--
17
1 file changed, 12 insertions(+), 8 deletions(-)
32
2 files changed, 15 insertions(+), 5 deletions(-)
33
18
34
diff --git a/block/crypto.c b/block/crypto.c
19
diff --git a/block/mirror.c b/block/mirror.c
35
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
36
--- a/block/crypto.c
21
--- a/block/mirror.c
37
+++ b/block/crypto.c
22
+++ b/block/mirror.c
38
@@ -XXX,XX +XXX,XX @@
23
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs,
39
#include "qemu/osdep.h"
24
MirrorOp *op = NULL;
40
25
MirrorBDSOpaque *s = bs->opaque;
41
#include "block/block_int.h"
26
int ret = 0;
42
+#include "block/qdict.h"
27
- bool copy_to_target;
43
#include "sysemu/block-backend.h"
28
+ bool copy_to_target = false;
44
#include "crypto/block.h"
29
45
#include "qapi/opts-visitor.h"
30
- copy_to_target = s->job->ret >= 0 &&
46
#include "qapi/qapi-visit-crypto.h"
31
- !job_is_cancelled(&s->job->common.job) &&
47
-#include "qapi/qmp/qdict.h"
32
- s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING;
48
#include "qapi/qobject-input-visitor.h"
33
+ if (s->job) {
49
#include "qapi/error.h"
34
+ copy_to_target = s->job->ret >= 0 &&
50
#include "qemu/option.h"
35
+ !job_is_cancelled(&s->job->common.job) &&
51
@@ -XXX,XX +XXX,XX @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
36
+ s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING;
52
ret = g_new0(QCryptoBlockOpenOptions, 1);
53
ret->format = format;
54
55
- v = qobject_input_visitor_new_keyval(QOBJECT(opts));
56
+ v = qobject_input_visitor_new_flat_confused(opts, &local_err);
57
+ if (local_err) {
58
+ goto out;
59
+ }
37
+ }
60
38
61
visit_start_struct(v, NULL, NULL, 0, &local_err);
39
if (copy_to_target) {
62
if (local_err) {
40
op = active_write_prepare(s->job, offset, bytes);
63
@@ -XXX,XX +XXX,XX @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
41
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs,
64
ret = g_new0(QCryptoBlockCreateOptions, 1);
42
QEMUIOVector bounce_qiov;
65
ret->format = format;
43
void *bounce_buf;
66
44
int ret = 0;
67
- v = qobject_input_visitor_new_keyval(QOBJECT(opts));
45
- bool copy_to_target;
68
+ v = qobject_input_visitor_new_flat_confused(opts, &local_err);
46
+ bool copy_to_target = false;
69
+ if (local_err) {
47
70
+ goto out;
48
- copy_to_target = s->job->ret >= 0 &&
49
- !job_is_cancelled(&s->job->common.job) &&
50
- s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING;
51
+ if (s->job) {
52
+ copy_to_target = s->job->ret >= 0 &&
53
+ !job_is_cancelled(&s->job->common.job) &&
54
+ s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING;
71
+ }
55
+ }
72
56
73
visit_start_struct(v, NULL, NULL, 0, &local_err);
57
if (copy_to_target) {
74
if (local_err) {
58
/* The guest might concurrently modify the data to write; but
75
diff --git a/block/vdi.c b/block/vdi.c
76
index XXXXXXX..XXXXXXX 100644
77
--- a/block/vdi.c
78
+++ b/block/vdi.c
79
@@ -XXX,XX +XXX,XX @@
80
81
#include "qemu/osdep.h"
82
#include "qapi/error.h"
83
-#include "qapi/qmp/qdict.h"
84
#include "qapi/qobject-input-visitor.h"
85
#include "qapi/qapi-visit-block-core.h"
86
#include "block/block_int.h"
87
+#include "block/qdict.h"
88
#include "sysemu/block-backend.h"
89
#include "qemu/module.h"
90
#include "qemu/option.h"
91
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
92
}
93
94
/* Get the QAPI object */
95
- v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
96
+ v = qobject_input_visitor_new_flat_confused(qdict, errp);
97
+ if (!v) {
98
+ ret = -EINVAL;
99
+ goto done;
100
+ }
101
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
102
visit_free(v);
103
104
--
59
--
105
2.13.6
60
2.38.1
106
107
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
3
Before this series, a mirror job in write-blocking mode would pause
4
issuing background requests while active requests are in flight. Thus,
5
if the source is constantly in use by active requests, no actual
6
progress can be made.
7
8
This series should have fixed that, making the mirror job issue
9
background requests even while active requests are in flight.
10
11
Have a new test case in 151 verify this.
12
13
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
14
Message-Id: <20221109165452.67927-5-hreitz@redhat.com>
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
17
---
7
tests/check-block-qdict.c | 57 ++++++++++++++++++++++++-----------------------
18
tests/qemu-iotests/151 | 180 ++++++++++++++++++++++++++++++++++++-
8
1 file changed, 29 insertions(+), 28 deletions(-)
19
tests/qemu-iotests/151.out | 4 +-
9
20
2 files changed, 181 insertions(+), 3 deletions(-)
10
diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c
21
22
diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151
23
index XXXXXXX..XXXXXXX 100755
24
--- a/tests/qemu-iotests/151
25
+++ b/tests/qemu-iotests/151
26
@@ -XXX,XX +XXX,XX @@
27
# along with this program. If not, see <http://www.gnu.org/licenses/>.
28
#
29
30
+import math
31
import os
32
+import subprocess
33
+from typing import List
34
import iotests
35
from iotests import qemu_img
36
37
@@ -XXX,XX +XXX,XX @@ class TestActiveMirror(iotests.QMPTestCase):
38
self.vm = iotests.VM()
39
self.vm.add_drive_raw(self.vm.qmp_to_opts(blk_source))
40
self.vm.add_blockdev(self.vm.qmp_to_opts(blk_target))
41
- self.vm.add_device('virtio-blk,drive=source')
42
+ self.vm.add_device('virtio-blk,id=vblk,drive=source')
43
self.vm.launch()
44
45
def tearDown(self):
46
@@ -XXX,XX +XXX,XX @@ class TestActiveMirror(iotests.QMPTestCase):
47
self.potential_writes_in_flight = False
48
49
50
+class TestThrottledWithNbdExport(iotests.QMPTestCase):
51
+ image_len = 128 * 1024 * 1024 # MB
52
+ iops = 16
53
+ background_processes: List['subprocess.Popen[str]'] = []
54
+
55
+ def setUp(self):
56
+ qemu_img('create', '-f', iotests.imgfmt, source_img, '128M')
57
+ qemu_img('create', '-f', iotests.imgfmt, target_img, '128M')
58
+
59
+ self.vm = iotests.VM()
60
+ self.vm.launch()
61
+
62
+ result = self.vm.qmp('object-add', **{
63
+ 'qom-type': 'throttle-group',
64
+ 'id': 'thrgr',
65
+ 'limits': {
66
+ 'iops-total': self.iops,
67
+ 'iops-total-max': self.iops
68
+ }
69
+ })
70
+ self.assert_qmp(result, 'return', {})
71
+
72
+ result = self.vm.qmp('blockdev-add', **{
73
+ 'node-name': 'source-node',
74
+ 'driver': 'throttle',
75
+ 'throttle-group': 'thrgr',
76
+ 'file': {
77
+ 'driver': iotests.imgfmt,
78
+ 'file': {
79
+ 'driver': 'file',
80
+ 'filename': source_img
81
+ }
82
+ }
83
+ })
84
+ self.assert_qmp(result, 'return', {})
85
+
86
+ result = self.vm.qmp('blockdev-add', **{
87
+ 'node-name': 'target-node',
88
+ 'driver': iotests.imgfmt,
89
+ 'file': {
90
+ 'driver': 'file',
91
+ 'filename': target_img
92
+ }
93
+ })
94
+ self.assert_qmp(result, 'return', {})
95
+
96
+ self.nbd_sock = iotests.file_path('nbd.sock',
97
+ base_dir=iotests.sock_dir)
98
+ self.nbd_url = f'nbd+unix:///source-node?socket={self.nbd_sock}'
99
+
100
+ result = self.vm.qmp('nbd-server-start', addr={
101
+ 'type': 'unix',
102
+ 'data': {
103
+ 'path': self.nbd_sock
104
+ }
105
+ })
106
+ self.assert_qmp(result, 'return', {})
107
+
108
+ result = self.vm.qmp('block-export-add', id='exp0', type='nbd',
109
+ node_name='source-node', writable=True)
110
+ self.assert_qmp(result, 'return', {})
111
+
112
+ def tearDown(self):
113
+ # Wait for background requests to settle
114
+ try:
115
+ while True:
116
+ p = self.background_processes.pop()
117
+ while True:
118
+ try:
119
+ p.wait(timeout=0.0)
120
+ break
121
+ except subprocess.TimeoutExpired:
122
+ self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
123
+ except IndexError:
124
+ pass
125
+
126
+ # Cancel ongoing block jobs
127
+ for job in self.vm.qmp('query-jobs')['return']:
128
+ self.vm.qmp('block-job-cancel', device=job['id'], force=True)
129
+
130
+ while True:
131
+ self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
132
+ if len(self.vm.qmp('query-jobs')['return']) == 0:
133
+ break
134
+
135
+ self.vm.shutdown()
136
+ os.remove(source_img)
137
+ os.remove(target_img)
138
+
139
+ def testUnderLoad(self):
140
+ '''
141
+ Throttle the source node, then issue a whole bunch of external requests
142
+ while the mirror job (in write-blocking mode) is running. We want to
143
+ see background requests being issued even while the source is under
144
+ full load by active writes, so that progress can be made towards READY.
145
+ '''
146
+
147
+ # Fill the first half of the source image; do not fill the second half,
148
+ # that is where we will have active requests occur. This ensures that
149
+ # active mirroring itself will not directly contribute to the job's
150
+ # progress (because when the job was started, those areas were not
151
+ # intended to be copied, so active mirroring will only lead to not
152
+ # losing progress, but also not making any).
153
+ self.vm.hmp_qemu_io('source-node',
154
+ f'aio_write -P 1 0 {self.image_len // 2}')
155
+ self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
156
+
157
+ # Launch the mirror job
158
+ mirror_buf_size = 65536
159
+ result = self.vm.qmp('blockdev-mirror',
160
+ job_id='mirror',
161
+ filter_node_name='mirror-node',
162
+ device='source-node',
163
+ target='target-node',
164
+ sync='full',
165
+ copy_mode='write-blocking',
166
+ buf_size=mirror_buf_size)
167
+ self.assert_qmp(result, 'return', {})
168
+
169
+ # We create the external requests via qemu-io processes on the NBD
170
+ # server. Have their offset start in the middle of the image so they
171
+ # do not overlap with the background requests (which start from the
172
+ # beginning).
173
+ active_request_offset = self.image_len // 2
174
+ active_request_len = 4096
175
+
176
+ # Create enough requests to saturate the node for 5 seconds
177
+ for _ in range(0, 5 * self.iops):
178
+ req = f'write -P 42 {active_request_offset} {active_request_len}'
179
+ active_request_offset += active_request_len
180
+ p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req)
181
+ self.background_processes += [p]
182
+
183
+ # Now advance the clock one I/O operation at a time by the 4 seconds
184
+ # (i.e. one less than 5). We expect the mirror job to issue background
185
+ # operations here, even though active requests are still in flight.
186
+ # The active requests will take precedence, however, because they have
187
+ # been issued earlier than mirror's background requests.
188
+ # Once the active requests we have started above are done (i.e. after 5
189
+ # virtual seconds), we expect those background requests to be worked
190
+ # on. We only advance 4 seconds here to avoid race conditions.
191
+ for _ in range(0, 4 * self.iops):
192
+ step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops)
193
+ self.vm.qtest(f'clock_step {step}')
194
+
195
+ # Note how much remains to be done until the mirror job is finished
196
+ job_status = self.vm.qmp('query-jobs')['return'][0]
197
+ start_remaining = job_status['total-progress'] - \
198
+ job_status['current-progress']
199
+
200
+ # Create a whole bunch of more active requests
201
+ for _ in range(0, 10 * self.iops):
202
+ req = f'write -P 42 {active_request_offset} {active_request_len}'
203
+ active_request_offset += active_request_len
204
+ p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req)
205
+ self.background_processes += [p]
206
+
207
+ # Let the clock advance more. After 1 second, as noted above, we
208
+ # expect the background requests to be worked on. Give them a couple
209
+ # of seconds (specifically 4) to see their impact.
210
+ for _ in range(0, 5 * self.iops):
211
+ step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops)
212
+ self.vm.qtest(f'clock_step {step}')
213
+
214
+ # Note how much remains to be done now. We expect this number to be
215
+ # reduced thanks to those background requests.
216
+ job_status = self.vm.qmp('query-jobs')['return'][0]
217
+ end_remaining = job_status['total-progress'] - \
218
+ job_status['current-progress']
219
+
220
+ # See that indeed progress was being made on the job, even while the
221
+ # node was saturated with active requests
222
+ self.assertGreater(start_remaining - end_remaining, 0)
223
+
224
+
225
if __name__ == '__main__':
226
iotests.main(supported_fmts=['qcow2', 'raw'],
227
supported_protocols=['file'])
228
diff --git a/tests/qemu-iotests/151.out b/tests/qemu-iotests/151.out
11
index XXXXXXX..XXXXXXX 100644
229
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/check-block-qdict.c
230
--- a/tests/qemu-iotests/151.out
13
+++ b/tests/check-block-qdict.c
231
+++ b/tests/qemu-iotests/151.out
14
@@ -XXX,XX +XXX,XX @@ static void qdict_defaults_test(void)
232
@@ -XXX,XX +XXX,XX @@
15
233
-....
16
static void qdict_flatten_test(void)
234
+.....
17
{
235
----------------------------------------------------------------------
18
- QList *list1 = qlist_new();
236
-Ran 4 tests
19
- QList *list2 = qlist_new();
237
+Ran 5 tests
20
- QDict *dict1 = qdict_new();
238
21
- QDict *dict2 = qdict_new();
239
OK
22
- QDict *dict3 = qdict_new();
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
29
/*
30
* Test the flattening of
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
--
240
--
92
2.13.6
241
2.38.1
93
94
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
-blockdev and blockdev-add silently ignore empty objects and arrays in
3
Have write requests happen to the source node right when we start a
4
their argument. That's because qmp_blockdev_add() converts the
4
mirror job. The mirror filter node may encounter MirrorBDSOpaque.job
5
argument to a flat QDict, and qdict_flatten() eats empty QDict and
5
being NULL, but this should not cause a segfault.
6
QList members. For instance, we ignore an empty BlockdevOptions
7
member @cache. No real harm, as absent means the same as empty there.
8
6
9
Thus, the flaw puts an artificial restriction on the QAPI schema: we
7
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
10
can't have potentially empty objects and arrays within
8
Message-Id: <20221109165452.67927-6-hreitz@redhat.com>
11
BlockdevOptions, except when they're optional and "empty" has the same
12
meaning as "absent".
13
14
Our QAPI schema satisfies this restriction (I checked), but it's a
15
trap for the unwary, and a temptation to employ awkward workarounds
16
for the wary. Let's get rid of it.
17
18
Change qdict_flatten() and qdict_crumple() to treat empty dictionaries
19
and lists exactly like scalars.
20
21
Signed-off-by: Markus Armbruster <armbru@redhat.com>
22
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
24
---
11
---
25
qobject/block-qdict.c | 54 +++++++++++++++++++++++++++++------------------
12
tests/qemu-iotests/151 | 53 +++++++++++++++++++++++++++++++++++---
26
tests/check-block-qdict.c | 38 ++++++++++++++++++++++++++-------
13
tests/qemu-iotests/151.out | 4 +--
27
2 files changed, 63 insertions(+), 29 deletions(-)
14
2 files changed, 52 insertions(+), 5 deletions(-)
28
15
29
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
16
diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151
17
index XXXXXXX..XXXXXXX 100755
18
--- a/tests/qemu-iotests/151
19
+++ b/tests/qemu-iotests/151
20
@@ -XXX,XX +XXX,XX @@
21
import math
22
import os
23
import subprocess
24
-from typing import List
25
+import time
26
+from typing import List, Optional
27
import iotests
28
from iotests import qemu_img
29
30
@@ -XXX,XX +XXX,XX @@ class TestActiveMirror(iotests.QMPTestCase):
31
self.potential_writes_in_flight = False
32
33
34
-class TestThrottledWithNbdExport(iotests.QMPTestCase):
35
+class TestThrottledWithNbdExportBase(iotests.QMPTestCase):
36
image_len = 128 * 1024 * 1024 # MB
37
- iops = 16
38
+ iops: Optional[int] = None
39
background_processes: List['subprocess.Popen[str]'] = []
40
41
def setUp(self):
42
+ # Must be set by subclasses
43
+ self.assertIsNotNone(self.iops)
44
+
45
qemu_img('create', '-f', iotests.imgfmt, source_img, '128M')
46
qemu_img('create', '-f', iotests.imgfmt, target_img, '128M')
47
48
@@ -XXX,XX +XXX,XX @@ class TestThrottledWithNbdExport(iotests.QMPTestCase):
49
os.remove(source_img)
50
os.remove(target_img)
51
52
+
53
+class TestLowThrottledWithNbdExport(TestThrottledWithNbdExportBase):
54
+ iops = 16
55
+
56
def testUnderLoad(self):
57
'''
58
Throttle the source node, then issue a whole bunch of external requests
59
@@ -XXX,XX +XXX,XX @@ class TestThrottledWithNbdExport(iotests.QMPTestCase):
60
self.assertGreater(start_remaining - end_remaining, 0)
61
62
63
+class TestHighThrottledWithNbdExport(TestThrottledWithNbdExportBase):
64
+ iops = 1024
65
+
66
+ def testActiveOnCreation(self):
67
+ '''
68
+ Issue requests on the mirror source node right as the mirror is
69
+ instated. It's possible that requests occur before the actual job is
70
+ created, but after the node has been put into the graph. Write
71
+ requests across the node must in that case be forwarded to the source
72
+ node without attempting to mirror them (there is no job object yet, so
73
+ attempting to access it would cause a segfault).
74
+ We do this with a lightly throttled node (i.e. quite high IOPS limit).
75
+ Using throttling seems to increase reproductivity, but if the limit is
76
+ too low, all requests allowed per second will be submitted before
77
+ mirror_start_job() gets to the problematic point.
78
+ '''
79
+
80
+ # Let qemu-img bench create write requests (enough for two seconds on
81
+ # the virtual clock)
82
+ bench_args = ['bench', '-w', '-d', '1024', '-f', 'nbd',
83
+ '-c', str(self.iops * 2), self.nbd_url]
84
+ p = iotests.qemu_tool_popen(iotests.qemu_img_args + bench_args)
85
+ self.background_processes += [p]
86
+
87
+ # Give qemu-img bench time to start up and issue requests
88
+ time.sleep(1.0)
89
+ # Flush the request queue, so new requests can come in right as we
90
+ # start blockdev-mirror
91
+ self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
92
+
93
+ result = self.vm.qmp('blockdev-mirror',
94
+ job_id='mirror',
95
+ device='source-node',
96
+ target='target-node',
97
+ sync='full',
98
+ copy_mode='write-blocking')
99
+ self.assert_qmp(result, 'return', {})
100
+
101
+
102
if __name__ == '__main__':
103
iotests.main(supported_fmts=['qcow2', 'raw'],
104
supported_protocols=['file'])
105
diff --git a/tests/qemu-iotests/151.out b/tests/qemu-iotests/151.out
30
index XXXXXXX..XXXXXXX 100644
106
index XXXXXXX..XXXXXXX 100644
31
--- a/qobject/block-qdict.c
107
--- a/tests/qemu-iotests/151.out
32
+++ b/qobject/block-qdict.c
108
+++ b/tests/qemu-iotests/151.out
33
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
109
@@ -XXX,XX +XXX,XX @@
34
{
110
-.....
35
QObject *value;
111
+......
36
const QListEntry *entry;
112
----------------------------------------------------------------------
37
+ QDict *dict_val;
113
-Ran 5 tests
38
+ QList *list_val;
114
+Ran 6 tests
39
char *new_key;
115
40
int i;
116
OK
41
42
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
43
44
for (i = 0; entry; entry = qlist_next(entry), i++) {
45
value = qlist_entry_obj(entry);
46
+ dict_val = qobject_to(QDict, value);
47
+ list_val = qobject_to(QList, value);
48
new_key = g_strdup_printf("%s.%i", prefix, i);
49
50
/*
51
* Flatten non-empty QDict and QList recursively into @target,
52
* copy other objects to @target
53
*/
54
- if (qobject_type(value) == QTYPE_QDICT) {
55
- qdict_flatten_qdict(qobject_to(QDict, value), target, new_key);
56
- } else if (qobject_type(value) == QTYPE_QLIST) {
57
- qdict_flatten_qlist(qobject_to(QList, value), target, new_key);
58
+ if (dict_val && qdict_size(dict_val)) {
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
--
117
--
262
2.13.6
118
2.38.1
263
264
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Alberto Faria <afaria@redhat.com>
2
2
3
Parameter auth-client-required lets you configure authentication
3
The nvme-io_uring BlockDriver's path option must point at the character
4
methods. We tried to provide that in v2.9.0, but backed out due to
4
device of an NVMe namespace, not at an image file.
5
interface design doubts (commit 464444fcc16).
6
5
7
This commit is similar to what we backed out, but simpler: we use a
6
Fixes: fd66dbd424f5 ("blkio: add libblkio block driver")
8
list of enumeration values instead of a list of objects with a member
7
Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
9
of enumeration type.
8
Signed-off-by: Alberto Faria <afaria@redhat.com>
10
9
Message-Id: <20221108142347.1322674-1-afaria@redhat.com>
11
Let's review our reasons for backing out the first try, as stated in
10
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
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>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
62
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
63
---
13
---
64
qapi/block-core.json | 13 +++++++++++++
14
qapi/block-core.json | 2 +-
65
block/rbd.c | 42 ++++++++++++++++++++++++++++++++----------
15
1 file changed, 1 insertion(+), 1 deletion(-)
66
2 files changed, 45 insertions(+), 10 deletions(-)
67
16
68
diff --git a/qapi/block-core.json b/qapi/block-core.json
17
diff --git a/qapi/block-core.json b/qapi/block-core.json
69
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
70
--- a/qapi/block-core.json
19
--- a/qapi/block-core.json
71
+++ b/qapi/block-core.json
20
+++ b/qapi/block-core.json
72
@@ -XXX,XX +XXX,XX @@
21
@@ -XXX,XX +XXX,XX @@
73
22
#
74
23
# Driver specific block device options for the nvme-io_uring backend.
24
#
25
-# @path: path to the image file
26
+# @path: path to the NVMe namespace's character device (e.g. /dev/ng0n1).
27
#
28
# Since: 7.2
75
##
29
##
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
}
139
140
- rados_conf_set(cluster, "key", secret);
141
- g_free(secret);
142
+ if (opts->has_auth_client_required) {
143
+ accu = g_string_new("");
144
+ for (auth = opts->auth_client_required; auth; auth = auth->next) {
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
}
164
}
165
166
- if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
167
+ if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) {
168
r = -EIO;
169
goto failed_shutdown;
170
}
171
--
30
--
172
2.13.6
31
2.38.1
173
174
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Alberto Faria <afaria@redhat.com>
2
2
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
3
Setting it to true can cause the device size to be queried from libblkio
4
in otherwise fast paths, degrading performance. Set it to false and
5
require users to refresh the device size explicitly instead.
6
7
Fixes: 4c8f4fda0504 ("block/blkio: Tolerate device size changes")
8
Suggested-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Alberto Faria <afaria@redhat.com>
10
Message-Id: <20221108144433.1334074-1-afaria@redhat.com>
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
13
---
7
tests/check-block-qdict.c | 14 +++++++++++++-
14
block/blkio.c | 1 -
8
1 file changed, 13 insertions(+), 1 deletion(-)
15
1 file changed, 1 deletion(-)
9
16
10
diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c
17
diff --git a/block/blkio.c b/block/blkio.c
11
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/check-block-qdict.c
19
--- a/block/blkio.c
13
+++ b/tests/check-block-qdict.c
20
+++ b/block/blkio.c
14
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void)
21
@@ -XXX,XX +XXX,XX @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp)
15
QList *e = qlist_new();
22
{ \
16
QDict *e_1_2 = qdict_new();
23
.format_name = name, \
17
QDict *f = qdict_new();
24
.protocol_name = name, \
18
+ QList *y = qlist_new();
25
- .has_variable_length = true, \
19
+ QDict *z = qdict_new();
26
.instance_size = sizeof(BDRVBlkioState), \
20
QDict *root = qdict_new();
27
.bdrv_file_open = blkio_file_open, \
21
28
.bdrv_close = blkio_close, \
22
/*
23
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void)
24
* "c": 2,
25
* "d": 3,
26
* },
27
- * "g": 4
28
+ * "g": 4,
29
+ * "y": [{}],
30
+ * "z": {"a": []}
31
* }
32
*
33
* to
34
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void)
35
* "f.d": 3,
36
* "g": 4
37
* }
38
+ *
39
+ * Note that "y" and "z" get eaten.
40
*/
41
42
qdict_put_int(e_1_2, "a", 0);
43
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void)
44
qdict_put_int(f, "c", 2);
45
qdict_put_int(f, "d", 3);
46
47
+ qlist_append(y, qdict_new());
48
+
49
+ qdict_put(z, "a", qlist_new());
50
+
51
qdict_put(root, "e", e);
52
qdict_put(root, "f", f);
53
qdict_put_int(root, "g", 4);
54
+ qdict_put(root, "y", y);
55
+ qdict_put(root, "z", z);
56
57
qdict_flatten(root);
58
59
--
29
--
60
2.13.6
30
2.38.1
61
62
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
There are numerous QDict functions that have been introduced for and are
3
We want to use bdrv_child_get_parent_aio_context() from
4
used only by the block layer. Move their declarations into an own
4
bdrv_parent_drained_{begin,end}_single(), both of which are "I/O or GS"
5
header file to reflect that.
5
functions.
6
6
7
While qdict_extract_subqdict() is in fact used outside of the block
7
Prior to 3ed4f708fe1, all the implementations were I/O code anyway.
8
layer (in util/qemu-config.c), it is still a function related very
8
3ed4f708fe1 has put block jobs' AioContext field under the job mutex, so
9
closely to how the block layer works with nested QDicts, namely by
9
to make child_job_get_parent_aio_context() work in an I/O context, we
10
sometimes flattening them. Therefore, its declaration is put into this
10
need to take that lock there.
11
header as well and util/qemu-config.c includes it with a comment stating
12
exactly which function it needs.
13
11
14
Suggested-by: Markus Armbruster <armbru@redhat.com>
12
Furthermore, blk_root_get_parent_aio_context() is not marked as
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
anything, but is safe to run in an I/O context, so mark it that way now.
16
Message-Id: <20180509165530.29561-7-mreitz@redhat.com>
14
(blk_get_aio_context() is an I/O code function.)
17
[Copyright note tweaked, superfluous includes dropped]
15
18
Signed-off-by: Markus Armbruster <armbru@redhat.com>
16
With that done, all implementations explicitly are I/O code, so we can
17
mark bdrv_child_get_parent_aio_context() as I/O code, too, so callers
18
know it is safe to run from both GS and I/O contexts.
19
20
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
21
Message-Id: <20221107151321.211175-2-hreitz@redhat.com>
19
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
22
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
---
24
---
22
include/block/qdict.h | 32 ++++++++++++++++++++++++++++++++
25
include/block/block-global-state.h | 1 -
23
include/qapi/qmp/qdict.h | 17 -----------------
26
include/block/block-io.h | 2 ++
24
block.c | 1 +
27
include/block/block_int-common.h | 4 ++--
25
block/gluster.c | 1 +
28
block.c | 2 +-
26
block/iscsi.c | 1 +
29
block/block-backend.c | 1 +
27
block/nbd.c | 1 +
30
blockjob.c | 3 ++-
28
block/nfs.c | 1 +
31
6 files changed, 8 insertions(+), 5 deletions(-)
29
block/parallels.c | 1 +
30
block/qcow.c | 1 +
31
block/qcow2.c | 1 +
32
block/qed.c | 1 +
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
32
51
diff --git a/include/block/qdict.h b/include/block/qdict.h
33
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
52
new file mode 100644
34
index XXXXXXX..XXXXXXX 100644
53
index XXXXXXX..XXXXXXX
35
--- a/include/block/block-global-state.h
54
--- /dev/null
36
+++ b/include/block/block-global-state.h
55
+++ b/include/block/qdict.h
37
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_lock(BlockDriverState *bs);
56
@@ -XXX,XX +XXX,XX @@
38
*/
57
+/*
39
void coroutine_fn bdrv_co_unlock(BlockDriverState *bs);
58
+ * Special QDict functions used by the block layer
40
59
+ *
41
-AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c);
60
+ * Copyright (c) 2013-2018 Red Hat, Inc.
42
bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx,
61
+ *
43
GHashTable *visited, Transaction *tran,
62
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
44
Error **errp);
63
+ * See the COPYING.LIB file in the top-level directory.
45
diff --git a/include/block/block-io.h b/include/block/block-io.h
64
+ */
46
index XXXXXXX..XXXXXXX 100644
47
--- a/include/block/block-io.h
48
+++ b/include/block/block-io.h
49
@@ -XXX,XX +XXX,XX @@ void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event);
50
*/
51
AioContext *bdrv_get_aio_context(BlockDriverState *bs);
52
53
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c);
65
+
54
+
66
+#ifndef BLOCK_QDICT_H
55
/**
67
+#define BLOCK_QDICT_H
56
* Move the current coroutine to the AioContext of @bs and return the old
57
* AioContext of the coroutine. Increase bs->in_flight so that draining @bs
58
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
59
index XXXXXXX..XXXXXXX 100644
60
--- a/include/block/block_int-common.h
61
+++ b/include/block/block_int-common.h
62
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
63
GHashTable *visited, Transaction *tran,
64
Error **errp);
65
66
- AioContext *(*get_parent_aio_context)(BdrvChild *child);
67
-
68
/*
69
* I/O API functions. These functions are thread-safe.
70
*
71
@@ -XXX,XX +XXX,XX @@ struct BdrvChildClass {
72
*/
73
const char *(*get_name)(BdrvChild *child);
74
75
+ AioContext *(*get_parent_aio_context)(BdrvChild *child);
68
+
76
+
69
+#include "qapi/qmp/qdict.h"
77
/*
70
+
78
* If this pair of functions is implemented, the parent doesn't issue new
71
+void qdict_copy_default(QDict *dst, QDict *src, const char *key);
79
* requests after returning from .drained_begin() until .drained_end() is
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
80
diff --git a/block.c b/block.c
118
index XXXXXXX..XXXXXXX 100644
81
index XXXXXXX..XXXXXXX 100644
119
--- a/block.c
82
--- a/block.c
120
+++ b/block.c
83
+++ b/block.c
121
@@ -XXX,XX +XXX,XX @@
84
@@ -XXX,XX +XXX,XX @@ const BdrvChildClass child_of_bds = {
122
#include "block/block_int.h"
85
123
#include "block/blockjob.h"
86
AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c)
124
#include "block/nbd.h"
87
{
125
+#include "block/qdict.h"
88
- GLOBAL_STATE_CODE();
126
#include "qemu/error-report.h"
89
+ IO_CODE();
127
#include "module_block.h"
90
return c->klass->get_parent_aio_context(c);
128
#include "qemu/module.h"
91
}
129
diff --git a/block/gluster.c b/block/gluster.c
92
93
diff --git a/block/block-backend.c b/block/block-backend.c
130
index XXXXXXX..XXXXXXX 100644
94
index XXXXXXX..XXXXXXX 100644
131
--- a/block/gluster.c
95
--- a/block/block-backend.c
132
+++ b/block/gluster.c
96
+++ b/block/block-backend.c
133
@@ -XXX,XX +XXX,XX @@
97
@@ -XXX,XX +XXX,XX @@ static void blk_root_detach(BdrvChild *child)
134
#include "qemu/osdep.h"
98
static AioContext *blk_root_get_parent_aio_context(BdrvChild *c)
135
#include <glusterfs/api/glfs.h>
99
{
136
#include "block/block_int.h"
100
BlockBackend *blk = c->opaque;
137
+#include "block/qdict.h"
101
+ IO_CODE();
138
#include "qapi/error.h"
102
139
#include "qapi/qmp/qdict.h"
103
return blk_get_aio_context(blk);
140
#include "qapi/qmp/qerror.h"
104
}
141
diff --git a/block/iscsi.c b/block/iscsi.c
105
diff --git a/blockjob.c b/blockjob.c
142
index XXXXXXX..XXXXXXX 100644
106
index XXXXXXX..XXXXXXX 100644
143
--- a/block/iscsi.c
107
--- a/blockjob.c
144
+++ b/block/iscsi.c
108
+++ b/blockjob.c
145
@@ -XXX,XX +XXX,XX @@
109
@@ -XXX,XX +XXX,XX @@ static bool child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx,
146
#include "qemu/bitops.h"
110
static AioContext *child_job_get_parent_aio_context(BdrvChild *c)
147
#include "qemu/bitmap.h"
111
{
148
#include "block/block_int.h"
112
BlockJob *job = c->opaque;
149
+#include "block/qdict.h"
113
- GLOBAL_STATE_CODE();
150
#include "scsi/constants.h"
114
+ IO_CODE();
151
#include "qemu/iov.h"
115
+ JOB_LOCK_GUARD();
152
#include "qemu/option.h"
116
153
diff --git a/block/nbd.c b/block/nbd.c
117
return job->job.aio_context;
154
index XXXXXXX..XXXXXXX 100644
118
}
155
--- a/block/nbd.c
156
+++ b/block/nbd.c
157
@@ -XXX,XX +XXX,XX @@
158
159
#include "qemu/osdep.h"
160
#include "nbd-client.h"
161
+#include "block/qdict.h"
162
#include "qapi/error.h"
163
#include "qemu/uri.h"
164
#include "block/block_int.h"
165
diff --git a/block/nfs.c b/block/nfs.c
166
index XXXXXXX..XXXXXXX 100644
167
--- a/block/nfs.c
168
+++ b/block/nfs.c
169
@@ -XXX,XX +XXX,XX @@
170
#include "qemu/error-report.h"
171
#include "qapi/error.h"
172
#include "block/block_int.h"
173
+#include "block/qdict.h"
174
#include "trace.h"
175
#include "qemu/iov.h"
176
#include "qemu/option.h"
177
diff --git a/block/parallels.c b/block/parallels.c
178
index XXXXXXX..XXXXXXX 100644
179
--- a/block/parallels.c
180
+++ b/block/parallels.c
181
@@ -XXX,XX +XXX,XX @@
182
#include "qemu/osdep.h"
183
#include "qapi/error.h"
184
#include "block/block_int.h"
185
+#include "block/qdict.h"
186
#include "sysemu/block-backend.h"
187
#include "qemu/module.h"
188
#include "qemu/option.h"
189
diff --git a/block/qcow.c b/block/qcow.c
190
index XXXXXXX..XXXXXXX 100644
191
--- a/block/qcow.c
192
+++ b/block/qcow.c
193
@@ -XXX,XX +XXX,XX @@
194
#include "qapi/error.h"
195
#include "qemu/error-report.h"
196
#include "block/block_int.h"
197
+#include "block/qdict.h"
198
#include "sysemu/block-backend.h"
199
#include "qemu/module.h"
200
#include "qemu/option.h"
201
diff --git a/block/qcow2.c b/block/qcow2.c
202
index XXXXXXX..XXXXXXX 100644
203
--- a/block/qcow2.c
204
+++ b/block/qcow2.c
205
@@ -XXX,XX +XXX,XX @@
206
207
#include "qemu/osdep.h"
208
#include "block/block_int.h"
209
+#include "block/qdict.h"
210
#include "sysemu/block-backend.h"
211
#include "qemu/module.h"
212
#include <zlib.h>
213
diff --git a/block/qed.c b/block/qed.c
214
index XXXXXXX..XXXXXXX 100644
215
--- a/block/qed.c
216
+++ b/block/qed.c
217
@@ -XXX,XX +XXX,XX @@
218
*/
219
220
#include "qemu/osdep.h"
221
+#include "block/qdict.h"
222
#include "qapi/error.h"
223
#include "qemu/timer.h"
224
#include "qemu/bswap.h"
225
diff --git a/block/quorum.c b/block/quorum.c
226
index XXXXXXX..XXXXXXX 100644
227
--- a/block/quorum.c
228
+++ b/block/quorum.c
229
@@ -XXX,XX +XXX,XX @@
230
#include "qemu/cutils.h"
231
#include "qemu/option.h"
232
#include "block/block_int.h"
233
+#include "block/qdict.h"
234
#include "qapi/error.h"
235
#include "qapi/qapi-events-block.h"
236
#include "qapi/qmp/qdict.h"
237
diff --git a/block/rbd.c b/block/rbd.c
238
index XXXXXXX..XXXXXXX 100644
239
--- a/block/rbd.c
240
+++ b/block/rbd.c
241
@@ -XXX,XX +XXX,XX @@
242
#include "qemu/error-report.h"
243
#include "qemu/option.h"
244
#include "block/block_int.h"
245
+#include "block/qdict.h"
246
#include "crypto/secret.h"
247
#include "qemu/cutils.h"
248
#include "qapi/qmp/qstring.h"
249
diff --git a/block/sheepdog.c b/block/sheepdog.c
250
index XXXXXXX..XXXXXXX 100644
251
--- a/block/sheepdog.c
252
+++ b/block/sheepdog.c
253
@@ -XXX,XX +XXX,XX @@
254
#include "qemu/option.h"
255
#include "qemu/sockets.h"
256
#include "block/block_int.h"
257
+#include "block/qdict.h"
258
#include "sysemu/block-backend.h"
259
#include "qemu/bitops.h"
260
#include "qemu/cutils.h"
261
diff --git a/block/snapshot.c b/block/snapshot.c
262
index XXXXXXX..XXXXXXX 100644
263
--- a/block/snapshot.c
264
+++ b/block/snapshot.c
265
@@ -XXX,XX +XXX,XX @@
266
#include "qemu/osdep.h"
267
#include "block/snapshot.h"
268
#include "block/block_int.h"
269
+#include "block/qdict.h"
270
#include "qapi/error.h"
271
#include "qapi/qmp/qdict.h"
272
#include "qapi/qmp/qerror.h"
273
diff --git a/block/ssh.c b/block/ssh.c
274
index XXXXXXX..XXXXXXX 100644
275
--- a/block/ssh.c
276
+++ b/block/ssh.c
277
@@ -XXX,XX +XXX,XX @@
278
#include <libssh2_sftp.h>
279
280
#include "block/block_int.h"
281
+#include "block/qdict.h"
282
#include "qapi/error.h"
283
#include "qemu/error-report.h"
284
#include "qemu/option.h"
285
diff --git a/block/vhdx.c b/block/vhdx.c
286
index XXXXXXX..XXXXXXX 100644
287
--- a/block/vhdx.c
288
+++ b/block/vhdx.c
289
@@ -XXX,XX +XXX,XX @@
290
#include "qemu/osdep.h"
291
#include "qapi/error.h"
292
#include "block/block_int.h"
293
+#include "block/qdict.h"
294
#include "sysemu/block-backend.h"
295
#include "qemu/module.h"
296
#include "qemu/option.h"
297
diff --git a/block/vpc.c b/block/vpc.c
298
index XXXXXXX..XXXXXXX 100644
299
--- a/block/vpc.c
300
+++ b/block/vpc.c
301
@@ -XXX,XX +XXX,XX @@
302
#include "qemu/osdep.h"
303
#include "qapi/error.h"
304
#include "block/block_int.h"
305
+#include "block/qdict.h"
306
#include "sysemu/block-backend.h"
307
#include "qemu/module.h"
308
#include "qemu/option.h"
309
diff --git a/block/vvfat.c b/block/vvfat.c
310
index XXXXXXX..XXXXXXX 100644
311
--- a/block/vvfat.c
312
+++ b/block/vvfat.c
313
@@ -XXX,XX +XXX,XX @@
314
#include <dirent.h>
315
#include "qapi/error.h"
316
#include "block/block_int.h"
317
+#include "block/qdict.h"
318
#include "qemu/module.h"
319
#include "qemu/option.h"
320
#include "qemu/bswap.h"
321
diff --git a/block/vxhs.c b/block/vxhs.c
322
index XXXXXXX..XXXXXXX 100644
323
--- a/block/vxhs.c
324
+++ b/block/vxhs.c
325
@@ -XXX,XX +XXX,XX @@
326
#include <qnio/qnio_api.h>
327
#include <sys/param.h>
328
#include "block/block_int.h"
329
+#include "block/qdict.h"
330
#include "qapi/qmp/qerror.h"
331
#include "qapi/qmp/qdict.h"
332
#include "qapi/qmp/qstring.h"
333
diff --git a/blockdev.c b/blockdev.c
334
index XXXXXXX..XXXXXXX 100644
335
--- a/blockdev.c
336
+++ b/blockdev.c
337
@@ -XXX,XX +XXX,XX @@
338
#include "sysemu/blockdev.h"
339
#include "hw/block/block.h"
340
#include "block/blockjob.h"
341
+#include "block/qdict.h"
342
#include "block/throttle-groups.h"
343
#include "monitor/monitor.h"
344
#include "qemu/error-report.h"
345
diff --git a/qobject/qdict.c b/qobject/qdict.c
346
index XXXXXXX..XXXXXXX 100644
347
--- a/qobject/qdict.c
348
+++ b/qobject/qdict.c
349
@@ -XXX,XX +XXX,XX @@
350
*/
351
352
#include "qemu/osdep.h"
353
+#include "block/qdict.h"
354
#include "qapi/qmp/qnum.h"
355
#include "qapi/qmp/qdict.h"
356
#include "qapi/qmp/qbool.h"
357
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
358
index XXXXXXX..XXXXXXX 100644
359
--- a/tests/check-qdict.c
360
+++ b/tests/check-qdict.c
361
@@ -XXX,XX +XXX,XX @@
362
*/
363
364
#include "qemu/osdep.h"
365
+#include "block/qdict.h"
366
#include "qapi/qmp/qdict.h"
367
#include "qapi/qmp/qlist.h"
368
#include "qapi/qmp/qnum.h"
369
diff --git a/tests/check-qobject.c b/tests/check-qobject.c
370
index XXXXXXX..XXXXXXX 100644
371
--- a/tests/check-qobject.c
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
--
119
--
404
2.13.6
120
2.38.1
405
406
diff view generated by jsdifflib
1
The -drive option serial was deprecated in QEMU 2.10. It's time to
1
From: Hanna Reitz <hreitz@redhat.com>
2
remove it.
3
2
4
Tests need to be updated to set the serial number with -global instead
3
blk_get_aio_context() asserts that blk->ctx is always equal to the root
5
of using the -drive option.
4
BDS's context (if there is a root BDS). Therefore,
5
blk_do_set_aio_context() must update blk->ctx immediately after the root
6
BDS's context has changed.
6
7
8
Without this patch, the next patch would break iotest 238, because
9
bdrv_drained_begin() (called by blk_do_set_aio_context()) may then
10
invoke bdrv_child_get_parent_aio_context() on the root child, i.e.
11
blk_get_aio_context(). However, by this point, blk->ctx would not have
12
been updated and thus differ from the root node's context. This patch
13
fixes that.
14
15
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
17
Message-Id: <20221107151321.211175-3-hreitz@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
9
Reviewed-by: Jeff Cody <jcody@redhat.com>
10
---
19
---
11
include/hw/block/block.h | 1 -
20
block/block-backend.c | 8 +++++++-
12
include/sysemu/blockdev.h | 1 -
21
1 file changed, 7 insertions(+), 1 deletion(-)
13
block/block-backend.c | 1 -
14
blockdev.c | 10 ----------
15
hw/block/block.c | 13 -------------
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
22
27
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
28
index XXXXXXX..XXXXXXX 100644
29
--- a/include/hw/block/block.h
30
+++ b/include/hw/block/block.h
31
@@ -XXX,XX +XXX,XX @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
32
33
/* Configuration helpers */
34
35
-void blkconf_serial(BlockConf *conf, char **serial);
36
bool blkconf_geometry(BlockConf *conf, int *trans,
37
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
38
Error **errp);
39
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
40
index XXXXXXX..XXXXXXX 100644
41
--- a/include/sysemu/blockdev.h
42
+++ b/include/sysemu/blockdev.h
43
@@ -XXX,XX +XXX,XX @@ struct DriveInfo {
44
bool is_default; /* Added by default_drive() ? */
45
int media_cd;
46
QemuOpts *opts;
47
- char *serial;
48
QTAILQ_ENTRY(DriveInfo) next;
49
};
50
51
diff --git a/block/block-backend.c b/block/block-backend.c
23
diff --git a/block/block-backend.c b/block/block-backend.c
52
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
53
--- a/block/block-backend.c
25
--- a/block/block-backend.c
54
+++ b/block/block-backend.c
26
+++ b/block/block-backend.c
55
@@ -XXX,XX +XXX,XX @@ static void drive_info_del(DriveInfo *dinfo)
27
@@ -XXX,XX +XXX,XX @@ static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context,
56
return;
28
return ret;
29
}
30
}
31
+ /*
32
+ * Make blk->ctx consistent with the root node before we invoke any
33
+ * other operations like drain that might inquire blk->ctx
34
+ */
35
+ blk->ctx = new_context;
36
if (tgm->throttle_state) {
37
bdrv_drained_begin(bs);
38
throttle_group_detach_aio_context(tgm);
39
@@ -XXX,XX +XXX,XX @@ static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context,
40
}
41
42
bdrv_unref(bs);
43
+ } else {
44
+ blk->ctx = new_context;
57
}
45
}
58
qemu_opts_del(dinfo->opts);
46
59
- g_free(dinfo->serial);
47
- blk->ctx = new_context;
60
g_free(dinfo);
48
return 0;
61
}
49
}
62
50
63
diff --git a/blockdev.c b/blockdev.c
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
--
51
--
280
2.13.6
52
2.38.1
281
282
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
Parameter "filename" is deprecated since commit 5c3ad1a6a8f, v2.10.0.
3
bdrv_parent_drained_{begin,end}_single() are supposed to operate on the
4
Time to get rid of it.
4
parent, not on the child, so they should not attempt to get the context
5
to poll from the child but the parent instead. BDRV_POLL_WHILE(c->bs)
6
does get the context from the child, so we should replace it with
7
AIO_WAIT_WHILE() on the parent's context instead.
5
8
6
Signed-off-by: Markus Armbruster <armbru@redhat.com>
9
This problem becomes apparent when bdrv_replace_child_noperm() invokes
10
bdrv_parent_drained_end_single() after removing a child from a subgraph
11
that is in an I/O thread. By the time bdrv_parent_drained_end_single()
12
is called, child->bs is NULL, and so BDRV_POLL_WHILE(c->bs, ...) will
13
poll the main loop instead of the I/O thread; but anything that
14
bdrv_parent_drained_end_single_no_poll() may have scheduled is going to
15
want to run in the I/O thread, but because we poll the main loop, the
16
I/O thread is never unpaused, and nothing is run, resulting in a
17
deadlock.
18
19
Closes: https://gitlab.com/qemu-project/qemu/-/issues/1215
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
20
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
21
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
22
Message-Id: <20221107151321.211175-4-hreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
24
---
10
block/iscsi.c | 23 ++---------------------
25
block/io.c | 6 ++++--
11
1 file changed, 2 insertions(+), 21 deletions(-)
26
1 file changed, 4 insertions(+), 2 deletions(-)
12
27
13
diff --git a/block/iscsi.c b/block/iscsi.c
28
diff --git a/block/io.c b/block/io.c
14
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
15
--- a/block/iscsi.c
30
--- a/block/io.c
16
+++ b/block/iscsi.c
31
+++ b/block/io.c
17
@@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = {
32
@@ -XXX,XX +XXX,XX @@ static void bdrv_parent_drained_end_single_no_poll(BdrvChild *c,
18
.name = "timeout",
33
void bdrv_parent_drained_end_single(BdrvChild *c)
19
.type = QEMU_OPT_NUMBER,
34
{
20
},
35
int drained_end_counter = 0;
21
- {
36
+ AioContext *ctx = bdrv_child_get_parent_aio_context(c);
22
- .name = "filename",
37
IO_OR_GS_CODE();
23
- .type = QEMU_OPT_STRING,
38
bdrv_parent_drained_end_single_no_poll(c, &drained_end_counter);
24
- },
39
- BDRV_POLL_WHILE(c->bs, qatomic_read(&drained_end_counter) > 0);
25
{ /* end of list */ }
40
+ AIO_WAIT_WHILE(ctx, qatomic_read(&drained_end_counter) > 0);
26
},
41
}
27
};
42
28
@@ -XXX,XX +XXX,XX @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
43
static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore,
29
char *initiator_name = NULL;
44
@@ -XXX,XX +XXX,XX @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore,
30
QemuOpts *opts;
45
31
Error *local_err = NULL;
46
void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
32
- const char *transport_name, *portal, *target, *filename;
47
{
33
+ const char *transport_name, *portal, *target;
48
+ AioContext *ctx = bdrv_child_get_parent_aio_context(c);
34
#if LIBISCSI_API_VERSION >= (20160603)
49
IO_OR_GS_CODE();
35
enum iscsi_transport_type transport;
50
c->parent_quiesce_counter++;
36
#endif
51
if (c->klass->drained_begin) {
37
int i, ret = 0, timeout = 0, lun;
52
c->klass->drained_begin(c);
38
39
- /* If we are given a filename, parse the filename, with precedence given to
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
}
53
}
61
-exit:
54
if (poll) {
62
+
55
- BDRV_POLL_WHILE(c->bs, bdrv_parent_drained_poll_single(c));
63
return ret;
56
+ AIO_WAIT_WHILE(ctx, bdrv_parent_drained_poll_single(c));
57
}
64
}
58
}
65
59
66
--
60
--
67
2.13.6
61
2.38.1
68
69
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
Although qemu-img creates aligned files (by rounding up), it
3
Test streaming a base image into the top image underneath two throttle
4
must also gracefully handle files that are not sector-aligned.
4
nodes. This was reported to make qemu 7.1 hang
5
Test that the bug fixed in the previous patch does not recur.
5
(https://gitlab.com/qemu-project/qemu/-/issues/1215), so this serves as
6
a regression test.
6
7
7
It's a bit annoying that we can see the (implicit) hole past
8
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
8
the end of the file on to the next sector boundary, so if we
9
Message-Id: <20221110160921.33158-1-hreitz@redhat.com>
9
ever reach the point where we report a byte-accurate size rather
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
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>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
12
---
16
tests/qemu-iotests/221 | 60 ++++++++++++++++++++++++++++++++++++++++++++++
13
.../qemu-iotests/tests/stream-under-throttle | 121 ++++++++++++++++++
17
tests/qemu-iotests/221.out | 16 +++++++++++++
14
.../tests/stream-under-throttle.out | 5 +
18
tests/qemu-iotests/group | 1 +
15
2 files changed, 126 insertions(+)
19
3 files changed, 77 insertions(+)
16
create mode 100755 tests/qemu-iotests/tests/stream-under-throttle
20
create mode 100755 tests/qemu-iotests/221
17
create mode 100644 tests/qemu-iotests/tests/stream-under-throttle.out
21
create mode 100644 tests/qemu-iotests/221.out
22
18
23
diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221
19
diff --git a/tests/qemu-iotests/tests/stream-under-throttle b/tests/qemu-iotests/tests/stream-under-throttle
24
new file mode 100755
20
new file mode 100755
25
index XXXXXXX..XXXXXXX
21
index XXXXXXX..XXXXXXX
26
--- /dev/null
22
--- /dev/null
27
+++ b/tests/qemu-iotests/221
23
+++ b/tests/qemu-iotests/tests/stream-under-throttle
28
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@
29
+#!/bin/bash
25
+#!/usr/bin/env python3
26
+# group: rw
30
+#
27
+#
31
+# Test qemu-img vs. unaligned images
28
+# Test streaming with throttle nodes on top
32
+#
29
+#
33
+# Copyright (C) 2018 Red Hat, Inc.
30
+# Copyright (C) 2022 Red Hat, Inc.
34
+#
31
+#
35
+# This program is free software; you can redistribute it and/or modify
32
+# 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
33
+# 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
34
+# the Free Software Foundation; either version 2 of the License, or
38
+# (at your option) any later version.
35
+# (at your option) any later version.
...
...
44
+#
41
+#
45
+# You should have received a copy of the GNU General Public License
42
+# 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/>.
43
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
47
+#
44
+#
48
+
45
+
49
+seq="$(basename $0)"
46
+import asyncio
50
+echo "QA output created by $seq"
47
+import os
48
+from typing import List
49
+import iotests
50
+from iotests import qemu_img_create, qemu_io
51
+
51
+
52
+here="$PWD"
53
+status=1 # failure is the default!
54
+
52
+
55
+_cleanup()
53
+image_size = 256 * 1024 * 1024
56
+{
54
+base_img = os.path.join(iotests.test_dir, 'base.img')
57
+ _cleanup_test_img
55
+top_img = os.path.join(iotests.test_dir, 'top.img')
58
+}
59
+trap "_cleanup; exit \$status" 0 1 2 3 15
60
+
56
+
61
+# get standard environment, filters and checks
62
+. ./common.rc
63
+. ./common.filter
64
+
57
+
65
+_supported_fmt raw
58
+class TcgVM(iotests.VM):
66
+_supported_proto file
59
+ '''
67
+_supported_os Linux
60
+ Variant of iotests.VM that uses -accel tcg. Simply using
61
+ iotests.VM.add_args('-accel', 'tcg') is not sufficient, because that will
62
+ put -accel qtest before -accel tcg, and -accel arguments are prioritized in
63
+ the order they appear.
64
+ '''
65
+ @property
66
+ def _base_args(self) -> List[str]:
67
+ # Put -accel tcg first so it takes precedence
68
+ return ['-accel', 'tcg'] + super()._base_args
68
+
69
+
69
+echo
70
+echo "=== Check mapping of unaligned raw image ==="
71
+echo
72
+
70
+
73
+_make_test_img 43009 # qemu-img create rounds size up
71
+class TestStreamWithThrottle(iotests.QMPTestCase):
74
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
72
+ def setUp(self) -> None:
73
+ '''
74
+ Create a simple backing chain between two images, write something to
75
+ the base image. Attach them to the VM underneath two throttle nodes,
76
+ one of which has actually no limits set, but the other does. Then put
77
+ a virtio-blk device on top.
78
+ This test configuration has been taken from
79
+ https://gitlab.com/qemu-project/qemu/-/issues/1215
80
+ '''
81
+ qemu_img_create('-f', iotests.imgfmt, base_img, str(image_size))
82
+ qemu_img_create('-f', iotests.imgfmt, '-b', base_img, '-F',
83
+ iotests.imgfmt, top_img, str(image_size))
75
+
84
+
76
+truncate --size=43009 "$TEST_IMG" # so we resize it and check again
85
+ # Write something to stream
77
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
86
+ qemu_io(base_img, '-c', f'write 0 {image_size}')
78
+
87
+
79
+$QEMU_IO -c 'w 43008 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up
88
+ blockdev = {
80
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
89
+ 'driver': 'throttle',
90
+ 'node-name': 'throttled-node',
91
+ 'throttle-group': 'thrgr-limited',
92
+ 'file': {
93
+ 'driver': 'throttle',
94
+ 'throttle-group': 'thrgr-unlimited',
95
+ 'file': {
96
+ 'driver': iotests.imgfmt,
97
+ 'node-name': 'unthrottled-node',
98
+ 'file': {
99
+ 'driver': 'file',
100
+ 'filename': top_img
101
+ }
102
+ }
103
+ }
104
+ }
81
+
105
+
82
+truncate --size=43009 "$TEST_IMG" # so we resize it and check again
106
+ # Issue 1215 is not reproducible in qtest mode, which is why we need to
83
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
107
+ # create an -accel tcg VM
108
+ self.vm = TcgVM()
109
+ self.vm.add_object('iothread,id=iothr0')
110
+ self.vm.add_object('throttle-group,id=thrgr-unlimited')
111
+ self.vm.add_object('throttle-group,id=thrgr-limited,'
112
+ 'x-iops-total=10000,x-bps-total=104857600')
113
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev))
114
+ self.vm.add_device('virtio-blk,iothread=iothr0,drive=throttled-node')
115
+ self.vm.launch()
84
+
116
+
85
+# success, all done
117
+ def tearDown(self) -> None:
86
+echo '*** done'
118
+ self.vm.shutdown()
87
+rm -f $seq.full
119
+ os.remove(top_img)
88
+status=0
120
+ os.remove(base_img)
89
diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out
121
+
122
+ def test_stream(self) -> None:
123
+ '''
124
+ Do a simple stream beneath the two throttle nodes. Should complete
125
+ with no problems.
126
+ '''
127
+ result = self.vm.qmp('block-stream',
128
+ job_id='stream',
129
+ device='unthrottled-node')
130
+ self.assert_qmp(result, 'return', {})
131
+
132
+ # Should succeed and not time out
133
+ try:
134
+ self.vm.run_job('stream')
135
+ except asyncio.exceptions.TimeoutError:
136
+ # VM may be stuck, kill it before tearDown()
137
+ self.vm.kill()
138
+ raise
139
+
140
+
141
+if __name__ == '__main__':
142
+ # Must support backing images
143
+ iotests.main(supported_fmts=['qcow', 'qcow2', 'qed'],
144
+ supported_protocols=['file'],
145
+ required_fmts=['throttle'])
146
diff --git a/tests/qemu-iotests/tests/stream-under-throttle.out b/tests/qemu-iotests/tests/stream-under-throttle.out
90
new file mode 100644
147
new file mode 100644
91
index XXXXXXX..XXXXXXX
148
index XXXXXXX..XXXXXXX
92
--- /dev/null
149
--- /dev/null
93
+++ b/tests/qemu-iotests/221.out
150
+++ b/tests/qemu-iotests/tests/stream-under-throttle.out
94
@@ -XXX,XX +XXX,XX @@
151
@@ -XXX,XX +XXX,XX @@
95
+QA output created by 221
152
+.
153
+----------------------------------------------------------------------
154
+Ran 1 tests
96
+
155
+
97
+=== Check mapping of unaligned raw image ===
156
+OK
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
--
157
--
121
2.13.6
158
2.38.1
122
123
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
During the design for manual completion, we decided not to use the
4
"manual" property as a shorthand for both auto-dismiss and auto-finalize.
5
6
Fix the wording.
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>
12
---
13
qapi/job.json | 11 ++++++-----
14
1 file changed, 6 insertions(+), 5 deletions(-)
15
16
diff --git a/qapi/job.json b/qapi/job.json
17
index XXXXXXX..XXXXXXX 100644
18
--- a/qapi/job.json
19
+++ b/qapi/job.json
20
@@ -XXX,XX +XXX,XX @@
21
# the last job in a transaction.
22
#
23
# @pending: The job has finished its work, but has finalization steps that it
24
-# needs to make prior to completing. These changes may require
25
-# manual intervention by the management process if manual was set
26
-# to true. These changes may still fail.
27
+# needs to make prior to completing. These changes will require
28
+# manual intervention via @job-finalize if auto-finalize was set to
29
+# false. These pending changes may still fail.
30
#
31
# @aborting: The job is in the process of being aborted, and will finish with
32
# an error. The job will afterwards report that it is @concluded.
33
# This status may not be visible to the management process.
34
#
35
-# @concluded: The job has finished all work. If manual was set to true, the job
36
-# will remain in the query list until it is dismissed.
37
+# @concluded: The job has finished all work. If auto-dismiss was set to false,
38
+# the job will remain in the query list until it is dismissed via
39
+# @job-dismiss.
40
#
41
# @null: The job is in the process of being dismantled. This state should not
42
# ever be visible externally.
43
--
44
2.13.6
45
46
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
These point to the job versions now, not the blockjob versions which
4
don't really exist anymore.
5
6
Except set-speed, which does. It sticks out like a sore thumb. This
7
patch doesn't fix that, but it doesn't make it any worse, either.
8
9
Signed-off-by: John Snow <jsnow@redhat.com>
10
Reviewed-by: Jeff Cody <jcody@redhat.com>
11
Reviewed-by: Markus Armbruster <armbru@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
qapi/job.json | 12 ++++++------
15
1 file changed, 6 insertions(+), 6 deletions(-)
16
17
diff --git a/qapi/job.json b/qapi/job.json
18
index XXXXXXX..XXXXXXX 100644
19
--- a/qapi/job.json
20
+++ b/qapi/job.json
21
@@ -XXX,XX +XXX,XX @@
22
#
23
# Represents command verbs that can be applied to a job.
24
#
25
-# @cancel: see @block-job-cancel
26
+# @cancel: see @job-cancel
27
#
28
-# @pause: see @block-job-pause
29
+# @pause: see @job-pause
30
#
31
-# @resume: see @block-job-resume
32
+# @resume: see @job-resume
33
#
34
# @set-speed: see @block-job-set-speed
35
#
36
-# @complete: see @block-job-complete
37
+# @complete: see @job-complete
38
#
39
-# @dismiss: see @block-job-dismiss
40
+# @dismiss: see @job-dismiss
41
#
42
-# @finalize: see @block-job-finalize
43
+# @finalize: see @job-finalize
44
#
45
# Since: 2.12
46
##
47
--
48
2.13.6
49
50
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
Parameter "filename" is deprecated since commit 91589d9e5ca, v2.10.0.
4
Time to get rid of it.
5
6
Signed-off-by: Markus Armbruster <armbru@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
block/rbd.c | 16 ----------------
11
1 file changed, 16 deletions(-)
12
13
diff --git a/block/rbd.c b/block/rbd.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block/rbd.c
16
+++ b/block/rbd.c
17
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
18
QObject *crumpled = NULL;
19
const QDictEntry *e;
20
Error *local_err = NULL;
21
- const char *filename;
22
char *keypairs, *secretid;
23
int r;
24
25
- /* If we are given a filename, parse the filename, with precedence given to
26
- * filename encoded options */
27
- filename = qdict_get_try_str(options, "filename");
28
- if (filename) {
29
- warn_report("'filename' option specified. "
30
- "This is an unsupported option, and may be deprecated "
31
- "in the future");
32
- qemu_rbd_parse_filename(filename, options, &local_err);
33
- qdict_del(options, "filename");
34
- if (local_err) {
35
- error_propagate(errp, local_err);
36
- return -EINVAL;
37
- }
38
- }
39
-
40
keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
41
if (keypairs) {
42
qdict_del(options, "=keyvalue-pairs");
43
--
44
2.13.6
45
46
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
Pure code motion, except for two brace placements and a comment
4
tweaked to appease checkpatch.
5
6
Signed-off-by: Markus Armbruster <armbru@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
qobject/block-qdict.c | 640 ++++++++++++++++++++++++++++++++++++++++++++
11
qobject/qdict.c | 629 --------------------------------------------
12
tests/check-block-qdict.c | 655 ++++++++++++++++++++++++++++++++++++++++++++++
13
tests/check-qdict.c | 642 ---------------------------------------------
14
MAINTAINERS | 2 +
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
21
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
22
new file mode 100644
23
index XXXXXXX..XXXXXXX
24
--- /dev/null
25
+++ b/qobject/block-qdict.c
26
@@ -XXX,XX +XXX,XX @@
27
+/*
28
+ * Special QDict functions used by the block layer
29
+ *
30
+ * Copyright (c) 2013-2018 Red Hat, Inc.
31
+ *
32
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
33
+ * See the COPYING.LIB file in the top-level directory.
34
+ */
35
+
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
+{
49
+ QObject *val;
50
+
51
+ if (qdict_haskey(dst, key)) {
52
+ return;
53
+ }
54
+
55
+ val = qdict_get(src, key);
56
+ if (val) {
57
+ qdict_put_obj(dst, key, qobject_ref(val));
58
+ }
59
+}
60
+
61
+/**
62
+ * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a
63
+ * new QString initialised by 'val' is put there.
64
+ */
65
+void qdict_set_default_str(QDict *dst, const char *key, const char *val)
66
+{
67
+ if (qdict_haskey(dst, key)) {
68
+ return;
69
+ }
70
+
71
+ qdict_put_str(dst, key, val);
72
+}
73
+
74
+static void qdict_flatten_qdict(QDict *qdict, QDict *target,
75
+ const char *prefix);
76
+
77
+static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
78
+{
79
+ QObject *value;
80
+ const QListEntry *entry;
81
+ char *new_key;
82
+ int i;
83
+
84
+ /* This function is never called with prefix == NULL, i.e., it is always
85
+ * called from within qdict_flatten_q(list|dict)(). Therefore, it does not
86
+ * need to remove list entries during the iteration (the whole list will be
87
+ * deleted eventually anyway from qdict_flatten_qdict()). */
88
+ assert(prefix);
89
+
90
+ entry = qlist_first(qlist);
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
669
--- a/qobject/qdict.c
670
+++ b/qobject/qdict.c
671
@@ -XXX,XX +XXX,XX @@
672
*/
673
674
#include "qemu/osdep.h"
675
-#include "block/qdict.h"
676
#include "qapi/qmp/qnum.h"
677
#include "qapi/qmp/qdict.h"
678
#include "qapi/qmp/qbool.h"
679
-#include "qapi/qmp/qlist.h"
680
#include "qapi/qmp/qnull.h"
681
#include "qapi/qmp/qstring.h"
682
-#include "qapi/error.h"
683
-#include "qemu/queue.h"
684
-#include "qemu-common.h"
685
-#include "qemu/cutils.h"
686
687
/**
688
* qdict_new(): Create a new QDict
689
@@ -XXX,XX +XXX,XX @@ void qdict_destroy_obj(QObject *obj)
690
691
g_free(qdict);
692
}
693
-
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
--
2712
2.13.6
2713
2714
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
The previous commit fixed -blockdev breakage due to misuse of the
4
qobject input visitor's keyval flavor in bdrv_file_open(). The commit
5
message explain why using the plain flavor would be just as wrong; it
6
would break -drive. Turns out we break it in three places:
7
nbd_open(), sd_open() and ssh_file_open(). They are even marked
8
FIXME. Example breakage:
9
10
$ qemu-system-x86 -drive node-name=n1,driver=nbd,server.type=inet,server.host=localhost,server.port=1234,server.numeric=off
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
12
13
Fix it the same way: replace qdict_crumple() by
14
qdict_crumple_for_keyval_qiv(), and switch from plain to the keyval
15
flavor.
16
17
Signed-off-by: Markus Armbruster <armbru@redhat.com>
18
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
21
block/nbd.c | 12 ++----------
22
block/sheepdog.c | 12 ++----------
23
block/ssh.c | 12 ++----------
24
3 files changed, 6 insertions(+), 30 deletions(-)
25
26
diff --git a/block/nbd.c b/block/nbd.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/block/nbd.c
29
+++ b/block/nbd.c
30
@@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
31
goto done;
32
}
33
34
- crumpled_addr = qdict_crumple(addr, errp);
35
+ crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp);
36
if (!crumpled_addr) {
37
goto done;
38
}
39
40
- /*
41
- * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive
42
- * server.type=inet. .to doesn't matter, it's ignored anyway.
43
- * That's because when @options come from -blockdev or
44
- * blockdev_add, members are typed according to the QAPI schema,
45
- * but when they come from -drive, they're all QString. The
46
- * visitor expects the former.
47
- */
48
- iv = qobject_input_visitor_new(crumpled_addr);
49
+ iv = qobject_input_visitor_new_keyval(crumpled_addr);
50
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
51
if (local_err) {
52
error_propagate(errp, local_err);
53
diff --git a/block/sheepdog.c b/block/sheepdog.c
54
index XXXXXXX..XXXXXXX 100644
55
--- a/block/sheepdog.c
56
+++ b/block/sheepdog.c
57
@@ -XXX,XX +XXX,XX @@ static SocketAddress *sd_server_config(QDict *options, Error **errp)
58
59
qdict_extract_subqdict(options, &server, "server.");
60
61
- crumpled_server = qdict_crumple(server, errp);
62
+ crumpled_server = qdict_crumple_for_keyval_qiv(server, errp);
63
if (!crumpled_server) {
64
goto done;
65
}
66
67
- /*
68
- * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive
69
- * server.type=inet. .to doesn't matter, it's ignored anyway.
70
- * That's because when @options come from -blockdev or
71
- * blockdev_add, members are typed according to the QAPI schema,
72
- * but when they come from -drive, they're all QString. The
73
- * visitor expects the former.
74
- */
75
- iv = qobject_input_visitor_new(crumpled_server);
76
+ iv = qobject_input_visitor_new_keyval(crumpled_server);
77
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
78
if (local_err) {
79
error_propagate(errp, local_err);
80
diff --git a/block/ssh.c b/block/ssh.c
81
index XXXXXXX..XXXXXXX 100644
82
--- a/block/ssh.c
83
+++ b/block/ssh.c
84
@@ -XXX,XX +XXX,XX @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
85
}
86
87
/* Create the QAPI object */
88
- crumpled = qdict_crumple(options, errp);
89
+ crumpled = qdict_crumple_for_keyval_qiv(options, errp);
90
if (crumpled == NULL) {
91
goto fail;
92
}
93
94
- /*
95
- * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive.
96
- * .to doesn't matter, it's ignored anyway.
97
- * That's because when @options come from -blockdev or
98
- * blockdev_add, members are typed according to the QAPI schema,
99
- * but when they come from -drive, they're all QString. The
100
- * visitor expects the former.
101
- */
102
- v = qobject_input_visitor_new(crumpled);
103
+ v = qobject_input_visitor_new_keyval(crumpled);
104
visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
105
visit_free(v);
106
qobject_unref(crumpled);
107
--
108
2.13.6
109
110
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
The following pattern occurs in the .bdrv_co_create_opts() methods of
4
parallels, qcow, qcow2, qed, vhdx and vpc:
5
6
qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
7
qobject_unref(qdict);
8
qdict = qobject_to(QDict, qobj);
9
if (qdict == NULL) {
10
ret = -EINVAL;
11
goto done;
12
}
13
14
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
15
[...]
16
ret = 0;
17
done:
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>
28
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
29
---
30
block/parallels.c | 9 ++++-----
31
block/qcow.c | 9 ++++-----
32
block/qcow2.c | 9 ++++-----
33
block/qed.c | 9 ++++-----
34
block/vhdx.c | 9 ++++-----
35
block/vpc.c | 9 ++++-----
36
6 files changed, 24 insertions(+), 30 deletions(-)
37
38
diff --git a/block/parallels.c b/block/parallels.c
39
index XXXXXXX..XXXXXXX 100644
40
--- a/block/parallels.c
41
+++ b/block/parallels.c
42
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
43
BlockdevCreateOptions *create_options = NULL;
44
Error *local_err = NULL;
45
BlockDriverState *bs = NULL;
46
- QDict *qdict = NULL;
47
+ QDict *qdict;
48
QObject *qobj;
49
Visitor *v;
50
int ret;
51
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
52
qdict_put_str(qdict, "file", bs->node_name);
53
54
qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
55
- qobject_unref(qdict);
56
- qdict = qobject_to(QDict, qobj);
57
- if (qdict == NULL) {
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
69
diff --git a/block/qcow.c b/block/qcow.c
70
index XXXXXXX..XXXXXXX 100644
71
--- a/block/qcow.c
72
+++ b/block/qcow.c
73
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
74
{
75
BlockdevCreateOptions *create_options = NULL;
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
}
93
94
- v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
95
+ v = qobject_input_visitor_new_keyval(qobj);
96
+ qobject_unref(qobj);
97
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
98
visit_free(v);
99
100
diff --git a/block/qcow2.c b/block/qcow2.c
101
index XXXXXXX..XXXXXXX 100644
102
--- a/block/qcow2.c
103
+++ b/block/qcow2.c
104
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
105
Error **errp)
106
{
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
}
124
125
- v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
126
+ v = qobject_input_visitor_new_keyval(qobj);
127
+ qobject_unref(qobj);
128
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
129
visit_free(v);
130
131
diff --git a/block/qed.c b/block/qed.c
132
index XXXXXXX..XXXXXXX 100644
133
--- a/block/qed.c
134
+++ b/block/qed.c
135
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
136
Error **errp)
137
{
138
BlockdevCreateOptions *create_options = NULL;
139
- QDict *qdict = NULL;
140
+ QDict *qdict;
141
QObject *qobj;
142
Visitor *v;
143
BlockDriverState *bs = NULL;
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
--
225
2.13.6
226
227
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
7
include/block/qdict.h | 3 ++-
8
block/nbd.c | 7 ++-----
9
block/nfs.c | 7 ++-----
10
block/parallels.c | 7 ++-----
11
block/qcow.c | 7 ++-----
12
block/qcow2.c | 7 ++-----
13
block/qed.c | 7 ++-----
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
22
diff --git a/include/block/qdict.h b/include/block/qdict.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/qdict.h
25
+++ b/include/block/qdict.h
26
@@ -XXX,XX +XXX,XX @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
27
void qdict_array_split(QDict *src, QList **dst);
28
int qdict_array_entries(QDict *src, const char *subqdict);
29
QObject *qdict_crumple(const QDict *src, Error **errp);
30
-QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp);
31
void qdict_flatten(QDict *qdict);
32
33
typedef struct QDictRenames {
34
@@ -XXX,XX +XXX,XX @@ typedef struct QDictRenames {
35
} QDictRenames;
36
bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
37
38
+Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict,
39
+ Error **errp);
40
#endif
41
diff --git a/block/nbd.c b/block/nbd.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/block/nbd.c
44
+++ b/block/nbd.c
45
@@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
46
{
47
SocketAddress *saddr = NULL;
48
QDict *addr = NULL;
49
- QObject *crumpled_addr = NULL;
50
Visitor *iv = NULL;
51
Error *local_err = NULL;
52
53
@@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
54
goto done;
55
}
56
57
- crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp);
58
- if (!crumpled_addr) {
59
+ iv = qobject_input_visitor_new_flat_confused(addr, errp);
60
+ if (!iv) {
61
goto done;
62
}
63
64
- iv = qobject_input_visitor_new_keyval(crumpled_addr);
65
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
66
if (local_err) {
67
error_propagate(errp, local_err);
68
@@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
69
70
done:
71
qobject_unref(addr);
72
- qobject_unref(crumpled_addr);
73
visit_free(iv);
74
return saddr;
75
}
76
diff --git a/block/nfs.c b/block/nfs.c
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;
94
}
95
96
- v = qobject_input_visitor_new_keyval(crumpled);
97
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
98
visit_free(v);
99
- qobject_unref(crumpled);
100
101
if (local_err) {
102
error_propagate(errp, local_err);
103
diff --git a/block/parallels.c b/block/parallels.c
104
index XXXXXXX..XXXXXXX 100644
105
--- a/block/parallels.c
106
+++ b/block/parallels.c
107
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
108
Error *local_err = NULL;
109
BlockDriverState *bs = NULL;
110
QDict *qdict;
111
- QObject *qobj;
112
Visitor *v;
113
int ret;
114
115
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
116
qdict_put_str(qdict, "driver", "parallels");
117
qdict_put_str(qdict, "file", bs->node_name);
118
119
- qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
120
- if (!qobj) {
121
+ v = qobject_input_visitor_new_flat_confused(qdict, errp);
122
+ if (!v) {
123
ret = -EINVAL;
124
goto done;
125
}
126
127
- v = qobject_input_visitor_new_keyval(qobj);
128
- qobject_unref(qobj);
129
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
130
visit_free(v);
131
132
diff --git a/block/qcow.c b/block/qcow.c
133
index XXXXXXX..XXXXXXX 100644
134
--- a/block/qcow.c
135
+++ b/block/qcow.c
136
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
137
BlockdevCreateOptions *create_options = NULL;
138
BlockDriverState *bs = NULL;
139
QDict *qdict;
140
- QObject *qobj;
141
Visitor *v;
142
const char *val;
143
Error *local_err = NULL;
144
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
145
qdict_put_str(qdict, "driver", "qcow");
146
qdict_put_str(qdict, "file", bs->node_name);
147
148
- qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
149
- if (!qobj) {
150
+ v = qobject_input_visitor_new_flat_confused(qdict, errp);
151
+ if (!v) {
152
ret = -EINVAL;
153
goto fail;
154
}
155
156
- v = qobject_input_visitor_new_keyval(qobj);
157
- qobject_unref(qobj);
158
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
159
visit_free(v);
160
161
diff --git a/block/qcow2.c b/block/qcow2.c
162
index XXXXXXX..XXXXXXX 100644
163
--- a/block/qcow2.c
164
+++ b/block/qcow2.c
165
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
166
{
167
BlockdevCreateOptions *create_options = NULL;
168
QDict *qdict;
169
- QObject *qobj;
170
Visitor *v;
171
BlockDriverState *bs = NULL;
172
Error *local_err = NULL;
173
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
174
qdict_put_str(qdict, "file", bs->node_name);
175
176
/* Now get the QAPI type BlockdevCreateOptions */
177
- qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
178
- if (!qobj) {
179
+ v = qobject_input_visitor_new_flat_confused(qdict, errp);
180
+ if (!v) {
181
ret = -EINVAL;
182
goto finish;
183
}
184
185
- v = qobject_input_visitor_new_keyval(qobj);
186
- qobject_unref(qobj);
187
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
188
visit_free(v);
189
190
diff --git a/block/qed.c b/block/qed.c
191
index XXXXXXX..XXXXXXX 100644
192
--- a/block/qed.c
193
+++ b/block/qed.c
194
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
195
{
196
BlockdevCreateOptions *create_options = NULL;
197
QDict *qdict;
198
- QObject *qobj;
199
Visitor *v;
200
BlockDriverState *bs = NULL;
201
Error *local_err = NULL;
202
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
203
qdict_put_str(qdict, "driver", "qed");
204
qdict_put_str(qdict, "file", bs->node_name);
205
206
- qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
207
- if (!qobj) {
208
+ v = qobject_input_visitor_new_flat_confused(qdict, errp);
209
+ if (!v) {
210
ret = -EINVAL;
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
--
450
2.13.6
451
452
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
There's no need to restart the loop. We don't elsewhere, e.g. in
4
qdict_extract_subqdict(), qdict_join() and qemu_opts_absorb_qdict().
5
Simplify accordingly.
6
7
Signed-off-by: Markus Armbruster <armbru@redhat.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
qobject/block-qdict.c | 18 +++---------------
12
1 file changed, 3 insertions(+), 15 deletions(-)
13
14
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qobject/block-qdict.c
17
+++ b/qobject/block-qdict.c
18
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
19
QObject *value;
20
const QDictEntry *entry, *next;
21
char *new_key;
22
- bool delete;
23
24
entry = qdict_first(qdict);
25
26
while (entry != NULL) {
27
-
28
next = qdict_next(qdict, entry);
29
value = qdict_entry_value(entry);
30
new_key = NULL;
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
-
52
- g_free(new_key);
53
-
54
- if (delete) {
55
qdict_del(qdict, entry->key);
56
-
57
- /* Restart loop after modifying the iterated QDict */
58
- entry = qdict_first(qdict);
59
- continue;
60
}
61
62
+ g_free(new_key);
63
entry = next;
64
}
65
}
66
--
67
2.13.6
68
69
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
qdict_flatten_qdict() skips copying scalars from @qdict to @target
4
when the two are the same. Fair enough, but it uses a non-obvious
5
test for "same". Replace it by the obvious one. While there, improve
6
comments.
7
8
Signed-off-by: Markus Armbruster <armbru@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
qobject/block-qdict.c | 14 +++++++++-----
13
1 file changed, 9 insertions(+), 5 deletions(-)
14
15
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/qobject/block-qdict.c
18
+++ b/qobject/block-qdict.c
19
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
20
value = qlist_entry_obj(entry);
21
new_key = g_strdup_printf("%s.%i", prefix, i);
22
23
+ /*
24
+ * Flatten non-empty QDict and QList recursively into @target,
25
+ * copy other objects to @target
26
+ */
27
if (qobject_type(value) == QTYPE_QDICT) {
28
qdict_flatten_qdict(qobject_to(QDict, value), target, new_key);
29
} else if (qobject_type(value) == QTYPE_QLIST) {
30
qdict_flatten_qlist(qobject_to(QList, value), target, new_key);
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
--
61
2.13.6
62
63
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
When you mix scalar and non-scalar keys, whether you get an "already
4
set as scalar" or an "already set as dict" error depends on qdict
5
iteration order. Neither message makes much sense. Replace by
6
""Cannot mix scalar and non-scalar keys". This is similar to the
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>
14
---
15
qobject/block-qdict.c | 32 ++++++++++++++++----------------
16
1 file changed, 16 insertions(+), 16 deletions(-)
17
18
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/qobject/block-qdict.c
21
+++ b/qobject/block-qdict.c
22
@@ -XXX,XX +XXX,XX @@ static int qdict_is_list(QDict *maybe_list, Error **errp)
23
QObject *qdict_crumple(const QDict *src, Error **errp)
24
{
25
const QDictEntry *ent;
26
- QDict *two_level, *multi_level = NULL;
27
+ QDict *two_level, *multi_level = NULL, *child_dict;
28
QObject *dst = NULL, *child;
29
size_t i;
30
char *prefix = NULL;
31
@@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp)
32
}
33
34
qdict_split_flat_key(ent->key, &prefix, &suffix);
35
-
36
child = qdict_get(two_level, prefix);
37
+ child_dict = qobject_to(QDict, child);
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
75
--
76
2.13.6
77
78
diff view generated by jsdifflib
Deleted patch
1
From: Markus Armbruster <armbru@redhat.com>
2
1
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
7
qobject/block-qdict.c | 27 +++++++++++----------------
8
1 file changed, 11 insertions(+), 16 deletions(-)
9
10
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/qobject/block-qdict.c
13
+++ b/qobject/block-qdict.c
14
@@ -XXX,XX +XXX,XX @@ static int qdict_is_list(QDict *maybe_list, Error **errp)
15
16
for (ent = qdict_first(maybe_list); ent != NULL;
17
ent = qdict_next(maybe_list, ent)) {
18
+ int is_index = !qemu_strtoi64(ent->key, NULL, 10, &val);
19
20
- if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) {
21
- if (is_list == -1) {
22
- is_list = 1;
23
- } else if (!is_list) {
24
- error_setg(errp,
25
- "Cannot mix list and non-list keys");
26
- return -1;
27
- }
28
+ if (is_list == -1) {
29
+ is_list = is_index;
30
+ }
31
+
32
+ if (is_index != is_list) {
33
+ error_setg(errp, "Cannot mix list and non-list keys");
34
+ return -1;
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
}
52
53
--
54
2.13.6
55
56
diff view generated by jsdifflib
Deleted patch
1
The -drive options cyls, heads, secs and trans were deprecated in
2
QEMU 2.10. It's time to remove them.
3
1
4
hd-geo-test tested both the old version with geometry options in -drive
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
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Markus Armbruster <armbru@redhat.com>
11
---
12
include/sysemu/blockdev.h | 1 -
13
blockdev.c | 75 +----------------------------------------------
14
hw/block/block.c | 14 ---------
15
tests/hd-geo-test.c | 37 +++++------------------
16
hmp-commands.hx | 1 -
17
qemu-doc.texi | 5 ----
18
qemu-options.hx | 7 +----
19
7 files changed, 9 insertions(+), 131 deletions(-)
20
21
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/include/sysemu/blockdev.h
24
+++ b/include/sysemu/blockdev.h
25
@@ -XXX,XX +XXX,XX @@ struct DriveInfo {
26
int auto_del; /* see blockdev_mark_auto_del() */
27
bool is_default; /* Added by default_drive() ? */
28
int media_cd;
29
- int cyls, heads, secs, trans;
30
QemuOpts *opts;
31
char *serial;
32
QTAILQ_ENTRY(DriveInfo) next;
33
diff --git a/blockdev.c b/blockdev.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/blockdev.c
36
+++ b/blockdev.c
37
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
38
.type = QEMU_OPT_STRING,
39
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
40
},{
41
- .name = "cyls",
42
- .type = QEMU_OPT_NUMBER,
43
- .help = "number of cylinders (ide disk geometry)",
44
- },{
45
- .name = "heads",
46
- .type = QEMU_OPT_NUMBER,
47
- .help = "number of heads (ide disk geometry)",
48
- },{
49
- .name = "secs",
50
- .type = QEMU_OPT_NUMBER,
51
- .help = "number of sectors (ide disk geometry)",
52
- },{
53
- .name = "trans",
54
- .type = QEMU_OPT_STRING,
55
- .help = "chs translation (auto, lba, none)",
56
- },{
57
.name = "addr",
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
}
80
81
- /* Geometry */
82
- cyls = qemu_opt_get_number(legacy_opts, "cyls", 0);
83
- heads = qemu_opt_get_number(legacy_opts, "heads", 0);
84
- secs = qemu_opt_get_number(legacy_opts, "secs", 0);
85
-
86
- if (cyls || heads || secs) {
87
- if (cyls < 1) {
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
}
188
argc = append_arg(argc, argv, argv_sz,
189
- g_strdup_printf("%s%s%s%s", s1, s2, s3, opts));
190
+ g_strdup_printf("%s%s%s", s1, s2, s3));
191
g_free(s1);
192
g_free(s2);
193
g_free(s3);
194
@@ -XXX,XX +XXX,XX @@ static void test_ide_mbr(bool use_device, MBRcontents mbr)
195
for (i = 0; i < backend_last; i++) {
196
cur_ide[i] = &hd_chst[i][mbr];
197
dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL;
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
}
201
args = g_strjoinv(" ", argv);
202
qtest_start(args);
203
@@ -XXX,XX +XXX,XX @@ static void test_ide_drive_user(const char *dev, bool trans)
204
const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans };
205
206
argc = setup_common(argv, ARGV_SIZE);
207
- opts = g_strdup_printf("%s,%s%scyls=%d,heads=%d,secs=%d",
208
- dev ?: "",
209
- trans && dev ? "bios-chs-" : "",
210
- trans ? "trans=lba," : "",
211
+ opts = g_strdup_printf("%s,%scyls=%d,heads=%d,secs=%d",
212
+ dev, trans ? "bios-chs-trans=lba," : "",
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
}
225
226
/*
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
--
320
2.13.6
321
322
diff view generated by jsdifflib
Deleted patch
1
The -drive option addr was deprecated in QEMU 2.10. It's time to remove
2
it.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Markus Armbruster <armbru@redhat.com>
6
Reviewed-by: Jeff Cody <jcody@redhat.com>
7
---
8
include/sysemu/blockdev.h | 1 -
9
blockdev.c | 17 +----------------
10
device-hotplug.c | 4 ----
11
qemu-doc.texi | 5 -----
12
qemu-options.hx | 5 +----
13
5 files changed, 2 insertions(+), 30 deletions(-)
14
15
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/include/sysemu/blockdev.h
18
+++ b/include/sysemu/blockdev.h
19
@@ -XXX,XX +XXX,XX @@ typedef enum {
20
} BlockInterfaceType;
21
22
struct DriveInfo {
23
- const char *devaddr;
24
BlockInterfaceType type;
25
int bus;
26
int unit;
27
diff --git a/blockdev.c b/blockdev.c
28
index XXXXXXX..XXXXXXX 100644
29
--- a/blockdev.c
30
+++ b/blockdev.c
31
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
32
.type = QEMU_OPT_STRING,
33
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
34
},{
35
- .name = "addr",
36
- .type = QEMU_OPT_STRING,
37
- .help = "pci address (virtio only)",
38
- },{
39
.name = "serial",
40
.type = QEMU_OPT_STRING,
41
.help = "disk serial number",
42
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
43
DriveMediaType media = MEDIA_DISK;
44
BlockInterfaceType type;
45
int max_devs, bus_id, unit_id, index;
46
- const char *devaddr;
47
const char *werror, *rerror;
48
bool read_only = false;
49
bool copy_on_read;
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
}
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
--
145
2.13.6
146
147
diff view generated by jsdifflib
Deleted patch
1
We removed all options from the 'deprecated' array, so the code is dead
2
and can be removed as well.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Markus Armbruster <armbru@redhat.com>
6
---
7
blockdev.c | 12 ------------
8
1 file changed, 12 deletions(-)
9
10
diff --git a/blockdev.c b/blockdev.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/blockdev.c
13
+++ b/blockdev.c
14
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
15
const char *filename;
16
Error *local_err = NULL;
17
int i;
18
- const char *deprecated[] = {
19
- };
20
21
/* Change legacy command line options into QMP ones */
22
static const struct {
23
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
24
goto fail;
25
}
26
27
- /* Other deprecated options */
28
- if (!qtest_enabled()) {
29
- for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
30
- if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) {
31
- error_report("'%s' is deprecated, please use the corresponding "
32
- "option of '-device' instead", deprecated[i]);
33
- }
34
- }
35
- }
36
-
37
/* Media type */
38
value = qemu_opt_get(legacy_opts, "media");
39
if (value) {
40
--
41
2.13.6
42
43
diff view generated by jsdifflib