1
The following changes since commit 91fe7a376ad46e3cc5e82d418aad22173c948a3c:
1
The following changes since commit 9a7beaad3dbba982f7a461d676b55a5c3851d312:
2
2
3
Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging (2018-06-15 11:41:44 +0100)
3
Merge remote-tracking branch 'remotes/alistair/tags/pull-riscv-to-apply-20210304' into staging (2021-03-05 10:47:46 +0000)
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
git://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 67bedc3aed5c455b629c2cb5f523b536c46adff9:
10
10
11
block: Remove dead deprecation warning code (2018-06-15 14:49:44 +0200)
11
docs: qsd: Explain --export nbd,name=... default (2021-03-05 17:09:46 +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
- qemu-storage-daemon: add --pidfile option
17
both, because of QDict type confusion
17
- qemu-storage-daemon: CLI error messages include the option name now
18
- rbd: Add options 'auth-client-required' and 'key-secret'
18
- vhost-user-blk export: Misc fixes, added test cases
19
- Remove deprecated -drive options serial/addr/cyls/heads/secs/trans
19
- docs: Improvements for qemu-storage-daemon documentation
20
- rbd, iscsi: Remove deprecated 'filename' option
20
- parallels: load bitmap extension
21
- Fix 'qemu-img map' crash with unaligned image size
21
- backup-top: Don't crash on post-finalize accesses
22
- Improve QMP documentation for jobs
22
- iotests improvements
23
23
24
----------------------------------------------------------------
24
----------------------------------------------------------------
25
Eric Blake (2):
25
Alberto Garcia (1):
26
qemu-img: Fix assert when mapping unaligned raw file
26
iotests: Drop deprecated 'props' from object-add
27
iotests: Add test 221 to catch qemu-img map regression
28
27
29
John Snow (2):
28
Coiby Xu (1):
30
jobs: fix stale wording
29
test: new qTest case to test the vhost-user-blk-server
31
jobs: fix verb references in docs
32
30
33
Kevin Wolf (4):
31
Eric Blake (1):
34
block: Remove deprecated -drive geometry options
32
iotests: Fix up python style in 300
35
block: Remove deprecated -drive option addr
36
block: Remove deprecated -drive option serial
37
block: Remove dead deprecation warning code
38
33
39
Markus Armbruster (17):
34
Kevin Wolf (1):
40
rbd: Drop deprecated -drive parameter "filename"
35
docs: qsd: Explain --export nbd,name=... default
41
iscsi: Drop deprecated -drive parameter "filename"
42
qobject: Move block-specific qdict code to block-qdict.c
43
block: Fix -blockdev for certain non-string scalars
44
block: Fix -drive for certain non-string scalars
45
block: Clean up a misuse of qobject_to() in .bdrv_co_create_opts()
46
block: Factor out qobject_input_visitor_new_flat_confused()
47
block: Make remaining uses of qobject input visitor more robust
48
block-qdict: Simplify qdict_flatten_qdict()
49
block-qdict: Tweak qdict_flatten_qdict(), qdict_flatten_qlist()
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
36
58
Max Reitz (1):
37
Max Reitz (3):
59
block: Add block-specific QDict header
38
backup: Remove nodes from job in .clean()
39
backup-top: Refuse I/O in inactive state
40
iotests/283: Check that finalize drops backup-top
60
41
61
qapi/block-core.json | 19 ++
42
Paolo Bonzini (2):
62
qapi/job.json | 23 +-
43
storage-daemon: report unexpected arguments on the fly
63
include/block/qdict.h | 34 +++
44
storage-daemon: include current command line option in the errors
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
45
46
Stefan Hajnoczi (14):
47
qemu-storage-daemon: add --pidfile option
48
docs: show how to spawn qemu-storage-daemon with fd passing
49
docs: replace insecure /tmp examples in qsd docs
50
vhost-user-blk: fix blkcfg->num_queues endianness
51
libqtest: add qtest_socket_server()
52
libqtest: add qtest_kill_qemu()
53
libqtest: add qtest_remove_abrt_handler()
54
tests/qtest: add multi-queue test case to vhost-user-blk-test
55
block/export: fix blk_size double byteswap
56
block/export: use VIRTIO_BLK_SECTOR_BITS
57
block/export: fix vhost-user-blk export sector number calculation
58
block/export: port virtio-blk discard/write zeroes input validation
59
vhost-user-blk-test: test discard/write zeroes invalid inputs
60
block/export: port virtio-blk read/write range check
61
62
Stefano Garzarella (1):
63
blockjob: report a better error message
64
65
Vladimir Sementsov-Ogievskiy (7):
66
qcow2-bitmap: make bytes_covered_by_bitmap_cluster() public
67
parallels.txt: fix bitmap L1 table description
68
block/parallels: BDRVParallelsState: add cluster_size field
69
parallels: support bitmap extension for read-only mode
70
iotests.py: add unarchive_sample_image() helper
71
iotests: add parallels-read-bitmap test
72
MAINTAINERS: update parallels block driver
73
74
docs/interop/parallels.txt | 28 +-
75
docs/tools/qemu-storage-daemon.rst | 68 +-
76
block/parallels.h | 7 +-
77
include/block/dirty-bitmap.h | 2 +
78
tests/qtest/libqos/libqtest.h | 37 +
79
tests/qtest/libqos/vhost-user-blk.h | 48 +
80
block/backup-top.c | 10 +
81
block/backup.c | 1 +
82
block/dirty-bitmap.c | 13 +
83
block/export/vhost-user-blk-server.c | 150 +++-
84
block/parallels-ext.c | 300 +++++++
85
block/parallels.c | 26 +-
86
block/qcow2-bitmap.c | 16 +-
87
blockjob.c | 10 +-
88
hw/block/vhost-user-blk.c | 7 +-
89
storage-daemon/qemu-storage-daemon.c | 56 +-
90
tests/qtest/libqos/vhost-user-blk.c | 130 +++
91
tests/qtest/libqtest.c | 82 +-
92
tests/qtest/vhost-user-blk-test.c | 983 +++++++++++++++++++++
93
tests/qemu-iotests/iotests.py | 10 +
94
MAINTAINERS | 5 +
95
block/meson.build | 3 +-
96
tests/qemu-iotests/087 | 8 +-
97
tests/qemu-iotests/184 | 18 +-
98
tests/qemu-iotests/218 | 2 +-
99
tests/qemu-iotests/235 | 2 +-
100
tests/qemu-iotests/245 | 4 +-
101
tests/qemu-iotests/258 | 6 +-
102
tests/qemu-iotests/258.out | 4 +-
103
tests/qemu-iotests/283 | 53 ++
104
tests/qemu-iotests/283.out | 15 +
105
tests/qemu-iotests/295 | 2 +-
106
tests/qemu-iotests/296 | 2 +-
107
tests/qemu-iotests/300 | 10 +-
108
.../sample_images/parallels-with-bitmap.bz2 | Bin 0 -> 203 bytes
109
.../sample_images/parallels-with-bitmap.sh | 51 ++
110
tests/qemu-iotests/tests/parallels-read-bitmap | 55 ++
111
tests/qemu-iotests/tests/parallels-read-bitmap.out | 6 +
112
tests/qtest/libqos/meson.build | 1 +
113
tests/qtest/meson.build | 4 +
114
40 files changed, 2098 insertions(+), 137 deletions(-)
115
create mode 100644 tests/qtest/libqos/vhost-user-blk.h
116
create mode 100644 block/parallels-ext.c
117
create mode 100644 tests/qtest/libqos/vhost-user-blk.c
118
create mode 100644 tests/qtest/vhost-user-blk-test.c
119
create mode 100644 tests/qemu-iotests/sample_images/parallels-with-bitmap.bz2
120
create mode 100755 tests/qemu-iotests/sample_images/parallels-with-bitmap.sh
121
create mode 100755 tests/qemu-iotests/tests/parallels-read-bitmap
122
create mode 100644 tests/qemu-iotests/tests/parallels-read-bitmap.out
123
124
diff view generated by jsdifflib
New patch
1
1
From: Alberto Garcia <berto@igalia.com>
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
4
Message-Id: <20210222115737.2993-1-berto@igalia.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
7
tests/qemu-iotests/087 | 8 ++------
8
tests/qemu-iotests/184 | 18 ++++++------------
9
tests/qemu-iotests/218 | 2 +-
10
tests/qemu-iotests/235 | 2 +-
11
tests/qemu-iotests/245 | 4 ++--
12
tests/qemu-iotests/258 | 6 +++---
13
tests/qemu-iotests/258.out | 4 ++--
14
tests/qemu-iotests/295 | 2 +-
15
tests/qemu-iotests/296 | 2 +-
16
9 files changed, 19 insertions(+), 29 deletions(-)
17
18
diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/087
21
+++ b/tests/qemu-iotests/087
22
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
23
"arguments": {
24
"qom-type": "secret",
25
"id": "sec0",
26
- "props": {
27
- "data": "123456"
28
- }
29
+ "data": "123456"
30
}
31
}
32
{ "execute": "blockdev-add",
33
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
34
"arguments": {
35
"qom-type": "secret",
36
"id": "sec0",
37
- "props": {
38
- "data": "123456"
39
- }
40
+ "data": "123456"
41
}
42
}
43
{ "execute": "blockdev-add",
44
diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184
45
index XXXXXXX..XXXXXXX 100755
46
--- a/tests/qemu-iotests/184
47
+++ b/tests/qemu-iotests/184
48
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
49
"arguments": {
50
"qom-type": "throttle-group",
51
"id": "group0",
52
- "props": {
53
- "limits" : {
54
- "iops-total": 1000
55
- }
56
+ "limits" : {
57
+ "iops-total": 1000
58
}
59
}
60
}
61
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
62
"arguments": {
63
"qom-type": "throttle-group",
64
"id": "group0",
65
- "props" : {
66
- "limits": {
67
- "iops-total": 1000
68
- }
69
+ "limits": {
70
+ "iops-total": 1000
71
}
72
}
73
}
74
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
75
"arguments": {
76
"qom-type": "throttle-group",
77
"id": "group0",
78
- "props" : {
79
- "limits": {
80
- "iops-total": 1000
81
- }
82
+ "limits": {
83
+ "iops-total": 1000
84
}
85
}
86
}
87
diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218
88
index XXXXXXX..XXXXXXX 100755
89
--- a/tests/qemu-iotests/218
90
+++ b/tests/qemu-iotests/218
91
@@ -XXX,XX +XXX,XX @@ with iotests.VM() as vm, \
92
vm.launch()
93
94
ret = vm.qmp('object-add', qom_type='throttle-group', id='tg',
95
- props={'x-bps-read': 4096})
96
+ limits={'bps-read': 4096})
97
assert ret['return'] == {}
98
99
ret = vm.qmp('blockdev-add',
100
diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235
101
index XXXXXXX..XXXXXXX 100755
102
--- a/tests/qemu-iotests/235
103
+++ b/tests/qemu-iotests/235
104
@@ -XXX,XX +XXX,XX @@ vm.add_args('-drive', 'id=src,file=' + disk)
105
vm.launch()
106
107
log(vm.qmp('object-add', qom_type='throttle-group', id='tg0',
108
- props={ 'x-bps-total': size }))
109
+ limits={'bps-total': size}))
110
111
log(vm.qmp('blockdev-add',
112
**{ 'node-name': 'target',
113
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
114
index XXXXXXX..XXXXXXX 100755
115
--- a/tests/qemu-iotests/245
116
+++ b/tests/qemu-iotests/245
117
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
118
###### throttle ######
119
######################
120
opts = { 'qom-type': 'throttle-group', 'id': 'group0',
121
- 'props': { 'limits': { 'iops-total': 1000 } } }
122
+ 'limits': { 'iops-total': 1000 } }
123
result = self.vm.qmp('object-add', conv_keys = False, **opts)
124
self.assert_qmp(result, 'return', {})
125
126
opts = { 'qom-type': 'throttle-group', 'id': 'group1',
127
- 'props': { 'limits': { 'iops-total': 2000 } } }
128
+ 'limits': { 'iops-total': 2000 } }
129
result = self.vm.qmp('object-add', conv_keys = False, **opts)
130
self.assert_qmp(result, 'return', {})
131
132
diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258
133
index XXXXXXX..XXXXXXX 100755
134
--- a/tests/qemu-iotests/258
135
+++ b/tests/qemu-iotests/258
136
@@ -XXX,XX +XXX,XX @@ def test_concurrent_finish(write_to_stream_node):
137
vm.qmp_log('object-add',
138
qom_type='throttle-group',
139
id='tg',
140
- props={
141
- 'x-iops-write': 1,
142
- 'x-iops-write-max': 1
143
+ limits={
144
+ 'iops-write': 1,
145
+ 'iops-write-max': 1
146
})
147
148
vm.qmp_log('blockdev-add',
149
diff --git a/tests/qemu-iotests/258.out b/tests/qemu-iotests/258.out
150
index XXXXXXX..XXXXXXX 100644
151
--- a/tests/qemu-iotests/258.out
152
+++ b/tests/qemu-iotests/258.out
153
@@ -XXX,XX +XXX,XX @@ Running tests:
154
155
=== Commit and stream finish concurrently (letting stream write) ===
156
157
-{"execute": "object-add", "arguments": {"id": "tg", "props": {"x-iops-write": 1, "x-iops-write-max": 1}, "qom-type": "throttle-group"}}
158
+{"execute": "object-add", "arguments": {"id": "tg", "limits": {"iops-write": 1, "iops-write-max": 1}, "qom-type": "throttle-group"}}
159
{"return": {}}
160
{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"backing": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-node0.img"}, "node-name": "node0"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node1.img"}, "node-name": "node1"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node2.img"}, "node-name": "node2"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node3.img"}, "node-name": "node3"}, "driver": "IMGFMT", "file": {"driver": "throttle", "file": {"driver": "file", "filename": "TEST_DIR/PID-node4.img"}, "throttle-group": "tg"}, "node-name": "node4"}}
161
{"return": {}}
162
@@ -XXX,XX +XXX,XX @@ Running tests:
163
164
=== Commit and stream finish concurrently (letting commit write) ===
165
166
-{"execute": "object-add", "arguments": {"id": "tg", "props": {"x-iops-write": 1, "x-iops-write-max": 1}, "qom-type": "throttle-group"}}
167
+{"execute": "object-add", "arguments": {"id": "tg", "limits": {"iops-write": 1, "iops-write-max": 1}, "qom-type": "throttle-group"}}
168
{"return": {}}
169
{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"backing": {"driver": "raw", "file": {"driver": "throttle", "file": {"driver": "file", "filename": "TEST_DIR/PID-node0.img"}, "throttle-group": "tg"}, "node-name": "node0"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node1.img"}, "node-name": "node1"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node2.img"}, "node-name": "node2"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node3.img"}, "node-name": "node3"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node4.img"}, "node-name": "node4"}}
170
{"return": {}}
171
diff --git a/tests/qemu-iotests/295 b/tests/qemu-iotests/295
172
index XXXXXXX..XXXXXXX 100755
173
--- a/tests/qemu-iotests/295
174
+++ b/tests/qemu-iotests/295
175
@@ -XXX,XX +XXX,XX @@ class Secret:
176
177
def to_qmp_object(self):
178
return { "qom_type" : "secret", "id": self.id(),
179
- "props": { "data": self.secret() } }
180
+ "data": self.secret() }
181
182
################################################################################
183
class EncryptionSetupTestCase(iotests.QMPTestCase):
184
diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296
185
index XXXXXXX..XXXXXXX 100755
186
--- a/tests/qemu-iotests/296
187
+++ b/tests/qemu-iotests/296
188
@@ -XXX,XX +XXX,XX @@ class Secret:
189
190
def to_qmp_object(self):
191
return { "qom_type" : "secret", "id": self.id(),
192
- "props": { "data": self.secret() } }
193
+ "data": self.secret() }
194
195
################################################################################
196
197
--
198
2.29.2
199
200
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Max Reitz <mreitz@redhat.com>
2
2
3
Parameter "filename" is deprecated since commit 5c3ad1a6a8f, v2.10.0.
3
The block job holds a reference to the backup-top node (because it is
4
Time to get rid of it.
4
passed as the main job BDS to block_job_create()). Therefore,
5
bdrv_backup_top_drop() cannot delete the backup-top node (replacing it
6
by its child does not affect the job parent, because that has
7
.stay_at_node set). That is a problem, because all of its I/O functions
8
assume the BlockCopyState (s->bcs) to be valid and that it has a
9
filtered child; but after bdrv_backup_top_drop(), neither of those
10
things are true.
5
11
6
Signed-off-by: Markus Armbruster <armbru@redhat.com>
12
It does not make sense to add new parents to backup-top after
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
backup_clean(), so we should detach it from the job before
14
bdrv_backup_top_drop(). Because there is no function to do that for a
15
single node, just detach all of the job's nodes -- the job does not do
16
anything past backup_clean() anyway.
17
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
Message-Id: <20210219153348.41861-2-mreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
21
---
10
block/iscsi.c | 23 ++---------------------
22
block/backup.c | 1 +
11
1 file changed, 2 insertions(+), 21 deletions(-)
23
1 file changed, 1 insertion(+)
12
24
13
diff --git a/block/iscsi.c b/block/iscsi.c
25
diff --git a/block/backup.c b/block/backup.c
14
index XXXXXXX..XXXXXXX 100644
26
index XXXXXXX..XXXXXXX 100644
15
--- a/block/iscsi.c
27
--- a/block/backup.c
16
+++ b/block/iscsi.c
28
+++ b/block/backup.c
17
@@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = {
29
@@ -XXX,XX +XXX,XX @@ static void backup_abort(Job *job)
18
.name = "timeout",
30
static void backup_clean(Job *job)
19
.type = QEMU_OPT_NUMBER,
31
{
20
},
32
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
21
- {
33
+ block_job_remove_all_bdrv(&s->common);
22
- .name = "filename",
34
bdrv_backup_top_drop(s->backup_top);
23
- .type = QEMU_OPT_STRING,
24
- },
25
{ /* end of list */ }
26
},
27
};
28
@@ -XXX,XX +XXX,XX @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
29
char *initiator_name = NULL;
30
QemuOpts *opts;
31
Error *local_err = NULL;
32
- const char *transport_name, *portal, *target, *filename;
33
+ const char *transport_name, *portal, *target;
34
#if LIBISCSI_API_VERSION >= (20160603)
35
enum iscsi_transport_type transport;
36
#endif
37
int i, ret = 0, timeout = 0, lun;
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
}
61
-exit:
62
+
63
return ret;
64
}
35
}
65
36
66
--
37
--
67
2.13.6
38
2.29.2
68
39
69
40
diff view generated by jsdifflib
New patch
1
From: Max Reitz <mreitz@redhat.com>
1
2
3
When the backup-top node transitions from active to inactive in
4
bdrv_backup_top_drop(), the BlockCopyState is freed and the filtered
5
child is removed, so the node effectively becomes unusable.
6
7
However, noone told its I/O functions this, so they will happily
8
continue accessing bs->backing and s->bcs. Prevent that by aborting
9
early when s->active is false.
10
11
(After the preceding patch, the node should be gone after
12
bdrv_backup_top_drop(), so this should largely be a theoretical problem.
13
But still, better to be safe than sorry, and also I think it just makes
14
sense to check s->active in the I/O functions.)
15
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
Message-Id: <20210219153348.41861-3-mreitz@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
20
block/backup-top.c | 10 ++++++++++
21
1 file changed, 10 insertions(+)
22
23
diff --git a/block/backup-top.c b/block/backup-top.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/block/backup-top.c
26
+++ b/block/backup-top.c
27
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_co_preadv(
28
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
29
QEMUIOVector *qiov, int flags)
30
{
31
+ BDRVBackupTopState *s = bs->opaque;
32
+
33
+ if (!s->active) {
34
+ return -EIO;
35
+ }
36
+
37
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
38
}
39
40
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
41
BDRVBackupTopState *s = bs->opaque;
42
uint64_t off, end;
43
44
+ if (!s->active) {
45
+ return -EIO;
46
+ }
47
+
48
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
49
return 0;
50
}
51
--
52
2.29.2
53
54
diff view generated by jsdifflib
New patch
1
From: Max Reitz <mreitz@redhat.com>
1
2
3
Without any of HEAD^ or HEAD^^ applied, qemu will most likely crash on
4
the qemu-io invocation, for a variety of immediate reasons. The
5
underlying problem is generally a use-after-free access into
6
backup-top's BlockCopyState.
7
8
With only HEAD^ applied, qemu-io will run into an EIO (which is not
9
capture by the output, but you can see that the qemu-io invocation will
10
be accepted (i.e., qemu-io will run) in contrast to the reference
11
output, where the node name cannot be found), and qemu will then crash
12
in query-named-block-nodes: bdrv_get_allocated_file_size() detects
13
backup-top to be a filter and passes the request through to its child.
14
However, after bdrv_backup_top_drop(), that child is NULL, so the
15
recursive call crashes.
16
17
With HEAD^^ applied, this test should pass.
18
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
20
Message-Id: <20210219153348.41861-4-mreitz@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
23
tests/qemu-iotests/283 | 53 ++++++++++++++++++++++++++++++++++++++
24
tests/qemu-iotests/283.out | 15 +++++++++++
25
2 files changed, 68 insertions(+)
26
27
diff --git a/tests/qemu-iotests/283 b/tests/qemu-iotests/283
28
index XXXXXXX..XXXXXXX 100755
29
--- a/tests/qemu-iotests/283
30
+++ b/tests/qemu-iotests/283
31
@@ -XXX,XX +XXX,XX @@ vm.qmp_log('blockdev-add', **{
32
vm.qmp_log('blockdev-backup', sync='full', device='source', target='target')
33
34
vm.shutdown()
35
+
36
+
37
+print('\n=== backup-top should be gone after job-finalize ===\n')
38
+
39
+# Check that the backup-top node is gone after job-finalize.
40
+#
41
+# During finalization, the node becomes inactive and can no longer
42
+# function. If it is still present, new parents might be attached, and
43
+# there would be no meaningful way to handle their I/O requests.
44
+
45
+vm = iotests.VM()
46
+vm.launch()
47
+
48
+vm.qmp_log('blockdev-add', **{
49
+ 'node-name': 'source',
50
+ 'driver': 'null-co',
51
+})
52
+
53
+vm.qmp_log('blockdev-add', **{
54
+ 'node-name': 'target',
55
+ 'driver': 'null-co',
56
+})
57
+
58
+vm.qmp_log('blockdev-backup',
59
+ job_id='backup',
60
+ device='source',
61
+ target='target',
62
+ sync='full',
63
+ filter_node_name='backup-filter',
64
+ auto_finalize=False,
65
+ auto_dismiss=False)
66
+
67
+vm.event_wait('BLOCK_JOB_PENDING', 5.0)
68
+
69
+# The backup-top filter should still be present prior to finalization
70
+assert vm.node_info('backup-filter') is not None
71
+
72
+vm.qmp_log('job-finalize', id='backup')
73
+vm.event_wait('BLOCK_JOB_COMPLETED', 5.0)
74
+
75
+# The filter should be gone now. Check that by trying to access it
76
+# with qemu-io (which will most likely crash qemu if it is still
77
+# there.).
78
+vm.qmp_log('human-monitor-command',
79
+ command_line='qemu-io backup-filter "write 0 1M"')
80
+
81
+# (Also, do an explicit check.)
82
+assert vm.node_info('backup-filter') is None
83
+
84
+vm.qmp_log('job-dismiss', id='backup')
85
+vm.event_wait('JOB_STATUS_CHANGE', 5.0, {'data': {'status': 'null'}})
86
+
87
+vm.shutdown()
88
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
89
index XXXXXXX..XXXXXXX 100644
90
--- a/tests/qemu-iotests/283.out
91
+++ b/tests/qemu-iotests/283.out
92
@@ -XXX,XX +XXX,XX @@
93
{"return": {}}
94
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
95
{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by other as 'image', which uses 'write' on base"}}
96
+
97
+=== backup-top should be gone after job-finalize ===
98
+
99
+{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "source"}}
100
+{"return": {}}
101
+{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "target"}}
102
+{"return": {}}
103
+{"execute": "blockdev-backup", "arguments": {"auto-dismiss": false, "auto-finalize": false, "device": "source", "filter-node-name": "backup-filter", "job-id": "backup", "sync": "full", "target": "target"}}
104
+{"return": {}}
105
+{"execute": "job-finalize", "arguments": {"id": "backup"}}
106
+{"return": {}}
107
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io backup-filter \"write 0 1M\""}}
108
+{"return": "Error: Cannot find device= nor node_name=backup-filter\r\n"}
109
+{"execute": "job-dismiss", "arguments": {"id": "backup"}}
110
+{"return": {}}
111
--
112
2.29.2
113
114
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Eric Blake <eblake@redhat.com>
2
2
3
Commit a290f085 exposed a latent bug in qemu-img map introduced
3
Break some long lines, and relax our type hints to be more generic to
4
during the conversion of block status to be byte-based. Earlier in
4
any JSON, in order to more easily permit the additional JSON depth now
5
commit 5e344dd8, the internal interface get_block_status() switched
5
possible in migration parameters. Detected by iotest 297.
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
6
16
See also https://bugzilla.redhat.com/1589738
7
Fixes: ca4bfec41d56
17
8
(qemu-iotests: 300: Add test case for modifying persistence of bitmap)
18
Fixes: 237d78f8
9
Reported-by: Kevin Wolf <kwolf@redhat.com>
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>
10
Signed-off-by: Eric Blake <eblake@redhat.com>
11
Message-Id: <20210215220518.1745469-1-eblake@redhat.com>
12
Reviewed-by: John Snow <jsnow@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
---
15
---
26
qemu-img.c | 2 +-
16
tests/qemu-iotests/300 | 10 ++++++----
27
1 file changed, 1 insertion(+), 1 deletion(-)
17
1 file changed, 6 insertions(+), 4 deletions(-)
28
18
29
diff --git a/qemu-img.c b/qemu-img.c
19
diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300
30
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100755
31
--- a/qemu-img.c
21
--- a/tests/qemu-iotests/300
32
+++ b/qemu-img.c
22
+++ b/tests/qemu-iotests/300
33
@@ -XXX,XX +XXX,XX @@ static int img_map(int argc, char **argv)
23
@@ -XXX,XX +XXX,XX @@
34
int64_t n;
24
import os
35
25
import random
36
/* Probe up to 1 GiB at a time. */
26
import re
37
- n = QEMU_ALIGN_DOWN(MIN(1 << 30, length - offset), BDRV_SECTOR_SIZE);
27
-from typing import Dict, List, Optional, Union
38
+ n = MIN(1 << 30, length - offset);
28
+from typing import Dict, List, Optional
39
ret = get_block_status(bs, offset, n, &next);
29
40
30
import iotests
41
if (ret < 0) {
31
32
@@ -XXX,XX +XXX,XX @@ import iotests
33
# pylint: disable=wrong-import-order
34
import qemu
35
36
-BlockBitmapMapping = List[Dict[str, Union[str, List[Dict[str, str]]]]]
37
+BlockBitmapMapping = List[Dict[str, object]]
38
39
mig_sock = os.path.join(iotests.sock_dir, 'mig_sock')
40
41
@@ -XXX,XX +XXX,XX @@ class TestCrossAliasMigration(TestDirtyBitmapMigration):
42
43
class TestAliasTransformMigration(TestDirtyBitmapMigration):
44
"""
45
- Tests the 'transform' option which modifies bitmap persistence on migration.
46
+ Tests the 'transform' option which modifies bitmap persistence on
47
+ migration.
48
"""
49
50
src_node_name = 'node-a'
51
@@ -XXX,XX +XXX,XX @@ class TestAliasTransformMigration(TestDirtyBitmapMigration):
52
bitmaps = self.vm_b.query_bitmaps()
53
54
for node in bitmaps:
55
- bitmaps[node] = sorted(((bmap['name'], bmap['persistent']) for bmap in bitmaps[node]))
56
+ bitmaps[node] = sorted(((bmap['name'], bmap['persistent'])
57
+ for bmap in bitmaps[node]))
58
59
self.assertEqual(bitmaps,
60
{'node-a': [('bmap-a', True), ('bmap-b', False)],
42
--
61
--
43
2.13.6
62
2.29.2
44
63
45
64
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefano Garzarella <sgarzare@redhat.com>
2
2
3
Parameter auth-client-required lets you configure authentication
3
When a block job fails, we report strerror(-job->job.ret) error
4
methods. We tried to provide that in v2.9.0, but backed out due to
4
message, also if the job set an error object.
5
interface design doubts (commit 464444fcc16).
5
Let's report a better error message using error_get_pretty(job->job.err).
6
6
7
This commit is similar to what we backed out, but simpler: we use a
7
If an error object was not set, strerror(-job->ret) is used as fallback,
8
list of enumeration values instead of a list of objects with a member
8
as explained in include/qemu/job.h:
9
of enumeration type.
10
9
11
Let's review our reasons for backing out the first try, as stated in
10
typedef struct Job {
12
the commit message:
11
...
12
/**
13
* Error object for a failed job.
14
* If job->ret is nonzero and an error object was not set, it will be set
15
* to strerror(-job->ret) during job_completed.
16
*/
17
Error *err;
18
}
13
19
14
* The implementation uses deprecated rados_conf_set() key
20
In block_job_query() there can be a transient where 'job.err' is not set
15
"auth_supported". No biggie.
21
by a scheduled bottom half. In that case we use strerror(-job->ret) as it
22
was before.
16
23
17
Fixed: we use "auth-client-required".
24
Suggested-by: Kevin Wolf <kwolf@redhat.com>
18
25
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
19
* The implementation makes -drive silently ignore invalid parameters
26
Message-Id: <20210225103633.76746-1-sgarzare@redhat.com>
20
"auth" and "auth-supported.*.X" where X isn't "auth". Fixable (in
21
fact I'm going to fix similar bugs around parameter server), so
22
again no biggie.
23
24
That fix is commit 2836284db60. This commit doesn't bring the bugs
25
back.
26
27
* BlockdevOptionsRbd member @password-secret applies only to
28
authentication method cephx. Should it be a variant member of
29
RbdAuthMethod?
30
31
We've had time to ponder, and we decided to stick to the way Ceph
32
configuration works: the key configured separately, and silently
33
ignored if the authentication method doesn't use it.
34
35
* BlockdevOptionsRbd member @user could apply to both methods cephx
36
and none, but I'm not sure it's actually used with none. If it
37
isn't, should it be a variant member of RbdAuthMethod?
38
39
Likewise.
40
41
* The client offers a *set* of authentication methods, not a list.
42
Should the methods be optional members of BlockdevOptionsRbd instead
43
of members of list @auth-supported? The latter begs the question
44
what multiple entries for the same method mean. Trivial question
45
now that RbdAuthMethod contains nothing but @type, but less so when
46
RbdAuthMethod acquires other members, such the ones discussed above.
47
48
Again, we decided to stick to the way Ceph configuration works, except
49
we make auth-client-required a list of enumeration values instead of a
50
string containing keywords separated by delimiters.
51
52
* How BlockdevOptionsRbd member @auth-supported interacts with
53
settings from a configuration file specified with @conf is
54
undocumented. I suspect it's untested, too.
55
56
Not actually true, the documentation for @conf says "Values in the
57
configuration file will be overridden by options specified via QAPI",
58
and we've tested this.
59
60
Signed-off-by: Markus Armbruster <armbru@redhat.com>
61
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
62
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
27
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
63
---
28
---
64
qapi/block-core.json | 13 +++++++++++++
29
blockjob.c | 10 +++++++---
65
block/rbd.c | 42 ++++++++++++++++++++++++++++++++----------
30
1 file changed, 7 insertions(+), 3 deletions(-)
66
2 files changed, 45 insertions(+), 10 deletions(-)
67
31
68
diff --git a/qapi/block-core.json b/qapi/block-core.json
32
diff --git a/blockjob.c b/blockjob.c
69
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
70
--- a/qapi/block-core.json
34
--- a/blockjob.c
71
+++ b/qapi/block-core.json
35
+++ b/blockjob.c
72
@@ -XXX,XX +XXX,XX @@
36
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
73
37
info->status = job->job.status;
74
38
info->auto_finalize = job->job.auto_finalize;
75
##
39
info->auto_dismiss = job->job.auto_dismiss;
76
+# @RbdAuthMode:
40
- info->has_error = job->job.ret != 0;
77
+#
41
- info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL;
78
+# Since: 3.0
42
+ if (job->job.ret) {
79
+##
43
+ info->has_error = true;
80
+{ 'enum': 'RbdAuthMode',
44
+ info->error = job->job.err ?
81
+ 'data': [ 'cephx', 'none' ] }
45
+ g_strdup(error_get_pretty(job->job.err)) :
82
+
46
+ g_strdup(strerror(-job->job.ret));
83
+##
47
+ }
84
# @BlockdevOptionsRbd:
48
return info;
85
#
49
}
86
# @pool: Ceph pool name.
50
87
@@ -XXX,XX +XXX,XX @@
51
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(Notifier *n, void *opaque)
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
}
52
}
139
53
140
- rados_conf_set(cluster, "key", secret);
54
if (job->job.ret < 0) {
141
- g_free(secret);
55
- msg = strerror(-job->job.ret);
142
+ if (opts->has_auth_client_required) {
56
+ msg = error_get_pretty(job->job.err);
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
}
57
}
165
58
166
- if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) {
59
qapi_event_send_block_job_completed(job_type(&job->job),
167
+ if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) {
168
r = -EIO;
169
goto failed_shutdown;
170
}
171
--
60
--
172
2.13.6
61
2.29.2
173
62
174
63
diff view generated by jsdifflib
New patch
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
2
3
If the first character of optstring is '-', then each nonoption argv
4
element is handled as if it were the argument of an option with character
5
code 1. This removes the reordering of the argv array, and enables usage
6
of loc_set_cmdline to provide better error messages.
7
8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9
Message-Id: <20210301152844.291799-2-pbonzini@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
storage-daemon/qemu-storage-daemon.c | 9 ++++-----
14
1 file changed, 4 insertions(+), 5 deletions(-)
15
16
diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/storage-daemon/qemu-storage-daemon.c
19
+++ b/storage-daemon/qemu-storage-daemon.c
20
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
21
* they are given on the command lines. This means that things must be
22
* defined first before they can be referenced in another option.
23
*/
24
- while ((c = getopt_long(argc, argv, "hT:V", long_options, NULL)) != -1) {
25
+ while ((c = getopt_long(argc, argv, "-hT:V", long_options, NULL)) != -1) {
26
switch (c) {
27
case '?':
28
exit(EXIT_FAILURE);
29
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
30
qobject_unref(args);
31
break;
32
}
33
+ case 1:
34
+ error_report("Unexpected argument: %s", optarg);
35
+ exit(EXIT_FAILURE);
36
default:
37
g_assert_not_reached();
38
}
39
}
40
- if (optind != argc) {
41
- error_report("Unexpected argument: %s", argv[optind]);
42
- exit(EXIT_FAILURE);
43
- }
44
}
45
46
int main(int argc, char *argv[])
47
--
48
2.29.2
49
50
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Legacy -drive supports "password-secret" parameter that isn't
3
Use the location management facilities that the emulator uses, so that
4
available with -blockdev / blockdev-add. That's because we backed out
4
the current command line option appears in the error message.
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
Before:
9
named "key-secret" now.
10
7
11
Let's review our reasons for backing out the first try, as stated in
8
$ storage-daemon/qemu-storage-daemon --nbd key..=
12
the commit message:
9
qemu-storage-daemon: Invalid parameter 'key..'
13
10
14
* BlockdevOptionsRbd member @password-secret isn't actually a
11
After:
15
password, it's a key generated by Ceph.
16
12
17
Addressed by the rename.
13
$ storage-daemon/qemu-storage-daemon --nbd key..=
14
qemu-storage-daemon: --nbd key..=: Invalid parameter 'key..'
18
15
19
* We're not sure where member @password-secret belongs (see the
16
Reviewed-by: Eric Blake <eblake@redhat.com>
20
previous commit).
17
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
21
18
Message-Id: <20210301152844.291799-3-pbonzini@redhat.com>
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>
33
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
34
---
20
---
35
qapi/block-core.json | 6 ++++++
21
storage-daemon/qemu-storage-daemon.c | 19 +++++++++++++++++--
36
block/rbd.c | 41 +++++++++++++++++++++++++----------------
22
1 file changed, 17 insertions(+), 2 deletions(-)
37
2 files changed, 31 insertions(+), 16 deletions(-)
38
23
39
diff --git a/qapi/block-core.json b/qapi/block-core.json
24
diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c
40
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
41
--- a/qapi/block-core.json
26
--- a/storage-daemon/qemu-storage-daemon.c
42
+++ b/qapi/block-core.json
27
+++ b/storage-daemon/qemu-storage-daemon.c
43
@@ -XXX,XX +XXX,XX @@
28
@@ -XXX,XX +XXX,XX @@ static void init_qmp_commands(void)
44
# This maps to Ceph configuration option
29
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
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
}
30
}
69
31
70
32
+static int getopt_set_loc(int argc, char **argv, const char *optstring,
71
-static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
33
+ const struct option *longopts)
72
- BlockdevOptionsRbd *opts,
34
+{
73
+static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts,
35
+ int c, save_index;
74
Error **errp)
36
+
37
+ optarg = NULL;
38
+ save_index = optind;
39
+ c = getopt_long(argc, argv, optstring, longopts, NULL);
40
+ if (optarg) {
41
+ loc_set_cmdline(argv, save_index, MAX(1, optind - save_index));
42
+ }
43
+ return c;
44
+}
45
+
46
static void process_options(int argc, char *argv[])
75
{
47
{
76
- char *acr;
48
int c;
77
+ char *key, *acr;
49
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
78
int r;
50
* they are given on the command lines. This means that things must be
79
GString *accu;
51
* defined first before they can be referenced in another option.
80
RbdAuthModeList *auth;
52
*/
81
53
- while ((c = getopt_long(argc, argv, "-hT:V", long_options, NULL)) != -1) {
82
- if (secretid) {
54
+ while ((c = getopt_set_loc(argc, argv, "-hT:V", long_options)) != -1) {
83
- gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
55
switch (c) {
84
- errp);
56
case '?':
85
- if (!secret) {
57
exit(EXIT_FAILURE);
86
- return -1;
58
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
87
+ if (opts->key_secret) {
59
break;
88
+ key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp);
60
}
89
+ if (!key) {
61
case 1:
90
+ return -EIO;
62
- error_report("Unexpected argument: %s", optarg);
91
+ }
63
+ error_report("Unexpected argument");
92
+ r = rados_conf_set(cluster, "key", key);
64
exit(EXIT_FAILURE);
93
+ g_free(key);
65
default:
94
+ if (r < 0) {
66
g_assert_not_reached();
95
+ error_setg_errno(errp, -r, "Could not set 'key'");
96
+ return r;
97
}
98
-
99
- rados_conf_set(cluster, "key", secret);
100
- g_free(secret);
101
}
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
}
67
}
134
}
68
}
135
69
+ loc_set_none();
136
- if (qemu_rbd_set_auth(*cluster, secretid, opts, errp) < 0) {
70
}
137
- r = -EIO;
71
138
+ r = qemu_rbd_set_auth(*cluster, opts, errp);
72
int main(int argc, char *argv[])
139
+ if (r < 0) {
140
goto failed_shutdown;
141
}
142
143
--
73
--
144
2.13.6
74
2.29.2
145
75
146
76
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
3
Daemons often have a --pidfile option where the pid is written to a file
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
so that scripts can stop the daemon by sending a signal.
5
6
The pid file also acts as a lock to prevent multiple instances of the
7
daemon from launching for a given pid file.
8
9
QEMU, qemu-nbd, qemu-ga, virtiofsd, and qemu-pr-helper all support the
10
--pidfile option. Add it to qemu-storage-daemon too.
11
12
Reported-by: Richard W.M. Jones <rjones@redhat.com>
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Message-Id: <20210302142746.170535-1-stefanha@redhat.com>
15
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
17
---
7
include/block/qdict.h | 3 ++-
18
docs/tools/qemu-storage-daemon.rst | 14 +++++++++++
8
block/nbd.c | 7 ++-----
19
storage-daemon/qemu-storage-daemon.c | 36 ++++++++++++++++++++++++++++
9
block/nfs.c | 7 ++-----
20
2 files changed, 50 insertions(+)
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
21
22
diff --git a/include/block/qdict.h b/include/block/qdict.h
22
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
23
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/qdict.h
24
--- a/docs/tools/qemu-storage-daemon.rst
25
+++ b/include/block/qdict.h
25
+++ b/docs/tools/qemu-storage-daemon.rst
26
@@ -XXX,XX +XXX,XX @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
26
@@ -XXX,XX +XXX,XX @@ Standard options:
27
void qdict_array_split(QDict *src, QList **dst);
27
List object properties with ``<type>,help``. See the :manpage:`qemu(1)`
28
int qdict_array_entries(QDict *src, const char *subqdict);
28
manual page for a description of the object properties.
29
QObject *qdict_crumple(const QDict *src, Error **errp);
29
30
-QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp);
30
+.. option:: --pidfile PATH
31
void qdict_flatten(QDict *qdict);
31
+
32
32
+ is the path to a file where the daemon writes its pid. This allows scripts to
33
typedef struct QDictRenames {
33
+ stop the daemon by sending a signal::
34
@@ -XXX,XX +XXX,XX @@ typedef struct QDictRenames {
34
+
35
} QDictRenames;
35
+ $ kill -SIGTERM $(<path/to/qsd.pid)
36
bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
36
+
37
37
+ A file lock is applied to the file so only one instance of the daemon can run
38
+Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict,
38
+ with a given pid file path. The daemon unlinks its pid file when terminating.
39
+ Error **errp);
39
+
40
#endif
40
+ The pid file is written after chardevs, exports, and NBD servers have been
41
diff --git a/block/nbd.c b/block/nbd.c
41
+ created but before accepting connections. The daemon has started successfully
42
+ when the pid file is written and clients may begin connecting.
43
+
44
Examples
45
--------
46
Launch the daemon with QMP monitor socket ``qmp.sock`` so clients can execute
47
diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c
42
index XXXXXXX..XXXXXXX 100644
48
index XXXXXXX..XXXXXXX 100644
43
--- a/block/nbd.c
49
--- a/storage-daemon/qemu-storage-daemon.c
44
+++ b/block/nbd.c
50
+++ b/storage-daemon/qemu-storage-daemon.c
45
@@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
51
@@ -XXX,XX +XXX,XX @@
46
{
52
#include "sysemu/runstate.h"
47
SocketAddress *saddr = NULL;
53
#include "trace/control.h"
48
QDict *addr = NULL;
54
49
- QObject *crumpled_addr = NULL;
55
+static const char *pid_file;
50
Visitor *iv = NULL;
56
static volatile bool exit_requested = false;
51
Error *local_err = NULL;
57
52
58
void qemu_system_killed(int signal, pid_t pid)
53
@@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
59
@@ -XXX,XX +XXX,XX @@ static void help(void)
54
goto done;
60
" See the qemu(1) man page for documentation of the\n"
55
}
61
" objects that can be added.\n"
56
62
"\n"
57
- crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp);
63
+" --pidfile <path> write process ID to a file after startup\n"
58
- if (!crumpled_addr) {
64
+"\n"
59
+ iv = qobject_input_visitor_new_flat_confused(addr, errp);
65
QEMU_HELP_BOTTOM "\n",
60
+ if (!iv) {
66
error_get_progname());
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
}
67
}
76
diff --git a/block/nfs.c b/block/nfs.c
68
@@ -XXX,XX +XXX,XX @@ enum {
77
index XXXXXXX..XXXXXXX 100644
69
OPTION_MONITOR,
78
--- a/block/nfs.c
70
OPTION_NBD_SERVER,
79
+++ b/block/nfs.c
71
OPTION_OBJECT,
80
@@ -XXX,XX +XXX,XX @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
72
+ OPTION_PIDFILE,
81
Error **errp)
73
};
82
{
74
83
BlockdevOptionsNfs *opts = NULL;
75
extern QemuOptsList qemu_chardev_opts;
84
- QObject *crumpled = NULL;
76
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
85
Visitor *v;
77
{"monitor", required_argument, NULL, OPTION_MONITOR},
86
const QDictEntry *e;
78
{"nbd-server", required_argument, NULL, OPTION_NBD_SERVER},
87
Error *local_err = NULL;
79
{"object", required_argument, NULL, OPTION_OBJECT},
88
80
+ {"pidfile", required_argument, NULL, OPTION_PIDFILE},
89
- crumpled = qdict_crumple_for_keyval_qiv(options, errp);
81
{"trace", required_argument, NULL, 'T'},
90
- if (crumpled == NULL) {
82
{"version", no_argument, NULL, 'V'},
91
+ v = qobject_input_visitor_new_flat_confused(options, errp);
83
{0, 0, 0, 0}
92
+ if (!v) {
84
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
93
return NULL;
85
qobject_unref(args);
94
}
86
break;
95
87
}
96
- v = qobject_input_visitor_new_keyval(crumpled);
88
+ case OPTION_PIDFILE:
97
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
89
+ pid_file = optarg;
98
visit_free(v);
90
+ break;
99
- qobject_unref(crumpled);
91
case 1:
100
92
error_report("Unexpected argument");
101
if (local_err) {
93
exit(EXIT_FAILURE);
102
error_propagate(errp, local_err);
94
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
103
diff --git a/block/parallels.c b/block/parallels.c
95
loc_set_none();
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
}
96
}
284
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
97
285
{
98
+static void pid_file_cleanup(void)
286
BlockdevCreateOptions *create_options = NULL;
99
+{
287
QDict *qdict, *location_qdict;
100
+ unlink(pid_file);
288
- QObject *crumpled;
101
+}
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
+
102
+
425
+/*
103
+static void pid_file_init(void)
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
+{
104
+{
437
+ QObject *crumpled;
105
+ Error *err = NULL;
438
+ Visitor *v;
439
+
106
+
440
+ crumpled = qdict_crumple_for_keyval_qiv(qdict, errp);
107
+ if (!pid_file) {
441
+ if (!crumpled) {
108
+ return;
442
+ return NULL;
443
+ }
109
+ }
444
+
110
+
445
+ v = qobject_input_visitor_new_keyval(crumpled);
111
+ if (!qemu_write_pidfile(pid_file, &err)) {
446
+ qobject_unref(crumpled);
112
+ error_reportf_err(err, "cannot create PID file: ");
447
+ return v;
113
+ exit(EXIT_FAILURE);
114
+ }
115
+
116
+ atexit(pid_file_cleanup);
448
+}
117
+}
118
+
119
int main(int argc, char *argv[])
120
{
121
#ifdef CONFIG_POSIX
122
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
123
qemu_init_main_loop(&error_fatal);
124
process_options(argc, argv);
125
126
+ /*
127
+ * Write the pid file after creating chardevs, exports, and NBD servers but
128
+ * before accepting connections. This ordering is documented. Do not change
129
+ * it.
130
+ */
131
+ pid_file_init();
132
+
133
while (!exit_requested) {
134
main_loop_wait(false);
135
}
449
--
136
--
450
2.13.6
137
2.29.2
451
138
452
139
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
The QMP monitor, NBD server, and vhost-user-blk export all support file
4
descriptor passing. This is a useful technique because it allows the
5
parent process to spawn and wait for qemu-storage-daemon without busy
6
waiting, which may delay startup due to arbitrary sleep() calls.
7
8
This Python example is inspired by the test case written for libnbd by
9
Richard W.M. Jones <rjones@redhat.com>:
10
https://gitlab.com/nbdkit/libnbd/-/commit/89113f484effb0e6c322314ba75c1cbe07a04543
11
12
Thanks to Daniel P. Berrangé <berrange@redhat.com> for suggestions on
13
how to get this working. Now let's document it!
14
15
Reported-by: Richard W.M. Jones <rjones@redhat.com>
16
Cc: Kevin Wolf <kwolf@redhat.com>
17
Cc: Daniel P. Berrangé <berrange@redhat.com>
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Message-Id: <20210301172728.135331-2-stefanha@redhat.com>
20
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
21
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
24
docs/tools/qemu-storage-daemon.rst | 42 ++++++++++++++++++++++++++++--
25
1 file changed, 40 insertions(+), 2 deletions(-)
26
27
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
28
index XXXXXXX..XXXXXXX 100644
29
--- a/docs/tools/qemu-storage-daemon.rst
30
+++ b/docs/tools/qemu-storage-daemon.rst
31
@@ -XXX,XX +XXX,XX @@ Standard options:
32
33
.. option:: --nbd-server addr.type=inet,addr.host=<host>,addr.port=<port>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
34
--nbd-server addr.type=unix,addr.path=<path>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
35
+ --nbd-server addr.type=fd,addr.str=<fd>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
36
37
is a server for NBD exports. Both TCP and UNIX domain sockets are supported.
38
- TLS encryption can be configured using ``--object`` tls-creds-* and authz-*
39
- secrets (see below).
40
+ A listen socket can be provided via file descriptor passing (see Examples
41
+ below). TLS encryption can be configured using ``--object`` tls-creds-* and
42
+ authz-* secrets (see below).
43
44
To configure an NBD server on UNIX domain socket path ``/tmp/nbd.sock``::
45
46
@@ -XXX,XX +XXX,XX @@ QMP commands::
47
--chardev socket,path=qmp.sock,server=on,wait=off,id=char1 \
48
--monitor chardev=char1
49
50
+Launch the daemon from Python with a QMP monitor socket using file descriptor
51
+passing so there is no need to busy wait for the QMP monitor to become
52
+available::
53
+
54
+ #!/usr/bin/env python3
55
+ import subprocess
56
+ import socket
57
+
58
+ sock_path = '/var/run/qmp.sock'
59
+
60
+ with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as listen_sock:
61
+ listen_sock.bind(sock_path)
62
+ listen_sock.listen()
63
+
64
+ fd = listen_sock.fileno()
65
+
66
+ subprocess.Popen(
67
+ ['qemu-storage-daemon',
68
+ '--chardev', f'socket,fd={fd},server=on,id=char1',
69
+ '--monitor', 'chardev=char1'],
70
+ pass_fds=[fd],
71
+ )
72
+
73
+ # listen_sock was automatically closed when leaving the 'with' statement
74
+ # body. If the daemon process terminated early then the following connect()
75
+ # will fail with "Connection refused" because no process has the listen
76
+ # socket open anymore. Launch errors can be detected this way.
77
+
78
+ qmp_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
79
+ qmp_sock.connect(sock_path)
80
+ ...QMP interaction...
81
+
82
+The same socket spawning approach also works with the ``--nbd-server
83
+addr.type=fd,addr.str=<fd>`` and ``--export
84
+type=vhost-user-blk,addr.type=fd,addr.str=<fd>`` options.
85
+
86
Export raw image file ``disk.img`` over NBD UNIX domain socket ``nbd.sock``::
87
88
$ qemu-storage-daemon \
89
--
90
2.29.2
91
92
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
3
World-writeable directories have security issues. Avoid showing them in
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
the documentation since someone might accidentally use them in
5
situations where they are insecure.
6
7
There tend to be 3 security problems:
8
1. Denial of service. An adversary may be able to create the file
9
beforehand, consume all space/inodes, etc to sabotage us.
10
2. Impersonation. An adversary may be able to create a listen socket and
11
accept incoming connections that were meant for us.
12
3. Unauthenticated client access. An adversary may be able to connect to
13
us if we did not set the uid/gid and permissions correctly.
14
15
These can be prevented or mitigated with private /tmp, carefully setting
16
the umask, etc but that requires special action and does not apply to
17
all situations. Just avoid using /tmp in examples.
18
19
Reported-by: Richard W.M. Jones <rjones@redhat.com>
20
Reported-by: Daniel P. Berrangé <berrange@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
22
Message-Id: <20210301172728.135331-3-stefanha@redhat.com>
23
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
24
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
26
---
7
tests/check-block-qdict.c | 14 +++++++++++++-
27
docs/tools/qemu-storage-daemon.rst | 7 ++++---
8
1 file changed, 13 insertions(+), 1 deletion(-)
28
1 file changed, 4 insertions(+), 3 deletions(-)
9
29
10
diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c
30
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
11
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/check-block-qdict.c
32
--- a/docs/tools/qemu-storage-daemon.rst
13
+++ b/tests/check-block-qdict.c
33
+++ b/docs/tools/qemu-storage-daemon.rst
14
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void)
34
@@ -XXX,XX +XXX,XX @@ Standard options:
15
QList *e = qlist_new();
35
a description of character device properties. A common character device
16
QDict *e_1_2 = qdict_new();
36
definition configures a UNIX domain socket::
17
QDict *f = qdict_new();
37
18
+ QList *y = qlist_new();
38
- --chardev socket,id=char1,path=/tmp/qmp.sock,server=on,wait=off
19
+ QDict *z = qdict_new();
39
+ --chardev socket,id=char1,path=/var/run/qsd-qmp.sock,server=on,wait=off
20
QDict *root = qdict_new();
40
21
41
.. option:: --export [type=]nbd,id=<id>,node-name=<node-name>[,name=<export-name>][,writable=on|off][,bitmap=<name>]
22
/*
42
--export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,addr.type=unix,addr.path=<socket-path>[,writable=on|off][,logical-block-size=<block-size>][,num-queues=<num-queues>]
23
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void)
43
@@ -XXX,XX +XXX,XX @@ Standard options:
24
* "c": 2,
44
below). TLS encryption can be configured using ``--object`` tls-creds-* and
25
* "d": 3,
45
authz-* secrets (see below).
26
* },
46
27
- * "g": 4
47
- To configure an NBD server on UNIX domain socket path ``/tmp/nbd.sock``::
28
+ * "g": 4,
48
+ To configure an NBD server on UNIX domain socket path
29
+ * "y": [{}],
49
+ ``/var/run/qsd-nbd.sock``::
30
+ * "z": {"a": []}
50
31
* }
51
- --nbd-server addr.type=unix,addr.path=/tmp/nbd.sock
32
*
52
+ --nbd-server addr.type=unix,addr.path=/var/run/qsd-nbd.sock
33
* to
53
34
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void)
54
.. option:: --object help
35
* "f.d": 3,
55
--object <type>,help
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
--
56
--
60
2.13.6
57
2.29.2
61
58
62
59
diff view generated by jsdifflib
1
We removed all options from the 'deprecated' array, so the code is dead
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
and can be removed as well.
3
2
3
Treat the num_queues field as virtio-endian. On big-endian hosts the
4
vhost-user-blk num_queues field was in the wrong endianness.
5
6
Move the blkcfg.num_queues store operation from realize to
7
vhost_user_blk_update_config() so feature negotiation has finished and
8
we know the endianness of the device. VIRTIO 1.0 devices are
9
little-endian, but in case someone wants to use legacy VIRTIO we support
10
all endianness cases.
11
12
Cc: qemu-stable@nongnu.org
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
15
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
16
Message-Id: <20210223144653.811468-2-stefanha@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Markus Armbruster <armbru@redhat.com>
6
---
18
---
7
blockdev.c | 12 ------------
19
hw/block/vhost-user-blk.c | 7 +++----
8
1 file changed, 12 deletions(-)
20
1 file changed, 3 insertions(+), 4 deletions(-)
9
21
10
diff --git a/blockdev.c b/blockdev.c
22
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
11
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
12
--- a/blockdev.c
24
--- a/hw/block/vhost-user-blk.c
13
+++ b/blockdev.c
25
+++ b/hw/block/vhost-user-blk.c
14
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
26
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
15
const char *filename;
27
{
16
Error *local_err = NULL;
28
VHostUserBlk *s = VHOST_USER_BLK(vdev);
17
int i;
29
18
- const char *deprecated[] = {
30
+ /* Our num_queues overrides the device backend */
19
- };
31
+ virtio_stw_p(vdev, &s->blkcfg.num_queues, s->num_queues);
20
32
+
21
/* Change legacy command line options into QMP ones */
33
memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config));
22
static const struct {
34
}
23
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
35
24
goto fail;
36
@@ -XXX,XX +XXX,XX @@ reconnect:
37
goto reconnect;
25
}
38
}
26
39
27
- /* Other deprecated options */
40
- if (s->blkcfg.num_queues != s->num_queues) {
28
- if (!qtest_enabled()) {
41
- s->blkcfg.num_queues = s->num_queues;
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
- }
42
- }
36
-
43
-
37
/* Media type */
44
return;
38
value = qemu_opt_get(legacy_opts, "media");
45
39
if (value) {
46
virtio_err:
40
--
47
--
41
2.13.6
48
2.29.2
42
49
43
50
diff view generated by jsdifflib
1
The -drive options cyls, heads, secs and trans were deprecated in
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
QEMU 2.10. It's time to remove them.
3
2
4
hd-geo-test tested both the old version with geometry options in -drive
3
Add an API that returns a new UNIX domain socket in the listen state.
5
and the new one with -device. Therefore the code using -drive doesn't
4
The code for this was already there but only used internally in
6
have to be replaced there, we just need to remove the -drive test cases.
5
init_socket().
7
This in turn allows some simplification of the code.
8
6
7
This new API will be used by vhost-user-blk-test.
8
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Reviewed-by: Thomas Huth <thuth@redhat.com>
11
Reviewed-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
12
Message-Id: <20210223144653.811468-3-stefanha@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Markus Armbruster <armbru@redhat.com>
11
---
14
---
12
include/sysemu/blockdev.h | 1 -
15
tests/qtest/libqos/libqtest.h | 8 +++++++
13
blockdev.c | 75 +----------------------------------------------
16
tests/qtest/libqtest.c | 40 ++++++++++++++++++++---------------
14
hw/block/block.c | 14 ---------
17
2 files changed, 31 insertions(+), 17 deletions(-)
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
18
21
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
19
diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h
22
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
23
--- a/include/sysemu/blockdev.h
21
--- a/tests/qtest/libqos/libqtest.h
24
+++ b/include/sysemu/blockdev.h
22
+++ b/tests/qtest/libqos/libqtest.h
25
@@ -XXX,XX +XXX,XX @@ struct DriveInfo {
23
@@ -XXX,XX +XXX,XX @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...)
26
int auto_del; /* see blockdev_mark_auto_del() */
24
void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
27
bool is_default; /* Added by default_drive() ? */
25
GCC_FMT_ATTR(2, 3);
28
int media_cd;
26
29
- int cyls, heads, secs, trans;
27
+/**
30
QemuOpts *opts;
28
+ * qtest_socket_server:
31
char *serial;
29
+ * @socket_path: the UNIX domain socket path
32
QTAILQ_ENTRY(DriveInfo) next;
30
+ *
33
diff --git a/blockdev.c b/blockdev.c
31
+ * Create and return a listen socket file descriptor, or abort on failure.
32
+ */
33
+int qtest_socket_server(const char *socket_path);
34
+
35
/**
36
* qtest_vqmp_fds:
37
* @s: #QTestState instance to operate on.
38
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
34
index XXXXXXX..XXXXXXX 100644
39
index XXXXXXX..XXXXXXX 100644
35
--- a/blockdev.c
40
--- a/tests/qtest/libqtest.c
36
+++ b/blockdev.c
41
+++ b/tests/qtest/libqtest.c
37
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
42
@@ -XXX,XX +XXX,XX @@ static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv);
38
.type = QEMU_OPT_STRING,
43
39
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
44
static int init_socket(const char *socket_path)
40
},{
45
{
41
- .name = "cyls",
46
- struct sockaddr_un addr;
42
- .type = QEMU_OPT_NUMBER,
47
- int sock;
43
- .help = "number of cylinders (ide disk geometry)",
48
- int ret;
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
-
49
-
86
- if (cyls || heads || secs) {
50
- sock = socket(PF_UNIX, SOCK_STREAM, 0);
87
- if (cyls < 1) {
51
- g_assert_cmpint(sock, !=, -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
-
52
-
101
- translation = BIOS_ATA_TRANSLATION_AUTO;
53
- addr.sun_family = AF_UNIX;
102
- value = qemu_opt_get(legacy_opts, "trans");
54
- snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
103
- if (value != NULL) {
55
+ int sock = qtest_socket_server(socket_path);
104
- if (!cyls) {
56
qemu_set_cloexec(sock);
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
-
57
-
125
- if (media == MEDIA_CDROM) {
58
- do {
126
- if (cyls || secs || heads) {
59
- ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
127
- error_report("CHS can't be set with media=cdrom");
60
- } while (ret == -1 && errno == EINTR);
128
- goto fail;
61
- g_assert_cmpint(ret, !=, -1);
129
- }
62
- ret = listen(sock, 1);
130
- }
63
- g_assert_cmpint(ret, !=, -1);
131
-
64
-
132
/* Device address specified by bus/unit or index.
65
return sock;
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
}
66
}
225
67
226
/*
68
@@ -XXX,XX +XXX,XX @@ QDict *qtest_qmp_receive_dict(QTestState *s)
227
- * Test case: IDE device (if=ide) with explicit CHS
69
return qmp_fd_receive(s->qmp_fd);
228
- */
70
}
229
-static void test_ide_drive_user_chs(void)
71
230
-{
72
+int qtest_socket_server(const char *socket_path)
231
- test_ide_drive_user(NULL, false);
73
+{
232
-}
74
+ struct sockaddr_un addr;
233
-
75
+ int sock;
234
-/*
76
+ int ret;
235
- * Test case: IDE device (if=ide) with explicit CHS and translation
77
+
236
- */
78
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
237
-static void test_ide_drive_user_chst(void)
79
+ g_assert_cmpint(sock, !=, -1);
238
-{
80
+
239
- test_ide_drive_user(NULL, true);
81
+ addr.sun_family = AF_UNIX;
240
-}
82
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
241
-
83
+
242
-/*
84
+ do {
243
* Test case: IDE device (if=none) with explicit CHS
85
+ ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
244
*/
86
+ } while (ret == -1 && errno == EINTR);
245
static void test_ide_device_user_chs(void)
87
+ g_assert_cmpint(ret, !=, -1);
246
@@ -XXX,XX +XXX,XX @@ static void test_ide_drive_cd_0(void)
88
+ ret = listen(sock, 1);
247
for (i = 0; i <= backend_empty; i++) {
89
+ g_assert_cmpint(ret, !=, -1);
248
ide_idx = backend_empty - i;
90
+
249
cur_ide[ide_idx] = &hd_chst[i][mbr_blank];
91
+ return sock;
250
- argc = setup_ide(argc, argv, ARGV_SIZE,
92
+}
251
- ide_idx, NULL, i, mbr_blank, "");
93
+
252
+ argc = setup_ide(argc, argv, ARGV_SIZE, ide_idx, NULL, i, mbr_blank);
94
/**
253
}
95
* Allow users to send a message without waiting for the reply,
254
args = g_strjoinv(" ", argv);
96
* in the case that they choose to discard all replies up until
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
--
97
--
320
2.13.6
98
2.29.2
321
99
322
100
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
3
Tests that manage multiple processes may wish to kill QEMU before
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
destroying the QTestState. Expose a function to do that.
5
6
The vhost-user-blk-test testcase will need this.
7
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Reviewed-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
10
Message-Id: <20210223144653.811468-4-stefanha@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
12
---
7
tests/check-block-qdict.c | 57 ++++++++++++++++++++++++-----------------------
13
tests/qtest/libqos/libqtest.h | 11 +++++++++++
8
1 file changed, 29 insertions(+), 28 deletions(-)
14
tests/qtest/libqtest.c | 7 ++++---
15
2 files changed, 15 insertions(+), 3 deletions(-)
9
16
10
diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c
17
diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h
11
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/check-block-qdict.c
19
--- a/tests/qtest/libqos/libqtest.h
13
+++ b/tests/check-block-qdict.c
20
+++ b/tests/qtest/libqos/libqtest.h
14
@@ -XXX,XX +XXX,XX @@ static void qdict_defaults_test(void)
21
@@ -XXX,XX +XXX,XX @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
15
22
*/
16
static void qdict_flatten_test(void)
23
QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd);
24
25
+/**
26
+ * qtest_kill_qemu:
27
+ * @s: #QTestState instance to operate on.
28
+ *
29
+ * Kill the QEMU process and wait for it to terminate. It is safe to call this
30
+ * function multiple times. Normally qtest_quit() is used instead because it
31
+ * also frees QTestState. Use qtest_kill_qemu() when you just want to kill QEMU
32
+ * and qtest_quit() will be called later.
33
+ */
34
+void qtest_kill_qemu(QTestState *s);
35
+
36
/**
37
* qtest_quit:
38
* @s: #QTestState instance to operate on.
39
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/tests/qtest/libqtest.c
42
+++ b/tests/qtest/libqtest.c
43
@@ -XXX,XX +XXX,XX @@ void qtest_set_expected_status(QTestState *s, int status)
44
s->expected_status = status;
45
}
46
47
-static void kill_qemu(QTestState *s)
48
+void qtest_kill_qemu(QTestState *s)
17
{
49
{
18
- QList *list1 = qlist_new();
50
pid_t pid = s->qemu_pid;
19
- QList *list2 = qlist_new();
51
int wstatus;
20
- QDict *dict1 = qdict_new();
52
@@ -XXX,XX +XXX,XX @@ static void kill_qemu(QTestState *s)
21
- QDict *dict2 = qdict_new();
53
kill(pid, SIGTERM);
22
- QDict *dict3 = qdict_new();
54
TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0));
23
+ QList *e_1 = qlist_new();
55
assert(pid == s->qemu_pid);
24
+ QList *e = qlist_new();
56
+ s->qemu_pid = -1;
25
+ QDict *e_1_2 = qdict_new();
57
}
26
+ QDict *f = qdict_new();
27
+ QDict *root = qdict_new();
28
58
29
/*
59
/*
30
* Test the flattening of
60
@@ -XXX,XX +XXX,XX @@ static void kill_qemu(QTestState *s)
31
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_test(void)
61
32
* }
62
static void kill_qemu_hook_func(void *s)
33
*/
63
{
34
64
- kill_qemu(s);
35
- qdict_put_int(dict1, "a", 0);
65
+ qtest_kill_qemu(s);
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
}
66
}
89
67
90
static void qdict_array_split_test(void)
68
static void sigabrt_handler(int signo)
69
@@ -XXX,XX +XXX,XX @@ void qtest_quit(QTestState *s)
70
/* Uninstall SIGABRT handler on last instance */
71
cleanup_sigabrt_handler();
72
73
- kill_qemu(s);
74
+ qtest_kill_qemu(s);
75
close(s->fd);
76
close(s->qmp_fd);
77
g_string_free(s->rx, true);
91
--
78
--
92
2.13.6
79
2.29.2
93
80
94
81
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
Configuration flows through the block subsystem in a rather peculiar
3
Add a function to remove previously-added abrt handler functions.
4
way. Configuration made with -drive enters it as QemuOpts.
5
Configuration made with -blockdev / blockdev-add enters it as QAPI
6
type BlockdevOptions. The block subsystem uses QDict, QemuOpts and
7
QAPI types internally. The precise flow is next to impossible to
8
explain (I tried for this commit message, but gave up after wasting
9
several hours). What I can explain is a flaw in the BlockDriver
10
interface that leads to this bug:
11
4
12
$ qemu-system-x86_64 -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234
5
Now that a symmetric pair of add/remove functions exists we can also
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
6
balance the SIGABRT handler installation. The signal handler was
7
installed each time qtest_add_abrt_handler() was called. Now it is
8
installed when the abrt handler list becomes non-empty and removed again
9
when the list becomes empty.
14
10
15
QMP blockdev-add is broken the same way.
11
The qtest_remove_abrt_handler() function will be used by
12
vhost-user-blk-test.
16
13
17
Here's what happens. The block layer passes configuration represented
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
18
as flat QDict (with dotted keys) to BlockDriver methods
15
Reviewed-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
19
.bdrv_file_open(). The QDict's members are typed according to the
16
Message-Id: <20210223144653.811468-5-stefanha@redhat.com>
20
QAPI schema.
21
22
nfs_file_open() converts it to QAPI type BlockdevOptionsNfs, with
23
qdict_crumple() and a qobject input visitor.
24
25
This visitor comes in two flavors. The plain flavor requires scalars
26
to be typed according to the QAPI schema. That's the case here. The
27
keyval flavor requires string scalars. That's not the case here.
28
nfs_file_open() uses the latter, and promptly falls apart for members
29
@user, @group, @tcp-syn-count, @readahead-size, @page-cache-size,
30
@debug.
31
32
Switching to the plain flavor would fix -blockdev, but break -drive,
33
because there the scalars arrive in nfs_file_open() as strings.
34
35
The proper fix would be to replace the QDict by QAPI type
36
BlockdevOptions in the BlockDriver interface. Sadly, that's beyond my
37
reach right now.
38
39
Next best would be to fix the block layer to always pass correctly
40
typed QDicts to the BlockDriver methods. Also beyond my reach.
41
42
What I can do is throw another hack onto the pile: have
43
nfs_file_open() convert all members to string, so use of the keyval
44
flavor actually works, by replacing qdict_crumple() by new function
45
qdict_crumple_for_keyval_qiv().
46
47
The pattern "pass result of qdict_crumple() to
48
qobject_input_visitor_new_keyval()" occurs several times more:
49
50
* qemu_rbd_open()
51
52
Same issue as nfs_file_open(), but since BlockdevOptionsRbd has only
53
string members, its only a latent bug. Fix it anyway.
54
55
* parallels_co_create_opts(), qcow_co_create_opts(),
56
qcow2_co_create_opts(), bdrv_qed_co_create_opts(),
57
sd_co_create_opts(), vhdx_co_create_opts(), vpc_co_create_opts()
58
59
These work, because they create the QDict with
60
qemu_opts_to_qdict_filtered(), which creates only string scalars.
61
The function sports a TODO comment asking for better typing; that's
62
going to be fun. Use qdict_crumple_for_keyval_qiv() to be safe.
63
64
Signed-off-by: Markus Armbruster <armbru@redhat.com>
65
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
66
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
67
---
18
---
68
include/block/qdict.h | 1 +
19
tests/qtest/libqos/libqtest.h | 18 ++++++++++++++++++
69
block/nfs.c | 2 +-
20
tests/qtest/libqtest.c | 35 +++++++++++++++++++++++++++++------
70
block/parallels.c | 2 +-
21
2 files changed, 47 insertions(+), 6 deletions(-)
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
22
81
diff --git a/include/block/qdict.h b/include/block/qdict.h
23
diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h
82
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
83
--- a/include/block/qdict.h
25
--- a/tests/qtest/libqos/libqtest.h
84
+++ b/include/block/qdict.h
26
+++ b/tests/qtest/libqos/libqtest.h
85
@@ -XXX,XX +XXX,XX @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
27
@@ -XXX,XX +XXX,XX @@ void qtest_add_data_func_full(const char *str, void *data,
86
void qdict_array_split(QDict *src, QList **dst);
28
g_free(path); \
87
int qdict_array_entries(QDict *src, const char *subqdict);
29
} while (0)
88
QObject *qdict_crumple(const QDict *src, Error **errp);
30
89
+QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp);
31
+/**
90
void qdict_flatten(QDict *qdict);
32
+ * qtest_add_abrt_handler:
91
33
+ * @fn: Handler function
92
typedef struct QDictRenames {
34
+ * @data: Argument that is passed to the handler
93
diff --git a/block/nfs.c b/block/nfs.c
35
+ *
36
+ * Add a handler function that is invoked on SIGABRT. This can be used to
37
+ * terminate processes and perform other cleanup. The handler can be removed
38
+ * with qtest_remove_abrt_handler().
39
+ */
40
void qtest_add_abrt_handler(GHookFunc fn, const void *data);
41
42
+/**
43
+ * qtest_remove_abrt_handler:
44
+ * @data: Argument previously passed to qtest_add_abrt_handler()
45
+ *
46
+ * Remove an abrt handler that was previously added with
47
+ * qtest_add_abrt_handler().
48
+ */
49
+void qtest_remove_abrt_handler(void *data);
50
+
51
/**
52
* qtest_qmp_assert_success:
53
* @qts: QTestState instance to operate on
54
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
94
index XXXXXXX..XXXXXXX 100644
55
index XXXXXXX..XXXXXXX 100644
95
--- a/block/nfs.c
56
--- a/tests/qtest/libqtest.c
96
+++ b/block/nfs.c
57
+++ b/tests/qtest/libqtest.c
97
@@ -XXX,XX +XXX,XX @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
58
@@ -XXX,XX +XXX,XX @@ static void cleanup_sigabrt_handler(void)
98
const QDictEntry *e;
59
sigaction(SIGABRT, &sigact_old, NULL);
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
}
106
diff --git a/block/parallels.c b/block/parallels.c
107
index XXXXXXX..XXXXXXX 100644
108
--- a/block/parallels.c
109
+++ b/block/parallels.c
110
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
111
qdict_put_str(qdict, "driver", "parallels");
112
qdict_put_str(qdict, "file", bs->node_name);
113
114
- qobj = qdict_crumple(qdict, errp);
115
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
116
qobject_unref(qdict);
117
qdict = qobject_to(QDict, qobj);
118
if (qdict == NULL) {
119
diff --git a/block/qcow.c b/block/qcow.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/block/qcow.c
122
+++ b/block/qcow.c
123
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
124
qdict_put_str(qdict, "driver", "qcow");
125
qdict_put_str(qdict, "file", bs->node_name);
126
127
- qobj = qdict_crumple(qdict, errp);
128
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
129
qobject_unref(qdict);
130
qdict = qobject_to(QDict, qobj);
131
if (qdict == NULL) {
132
diff --git a/block/qcow2.c b/block/qcow2.c
133
index XXXXXXX..XXXXXXX 100644
134
--- a/block/qcow2.c
135
+++ b/block/qcow2.c
136
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
137
qdict_put_str(qdict, "file", bs->node_name);
138
139
/* Now get the QAPI type BlockdevCreateOptions */
140
- qobj = qdict_crumple(qdict, errp);
141
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
142
qobject_unref(qdict);
143
qdict = qobject_to(QDict, qobj);
144
if (qdict == NULL) {
145
diff --git a/block/qed.c b/block/qed.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/block/qed.c
148
+++ b/block/qed.c
149
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
150
qdict_put_str(qdict, "driver", "qed");
151
qdict_put_str(qdict, "file", bs->node_name);
152
153
- qobj = qdict_crumple(qdict, errp);
154
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
155
qobject_unref(qdict);
156
qdict = qobject_to(QDict, qobj);
157
if (qdict == NULL) {
158
diff --git a/block/rbd.c b/block/rbd.c
159
index XXXXXXX..XXXXXXX 100644
160
--- a/block/rbd.c
161
+++ b/block/rbd.c
162
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
163
}
164
165
/* Convert the remaining options into a QAPI object */
166
- crumpled = qdict_crumple(options, errp);
167
+ crumpled = qdict_crumple_for_keyval_qiv(options, errp);
168
if (crumpled == NULL) {
169
r = -EINVAL;
170
goto out;
171
diff --git a/block/sheepdog.c b/block/sheepdog.c
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
}
177
178
/* Get the QAPI object */
179
- crumpled = qdict_crumple(qdict, errp);
180
+ crumpled = qdict_crumple_for_keyval_qiv(qdict, errp);
181
if (crumpled == NULL) {
182
ret = -EINVAL;
183
goto fail;
184
diff --git a/block/vhdx.c b/block/vhdx.c
185
index XXXXXXX..XXXXXXX 100644
186
--- a/block/vhdx.c
187
+++ b/block/vhdx.c
188
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename,
189
qdict_put_str(qdict, "driver", "vhdx");
190
qdict_put_str(qdict, "file", bs->node_name);
191
192
- qobj = qdict_crumple(qdict, errp);
193
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
194
qobject_unref(qdict);
195
qdict = qobject_to(QDict, qobj);
196
if (qdict == NULL) {
197
diff --git a/block/vpc.c b/block/vpc.c
198
index XXXXXXX..XXXXXXX 100644
199
--- a/block/vpc.c
200
+++ b/block/vpc.c
201
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
202
qdict_put_str(qdict, "driver", "vpc");
203
qdict_put_str(qdict, "file", bs->node_name);
204
205
- qobj = qdict_crumple(qdict, errp);
206
+ qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
207
qobject_unref(qdict);
208
qdict = qobject_to(QDict, qobj);
209
if (qdict == NULL) {
210
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
211
index XXXXXXX..XXXXXXX 100644
212
--- a/qobject/block-qdict.c
213
+++ b/qobject/block-qdict.c
214
@@ -XXX,XX +XXX,XX @@
215
216
#include "qemu/osdep.h"
217
#include "block/qdict.h"
218
+#include "qapi/qmp/qbool.h"
219
#include "qapi/qmp/qlist.h"
220
+#include "qapi/qmp/qnum.h"
221
+#include "qapi/qmp/qstring.h"
222
#include "qemu/cutils.h"
223
#include "qapi/error.h"
224
225
@@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp)
226
}
60
}
227
61
228
/**
62
+static bool hook_list_is_empty(GHookList *hook_list)
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
+{
63
+{
243
+ QDict *tmp = NULL;
64
+ GHook *hook = g_hook_first_valid(hook_list, TRUE);
244
+ char *buf;
245
+ const char *s;
246
+ const QDictEntry *ent;
247
+ QObject *dst;
248
+
65
+
249
+ for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) {
66
+ if (!hook) {
250
+ buf = NULL;
67
+ return false;
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
+ }
68
+ }
276
+
69
+
277
+ dst = qdict_crumple(tmp ?: src, errp);
70
+ g_hook_unref(hook_list, hook);
278
+ qobject_unref(tmp);
71
+ return true;
279
+ return dst;
280
+}
72
+}
281
+
73
+
282
+/**
74
void qtest_add_abrt_handler(GHookFunc fn, const void *data)
283
* qdict_array_entries(): Returns the number of direct array entries if the
75
{
284
* sub-QDict of src specified by the prefix in subqdict (or src itself for
76
GHook *hook;
285
* prefix == "") is valid as an array, i.e. the length of the created list if
77
78
- /* Only install SIGABRT handler once */
79
if (!abrt_hooks.is_setup) {
80
g_hook_list_init(&abrt_hooks, sizeof(GHook));
81
}
82
- setup_sigabrt_handler();
83
+
84
+ /* Only install SIGABRT handler once */
85
+ if (hook_list_is_empty(&abrt_hooks)) {
86
+ setup_sigabrt_handler();
87
+ }
88
89
hook = g_hook_alloc(&abrt_hooks);
90
hook->func = fn;
91
@@ -XXX,XX +XXX,XX @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data)
92
g_hook_prepend(&abrt_hooks, hook);
93
}
94
95
+void qtest_remove_abrt_handler(void *data)
96
+{
97
+ GHook *hook = g_hook_find_data(&abrt_hooks, TRUE, data);
98
+ g_hook_destroy_link(&abrt_hooks, hook);
99
+
100
+ /* Uninstall SIGABRT handler on last instance */
101
+ if (hook_list_is_empty(&abrt_hooks)) {
102
+ cleanup_sigabrt_handler();
103
+ }
104
+}
105
+
106
static const char *qtest_qemu_binary(void)
107
{
108
const char *qemu_bin;
109
@@ -XXX,XX +XXX,XX @@ QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd)
110
111
void qtest_quit(QTestState *s)
112
{
113
- g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s));
114
-
115
- /* Uninstall SIGABRT handler on last instance */
116
- cleanup_sigabrt_handler();
117
+ qtest_remove_abrt_handler(s);
118
119
qtest_kill_qemu(s);
120
close(s->fd);
286
--
121
--
287
2.13.6
122
2.29.2
288
123
289
124
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Coiby Xu <coiby.xu@gmail.com>
2
2
3
Pure code motion, except for two brace placements and a comment
3
This test case has the same tests as tests/virtio-blk-test.c except for
4
tweaked to appease checkpatch.
4
tests have block_resize. Since the vhost-user-blk export only serves one
5
client one time, two exports are started by qemu-storage-daemon for the
6
hotplug test.
5
7
6
Signed-off-by: Markus Armbruster <armbru@redhat.com>
8
Suggested-by: Thomas Huth <thuth@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Coiby Xu <coiby.xu@gmail.com>
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Message-Id: <20210223144653.811468-6-stefanha@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
13
---
10
qobject/block-qdict.c | 640 ++++++++++++++++++++++++++++++++++++++++++++
14
tests/qtest/libqos/vhost-user-blk.h | 48 ++
11
qobject/qdict.c | 629 --------------------------------------------
15
tests/qtest/libqos/vhost-user-blk.c | 130 +++++
12
tests/check-block-qdict.c | 655 ++++++++++++++++++++++++++++++++++++++++++++++
16
tests/qtest/vhost-user-blk-test.c | 788 ++++++++++++++++++++++++++++
13
tests/check-qdict.c | 642 ---------------------------------------------
17
MAINTAINERS | 2 +
14
MAINTAINERS | 2 +
18
tests/qtest/libqos/meson.build | 1 +
15
qobject/Makefile.objs | 1 +
19
tests/qtest/meson.build | 4 +
16
tests/Makefile.include | 4 +
20
6 files changed, 973 insertions(+)
17
7 files changed, 1302 insertions(+), 1271 deletions(-)
21
create mode 100644 tests/qtest/libqos/vhost-user-blk.h
18
create mode 100644 qobject/block-qdict.c
22
create mode 100644 tests/qtest/libqos/vhost-user-blk.c
19
create mode 100644 tests/check-block-qdict.c
23
create mode 100644 tests/qtest/vhost-user-blk-test.c
20
24
21
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
25
diff --git a/tests/qtest/libqos/vhost-user-blk.h b/tests/qtest/libqos/vhost-user-blk.h
22
new file mode 100644
26
new file mode 100644
23
index XXXXXXX..XXXXXXX
27
index XXXXXXX..XXXXXXX
24
--- /dev/null
28
--- /dev/null
25
+++ b/qobject/block-qdict.c
29
+++ b/tests/qtest/libqos/vhost-user-blk.h
26
@@ -XXX,XX +XXX,XX @@
30
@@ -XXX,XX +XXX,XX @@
27
+/*
31
+/*
28
+ * Special QDict functions used by the block layer
32
+ * libqos driver framework
29
+ *
33
+ *
30
+ * Copyright (c) 2013-2018 Red Hat, Inc.
34
+ * Based on tests/qtest/libqos/virtio-blk.c
31
+ *
35
+ *
32
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
36
+ * Copyright (c) 2020 Coiby Xu <coiby.xu@gmail.com>
33
+ * See the COPYING.LIB file in the top-level directory.
37
+ *
38
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
39
+ *
40
+ * This library is free software; you can redistribute it and/or
41
+ * modify it under the terms of the GNU Lesser General Public
42
+ * License version 2 as published by the Free Software Foundation.
43
+ *
44
+ * This library is distributed in the hope that it will be useful,
45
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
46
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47
+ * Lesser General Public License for more details.
48
+ *
49
+ * You should have received a copy of the GNU Lesser General Public
50
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
34
+ */
51
+ */
35
+
52
+
36
+#include "qemu/osdep.h"
53
+#ifndef TESTS_LIBQOS_VHOST_USER_BLK_H
37
+#include "block/qdict.h"
54
+#define TESTS_LIBQOS_VHOST_USER_BLK_H
38
+#include "qapi/qmp/qlist.h"
55
+
39
+#include "qemu/cutils.h"
56
+#include "qgraph.h"
40
+#include "qapi/error.h"
57
+#include "virtio.h"
41
+
58
+#include "virtio-pci.h"
42
+/**
59
+
43
+ * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the
60
+typedef struct QVhostUserBlk QVhostUserBlk;
44
+ * value of 'key' in 'src' is copied there (and the refcount increased
61
+typedef struct QVhostUserBlkPCI QVhostUserBlkPCI;
45
+ * accordingly).
62
+typedef struct QVhostUserBlkDevice QVhostUserBlkDevice;
46
+ */
63
+
47
+void qdict_copy_default(QDict *dst, QDict *src, const char *key)
64
+struct QVhostUserBlk {
48
+{
65
+ QVirtioDevice *vdev;
49
+ QObject *val;
66
+};
50
+
67
+
51
+ if (qdict_haskey(dst, key)) {
68
+struct QVhostUserBlkPCI {
52
+ return;
69
+ QVirtioPCIDevice pci_vdev;
53
+ }
70
+ QVhostUserBlk blk;
54
+
71
+};
55
+ val = qdict_get(src, key);
72
+
56
+ if (val) {
73
+struct QVhostUserBlkDevice {
57
+ qdict_put_obj(dst, key, qobject_ref(val));
74
+ QOSGraphObject obj;
58
+ }
75
+ QVhostUserBlk blk;
59
+}
76
+};
60
+
77
+
61
+/**
78
+#endif
62
+ * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a
79
diff --git a/tests/qtest/libqos/vhost-user-blk.c b/tests/qtest/libqos/vhost-user-blk.c
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
80
new file mode 100644
1318
index XXXXXXX..XXXXXXX
81
index XXXXXXX..XXXXXXX
1319
--- /dev/null
82
--- /dev/null
1320
+++ b/tests/check-block-qdict.c
83
+++ b/tests/qtest/libqos/vhost-user-blk.c
1321
@@ -XXX,XX +XXX,XX @@
84
@@ -XXX,XX +XXX,XX @@
1322
+/*
85
+/*
1323
+ * Unit-tests for Block layer QDict extras
86
+ * libqos driver framework
1324
+ *
87
+ *
1325
+ * Copyright (c) 2013-2018 Red Hat, Inc.
88
+ * Based on tests/qtest/libqos/virtio-blk.c
1326
+ *
89
+ *
1327
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
90
+ * Copyright (c) 2020 Coiby Xu <coiby.xu@gmail.com>
1328
+ * See the COPYING.LIB file in the top-level directory.
91
+ *
92
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
93
+ *
94
+ * This library is free software; you can redistribute it and/or
95
+ * modify it under the terms of the GNU Lesser General Public
96
+ * License version 2.1 as published by the Free Software Foundation.
97
+ *
98
+ * This library is distributed in the hope that it will be useful,
99
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
100
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
101
+ * Lesser General Public License for more details.
102
+ *
103
+ * You should have received a copy of the GNU Lesser General Public
104
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
1329
+ */
105
+ */
1330
+
106
+
1331
+#include "qemu/osdep.h"
107
+#include "qemu/osdep.h"
1332
+#include "block/qdict.h"
108
+#include "libqtest.h"
1333
+#include "qapi/qmp/qlist.h"
109
+#include "qemu/module.h"
1334
+#include "qapi/qmp/qnum.h"
110
+#include "standard-headers/linux/virtio_blk.h"
1335
+#include "qapi/error.h"
111
+#include "vhost-user-blk.h"
1336
+
112
+
1337
+static void qdict_defaults_test(void)
113
+#define PCI_SLOT 0x04
1338
+{
114
+#define PCI_FN 0x00
1339
+ QDict *dict, *copy;
115
+
1340
+
116
+/* virtio-blk-device */
1341
+ dict = qdict_new();
117
+static void *qvhost_user_blk_get_driver(QVhostUserBlk *v_blk,
1342
+ copy = qdict_new();
118
+ const char *interface)
1343
+
119
+{
1344
+ qdict_set_default_str(dict, "foo", "abc");
120
+ if (!g_strcmp0(interface, "vhost-user-blk")) {
1345
+ qdict_set_default_str(dict, "foo", "def");
121
+ return v_blk;
1346
+ g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc");
122
+ }
1347
+ qdict_set_default_str(dict, "bar", "ghi");
123
+ if (!g_strcmp0(interface, "virtio")) {
1348
+
124
+ return v_blk->vdev;
1349
+ qdict_copy_default(copy, dict, "foo");
125
+ }
1350
+ g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc");
126
+
1351
+ qdict_set_default_str(copy, "bar", "xyz");
127
+ fprintf(stderr, "%s not present in vhost-user-blk-device\n", interface);
1352
+ qdict_copy_default(copy, dict, "bar");
128
+ g_assert_not_reached();
1353
+ g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz");
129
+}
1354
+
130
+
1355
+ qobject_unref(copy);
131
+static void *qvhost_user_blk_device_get_driver(void *object,
1356
+ qobject_unref(dict);
132
+ const char *interface)
1357
+}
133
+{
1358
+
134
+ QVhostUserBlkDevice *v_blk = object;
1359
+static void qdict_flatten_test(void)
135
+ return qvhost_user_blk_get_driver(&v_blk->blk, interface);
1360
+{
136
+}
1361
+ QList *list1 = qlist_new();
137
+
1362
+ QList *list2 = qlist_new();
138
+static void *vhost_user_blk_device_create(void *virtio_dev,
1363
+ QDict *dict1 = qdict_new();
139
+ QGuestAllocator *t_alloc,
1364
+ QDict *dict2 = qdict_new();
140
+ void *addr)
1365
+ QDict *dict3 = qdict_new();
141
+{
1366
+
142
+ QVhostUserBlkDevice *vhost_user_blk = g_new0(QVhostUserBlkDevice, 1);
143
+ QVhostUserBlk *interface = &vhost_user_blk->blk;
144
+
145
+ interface->vdev = virtio_dev;
146
+
147
+ vhost_user_blk->obj.get_driver = qvhost_user_blk_device_get_driver;
148
+
149
+ return &vhost_user_blk->obj;
150
+}
151
+
152
+/* virtio-blk-pci */
153
+static void *qvhost_user_blk_pci_get_driver(void *object, const char *interface)
154
+{
155
+ QVhostUserBlkPCI *v_blk = object;
156
+ if (!g_strcmp0(interface, "pci-device")) {
157
+ return v_blk->pci_vdev.pdev;
158
+ }
159
+ return qvhost_user_blk_get_driver(&v_blk->blk, interface);
160
+}
161
+
162
+static void *vhost_user_blk_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
163
+ void *addr)
164
+{
165
+ QVhostUserBlkPCI *vhost_user_blk = g_new0(QVhostUserBlkPCI, 1);
166
+ QVhostUserBlk *interface = &vhost_user_blk->blk;
167
+ QOSGraphObject *obj = &vhost_user_blk->pci_vdev.obj;
168
+
169
+ virtio_pci_init(&vhost_user_blk->pci_vdev, pci_bus, addr);
170
+ interface->vdev = &vhost_user_blk->pci_vdev.vdev;
171
+
172
+ g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_BLOCK);
173
+
174
+ obj->get_driver = qvhost_user_blk_pci_get_driver;
175
+
176
+ return obj;
177
+}
178
+
179
+static void vhost_user_blk_register_nodes(void)
180
+{
1367
+ /*
181
+ /*
1368
+ * Test the flattening of
182
+ * FIXME: every test using these two nodes needs to setup a
1369
+ *
183
+ * -drive,id=drive0 otherwise QEMU is not going to start.
1370
+ * {
184
+ * Therefore, we do not include "produces" edge for virtio
1371
+ * "e": [
185
+ * and pci-device yet.
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
+ */
186
+ */
1402
+
187
+
1403
+ qdict_put_int(dict1, "a", 0);
188
+ char *arg = g_strdup_printf("id=drv0,chardev=char1,addr=%x.%x",
1404
+ qdict_put_int(dict1, "b", 1);
189
+ PCI_SLOT, PCI_FN);
1405
+
190
+
1406
+ qlist_append_int(list1, 23);
191
+ QPCIAddress addr = {
1407
+ qlist_append_int(list1, 66);
192
+ .devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN),
1408
+ qlist_append(list1, dict1);
193
+ };
1409
+ qlist_append_int(list2, 42);
194
+
1410
+ qlist_append(list2, list1);
195
+ QOSGraphEdgeOptions opts = { };
1411
+
196
+
1412
+ qdict_put_int(dict2, "c", 2);
197
+ /* virtio-blk-device */
1413
+ qdict_put_int(dict2, "d", 3);
198
+ /** opts.extra_device_opts = "drive=drive0"; */
1414
+ qdict_put(dict3, "e", list2);
199
+ qos_node_create_driver("vhost-user-blk-device",
1415
+ qdict_put(dict3, "f", dict2);
200
+ vhost_user_blk_device_create);
1416
+ qdict_put_int(dict3, "g", 4);
201
+ qos_node_consumes("vhost-user-blk-device", "virtio-bus", &opts);
1417
+
202
+ qos_node_produces("vhost-user-blk-device", "vhost-user-blk");
1418
+ qdict_flatten(dict3);
203
+
1419
+
204
+ /* virtio-blk-pci */
1420
+ g_assert(qdict_get_int(dict3, "e.0") == 42);
205
+ opts.extra_device_opts = arg;
1421
+ g_assert(qdict_get_int(dict3, "e.1.0") == 23);
206
+ add_qpci_address(&opts, &addr);
1422
+ g_assert(qdict_get_int(dict3, "e.1.1") == 66);
207
+ qos_node_create_driver("vhost-user-blk-pci", vhost_user_blk_pci_create);
1423
+ g_assert(qdict_get_int(dict3, "e.1.2.a") == 0);
208
+ qos_node_consumes("vhost-user-blk-pci", "pci-bus", &opts);
1424
+ g_assert(qdict_get_int(dict3, "e.1.2.b") == 1);
209
+ qos_node_produces("vhost-user-blk-pci", "vhost-user-blk");
1425
+ g_assert(qdict_get_int(dict3, "f.c") == 2);
210
+
1426
+ g_assert(qdict_get_int(dict3, "f.d") == 3);
211
+ g_free(arg);
1427
+ g_assert(qdict_get_int(dict3, "g") == 4);
212
+}
1428
+
213
+
1429
+ g_assert(qdict_size(dict3) == 8);
214
+libqos_init(vhost_user_blk_register_nodes);
1430
+
215
diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c
1431
+ qobject_unref(dict3);
216
new file mode 100644
1432
+}
217
index XXXXXXX..XXXXXXX
1433
+
218
--- /dev/null
1434
+static void qdict_array_split_test(void)
219
+++ b/tests/qtest/vhost-user-blk-test.c
1435
+{
220
@@ -XXX,XX +XXX,XX @@
1436
+ QDict *test_dict = qdict_new();
221
+/*
1437
+ QDict *dict1, *dict2;
222
+ * QTest testcase for Vhost-user Block Device
1438
+ QNum *int1;
223
+ *
1439
+ QList *test_list;
224
+ * Based on tests/qtest//virtio-blk-test.c
225
+
226
+ * Copyright (c) 2014 SUSE LINUX Products GmbH
227
+ * Copyright (c) 2014 Marc Marí
228
+ * Copyright (c) 2020 Coiby Xu
229
+ *
230
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
231
+ * See the COPYING file in the top-level directory.
232
+ */
233
+
234
+#include "qemu/osdep.h"
235
+#include "libqtest-single.h"
236
+#include "qemu/bswap.h"
237
+#include "qemu/module.h"
238
+#include "standard-headers/linux/virtio_blk.h"
239
+#include "standard-headers/linux/virtio_pci.h"
240
+#include "libqos/qgraph.h"
241
+#include "libqos/vhost-user-blk.h"
242
+#include "libqos/libqos-pc.h"
243
+
244
+#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
245
+#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
246
+#define PCI_SLOT_HP 0x06
247
+
248
+typedef struct {
249
+ pid_t pid;
250
+} QemuStorageDaemonState;
251
+
252
+typedef struct QVirtioBlkReq {
253
+ uint32_t type;
254
+ uint32_t ioprio;
255
+ uint64_t sector;
256
+ char *data;
257
+ uint8_t status;
258
+} QVirtioBlkReq;
259
+
260
+#ifdef HOST_WORDS_BIGENDIAN
261
+static const bool host_is_big_endian = true;
262
+#else
263
+static const bool host_is_big_endian; /* false */
264
+#endif
265
+
266
+static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
267
+{
268
+ if (qvirtio_is_big_endian(d) != host_is_big_endian) {
269
+ req->type = bswap32(req->type);
270
+ req->ioprio = bswap32(req->ioprio);
271
+ req->sector = bswap64(req->sector);
272
+ }
273
+}
274
+
275
+static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
276
+ struct virtio_blk_discard_write_zeroes *dwz_hdr)
277
+{
278
+ if (qvirtio_is_big_endian(d) != host_is_big_endian) {
279
+ dwz_hdr->sector = bswap64(dwz_hdr->sector);
280
+ dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors);
281
+ dwz_hdr->flags = bswap32(dwz_hdr->flags);
282
+ }
283
+}
284
+
285
+static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
286
+ QVirtioBlkReq *req, uint64_t data_size)
287
+{
288
+ uint64_t addr;
289
+ uint8_t status = 0xFF;
290
+ QTestState *qts = global_qtest;
291
+
292
+ switch (req->type) {
293
+ case VIRTIO_BLK_T_IN:
294
+ case VIRTIO_BLK_T_OUT:
295
+ g_assert_cmpuint(data_size % 512, ==, 0);
296
+ break;
297
+ case VIRTIO_BLK_T_DISCARD:
298
+ case VIRTIO_BLK_T_WRITE_ZEROES:
299
+ g_assert_cmpuint(data_size %
300
+ sizeof(struct virtio_blk_discard_write_zeroes), ==, 0);
301
+ break;
302
+ default:
303
+ g_assert_cmpuint(data_size, ==, 0);
304
+ }
305
+
306
+ addr = guest_alloc(alloc, sizeof(*req) + data_size);
307
+
308
+ virtio_blk_fix_request(d, req);
309
+
310
+ qtest_memwrite(qts, addr, req, 16);
311
+ qtest_memwrite(qts, addr + 16, req->data, data_size);
312
+ qtest_memwrite(qts, addr + 16 + data_size, &status, sizeof(status));
313
+
314
+ return addr;
315
+}
316
+
317
+/* Returns the request virtqueue so the caller can perform further tests */
318
+static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
319
+{
320
+ QVirtioBlkReq req;
321
+ uint64_t req_addr;
322
+ uint64_t capacity;
323
+ uint64_t features;
324
+ uint32_t free_head;
325
+ uint8_t status;
326
+ char *data;
327
+ QTestState *qts = global_qtest;
328
+ QVirtQueue *vq;
329
+
330
+ features = qvirtio_get_features(dev);
331
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
332
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
333
+ (1u << VIRTIO_RING_F_EVENT_IDX) |
334
+ (1u << VIRTIO_BLK_F_SCSI));
335
+ qvirtio_set_features(dev, features);
336
+
337
+ capacity = qvirtio_config_readq(dev, 0);
338
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
339
+
340
+ vq = qvirtqueue_setup(dev, alloc, 0);
341
+
342
+ qvirtio_set_driver_ok(dev);
343
+
344
+ /* Write and read with 3 descriptor layout */
345
+ /* Write request */
346
+ req.type = VIRTIO_BLK_T_OUT;
347
+ req.ioprio = 1;
348
+ req.sector = 0;
349
+ req.data = g_malloc0(512);
350
+ strcpy(req.data, "TEST");
351
+
352
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
353
+
354
+ g_free(req.data);
355
+
356
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
357
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
358
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
359
+
360
+ qvirtqueue_kick(qts, dev, vq, free_head);
361
+
362
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
363
+ QVIRTIO_BLK_TIMEOUT_US);
364
+ status = readb(req_addr + 528);
365
+ g_assert_cmpint(status, ==, 0);
366
+
367
+ guest_free(alloc, req_addr);
368
+
369
+ /* Read request */
370
+ req.type = VIRTIO_BLK_T_IN;
371
+ req.ioprio = 1;
372
+ req.sector = 0;
373
+ req.data = g_malloc0(512);
374
+
375
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
376
+
377
+ g_free(req.data);
378
+
379
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
380
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
381
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
382
+
383
+ qvirtqueue_kick(qts, dev, vq, free_head);
384
+
385
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
386
+ QVIRTIO_BLK_TIMEOUT_US);
387
+ status = readb(req_addr + 528);
388
+ g_assert_cmpint(status, ==, 0);
389
+
390
+ data = g_malloc0(512);
391
+ qtest_memread(qts, req_addr + 16, data, 512);
392
+ g_assert_cmpstr(data, ==, "TEST");
393
+ g_free(data);
394
+
395
+ guest_free(alloc, req_addr);
396
+
397
+ if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
398
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
399
+ void *expected;
400
+
401
+ /*
402
+ * WRITE_ZEROES request on the same sector of previous test where
403
+ * we wrote "TEST".
404
+ */
405
+ req.type = VIRTIO_BLK_T_WRITE_ZEROES;
406
+ req.data = (char *) &dwz_hdr;
407
+ dwz_hdr.sector = 0;
408
+ dwz_hdr.num_sectors = 1;
409
+ dwz_hdr.flags = 0;
410
+
411
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
412
+
413
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
414
+
415
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
416
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
417
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
418
+ false);
419
+
420
+ qvirtqueue_kick(qts, dev, vq, free_head);
421
+
422
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
423
+ QVIRTIO_BLK_TIMEOUT_US);
424
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
425
+ g_assert_cmpint(status, ==, 0);
426
+
427
+ guest_free(alloc, req_addr);
428
+
429
+ /* Read request to check if the sector contains all zeroes */
430
+ req.type = VIRTIO_BLK_T_IN;
431
+ req.ioprio = 1;
432
+ req.sector = 0;
433
+ req.data = g_malloc0(512);
434
+
435
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
436
+
437
+ g_free(req.data);
438
+
439
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
440
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
441
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
442
+
443
+ qvirtqueue_kick(qts, dev, vq, free_head);
444
+
445
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
446
+ QVIRTIO_BLK_TIMEOUT_US);
447
+ status = readb(req_addr + 528);
448
+ g_assert_cmpint(status, ==, 0);
449
+
450
+ data = g_malloc(512);
451
+ expected = g_malloc0(512);
452
+ qtest_memread(qts, req_addr + 16, data, 512);
453
+ g_assert_cmpmem(data, 512, expected, 512);
454
+ g_free(expected);
455
+ g_free(data);
456
+
457
+ guest_free(alloc, req_addr);
458
+ }
459
+
460
+ if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
461
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
462
+
463
+ req.type = VIRTIO_BLK_T_DISCARD;
464
+ req.data = (char *) &dwz_hdr;
465
+ dwz_hdr.sector = 0;
466
+ dwz_hdr.num_sectors = 1;
467
+ dwz_hdr.flags = 0;
468
+
469
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
470
+
471
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
472
+
473
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
474
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
475
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr),
476
+ 1, true, false);
477
+
478
+ qvirtqueue_kick(qts, dev, vq, free_head);
479
+
480
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
481
+ QVIRTIO_BLK_TIMEOUT_US);
482
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
483
+ g_assert_cmpint(status, ==, 0);
484
+
485
+ guest_free(alloc, req_addr);
486
+ }
487
+
488
+ if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
489
+ /* Write and read with 2 descriptor layout */
490
+ /* Write request */
491
+ req.type = VIRTIO_BLK_T_OUT;
492
+ req.ioprio = 1;
493
+ req.sector = 1;
494
+ req.data = g_malloc0(512);
495
+ strcpy(req.data, "TEST");
496
+
497
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
498
+
499
+ g_free(req.data);
500
+
501
+ free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true);
502
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
503
+ qvirtqueue_kick(qts, dev, vq, free_head);
504
+
505
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
506
+ QVIRTIO_BLK_TIMEOUT_US);
507
+ status = readb(req_addr + 528);
508
+ g_assert_cmpint(status, ==, 0);
509
+
510
+ guest_free(alloc, req_addr);
511
+
512
+ /* Read request */
513
+ req.type = VIRTIO_BLK_T_IN;
514
+ req.ioprio = 1;
515
+ req.sector = 1;
516
+ req.data = g_malloc0(512);
517
+
518
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
519
+
520
+ g_free(req.data);
521
+
522
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
523
+ qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false);
524
+
525
+ qvirtqueue_kick(qts, dev, vq, free_head);
526
+
527
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
528
+ QVIRTIO_BLK_TIMEOUT_US);
529
+ status = readb(req_addr + 528);
530
+ g_assert_cmpint(status, ==, 0);
531
+
532
+ data = g_malloc0(512);
533
+ qtest_memread(qts, req_addr + 16, data, 512);
534
+ g_assert_cmpstr(data, ==, "TEST");
535
+ g_free(data);
536
+
537
+ guest_free(alloc, req_addr);
538
+ }
539
+
540
+ return vq;
541
+}
542
+
543
+static void basic(void *obj, void *data, QGuestAllocator *t_alloc)
544
+{
545
+ QVhostUserBlk *blk_if = obj;
546
+ QVirtQueue *vq;
547
+
548
+ vq = test_basic(blk_if->vdev, t_alloc);
549
+ qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc);
550
+
551
+}
552
+
553
+static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
554
+{
555
+ QVirtQueue *vq;
556
+ QVhostUserBlk *blk_if = obj;
557
+ QVirtioDevice *dev = blk_if->vdev;
558
+ QVirtioBlkReq req;
559
+ QVRingIndirectDesc *indirect;
560
+ uint64_t req_addr;
561
+ uint64_t capacity;
562
+ uint64_t features;
563
+ uint32_t free_head;
564
+ uint8_t status;
565
+ char *data;
566
+ QTestState *qts = global_qtest;
567
+
568
+ features = qvirtio_get_features(dev);
569
+ g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
570
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
571
+ (1u << VIRTIO_RING_F_EVENT_IDX) |
572
+ (1u << VIRTIO_BLK_F_SCSI));
573
+ qvirtio_set_features(dev, features);
574
+
575
+ capacity = qvirtio_config_readq(dev, 0);
576
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
577
+
578
+ vq = qvirtqueue_setup(dev, t_alloc, 0);
579
+ qvirtio_set_driver_ok(dev);
580
+
581
+ /* Write request */
582
+ req.type = VIRTIO_BLK_T_OUT;
583
+ req.ioprio = 1;
584
+ req.sector = 0;
585
+ req.data = g_malloc0(512);
586
+ strcpy(req.data, "TEST");
587
+
588
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
589
+
590
+ g_free(req.data);
591
+
592
+ indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
593
+ qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false);
594
+ qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true);
595
+ free_head = qvirtqueue_add_indirect(qts, vq, indirect);
596
+ qvirtqueue_kick(qts, dev, vq, free_head);
597
+
598
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
599
+ QVIRTIO_BLK_TIMEOUT_US);
600
+ status = readb(req_addr + 528);
601
+ g_assert_cmpint(status, ==, 0);
602
+
603
+ g_free(indirect);
604
+ guest_free(t_alloc, req_addr);
605
+
606
+ /* Read request */
607
+ req.type = VIRTIO_BLK_T_IN;
608
+ req.ioprio = 1;
609
+ req.sector = 0;
610
+ req.data = g_malloc0(512);
611
+ strcpy(req.data, "TEST");
612
+
613
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
614
+
615
+ g_free(req.data);
616
+
617
+ indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
618
+ qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false);
619
+ qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true);
620
+ free_head = qvirtqueue_add_indirect(qts, vq, indirect);
621
+ qvirtqueue_kick(qts, dev, vq, free_head);
622
+
623
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
624
+ QVIRTIO_BLK_TIMEOUT_US);
625
+ status = readb(req_addr + 528);
626
+ g_assert_cmpint(status, ==, 0);
627
+
628
+ data = g_malloc0(512);
629
+ qtest_memread(qts, req_addr + 16, data, 512);
630
+ g_assert_cmpstr(data, ==, "TEST");
631
+ g_free(data);
632
+
633
+ g_free(indirect);
634
+ guest_free(t_alloc, req_addr);
635
+ qvirtqueue_cleanup(dev->bus, vq, t_alloc);
636
+}
637
+
638
+static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc)
639
+{
640
+ QVirtQueue *vq;
641
+ QVhostUserBlkPCI *blk = obj;
642
+ QVirtioPCIDevice *pdev = &blk->pci_vdev;
643
+ QVirtioDevice *dev = &pdev->vdev;
644
+ QVirtioBlkReq req;
645
+ uint64_t req_addr;
646
+ uint64_t capacity;
647
+ uint64_t features;
648
+ uint32_t free_head;
649
+ uint32_t write_head;
650
+ uint32_t desc_idx;
651
+ uint8_t status;
652
+ char *data;
653
+ QOSGraphObject *blk_object = obj;
654
+ QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
655
+ QTestState *qts = global_qtest;
656
+
657
+ if (qpci_check_buggy_msi(pci_dev)) {
658
+ return;
659
+ }
660
+
661
+ qpci_msix_enable(pdev->pdev);
662
+ qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
663
+
664
+ features = qvirtio_get_features(dev);
665
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
666
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
667
+ (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
668
+ (1u << VIRTIO_BLK_F_SCSI));
669
+ qvirtio_set_features(dev, features);
670
+
671
+ capacity = qvirtio_config_readq(dev, 0);
672
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
673
+
674
+ vq = qvirtqueue_setup(dev, t_alloc, 0);
675
+ qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
676
+
677
+ qvirtio_set_driver_ok(dev);
678
+
679
+ /* Write request */
680
+ req.type = VIRTIO_BLK_T_OUT;
681
+ req.ioprio = 1;
682
+ req.sector = 0;
683
+ req.data = g_malloc0(512);
684
+ strcpy(req.data, "TEST");
685
+
686
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
687
+
688
+ g_free(req.data);
689
+
690
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
691
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
692
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
693
+ qvirtqueue_kick(qts, dev, vq, free_head);
694
+
695
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
696
+ QVIRTIO_BLK_TIMEOUT_US);
697
+
698
+ /* Write request */
699
+ req.type = VIRTIO_BLK_T_OUT;
700
+ req.ioprio = 1;
701
+ req.sector = 1;
702
+ req.data = g_malloc0(512);
703
+ strcpy(req.data, "TEST");
704
+
705
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
706
+
707
+ g_free(req.data);
708
+
709
+ /* Notify after processing the third request */
710
+ qvirtqueue_set_used_event(qts, vq, 2);
711
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
712
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
713
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
714
+ qvirtqueue_kick(qts, dev, vq, free_head);
715
+ write_head = free_head;
716
+
717
+ /* No notification expected */
718
+ status = qvirtio_wait_status_byte_no_isr(qts, dev,
719
+ vq, req_addr + 528,
720
+ QVIRTIO_BLK_TIMEOUT_US);
721
+ g_assert_cmpint(status, ==, 0);
722
+
723
+ guest_free(t_alloc, req_addr);
724
+
725
+ /* Read request */
726
+ req.type = VIRTIO_BLK_T_IN;
727
+ req.ioprio = 1;
728
+ req.sector = 1;
729
+ req.data = g_malloc0(512);
730
+
731
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
732
+
733
+ g_free(req.data);
734
+
735
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
736
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
737
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
738
+
739
+ qvirtqueue_kick(qts, dev, vq, free_head);
740
+
741
+ /* We get just one notification for both requests */
742
+ qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL,
743
+ QVIRTIO_BLK_TIMEOUT_US);
744
+ g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL));
745
+ g_assert_cmpint(desc_idx, ==, free_head);
746
+
747
+ status = readb(req_addr + 528);
748
+ g_assert_cmpint(status, ==, 0);
749
+
750
+ data = g_malloc0(512);
751
+ qtest_memread(qts, req_addr + 16, data, 512);
752
+ g_assert_cmpstr(data, ==, "TEST");
753
+ g_free(data);
754
+
755
+ guest_free(t_alloc, req_addr);
756
+
757
+ /* End test */
758
+ qpci_msix_disable(pdev->pdev);
759
+
760
+ qvirtqueue_cleanup(dev->bus, vq, t_alloc);
761
+}
762
+
763
+static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
764
+{
765
+ QVirtioPCIDevice *dev1 = obj;
766
+ QVirtioPCIDevice *dev;
767
+ QTestState *qts = dev1->pdev->bus->qts;
768
+
769
+ /* plug secondary disk */
770
+ qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1",
771
+ "{'addr': %s, 'chardev': 'char2'}",
772
+ stringify(PCI_SLOT_HP) ".0");
773
+
774
+ dev = virtio_pci_new(dev1->pdev->bus,
775
+ &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0)
776
+ });
777
+ g_assert_nonnull(dev);
778
+ g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
779
+ qvirtio_pci_device_disable(dev);
780
+ qos_object_destroy((QOSGraphObject *)dev);
781
+
782
+ /* unplug secondary disk */
783
+ qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
784
+}
785
+
786
+/*
787
+ * Check that setting the vring addr on a non-existent virtqueue does
788
+ * not crash.
789
+ */
790
+static void test_nonexistent_virtqueue(void *obj, void *data,
791
+ QGuestAllocator *t_alloc)
792
+{
793
+ QVhostUserBlkPCI *blk = obj;
794
+ QVirtioPCIDevice *pdev = &blk->pci_vdev;
795
+ QPCIBar bar0;
796
+ QPCIDevice *dev;
797
+
798
+ dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0));
799
+ g_assert(dev != NULL);
800
+ qpci_device_enable(dev);
801
+
802
+ bar0 = qpci_iomap(dev, 0, NULL);
803
+
804
+ qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2);
805
+ qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1);
806
+
807
+ g_free(dev);
808
+}
809
+
810
+static const char *qtest_qemu_storage_daemon_binary(void)
811
+{
812
+ const char *qemu_storage_daemon_bin;
813
+
814
+ qemu_storage_daemon_bin = getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY");
815
+ if (!qemu_storage_daemon_bin) {
816
+ fprintf(stderr, "Environment variable "
817
+ "QTEST_QEMU_STORAGE_DAEMON_BINARY required\n");
818
+ exit(0);
819
+ }
820
+
821
+ return qemu_storage_daemon_bin;
822
+}
823
+
824
+/* g_test_queue_destroy() cleanup function for files */
825
+static void destroy_file(void *path)
826
+{
827
+ unlink(path);
828
+ g_free(path);
829
+ qos_invalidate_command_line();
830
+}
831
+
832
+static char *drive_create(void)
833
+{
834
+ int fd, ret;
835
+ /** vhost-user-blk won't recognize drive located in /tmp */
836
+ char *t_path = g_strdup("qtest.XXXXXX");
837
+
838
+ /** Create a temporary raw image */
839
+ fd = mkstemp(t_path);
840
+ g_assert_cmpint(fd, >=, 0);
841
+ ret = ftruncate(fd, TEST_IMAGE_SIZE);
842
+ g_assert_cmpint(ret, ==, 0);
843
+ close(fd);
844
+
845
+ g_test_queue_destroy(destroy_file, t_path);
846
+ return t_path;
847
+}
848
+
849
+static char *create_listen_socket(int *fd)
850
+{
851
+ int tmp_fd;
852
+ char *path;
853
+
854
+ /* No race because our pid makes the path unique */
855
+ path = g_strdup_printf("/tmp/qtest-%d-sock.XXXXXX", getpid());
856
+ tmp_fd = mkstemp(path);
857
+ g_assert_cmpint(tmp_fd, >=, 0);
858
+ close(tmp_fd);
859
+ unlink(path);
860
+
861
+ *fd = qtest_socket_server(path);
862
+ g_test_queue_destroy(destroy_file, path);
863
+ return path;
864
+}
865
+
866
+/*
867
+ * g_test_queue_destroy() and qtest_add_abrt_handler() cleanup function for
868
+ * qemu-storage-daemon.
869
+ */
870
+static void quit_storage_daemon(void *data)
871
+{
872
+ QemuStorageDaemonState *qsd = data;
873
+ int wstatus;
874
+ pid_t pid;
1440
+
875
+
1441
+ /*
876
+ /*
1442
+ * Test the split of
877
+ * If we were invoked as a g_test_queue_destroy() cleanup function we need
1443
+ *
878
+ * to remove the abrt handler to avoid being called again if the code below
1444
+ * {
879
+ * aborts. Also, we must not leave the abrt handler installed after
1445
+ * "1.x": 0,
880
+ * cleanup.
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
+ */
881
+ */
1477
+
882
+ qtest_remove_abrt_handler(data);
1478
+ qdict_put_int(test_dict, "1.x", 0);
883
+
1479
+ qdict_put_int(test_dict, "4.y", 1);
884
+ /* Before quitting storage-daemon, quit qemu to avoid dubious messages */
1480
+ qdict_put_int(test_dict, "0.a", 42);
885
+ qtest_kill_qemu(global_qtest);
1481
+ qdict_put_int(test_dict, "o.o", 7);
886
+
1482
+ qdict_put_int(test_dict, "0.b", 23);
887
+ kill(qsd->pid, SIGTERM);
1483
+ qdict_put_int(test_dict, "2", 66);
888
+ pid = waitpid(qsd->pid, &wstatus, 0);
1484
+
889
+ g_assert_cmpint(pid, ==, qsd->pid);
1485
+ qdict_array_split(test_dict, &test_list);
890
+ if (!WIFEXITED(wstatus)) {
1486
+
891
+ fprintf(stderr, "%s: expected qemu-storage-daemon to exit\n",
1487
+ dict1 = qobject_to(QDict, qlist_pop(test_list));
892
+ __func__);
1488
+ dict2 = qobject_to(QDict, qlist_pop(test_list));
893
+ abort();
1489
+ int1 = qobject_to(QNum, qlist_pop(test_list));
894
+ }
1490
+
895
+ if (WEXITSTATUS(wstatus) != 0) {
1491
+ g_assert(dict1);
896
+ fprintf(stderr, "%s: expected qemu-storage-daemon to exit "
1492
+ g_assert(dict2);
897
+ "successfully, got %d\n",
1493
+ g_assert(int1);
898
+ __func__, WEXITSTATUS(wstatus));
1494
+ g_assert(qlist_empty(test_list));
899
+ abort();
1495
+
900
+ }
1496
+ qobject_unref(test_list);
901
+
1497
+
902
+ g_free(data);
1498
+ g_assert(qdict_get_int(dict1, "a") == 42);
903
+}
1499
+ g_assert(qdict_get_int(dict1, "b") == 23);
904
+
1500
+
905
+static void start_vhost_user_blk(GString *cmd_line, int vus_instances)
1501
+ g_assert(qdict_size(dict1) == 2);
906
+{
1502
+
907
+ const char *vhost_user_blk_bin = qtest_qemu_storage_daemon_binary();
1503
+ qobject_unref(dict1);
908
+ int i;
1504
+
909
+ gchar *img_path;
1505
+ g_assert(qdict_get_int(dict2, "x") == 0);
910
+ GString *storage_daemon_command = g_string_new(NULL);
1506
+
911
+ QemuStorageDaemonState *qsd;
1507
+ g_assert(qdict_size(dict2) == 1);
912
+
1508
+
913
+ g_string_append_printf(storage_daemon_command,
1509
+ qobject_unref(dict2);
914
+ "exec %s ",
1510
+
915
+ vhost_user_blk_bin);
1511
+ g_assert_cmpint(qnum_get_int(int1), ==, 66);
916
+
1512
+
917
+ g_string_append_printf(cmd_line,
1513
+ qobject_unref(int1);
918
+ " -object memory-backend-memfd,id=mem,size=256M,share=on "
1514
+
919
+ " -M memory-backend=mem -m 256M ");
1515
+ g_assert(qdict_get_int(test_dict, "4.y") == 1);
920
+
1516
+ g_assert(qdict_get_int(test_dict, "o.o") == 7);
921
+ for (i = 0; i < vus_instances; i++) {
1517
+
922
+ int fd;
1518
+ g_assert(qdict_size(test_dict) == 2);
923
+ char *sock_path = create_listen_socket(&fd);
1519
+
924
+
1520
+ qobject_unref(test_dict);
925
+ /* create image file */
926
+ img_path = drive_create();
927
+ g_string_append_printf(storage_daemon_command,
928
+ "--blockdev driver=file,node-name=disk%d,filename=%s "
929
+ "--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s,"
930
+ "node-name=disk%i,writable=on ",
931
+ i, img_path, i, sock_path, i);
932
+
933
+ g_string_append_printf(cmd_line, "-chardev socket,id=char%d,path=%s ",
934
+ i + 1, sock_path);
935
+ }
936
+
937
+ g_test_message("starting vhost-user backend: %s",
938
+ storage_daemon_command->str);
939
+ pid_t pid = fork();
940
+ if (pid == 0) {
941
+ /*
942
+ * Close standard file descriptors so tap-driver.pl pipe detects when
943
+ * our parent terminates.
944
+ */
945
+ close(0);
946
+ close(1);
947
+ open("/dev/null", O_RDONLY);
948
+ open("/dev/null", O_WRONLY);
949
+
950
+ execlp("/bin/sh", "sh", "-c", storage_daemon_command->str, NULL);
951
+ exit(1);
952
+ }
953
+ g_string_free(storage_daemon_command, true);
954
+
955
+ qsd = g_new(QemuStorageDaemonState, 1);
956
+ qsd->pid = pid;
957
+
958
+ /* Make sure qemu-storage-daemon is stopped */
959
+ qtest_add_abrt_handler(quit_storage_daemon, qsd);
960
+ g_test_queue_destroy(quit_storage_daemon, qsd);
961
+}
962
+
963
+static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg)
964
+{
965
+ start_vhost_user_blk(cmd_line, 1);
966
+ return arg;
967
+}
968
+
969
+/*
970
+ * Setup for hotplug.
971
+ *
972
+ * Since vhost-user server only serves one vhost-user client one time,
973
+ * another exprot
974
+ *
975
+ */
976
+static void *vhost_user_blk_hotplug_test_setup(GString *cmd_line, void *arg)
977
+{
978
+ /* "-chardev socket,id=char2" is used for pci_hotplug*/
979
+ start_vhost_user_blk(cmd_line, 2);
980
+ return arg;
981
+}
982
+
983
+static void register_vhost_user_blk_test(void)
984
+{
985
+ QOSGraphTestOptions opts = {
986
+ .before = vhost_user_blk_test_setup,
987
+ };
1521
+
988
+
1522
+ /*
989
+ /*
1523
+ * Test the split of
990
+ * tests for vhost-user-blk and vhost-user-blk-pci
1524
+ *
991
+ * The tests are borrowed from tests/virtio-blk-test.c. But some tests
1525
+ * {
992
+ * regarding block_resize don't work for vhost-user-blk.
1526
+ * "0": 42,
993
+ * vhost-user-blk device doesn't have -drive, so tests containing
1527
+ * "1": 23,
994
+ * block_resize are also abandoned,
1528
+ * "1.x": 84
995
+ * - config
1529
+ * }
996
+ * - resize
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
+ */
997
+ */
1547
+
998
+ qos_add_test("basic", "vhost-user-blk", basic, &opts);
1548
+ test_dict = qdict_new();
999
+ qos_add_test("indirect", "vhost-user-blk", indirect, &opts);
1549
+
1000
+ qos_add_test("idx", "vhost-user-blk-pci", idx, &opts);
1550
+ qdict_put_int(test_dict, "0", 42);
1001
+ qos_add_test("nxvirtq", "vhost-user-blk-pci",
1551
+ qdict_put_int(test_dict, "1", 23);
1002
+ test_nonexistent_virtqueue, &opts);
1552
+ qdict_put_int(test_dict, "1.x", 84);
1003
+
1553
+
1004
+ opts.before = vhost_user_blk_hotplug_test_setup;
1554
+ qdict_array_split(test_dict, &test_list);
1005
+ qos_add_test("hotplug", "vhost-user-blk-pci", pci_hotplug, &opts);
1555
+
1006
+}
1556
+ int1 = qobject_to(QNum, qlist_pop(test_list));
1007
+
1557
+
1008
+libqos_init(register_vhost_user_blk_test);
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
1009
diff --git a/MAINTAINERS b/MAINTAINERS
2662
index XXXXXXX..XXXXXXX 100644
1010
index XXXXXXX..XXXXXXX 100644
2663
--- a/MAINTAINERS
1011
--- a/MAINTAINERS
2664
+++ b/MAINTAINERS
1012
+++ b/MAINTAINERS
2665
@@ -XXX,XX +XXX,XX @@ F: qemu-img*
1013
@@ -XXX,XX +XXX,XX @@ F: block/export/vhost-user-blk-server.c
2666
F: qemu-io*
1014
F: block/export/vhost-user-blk-server.h
2667
F: tests/qemu-iotests/
1015
F: include/qemu/vhost-user-server.h
2668
F: util/qemu-progress.c
1016
F: tests/qtest/libqos/vhost-user-blk.c
2669
+F: qobject/block-qdict.c
1017
+F: tests/qtest/libqos/vhost-user-blk.h
2670
+F: test/check-block-qdict.c
1018
+F: tests/qtest/vhost-user-blk-test.c
2671
T: git git://repo.or.cz/qemu/kevin.git block
1019
F: util/vhost-user-server.c
2672
1020
2673
Block I/O path
1021
FUSE block device exports
2674
diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
1022
diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build
2675
index XXXXXXX..XXXXXXX 100644
1023
index XXXXXXX..XXXXXXX 100644
2676
--- a/qobject/Makefile.objs
1024
--- a/tests/qtest/libqos/meson.build
2677
+++ b/qobject/Makefile.objs
1025
+++ b/tests/qtest/libqos/meson.build
2678
@@ -XXX,XX +XXX,XX @@
1026
@@ -XXX,XX +XXX,XX @@ libqos_srcs = files('../libqtest.c',
2679
util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o
1027
'virtio-9p.c',
2680
util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
1028
'virtio-balloon.c',
2681
+util-obj-y += block-qdict.o
1029
'virtio-blk.c',
2682
diff --git a/tests/Makefile.include b/tests/Makefile.include
1030
+ 'vhost-user-blk.c',
1031
'virtio-mmio.c',
1032
'virtio-net.c',
1033
'virtio-pci.c',
1034
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
2683
index XXXXXXX..XXXXXXX 100644
1035
index XXXXXXX..XXXXXXX 100644
2684
--- a/tests/Makefile.include
1036
--- a/tests/qtest/meson.build
2685
+++ b/tests/Makefile.include
1037
+++ b/tests/qtest/meson.build
2686
@@ -XXX,XX +XXX,XX @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \
1038
@@ -XXX,XX +XXX,XX @@ if have_virtfs
2687
1039
qos_test_ss.add(files('virtio-9p-test.c'))
2688
check-unit-y = tests/check-qdict$(EXESUF)
1040
endif
2689
gcov-files-check-qdict-y = qobject/qdict.c
1041
qos_test_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user-test.c'))
2690
+check-unit-y = tests/check-block-qdict$(EXESUF)
1042
+if have_vhost_user_blk_server
2691
+gcov-files-check-block-qdict-y = qobject/block-qdict.c
1043
+ qos_test_ss.add(files('vhost-user-blk-test.c'))
2692
check-unit-y += tests/test-char$(EXESUF)
1044
+endif
2693
gcov-files-check-qdict-y = chardev/char.c
1045
2694
check-unit-y += tests/check-qnum$(EXESUF)
1046
tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c']
2695
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \
1047
2696
test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
1048
@@ -XXX,XX +XXX,XX @@ foreach dir : target_dirs
2697
    tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \
1049
endif
2698
    tests/check-qjson.o tests/check-qlit.o \
1050
qtest_env.set('G_TEST_DBUS_DAEMON', meson.source_root() / 'tests/dbus-vmstate-daemon.sh')
2699
+    tests/check-block-qtest.o \
1051
qtest_env.set('QTEST_QEMU_BINARY', './qemu-system-' + target_base)
2700
    tests/test-coroutine.o tests/test-string-output-visitor.o \
1052
+ qtest_env.set('QTEST_QEMU_STORAGE_DAEMON_BINARY', './storage-daemon/qemu-storage-daemon')
2701
    tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \
1053
2702
    tests/test-clone-visitor.o \
1054
foreach test : target_qtests
2703
@@ -XXX,XX +XXX,XX @@ test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
1055
# Executables are shared across targets, declare them only the first time we
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
--
1056
--
2712
2.13.6
1057
2.29.2
2713
1058
2714
1059
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
-blockdev and blockdev-add silently ignore empty objects and arrays in
3
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4
their argument. That's because qmp_blockdev_add() converts the
4
Message-Id: <20210223144653.811468-7-stefanha@redhat.com>
5
argument to a flat QDict, and qdict_flatten() eats empty QDict and
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
9
Thus, the flaw puts an artificial restriction on the QAPI schema: we
10
can't have potentially empty objects and arrays within
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>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
24
---
6
---
25
qobject/block-qdict.c | 54 +++++++++++++++++++++++++++++------------------
7
tests/qtest/vhost-user-blk-test.c | 81 +++++++++++++++++++++++++++++--
26
tests/check-block-qdict.c | 38 ++++++++++++++++++++++++++-------
8
1 file changed, 76 insertions(+), 5 deletions(-)
27
2 files changed, 63 insertions(+), 29 deletions(-)
28
9
29
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
10
diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c
30
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
31
--- a/qobject/block-qdict.c
12
--- a/tests/qtest/vhost-user-blk-test.c
32
+++ b/qobject/block-qdict.c
13
+++ b/tests/qtest/vhost-user-blk-test.c
33
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
14
@@ -XXX,XX +XXX,XX @@ static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
15
qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
16
}
17
18
+static void multiqueue(void *obj, void *data, QGuestAllocator *t_alloc)
19
+{
20
+ QVirtioPCIDevice *pdev1 = obj;
21
+ QVirtioDevice *dev1 = &pdev1->vdev;
22
+ QVirtioPCIDevice *pdev8;
23
+ QVirtioDevice *dev8;
24
+ QTestState *qts = pdev1->pdev->bus->qts;
25
+ uint64_t features;
26
+ uint16_t num_queues;
27
+
28
+ /*
29
+ * The primary device has 1 queue and VIRTIO_BLK_F_MQ is not enabled. The
30
+ * VIRTIO specification allows VIRTIO_BLK_F_MQ to be enabled when there is
31
+ * only 1 virtqueue, but --device vhost-user-blk-pci doesn't do this (which
32
+ * is also spec-compliant).
33
+ */
34
+ features = qvirtio_get_features(dev1);
35
+ g_assert_cmpint(features & (1u << VIRTIO_BLK_F_MQ), ==, 0);
36
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
37
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
38
+ (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
39
+ (1u << VIRTIO_BLK_F_SCSI));
40
+ qvirtio_set_features(dev1, features);
41
+
42
+ /* Hotplug a secondary device with 8 queues */
43
+ qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1",
44
+ "{'addr': %s, 'chardev': 'char2', 'num-queues': 8}",
45
+ stringify(PCI_SLOT_HP) ".0");
46
+
47
+ pdev8 = virtio_pci_new(pdev1->pdev->bus,
48
+ &(QPCIAddress) {
49
+ .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0)
50
+ });
51
+ g_assert_nonnull(pdev8);
52
+ g_assert_cmpint(pdev8->vdev.device_type, ==, VIRTIO_ID_BLOCK);
53
+
54
+ qos_object_start_hw(&pdev8->obj);
55
+
56
+ dev8 = &pdev8->vdev;
57
+ features = qvirtio_get_features(dev8);
58
+ g_assert_cmpint(features & (1u << VIRTIO_BLK_F_MQ),
59
+ ==,
60
+ (1u << VIRTIO_BLK_F_MQ));
61
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
62
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
63
+ (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
64
+ (1u << VIRTIO_BLK_F_SCSI) |
65
+ (1u << VIRTIO_BLK_F_MQ));
66
+ qvirtio_set_features(dev8, features);
67
+
68
+ num_queues = qvirtio_config_readw(dev8,
69
+ offsetof(struct virtio_blk_config, num_queues));
70
+ g_assert_cmpint(num_queues, ==, 8);
71
+
72
+ qvirtio_pci_device_disable(pdev8);
73
+ qos_object_destroy(&pdev8->obj);
74
+
75
+ /* unplug secondary disk */
76
+ qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
77
+}
78
+
79
/*
80
* Check that setting the vring addr on a non-existent virtqueue does
81
* not crash.
82
@@ -XXX,XX +XXX,XX @@ static void quit_storage_daemon(void *data)
83
g_free(data);
84
}
85
86
-static void start_vhost_user_blk(GString *cmd_line, int vus_instances)
87
+static void start_vhost_user_blk(GString *cmd_line, int vus_instances,
88
+ int num_queues)
34
{
89
{
35
QObject *value;
90
const char *vhost_user_blk_bin = qtest_qemu_storage_daemon_binary();
36
const QListEntry *entry;
37
+ QDict *dict_val;
38
+ QList *list_val;
39
char *new_key;
40
int i;
91
int i;
41
92
@@ -XXX,XX +XXX,XX @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances)
42
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
93
g_string_append_printf(storage_daemon_command,
43
94
"--blockdev driver=file,node-name=disk%d,filename=%s "
44
for (i = 0; entry; entry = qlist_next(entry), i++) {
95
"--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s,"
45
value = qlist_entry_obj(entry);
96
- "node-name=disk%i,writable=on ",
46
+ dict_val = qobject_to(QDict, value);
97
- i, img_path, i, sock_path, i);
47
+ list_val = qobject_to(QList, value);
98
+ "node-name=disk%i,writable=on,num-queues=%d ",
48
new_key = g_strdup_printf("%s.%i", prefix, i);
99
+ i, img_path, i, sock_path, i, num_queues);
49
100
50
/*
101
g_string_append_printf(cmd_line, "-chardev socket,id=char%d,path=%s ",
51
* Flatten non-empty QDict and QList recursively into @target,
102
i + 1, sock_path);
52
* copy other objects to @target
103
@@ -XXX,XX +XXX,XX @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances)
53
*/
104
54
- if (qobject_type(value) == QTYPE_QDICT) {
105
static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg)
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
{
106
{
67
QObject *value;
107
- start_vhost_user_blk(cmd_line, 1);
68
const QDictEntry *entry, *next;
108
+ start_vhost_user_blk(cmd_line, 1, 1);
69
+ QDict *dict_val;
109
return arg;
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
}
110
}
102
111
103
/**
112
@@ -XXX,XX +XXX,XX @@ static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg)
104
- * qdict_flatten(): For each nested QDict with key x, all fields with key y
113
static void *vhost_user_blk_hotplug_test_setup(GString *cmd_line, void *arg)
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
{
114
{
129
const QDictEntry *ent;
115
/* "-chardev socket,id=char2" is used for pci_hotplug*/
130
QDict *two_level, *multi_level = NULL, *child_dict;
116
- start_vhost_user_blk(cmd_line, 2);
131
+ QDict *dict_val;
117
+ start_vhost_user_blk(cmd_line, 2, 1);
132
+ QList *list_val;
118
+ return arg;
133
QObject *dst = NULL, *child;
119
+}
134
size_t i;
120
+
135
char *prefix = NULL;
121
+static void *vhost_user_blk_multiqueue_test_setup(GString *cmd_line, void *arg)
136
@@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp)
122
+{
137
123
+ start_vhost_user_blk(cmd_line, 2, 8);
138
/* Step 1: split our totally flat dict into a two level dict */
124
return arg;
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
}
125
}
195
@@ -XXX,XX +XXX,XX @@ static void qdict_join_test(void)
126
196
static void qdict_crumple_test_recursive(void)
127
@@ -XXX,XX +XXX,XX @@ static void register_vhost_user_blk_test(void)
197
{
128
198
QDict *src, *dst, *rule, *vnc, *acl, *listen;
129
opts.before = vhost_user_blk_hotplug_test_setup;
199
- QList *rules;
130
qos_add_test("hotplug", "vhost-user-blk-pci", pci_hotplug, &opts);
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
+
131
+
238
qobject_unref(src);
132
+ opts.before = vhost_user_blk_multiqueue_test_setup;
239
qobject_unref(dst);
133
+ qos_add_test("multiqueue", "vhost-user-blk-pci", multiqueue, &opts);
240
}
134
}
241
@@ -XXX,XX +XXX,XX @@ static void qdict_rename_keys_test(void)
135
242
136
libqos_init(register_vhost_user_blk_test);
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
--
137
--
262
2.13.6
138
2.29.2
263
139
264
140
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
There's no need to restart the loop. We don't elsewhere, e.g. in
3
The config->blk_size field is little-endian. Use the native-endian
4
qdict_extract_subqdict(), qdict_join() and qemu_opts_absorb_qdict().
4
blk_size variable to avoid double byteswapping.
5
Simplify accordingly.
6
5
7
Signed-off-by: Markus Armbruster <armbru@redhat.com>
6
Fixes: 11f60f7eaee2630dd6fa0c3a8c49f792e46c4cf1 ("block/export: make vhost-user-blk config space little-endian")
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-Id: <20210223144653.811468-8-stefanha@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
10
---
11
qobject/block-qdict.c | 18 +++---------------
11
block/export/vhost-user-blk-server.c | 2 +-
12
1 file changed, 3 insertions(+), 15 deletions(-)
12
1 file changed, 1 insertion(+), 1 deletion(-)
13
13
14
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
14
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
15
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qobject/block-qdict.c
16
--- a/block/export/vhost-user-blk-server.c
17
+++ b/qobject/block-qdict.c
17
+++ b/block/export/vhost-user-blk-server.c
18
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
18
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
19
QObject *value;
19
config->num_queues = cpu_to_le16(num_queues);
20
const QDictEntry *entry, *next;
20
config->max_discard_sectors = cpu_to_le32(32768);
21
char *new_key;
21
config->max_discard_seg = cpu_to_le32(1);
22
- bool delete;
22
- config->discard_sector_alignment = cpu_to_le32(config->blk_size >> 9);
23
23
+ config->discard_sector_alignment = cpu_to_le32(blk_size >> 9);
24
entry = qdict_first(qdict);
24
config->max_write_zeroes_sectors = cpu_to_le32(32768);
25
25
config->max_write_zeroes_seg = cpu_to_le32(1);
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
}
26
}
66
--
27
--
67
2.13.6
28
2.29.2
68
29
69
30
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
Remaining uses of qobject_input_visitor_new_keyval() in the block
3
Use VIRTIO_BLK_SECTOR_BITS and VIRTIO_BLK_SECTOR_SIZE when dealing with
4
subsystem:
4
virtio-blk sector numbers. Although the values happen to be the same as
5
BDRV_SECTOR_BITS and BDRV_SECTOR_SIZE, they are conceptually different.
6
This makes it clearer when we are dealing with virtio-blk sector units.
5
7
6
* block_crypto_open_opts_init()
8
Use VIRTIO_BLK_SECTOR_BITS in vu_blk_initialize_config(). Later patches
7
Currently doesn't visit any non-string scalars, thus safe. It's
9
will use it the new constants the virtqueue request processing code
8
called from
10
path.
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
11
15
* block_crypto_create_opts_init(), called from
12
Suggested-by: Max Reitz <mreitz@redhat.com>
16
- block_crypto_co_create_opts_luks()
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Also creates the QDict with qemu_opts_to_qdict_filtered().
14
Message-Id: <20210223144653.811468-9-stefanha@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>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
29
---
16
---
30
block/crypto.c | 12 +++++++++---
17
block/export/vhost-user-blk-server.c | 15 ++++++++++++---
31
block/vdi.c | 8 ++++++--
18
1 file changed, 12 insertions(+), 3 deletions(-)
32
2 files changed, 15 insertions(+), 5 deletions(-)
33
19
34
diff --git a/block/crypto.c b/block/crypto.c
20
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
35
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
36
--- a/block/crypto.c
22
--- a/block/export/vhost-user-blk-server.c
37
+++ b/block/crypto.c
23
+++ b/block/export/vhost-user-blk-server.c
38
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@
39
#include "qemu/osdep.h"
40
41
#include "block/block_int.h"
42
+#include "block/qdict.h"
43
#include "sysemu/block-backend.h"
25
#include "sysemu/block-backend.h"
44
#include "crypto/block.h"
26
#include "util/block-helpers.h"
45
#include "qapi/opts-visitor.h"
27
46
#include "qapi/qapi-visit-crypto.h"
28
+/*
47
-#include "qapi/qmp/qdict.h"
29
+ * Sector units are 512 bytes regardless of the
48
#include "qapi/qobject-input-visitor.h"
30
+ * virtio_blk_config->blk_size value.
49
#include "qapi/error.h"
31
+ */
50
#include "qemu/option.h"
32
+#define VIRTIO_BLK_SECTOR_BITS 9
51
@@ -XXX,XX +XXX,XX @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
33
+#define VIRTIO_BLK_SECTOR_SIZE (1ull << VIRTIO_BLK_SECTOR_BITS)
52
ret = g_new0(QCryptoBlockOpenOptions, 1);
34
+
53
ret->format = format;
35
enum {
54
36
VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
55
- v = qobject_input_visitor_new_keyval(QOBJECT(opts));
37
};
56
+ v = qobject_input_visitor_new_flat_confused(opts, &local_err);
38
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
57
+ if (local_err) {
39
uint32_t blk_size,
58
+ goto out;
40
uint16_t num_queues)
59
+ }
41
{
60
42
- config->capacity = cpu_to_le64(bdrv_getlength(bs) >> BDRV_SECTOR_BITS);
61
visit_start_struct(v, NULL, NULL, 0, &local_err);
43
+ config->capacity =
62
if (local_err) {
44
+ cpu_to_le64(bdrv_getlength(bs) >> VIRTIO_BLK_SECTOR_BITS);
63
@@ -XXX,XX +XXX,XX @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
45
config->blk_size = cpu_to_le32(blk_size);
64
ret = g_new0(QCryptoBlockCreateOptions, 1);
46
config->size_max = cpu_to_le32(0);
65
ret->format = format;
47
config->seg_max = cpu_to_le32(128 - 2);
66
48
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
67
- v = qobject_input_visitor_new_keyval(QOBJECT(opts));
49
config->num_queues = cpu_to_le16(num_queues);
68
+ v = qobject_input_visitor_new_flat_confused(opts, &local_err);
50
config->max_discard_sectors = cpu_to_le32(32768);
69
+ if (local_err) {
51
config->max_discard_seg = cpu_to_le32(1);
70
+ goto out;
52
- config->discard_sector_alignment = cpu_to_le32(blk_size >> 9);
71
+ }
53
+ config->discard_sector_alignment =
72
54
+ cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
73
visit_start_struct(v, NULL, NULL, 0, &local_err);
55
config->max_write_zeroes_sectors = cpu_to_le32(32768);
74
if (local_err) {
56
config->max_write_zeroes_seg = cpu_to_le32(1);
75
diff --git a/block/vdi.c b/block/vdi.c
57
}
76
index XXXXXXX..XXXXXXX 100644
58
@@ -XXX,XX +XXX,XX @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
77
--- a/block/vdi.c
59
if (vu_opts->has_logical_block_size) {
78
+++ b/block/vdi.c
60
logical_block_size = vu_opts->logical_block_size;
79
@@ -XXX,XX +XXX,XX @@
61
} else {
80
62
- logical_block_size = BDRV_SECTOR_SIZE;
81
#include "qemu/osdep.h"
63
+ logical_block_size = VIRTIO_BLK_SECTOR_SIZE;
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
}
64
}
93
65
check_block_size(exp->id, "logical-block-size", logical_block_size,
94
/* Get the QAPI object */
66
&local_err);
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
--
67
--
105
2.13.6
68
2.29.2
106
69
107
70
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
qdict_flatten_qdict() skips copying scalars from @qdict to @target
3
The driver is supposed to honor the blk_size field but the protocol
4
when the two are the same. Fair enough, but it uses a non-obvious
4
still uses 512-byte sector numbers. It is incorrect to multiply
5
test for "same". Replace it by the obvious one. While there, improve
5
req->sector_num by blk_size.
6
comments.
7
6
8
Signed-off-by: Markus Armbruster <armbru@redhat.com>
7
VIRTIO 1.1 5.2.5 Device Initialization says:
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
9
blk_size can be read to determine the optimal sector size for the
10
driver to use. This does not affect the units used in the protocol
11
(always 512 bytes), but awareness of the correct value can affect
12
performance.
13
14
Fixes: 3578389bcf76c824a5d82e6586a6f0c71e56f2aa ("block/export: vhost-user block device backend server")
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
16
Message-Id: <20210223144653.811468-10-stefanha@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
18
---
12
qobject/block-qdict.c | 14 +++++++++-----
19
block/export/vhost-user-blk-server.c | 2 +-
13
1 file changed, 9 insertions(+), 5 deletions(-)
20
1 file changed, 1 insertion(+), 1 deletion(-)
14
21
15
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
22
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
16
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
17
--- a/qobject/block-qdict.c
24
--- a/block/export/vhost-user-blk-server.c
18
+++ b/qobject/block-qdict.c
25
+++ b/block/export/vhost-user-blk-server.c
19
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
26
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
20
value = qlist_entry_obj(entry);
27
break;
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
}
28
}
35
29
36
@@ -XXX,XX +XXX,XX @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
30
- int64_t offset = req->sector_num * vexp->blk_size;
37
new_key = g_strdup_printf("%s.%s", prefix, entry->key);
31
+ int64_t offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS;
38
}
32
QEMUIOVector qiov;
39
33
if (is_write) {
40
+ /*
34
qemu_iovec_init_external(&qiov, out_iov, out_num);
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
--
35
--
61
2.13.6
36
2.29.2
62
37
63
38
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
Signed-off-by: Markus Armbruster <armbru@redhat.com>
3
Validate discard/write zeroes the same way we do for virtio-blk. Some of
4
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
these checks are mandated by the VIRTIO specification, others are
5
internal to QEMU.
6
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-Id: <20210223144653.811468-11-stefanha@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
10
---
7
qobject/block-qdict.c | 27 +++++++++++----------------
11
block/export/vhost-user-blk-server.c | 116 +++++++++++++++++++++------
8
1 file changed, 11 insertions(+), 16 deletions(-)
12
1 file changed, 93 insertions(+), 23 deletions(-)
9
13
10
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
14
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
11
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
12
--- a/qobject/block-qdict.c
16
--- a/block/export/vhost-user-blk-server.c
13
+++ b/qobject/block-qdict.c
17
+++ b/block/export/vhost-user-blk-server.c
14
@@ -XXX,XX +XXX,XX @@ static int qdict_is_list(QDict *maybe_list, Error **errp)
18
@@ -XXX,XX +XXX,XX @@
15
19
16
for (ent = qdict_first(maybe_list); ent != NULL;
20
enum {
17
ent = qdict_next(maybe_list, ent)) {
21
VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
18
+ int is_index = !qemu_strtoi64(ent->key, NULL, 10, &val);
22
+ VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768,
19
23
+ VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768,
20
- if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) {
24
};
21
- if (is_list == -1) {
25
struct virtio_blk_inhdr {
22
- is_list = 1;
26
unsigned char status;
23
- } else if (!is_list) {
27
@@ -XXX,XX +XXX,XX @@ static void vu_blk_req_complete(VuBlkReq *req)
24
- error_setg(errp,
28
free(req);
25
- "Cannot mix list and non-list keys");
29
}
26
- return -1;
30
27
- }
31
+static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector,
28
+ if (is_list == -1) {
32
+ size_t size)
29
+ is_list = is_index;
33
+{
34
+ uint64_t nb_sectors = size >> BDRV_SECTOR_BITS;
35
+ uint64_t total_sectors;
36
+
37
+ if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
38
+ return false;
39
+ }
40
+ if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) {
41
+ return false;
42
+ }
43
+ blk_get_geometry(vexp->export.blk, &total_sectors);
44
+ if (sector > total_sectors || nb_sectors > total_sectors - sector) {
45
+ return false;
46
+ }
47
+ return true;
48
+}
49
+
50
static int coroutine_fn
51
-vu_blk_discard_write_zeroes(BlockBackend *blk, struct iovec *iov,
52
+vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov,
53
uint32_t iovcnt, uint32_t type)
54
{
55
+ BlockBackend *blk = vexp->export.blk;
56
struct virtio_blk_discard_write_zeroes desc;
57
- ssize_t size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
58
+ ssize_t size;
59
+ uint64_t sector;
60
+ uint32_t num_sectors;
61
+ uint32_t max_sectors;
62
+ uint32_t flags;
63
+ int bytes;
64
+
65
+ /* Only one desc is currently supported */
66
+ if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) {
67
+ return VIRTIO_BLK_S_UNSUPP;
68
+ }
69
+
70
+ size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
71
if (unlikely(size != sizeof(desc))) {
72
- error_report("Invalid size %zd, expect %zu", size, sizeof(desc));
73
- return -EINVAL;
74
+ error_report("Invalid size %zd, expected %zu", size, sizeof(desc));
75
+ return VIRTIO_BLK_S_IOERR;
76
}
77
78
- uint64_t range[2] = { le64_to_cpu(desc.sector) << 9,
79
- le32_to_cpu(desc.num_sectors) << 9 };
80
- if (type == VIRTIO_BLK_T_DISCARD) {
81
- if (blk_co_pdiscard(blk, range[0], range[1]) == 0) {
82
- return 0;
83
+ sector = le64_to_cpu(desc.sector);
84
+ num_sectors = le32_to_cpu(desc.num_sectors);
85
+ flags = le32_to_cpu(desc.flags);
86
+ max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ?
87
+ VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS :
88
+ VHOST_USER_BLK_MAX_DISCARD_SECTORS;
89
+
90
+ /* This check ensures that 'bytes' fits in an int */
91
+ if (unlikely(num_sectors > max_sectors)) {
92
+ return VIRTIO_BLK_S_IOERR;
93
+ }
94
+
95
+ bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS;
96
+
97
+ if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) {
98
+ return VIRTIO_BLK_S_IOERR;
99
+ }
100
+
101
+ /*
102
+ * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
103
+ * and write zeroes commands if any unknown flag is set.
104
+ */
105
+ if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
106
+ return VIRTIO_BLK_S_UNSUPP;
107
+ }
108
+
109
+ if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
110
+ int blk_flags = 0;
111
+
112
+ if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
113
+ blk_flags |= BDRV_REQ_MAY_UNMAP;
30
+ }
114
+ }
31
+
115
+
32
+ if (is_index != is_list) {
116
+ if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS,
33
+ error_setg(errp, "Cannot mix list and non-list keys");
117
+ bytes, blk_flags) == 0) {
34
+ return -1;
118
+ return VIRTIO_BLK_S_OK;
119
}
120
- } else if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
121
- if (blk_co_pwrite_zeroes(blk, range[0], range[1], 0) == 0) {
122
- return 0;
123
+ } else if (type == VIRTIO_BLK_T_DISCARD) {
124
+ /*
125
+ * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
126
+ * discard commands if the unmap flag is set.
127
+ */
128
+ if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
129
+ return VIRTIO_BLK_S_UNSUPP;
35
+ }
130
+ }
36
+
131
+
37
+ if (is_index) {
132
+ if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS,
38
len++;
133
+ bytes) == 0) {
39
if (val > max) {
134
+ return VIRTIO_BLK_S_OK;
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
}
135
}
51
}
136
}
52
137
138
- return -EINVAL;
139
+ return VIRTIO_BLK_S_IOERR;
140
}
141
142
static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
143
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
144
}
145
case VIRTIO_BLK_T_DISCARD:
146
case VIRTIO_BLK_T_WRITE_ZEROES: {
147
- int rc;
148
-
149
if (!vexp->writable) {
150
req->in->status = VIRTIO_BLK_S_IOERR;
151
break;
152
}
153
154
- rc = vu_blk_discard_write_zeroes(blk, &elem->out_sg[1], out_num, type);
155
- if (rc == 0) {
156
- req->in->status = VIRTIO_BLK_S_OK;
157
- } else {
158
- req->in->status = VIRTIO_BLK_S_IOERR;
159
- }
160
+ req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num,
161
+ type);
162
break;
163
}
164
default:
165
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
166
config->min_io_size = cpu_to_le16(1);
167
config->opt_io_size = cpu_to_le32(1);
168
config->num_queues = cpu_to_le16(num_queues);
169
- config->max_discard_sectors = cpu_to_le32(32768);
170
+ config->max_discard_sectors =
171
+ cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS);
172
config->max_discard_seg = cpu_to_le32(1);
173
config->discard_sector_alignment =
174
cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
175
- config->max_write_zeroes_sectors = cpu_to_le32(32768);
176
+ config->max_write_zeroes_sectors
177
+ = cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS);
178
config->max_write_zeroes_seg = cpu_to_le32(1);
179
}
180
53
--
181
--
54
2.13.6
182
2.29.2
55
183
56
184
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
The previous commit fixed -blockdev breakage due to misuse of the
3
Exercise input validation code paths in
4
qobject input visitor's keyval flavor in bdrv_file_open(). The commit
4
block/export/vhost-user-blk-server.c.
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
5
10
$ qemu-system-x86 -drive node-name=n1,driver=nbd,server.type=inet,server.host=localhost,server.port=1234,server.numeric=off
6
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
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
7
Message-Id: <20210223144653.811468-12-stefanha@redhat.com>
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>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
9
---
21
block/nbd.c | 12 ++----------
10
tests/qtest/vhost-user-blk-test.c | 124 ++++++++++++++++++++++++++++++
22
block/sheepdog.c | 12 ++----------
11
1 file changed, 124 insertions(+)
23
block/ssh.c | 12 ++----------
24
3 files changed, 6 insertions(+), 30 deletions(-)
25
12
26
diff --git a/block/nbd.c b/block/nbd.c
13
diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c
27
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
28
--- a/block/nbd.c
15
--- a/tests/qtest/vhost-user-blk-test.c
29
+++ b/block/nbd.c
16
+++ b/tests/qtest/vhost-user-blk-test.c
30
@@ -XXX,XX +XXX,XX @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
17
@@ -XXX,XX +XXX,XX @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
31
goto done;
18
return addr;
19
}
20
21
+static void test_invalid_discard_write_zeroes(QVirtioDevice *dev,
22
+ QGuestAllocator *alloc,
23
+ QTestState *qts,
24
+ QVirtQueue *vq,
25
+ uint32_t type)
26
+{
27
+ QVirtioBlkReq req;
28
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
29
+ struct virtio_blk_discard_write_zeroes dwz_hdr2[2];
30
+ uint64_t req_addr;
31
+ uint32_t free_head;
32
+ uint8_t status;
33
+
34
+ /* More than one dwz is not supported */
35
+ req.type = type;
36
+ req.data = (char *) dwz_hdr2;
37
+ dwz_hdr2[0].sector = 0;
38
+ dwz_hdr2[0].num_sectors = 1;
39
+ dwz_hdr2[0].flags = 0;
40
+ dwz_hdr2[1].sector = 1;
41
+ dwz_hdr2[1].num_sectors = 1;
42
+ dwz_hdr2[1].flags = 0;
43
+
44
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr2[0]);
45
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr2[1]);
46
+
47
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr2));
48
+
49
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
50
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr2), false, true);
51
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr2), 1, true,
52
+ false);
53
+
54
+ qvirtqueue_kick(qts, dev, vq, free_head);
55
+
56
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
57
+ QVIRTIO_BLK_TIMEOUT_US);
58
+ status = readb(req_addr + 16 + sizeof(dwz_hdr2));
59
+ g_assert_cmpint(status, ==, VIRTIO_BLK_S_UNSUPP);
60
+
61
+ guest_free(alloc, req_addr);
62
+
63
+ /* num_sectors must be less than config->max_write_zeroes_sectors */
64
+ req.type = type;
65
+ req.data = (char *) &dwz_hdr;
66
+ dwz_hdr.sector = 0;
67
+ dwz_hdr.num_sectors = 0xffffffff;
68
+ dwz_hdr.flags = 0;
69
+
70
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
71
+
72
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
73
+
74
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
75
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
76
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
77
+ false);
78
+
79
+ qvirtqueue_kick(qts, dev, vq, free_head);
80
+
81
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
82
+ QVIRTIO_BLK_TIMEOUT_US);
83
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
84
+ g_assert_cmpint(status, ==, VIRTIO_BLK_S_IOERR);
85
+
86
+ guest_free(alloc, req_addr);
87
+
88
+ /* sector must be less than the device capacity */
89
+ req.type = type;
90
+ req.data = (char *) &dwz_hdr;
91
+ dwz_hdr.sector = TEST_IMAGE_SIZE / 512 + 1;
92
+ dwz_hdr.num_sectors = 1;
93
+ dwz_hdr.flags = 0;
94
+
95
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
96
+
97
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
98
+
99
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
100
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
101
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
102
+ false);
103
+
104
+ qvirtqueue_kick(qts, dev, vq, free_head);
105
+
106
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
107
+ QVIRTIO_BLK_TIMEOUT_US);
108
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
109
+ g_assert_cmpint(status, ==, VIRTIO_BLK_S_IOERR);
110
+
111
+ guest_free(alloc, req_addr);
112
+
113
+ /* reserved flag bits must be zero */
114
+ req.type = type;
115
+ req.data = (char *) &dwz_hdr;
116
+ dwz_hdr.sector = 0;
117
+ dwz_hdr.num_sectors = 1;
118
+ dwz_hdr.flags = ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP;
119
+
120
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
121
+
122
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
123
+
124
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
125
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
126
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
127
+ false);
128
+
129
+ qvirtqueue_kick(qts, dev, vq, free_head);
130
+
131
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
132
+ QVIRTIO_BLK_TIMEOUT_US);
133
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
134
+ g_assert_cmpint(status, ==, VIRTIO_BLK_S_UNSUPP);
135
+
136
+ guest_free(alloc, req_addr);
137
+}
138
+
139
/* Returns the request virtqueue so the caller can perform further tests */
140
static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
141
{
142
@@ -XXX,XX +XXX,XX @@ static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
143
g_free(data);
144
145
guest_free(alloc, req_addr);
146
+
147
+ test_invalid_discard_write_zeroes(dev, alloc, qts, vq,
148
+ VIRTIO_BLK_T_WRITE_ZEROES);
32
}
149
}
33
150
34
- crumpled_addr = qdict_crumple(addr, errp);
151
if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
35
+ crumpled_addr = qdict_crumple_for_keyval_qiv(addr, errp);
152
@@ -XXX,XX +XXX,XX @@ static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
36
if (!crumpled_addr) {
153
g_assert_cmpint(status, ==, 0);
37
goto done;
154
155
guest_free(alloc, req_addr);
156
+
157
+ test_invalid_discard_write_zeroes(dev, alloc, qts, vq,
158
+ VIRTIO_BLK_T_DISCARD);
38
}
159
}
39
160
40
- /*
161
if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
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
--
162
--
108
2.13.6
163
2.29.2
109
164
110
165
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
2
3
When you mix scalar and non-scalar keys, whether you get an "already
3
Check that the sector number and byte count are valid.
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
4
9
I find qdict_crumple()'s first loop hard to understand. Rearrange it
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
and add a comment.
6
Message-Id: <20210223144653.811468-13-stefanha@redhat.com>
11
12
Signed-off-by: Markus Armbruster <armbru@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
8
---
15
qobject/block-qdict.c | 32 ++++++++++++++++----------------
9
block/export/vhost-user-blk-server.c | 19 ++++++++++++++++---
16
1 file changed, 16 insertions(+), 16 deletions(-)
10
1 file changed, 16 insertions(+), 3 deletions(-)
17
11
18
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
12
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
19
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
20
--- a/qobject/block-qdict.c
14
--- a/block/export/vhost-user-blk-server.c
21
+++ b/qobject/block-qdict.c
15
+++ b/block/export/vhost-user-blk-server.c
22
@@ -XXX,XX +XXX,XX @@ static int qdict_is_list(QDict *maybe_list, Error **errp)
16
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
23
QObject *qdict_crumple(const QDict *src, Error **errp)
17
switch (type & ~VIRTIO_BLK_T_BARRIER) {
24
{
18
case VIRTIO_BLK_T_IN:
25
const QDictEntry *ent;
19
case VIRTIO_BLK_T_OUT: {
26
- QDict *two_level, *multi_level = NULL;
20
+ QEMUIOVector qiov;
27
+ QDict *two_level, *multi_level = NULL, *child_dict;
21
+ int64_t offset;
28
QObject *dst = NULL, *child;
22
ssize_t ret = 0;
29
size_t i;
23
bool is_write = type & VIRTIO_BLK_T_OUT;
30
char *prefix = NULL;
24
req->sector_num = le64_to_cpu(req->out.sector);
31
@@ -XXX,XX +XXX,XX @@ QObject *qdict_crumple(const QDict *src, Error **errp)
25
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
26
break;
32
}
27
}
33
28
34
qdict_split_flat_key(ent->key, &prefix, &suffix);
29
- int64_t offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS;
35
-
30
- QEMUIOVector qiov;
36
child = qdict_get(two_level, prefix);
31
if (is_write) {
37
+ child_dict = qobject_to(QDict, child);
32
qemu_iovec_init_external(&qiov, out_iov, out_num);
38
+
33
- ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
39
+ if (child) {
34
} else {
40
+ /*
35
qemu_iovec_init_external(&qiov, in_iov, in_num);
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
+ }
36
+ }
50
+
37
+
51
if (suffix) {
38
+ if (unlikely(!vu_blk_sect_range_ok(vexp,
52
- QDict *child_dict = qobject_to(QDict, child);
39
+ req->sector_num,
53
if (!child_dict) {
40
+ qiov.size))) {
54
- if (child) {
41
+ req->in->status = VIRTIO_BLK_S_IOERR;
55
- error_setg(errp, "Key %s prefix is already set as a scalar",
42
+ break;
56
- prefix);
43
+ }
57
- goto error;
44
+
58
- }
45
+ offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS;
59
-
46
+
60
child_dict = qdict_new();
47
+ if (is_write) {
61
- qdict_put_obj(two_level, prefix, QOBJECT(child_dict));
48
+ ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
62
+ qdict_put(two_level, prefix, child_dict);
49
+ } else {
63
}
50
ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0);
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
}
51
}
74
52
if (ret >= 0) {
75
--
53
--
76
2.13.6
54
2.29.2
77
55
78
56
diff view generated by jsdifflib
1
The -drive option serial was deprecated in QEMU 2.10. It's time to
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
remove it.
3
2
4
Tests need to be updated to set the serial number with -global instead
3
Rename bytes_covered_by_bitmap_cluster() to
5
of using the -drive option.
4
bdrv_dirty_bitmap_serialization_coverage() and make it public.
5
It is needed as we are going to share it with bitmap loading in
6
parallels format.
6
7
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Denis V. Lunev <den@openvz.org>
11
Message-Id: <20210224104707.88430-2-vsementsov@virtuozzo.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
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
---
13
---
11
include/hw/block/block.h | 1 -
14
include/block/dirty-bitmap.h | 2 ++
12
include/sysemu/blockdev.h | 1 -
15
block/dirty-bitmap.c | 13 +++++++++++++
13
block/block-backend.c | 1 -
16
block/qcow2-bitmap.c | 16 ++--------------
14
blockdev.c | 10 ----------
17
3 files changed, 17 insertions(+), 14 deletions(-)
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
18
27
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
19
diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
28
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
29
--- a/include/hw/block/block.h
21
--- a/include/block/dirty-bitmap.h
30
+++ b/include/hw/block/block.h
22
+++ b/include/block/dirty-bitmap.h
31
@@ -XXX,XX +XXX,XX @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
23
@@ -XXX,XX +XXX,XX @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter);
32
24
uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap,
33
/* Configuration helpers */
25
uint64_t offset, uint64_t bytes);
34
26
uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap);
35
-void blkconf_serial(BlockConf *conf, char **serial);
27
+uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size,
36
bool blkconf_geometry(BlockConf *conf, int *trans,
28
+ const BdrvDirtyBitmap *bitmap);
37
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
29
void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
38
Error **errp);
30
uint8_t *buf, uint64_t offset,
39
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
31
uint64_t bytes);
32
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
40
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
41
--- a/include/sysemu/blockdev.h
34
--- a/block/dirty-bitmap.c
42
+++ b/include/sysemu/blockdev.h
35
+++ b/block/dirty-bitmap.c
43
@@ -XXX,XX +XXX,XX @@ struct DriveInfo {
36
@@ -XXX,XX +XXX,XX @@ uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap)
44
bool is_default; /* Added by default_drive() ? */
37
return hbitmap_serialization_align(bitmap->bitmap);
45
int media_cd;
38
}
46
QemuOpts *opts;
39
47
- char *serial;
40
+/* Return the disk size covered by a chunk of serialized bitmap data. */
48
QTAILQ_ENTRY(DriveInfo) next;
41
+uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size,
49
};
42
+ const BdrvDirtyBitmap *bitmap)
50
43
+{
51
diff --git a/block/block-backend.c b/block/block-backend.c
44
+ uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
45
+ uint64_t limit = granularity * (serialized_chunk_size << 3);
46
+
47
+ assert(QEMU_IS_ALIGNED(limit,
48
+ bdrv_dirty_bitmap_serialization_align(bitmap)));
49
+ return limit;
50
+}
51
+
52
+
53
void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
54
uint8_t *buf, uint64_t offset,
55
uint64_t bytes)
56
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
52
index XXXXXXX..XXXXXXX 100644
57
index XXXXXXX..XXXXXXX 100644
53
--- a/block/block-backend.c
58
--- a/block/qcow2-bitmap.c
54
+++ b/block/block-backend.c
59
+++ b/block/qcow2-bitmap.c
55
@@ -XXX,XX +XXX,XX @@ static void drive_info_del(DriveInfo *dinfo)
60
@@ -XXX,XX +XXX,XX @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
56
return;
61
return 0;
57
}
58
qemu_opts_del(dinfo->opts);
59
- g_free(dinfo->serial);
60
g_free(dinfo);
61
}
62
}
62
63
63
diff --git a/blockdev.c b/blockdev.c
64
-/* Return the disk size covered by a single qcow2 cluster of bitmap data. */
64
index XXXXXXX..XXXXXXX 100644
65
-static uint64_t bytes_covered_by_bitmap_cluster(const BDRVQcow2State *s,
65
--- a/blockdev.c
66
- const BdrvDirtyBitmap *bitmap)
66
+++ b/blockdev.c
67
-{
67
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
68
- uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
68
.type = QEMU_OPT_STRING,
69
- uint64_t limit = granularity * (s->cluster_size << 3);
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
-
70
-
98
/* no id supplied -> create one */
71
- assert(QEMU_IS_ALIGNED(limit,
99
if (qemu_opts_id(all_opts) == NULL) {
72
- bdrv_dirty_bitmap_serialization_align(bitmap)));
100
char *new_id;
73
- return limit;
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
-}
74
-}
129
-
75
-
130
void blkconf_blocksizes(BlockConf *conf)
76
/* load_bitmap_data
131
{
77
* @bitmap_table entries must satisfy specification constraints.
132
BlockBackend *blk = conf->blk;
78
* @bitmap must be cleared */
133
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
79
@@ -XXX,XX +XXX,XX @@ static int load_bitmap_data(BlockDriverState *bs,
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
}
80
}
140
81
141
- blkconf_serial(&n->conf, &n->serial);
82
buf = g_malloc(s->cluster_size);
142
if (!n->serial) {
83
- limit = bytes_covered_by_bitmap_cluster(s, bitmap);
143
error_setg(errp, "serial property not set");
84
+ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
144
return;
85
for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
145
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
86
uint64_t count = MIN(bm_size - offset, limit);
146
index XXXXXXX..XXXXXXX 100644
87
uint64_t entry = bitmap_table[i];
147
--- a/hw/block/virtio-blk.c
88
@@ -XXX,XX +XXX,XX @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
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
}
89
}
152
90
153
- blkconf_serial(&conf->conf, &conf->serial);
91
buf = g_malloc(s->cluster_size);
154
if (!blkconf_apply_backend_options(&conf->conf,
92
- limit = bytes_covered_by_bitmap_cluster(s, bitmap);
155
blk_is_read_only(conf->conf.blk), true,
93
+ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
156
errp)) {
94
assert(DIV_ROUND_UP(bm_size, limit) == tb_size);
157
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
95
158
index XXXXXXX..XXXXXXX 100644
96
offset = 0;
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
--
97
--
280
2.13.6
98
2.29.2
281
99
282
100
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Parameter "filename" is deprecated since commit 91589d9e5ca, v2.10.0.
3
Actually L1 table entry offset is in 512 bytes sectors. Fix the spec.
4
Time to get rid of it.
5
4
6
Signed-off-by: Markus Armbruster <armbru@redhat.com>
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20210224104707.88430-3-vsementsov@virtuozzo.com>
7
Reviewed-by: Denis V. Lunev <den@openvz.org>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
9
---
10
block/rbd.c | 16 ----------------
10
docs/interop/parallels.txt | 28 ++++++++++++++++------------
11
1 file changed, 16 deletions(-)
11
1 file changed, 16 insertions(+), 12 deletions(-)
12
12
13
diff --git a/block/rbd.c b/block/rbd.c
13
diff --git a/docs/interop/parallels.txt b/docs/interop/parallels.txt
14
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block/rbd.c
15
--- a/docs/interop/parallels.txt
16
+++ b/block/rbd.c
16
+++ b/docs/interop/parallels.txt
17
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
17
@@ -XXX,XX +XXX,XX @@ of its data area are:
18
QObject *crumpled = NULL;
18
28 - 31: l1_size
19
const QDictEntry *e;
19
The number of entries in the L1 table of the bitmap.
20
Error *local_err = NULL;
20
21
- const char *filename;
21
- variable: l1_table (8 * l1_size bytes)
22
char *keypairs, *secretid;
22
- L1 offset table (in bytes)
23
int r;
23
+ variable: L1 offset table (l1_table), size: 8 * l1_size bytes
24
24
25
- /* If we are given a filename, parse the filename, with precedence given to
25
-A dirty bitmap is stored using a one-level structure for the mapping to host
26
- * filename encoded options */
26
-clusters - an L1 table.
27
- filename = qdict_get_try_str(options, "filename");
27
+The dirty bitmap described by this feature extension is stored in a set of
28
- if (filename) {
28
+clusters inside the Parallels image file. The offsets of these clusters are
29
- warn_report("'filename' option specified. "
29
+saved in the L1 offset table specified by the feature extension. Each L1 table
30
- "This is an unsupported option, and may be deprecated "
30
+entry is a 64 bit integer as described below:
31
- "in the future");
31
32
- qemu_rbd_parse_filename(filename, options, &local_err);
32
-Given an offset in bytes into the bitmap data, the offset in bytes into the
33
- qdict_del(options, "filename");
33
-image file can be obtained as follows:
34
- if (local_err) {
34
+Given an offset in bytes into the bitmap data, corresponding L1 entry is
35
- error_propagate(errp, local_err);
35
36
- return -EINVAL;
36
- offset = l1_table[offset / cluster_size] + (offset % cluster_size)
37
- }
37
+ l1_table[offset / cluster_size]
38
- }
38
39
-
39
-If an L1 table entry is 0, the corresponding cluster of the bitmap is assumed
40
keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
40
-to be zero.
41
if (keypairs) {
41
+If an L1 table entry is 0, all bits in the corresponding cluster of the bitmap
42
qdict_del(options, "=keyvalue-pairs");
42
+are assumed to be 0.
43
44
-If an L1 table entry is 1, the corresponding cluster of the bitmap is assumed
45
-to have all bits set.
46
+If an L1 table entry is 1, all bits in the corresponding cluster of the bitmap
47
+are assumed to be 1.
48
49
-If an L1 table entry is not 0 or 1, it allocates a cluster from the data area.
50
+If an L1 table entry is not 0 or 1, it contains the corresponding cluster
51
+offset (in 512b sectors). Given an offset in bytes into the bitmap data the
52
+offset in bytes into the image file can be obtained as follows:
53
+
54
+ offset = l1_table[offset / cluster_size] * 512 + (offset % cluster_size)
43
--
55
--
44
2.13.6
56
2.29.2
45
57
46
58
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The following pattern occurs in the .bdrv_co_create_opts() methods of
3
We are going to use it in more places, calculating
4
parallels, qcow, qcow2, qed, vhdx and vpc:
4
"s->tracks << BDRV_SECTOR_BITS" doesn't look good.
5
5
6
qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
qobject_unref(qdict);
7
Message-Id: <20210224104707.88430-4-vsementsov@virtuozzo.com>
8
qdict = qobject_to(QDict, qobj);
8
Reviewed-by: Denis V. Lunev <den@openvz.org>
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>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
29
---
10
---
30
block/parallels.c | 9 ++++-----
11
block/parallels.h | 1 +
31
block/qcow.c | 9 ++++-----
12
block/parallels.c | 8 ++++----
32
block/qcow2.c | 9 ++++-----
13
2 files changed, 5 insertions(+), 4 deletions(-)
33
block/qed.c | 9 ++++-----
34
block/vhdx.c | 9 ++++-----
35
block/vpc.c | 9 ++++-----
36
6 files changed, 24 insertions(+), 30 deletions(-)
37
14
15
diff --git a/block/parallels.h b/block/parallels.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/parallels.h
18
+++ b/block/parallels.h
19
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVParallelsState {
20
ParallelsPreallocMode prealloc_mode;
21
22
unsigned int tracks;
23
+ unsigned int cluster_size;
24
25
unsigned int off_multiplier;
26
Error *migration_blocker;
38
diff --git a/block/parallels.c b/block/parallels.c
27
diff --git a/block/parallels.c b/block/parallels.c
39
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
40
--- a/block/parallels.c
29
--- a/block/parallels.c
41
+++ b/block/parallels.c
30
+++ b/block/parallels.c
42
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
31
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
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;
32
int ret;
51
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
33
uint32_t i;
52
qdict_put_str(qdict, "file", bs->node_name);
34
bool flush_bat = false;
53
35
- int cluster_size = s->tracks << BDRV_SECTOR_BITS;
54
qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
36
55
- qobject_unref(qdict);
37
size = bdrv_getlength(bs->file->bs);
56
- qdict = qobject_to(QDict, qobj);
38
if (size < 0) {
57
- if (qdict == NULL) {
39
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
58
+ if (!qobj) {
40
high_off = off;
59
ret = -EINVAL;
41
}
60
goto done;
42
43
- if (prev_off != 0 && (prev_off + cluster_size) != off) {
44
+ if (prev_off != 0 && (prev_off + s->cluster_size) != off) {
45
res->bfi.fragmented_clusters++;
46
}
47
prev_off = off;
48
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
49
}
61
}
50
}
62
51
63
- v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
52
- res->image_end_offset = high_off + cluster_size;
64
+ v = qobject_input_visitor_new_keyval(qobj);
53
+ res->image_end_offset = high_off + s->cluster_size;
65
+ qobject_unref(qobj);
54
if (size > res->image_end_offset) {
66
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
55
int64_t count;
67
visit_free(v);
56
- count = DIV_ROUND_UP(size - res->image_end_offset, cluster_size);
68
57
+ count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size);
69
diff --git a/block/qcow.c b/block/qcow.c
58
fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n",
70
index XXXXXXX..XXXXXXX 100644
59
fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR",
71
--- a/block/qcow.c
60
size - res->image_end_offset);
72
+++ b/block/qcow.c
61
@@ -XXX,XX +XXX,XX @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
73
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
62
ret = -EFBIG;
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;
63
goto fail;
92
}
64
}
93
65
+ s->cluster_size = s->tracks << BDRV_SECTOR_BITS;
94
- v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
66
95
+ v = qobject_input_visitor_new_keyval(qobj);
67
s->bat_size = le32_to_cpu(ph.bat_entries);
96
+ qobject_unref(qobj);
68
if (s->bat_size > INT_MAX / sizeof(uint32_t)) {
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
--
69
--
225
2.13.6
70
2.29.2
226
71
227
72
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
There are numerous QDict functions that have been introduced for and are
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
used only by the block layer. Move their declarations into an own
4
Message-Id: <20210224104707.88430-5-vsementsov@virtuozzo.com>
5
header file to reflect that.
5
Reviewed-by: Denis V. Lunev <den@openvz.org>
6
7
While qdict_extract_subqdict() is in fact used outside of the block
8
layer (in util/qemu-config.c), it is still a function related very
9
closely to how the block layer works with nested QDicts, namely by
10
sometimes flattening them. Therefore, its declaration is put into this
11
header as well and util/qemu-config.c includes it with a comment stating
12
exactly which function it needs.
13
14
Suggested-by: Markus Armbruster <armbru@redhat.com>
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
Message-Id: <20180509165530.29561-7-mreitz@redhat.com>
17
[Copyright note tweaked, superfluous includes dropped]
18
Signed-off-by: Markus Armbruster <armbru@redhat.com>
19
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
---
7
---
22
include/block/qdict.h | 32 ++++++++++++++++++++++++++++++++
8
block/parallels.h | 6 +-
23
include/qapi/qmp/qdict.h | 17 -----------------
9
block/parallels-ext.c | 300 ++++++++++++++++++++++++++++++++++++++++++
24
block.c | 1 +
10
block/parallels.c | 18 +++
25
block/gluster.c | 1 +
11
block/meson.build | 3 +-
26
block/iscsi.c | 1 +
12
4 files changed, 325 insertions(+), 2 deletions(-)
27
block/nbd.c | 1 +
13
create mode 100644 block/parallels-ext.c
28
block/nfs.c | 1 +
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
14
51
diff --git a/include/block/qdict.h b/include/block/qdict.h
15
diff --git a/block/parallels.h b/block/parallels.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/parallels.h
18
+++ b/block/parallels.h
19
@@ -XXX,XX +XXX,XX @@ typedef struct ParallelsHeader {
20
uint64_t nb_sectors;
21
uint32_t inuse;
22
uint32_t data_off;
23
- char padding[12];
24
+ uint32_t flags;
25
+ uint64_t ext_off;
26
} QEMU_PACKED ParallelsHeader;
27
28
typedef enum ParallelsPreallocMode {
29
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVParallelsState {
30
Error *migration_blocker;
31
} BDRVParallelsState;
32
33
+int parallels_read_format_extension(BlockDriverState *bs,
34
+ int64_t ext_off, Error **errp);
35
+
36
#endif
37
diff --git a/block/parallels-ext.c b/block/parallels-ext.c
52
new file mode 100644
38
new file mode 100644
53
index XXXXXXX..XXXXXXX
39
index XXXXXXX..XXXXXXX
54
--- /dev/null
40
--- /dev/null
55
+++ b/include/block/qdict.h
41
+++ b/block/parallels-ext.c
56
@@ -XXX,XX +XXX,XX @@
42
@@ -XXX,XX +XXX,XX @@
57
+/*
43
+/*
58
+ * Special QDict functions used by the block layer
44
+ * Support of Parallels Format Extension. It's a part of Parallels format
45
+ * driver.
59
+ *
46
+ *
60
+ * Copyright (c) 2013-2018 Red Hat, Inc.
47
+ * Copyright (c) 2021 Virtuozzo International GmbH
61
+ *
48
+ *
62
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
49
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
63
+ * See the COPYING.LIB file in the top-level directory.
50
+ * of this software and associated documentation files (the "Software"), to deal
51
+ * in the Software without restriction, including without limitation the rights
52
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
53
+ * copies of the Software, and to permit persons to whom the Software is
54
+ * furnished to do so, subject to the following conditions:
55
+ *
56
+ * The above copyright notice and this permission notice shall be included in
57
+ * all copies or substantial portions of the Software.
58
+ *
59
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
62
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
63
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
64
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
65
+ * THE SOFTWARE.
64
+ */
66
+ */
65
+
67
+
66
+#ifndef BLOCK_QDICT_H
68
+#include "qemu/osdep.h"
67
+#define BLOCK_QDICT_H
69
+#include "qapi/error.h"
68
+
70
+#include "block/block_int.h"
69
+#include "qapi/qmp/qdict.h"
71
+#include "parallels.h"
70
+
72
+#include "crypto/hash.h"
71
+void qdict_copy_default(QDict *dst, QDict *src, const char *key);
73
+#include "qemu/uuid.h"
72
+void qdict_set_default_str(QDict *dst, const char *key, const char *val);
74
+
73
+
75
+#define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL
74
+void qdict_join(QDict *dest, QDict *src, bool overwrite);
76
+
75
+
77
+#define PARALLELS_END_OF_FEATURES_MAGIC 0x0ULL
76
+void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
78
+#define PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC 0x20385FAE252CB34AULL
77
+void qdict_array_split(QDict *src, QList **dst);
79
+
78
+int qdict_array_entries(QDict *src, const char *subqdict);
80
+typedef struct ParallelsFormatExtensionHeader {
79
+QObject *qdict_crumple(const QDict *src, Error **errp);
81
+ uint64_t magic; /* PARALLELS_FORMAT_EXTENSION_MAGIC */
80
+void qdict_flatten(QDict *qdict);
82
+ uint8_t check_sum[16];
81
+
83
+} QEMU_PACKED ParallelsFormatExtensionHeader;
82
+typedef struct QDictRenames {
84
+
83
+ const char *from;
85
+typedef struct ParallelsFeatureHeader {
84
+ const char *to;
86
+ uint64_t magic;
85
+} QDictRenames;
87
+ uint64_t flags;
86
+bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
88
+ uint32_t data_size;
87
+
89
+ uint32_t _unused;
88
+#endif
90
+} QEMU_PACKED ParallelsFeatureHeader;
89
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
91
+
90
index XXXXXXX..XXXXXXX 100644
92
+typedef struct ParallelsDirtyBitmapFeature {
91
--- a/include/qapi/qmp/qdict.h
93
+ uint64_t size;
92
+++ b/include/qapi/qmp/qdict.h
94
+ uint8_t id[16];
93
@@ -XXX,XX +XXX,XX @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key,
95
+ uint32_t granularity;
94
bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value);
96
+ uint32_t l1_size;
95
const char *qdict_get_try_str(const QDict *qdict, const char *key);
97
+ /* L1 table follows */
96
98
+} QEMU_PACKED ParallelsDirtyBitmapFeature;
97
-void qdict_copy_default(QDict *dst, QDict *src, const char *key);
99
+
98
-void qdict_set_default_str(QDict *dst, const char *key, const char *val);
100
+/* Given L1 table read bitmap data from the image and populate @bitmap */
99
-
101
+static int parallels_load_bitmap_data(BlockDriverState *bs,
100
QDict *qdict_clone_shallow(const QDict *src);
102
+ const uint64_t *l1_table,
101
-void qdict_flatten(QDict *qdict);
103
+ uint32_t l1_size,
102
-
104
+ BdrvDirtyBitmap *bitmap,
103
-void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
105
+ Error **errp)
104
-void qdict_array_split(QDict *src, QList **dst);
106
+{
105
-int qdict_array_entries(QDict *src, const char *subqdict);
107
+ BDRVParallelsState *s = bs->opaque;
106
-QObject *qdict_crumple(const QDict *src, Error **errp);
108
+ int ret = 0;
107
-
109
+ uint64_t offset, limit;
108
-void qdict_join(QDict *dest, QDict *src, bool overwrite);
110
+ uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
109
-
111
+ uint8_t *buf = NULL;
110
-typedef struct QDictRenames {
112
+ uint64_t i, tab_size =
111
- const char *from;
113
+ DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size),
112
- const char *to;
114
+ s->cluster_size);
113
-} QDictRenames;
115
+
114
-bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
116
+ if (tab_size != l1_size) {
115
117
+ error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond "
116
#endif /* QDICT_H */
118
+ "to bitmap size and cluster size. Expected %" PRIu64,
117
diff --git a/block.c b/block.c
119
+ l1_size, tab_size);
118
index XXXXXXX..XXXXXXX 100644
120
+ return -EINVAL;
119
--- a/block.c
121
+ }
120
+++ b/block.c
122
+
121
@@ -XXX,XX +XXX,XX @@
123
+ buf = qemu_blockalign(bs, s->cluster_size);
122
#include "block/block_int.h"
124
+ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
123
#include "block/blockjob.h"
125
+ for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
124
#include "block/nbd.h"
126
+ uint64_t count = MIN(bm_size - offset, limit);
125
+#include "block/qdict.h"
127
+ uint64_t entry = l1_table[i];
126
#include "qemu/error-report.h"
128
+
127
#include "module_block.h"
129
+ if (entry == 0) {
128
#include "qemu/module.h"
130
+ /* No need to deserialize zeros because @bitmap is cleared. */
129
diff --git a/block/gluster.c b/block/gluster.c
131
+ continue;
130
index XXXXXXX..XXXXXXX 100644
132
+ }
131
--- a/block/gluster.c
133
+
132
+++ b/block/gluster.c
134
+ if (entry == 1) {
133
@@ -XXX,XX +XXX,XX @@
135
+ bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false);
134
#include "qemu/osdep.h"
136
+ } else {
135
#include <glusterfs/api/glfs.h>
137
+ ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf,
136
#include "block/block_int.h"
138
+ s->cluster_size);
137
+#include "block/qdict.h"
139
+ if (ret < 0) {
138
#include "qapi/error.h"
140
+ error_setg_errno(errp, -ret,
139
#include "qapi/qmp/qdict.h"
141
+ "Failed to read bitmap data cluster");
140
#include "qapi/qmp/qerror.h"
142
+ goto finish;
141
diff --git a/block/iscsi.c b/block/iscsi.c
143
+ }
142
index XXXXXXX..XXXXXXX 100644
144
+ bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count,
143
--- a/block/iscsi.c
145
+ false);
144
+++ b/block/iscsi.c
146
+ }
145
@@ -XXX,XX +XXX,XX @@
147
+ }
146
#include "qemu/bitops.h"
148
+ ret = 0;
147
#include "qemu/bitmap.h"
149
+
148
#include "block/block_int.h"
150
+ bdrv_dirty_bitmap_deserialize_finish(bitmap);
149
+#include "block/qdict.h"
151
+
150
#include "scsi/constants.h"
152
+finish:
151
#include "qemu/iov.h"
153
+ qemu_vfree(buf);
152
#include "qemu/option.h"
154
+
153
diff --git a/block/nbd.c b/block/nbd.c
155
+ return ret;
154
index XXXXXXX..XXXXXXX 100644
156
+}
155
--- a/block/nbd.c
157
+
156
+++ b/block/nbd.c
158
+/*
157
@@ -XXX,XX +XXX,XX @@
159
+ * @data buffer (of @data_size size) is the Dirty bitmaps feature which
158
160
+ * consists of ParallelsDirtyBitmapFeature followed by L1 table.
159
#include "qemu/osdep.h"
161
+ */
160
#include "nbd-client.h"
162
+static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs,
161
+#include "block/qdict.h"
163
+ uint8_t *data,
162
#include "qapi/error.h"
164
+ size_t data_size,
163
#include "qemu/uri.h"
165
+ Error **errp)
164
#include "block/block_int.h"
166
+{
165
diff --git a/block/nfs.c b/block/nfs.c
167
+ int ret;
166
index XXXXXXX..XXXXXXX 100644
168
+ ParallelsDirtyBitmapFeature bf;
167
--- a/block/nfs.c
169
+ g_autofree uint64_t *l1_table = NULL;
168
+++ b/block/nfs.c
170
+ BdrvDirtyBitmap *bitmap;
169
@@ -XXX,XX +XXX,XX @@
171
+ QemuUUID uuid;
170
#include "qemu/error-report.h"
172
+ char uuidstr[UUID_FMT_LEN + 1];
171
#include "qapi/error.h"
173
+ int i;
172
#include "block/block_int.h"
174
+
173
+#include "block/qdict.h"
175
+ if (data_size < sizeof(bf)) {
174
#include "trace.h"
176
+ error_setg(errp, "Too small Bitmap Feature area in Parallels Format "
175
#include "qemu/iov.h"
177
+ "Extension: %zu bytes, expected at least %zu bytes",
176
#include "qemu/option.h"
178
+ data_size, sizeof(bf));
179
+ return NULL;
180
+ }
181
+ memcpy(&bf, data, sizeof(bf));
182
+ bf.size = le64_to_cpu(bf.size);
183
+ bf.granularity = le32_to_cpu(bf.granularity) << BDRV_SECTOR_BITS;
184
+ bf.l1_size = le32_to_cpu(bf.l1_size);
185
+ data += sizeof(bf);
186
+ data_size -= sizeof(bf);
187
+
188
+ if (bf.size != bs->total_sectors) {
189
+ error_setg(errp, "Bitmap size (in sectors) %" PRId64 " differs from "
190
+ "disk size in sectors %" PRId64, bf.size, bs->total_sectors);
191
+ return NULL;
192
+ }
193
+
194
+ if (bf.l1_size * sizeof(uint64_t) > data_size) {
195
+ error_setg(errp, "Bitmaps feature corrupted: l1 table exceeds "
196
+ "extension data_size");
197
+ return NULL;
198
+ }
199
+
200
+ memcpy(&uuid, bf.id, sizeof(uuid));
201
+ qemu_uuid_unparse(&uuid, uuidstr);
202
+ bitmap = bdrv_create_dirty_bitmap(bs, bf.granularity, uuidstr, errp);
203
+ if (!bitmap) {
204
+ return NULL;
205
+ }
206
+
207
+ l1_table = g_new(uint64_t, bf.l1_size);
208
+ for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) {
209
+ l1_table[i] = ldq_le_p(data);
210
+ }
211
+
212
+ ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp);
213
+ if (ret < 0) {
214
+ bdrv_release_dirty_bitmap(bitmap);
215
+ return NULL;
216
+ }
217
+
218
+ /* We support format extension only for RO parallels images. */
219
+ assert(!(bs->open_flags & BDRV_O_RDWR));
220
+ bdrv_dirty_bitmap_set_readonly(bitmap, true);
221
+
222
+ return bitmap;
223
+}
224
+
225
+static int parallels_parse_format_extension(BlockDriverState *bs,
226
+ uint8_t *ext_cluster, Error **errp)
227
+{
228
+ BDRVParallelsState *s = bs->opaque;
229
+ int ret;
230
+ int remaining = s->cluster_size;
231
+ uint8_t *pos = ext_cluster;
232
+ ParallelsFormatExtensionHeader eh;
233
+ g_autofree uint8_t *hash = NULL;
234
+ size_t hash_len = 0;
235
+ GSList *bitmaps = NULL, *el;
236
+
237
+ memcpy(&eh, pos, sizeof(eh));
238
+ eh.magic = le64_to_cpu(eh.magic);
239
+ pos += sizeof(eh);
240
+ remaining -= sizeof(eh);
241
+
242
+ if (eh.magic != PARALLELS_FORMAT_EXTENSION_MAGIC) {
243
+ error_setg(errp, "Wrong parallels Format Extension magic: 0x%" PRIx64
244
+ ", expected: 0x%llx", eh.magic,
245
+ PARALLELS_FORMAT_EXTENSION_MAGIC);
246
+ goto fail;
247
+ }
248
+
249
+ ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining,
250
+ &hash, &hash_len, errp);
251
+ if (ret < 0) {
252
+ goto fail;
253
+ }
254
+
255
+ if (hash_len != sizeof(eh.check_sum) ||
256
+ memcmp(hash, eh.check_sum, sizeof(eh.check_sum)) != 0) {
257
+ error_setg(errp, "Wrong checksum in Format Extension header. Format "
258
+ "extension is corrupted.");
259
+ goto fail;
260
+ }
261
+
262
+ while (true) {
263
+ ParallelsFeatureHeader fh;
264
+ BdrvDirtyBitmap *bitmap;
265
+
266
+ if (remaining < sizeof(fh)) {
267
+ error_setg(errp, "Can not read feature header, as remaining bytes "
268
+ "(%d) in Format Extension is less than Feature header "
269
+ "size (%zu)", remaining, sizeof(fh));
270
+ goto fail;
271
+ }
272
+
273
+ memcpy(&fh, pos, sizeof(fh));
274
+ pos += sizeof(fh);
275
+ remaining -= sizeof(fh);
276
+
277
+ fh.magic = le64_to_cpu(fh.magic);
278
+ fh.flags = le64_to_cpu(fh.flags);
279
+ fh.data_size = le32_to_cpu(fh.data_size);
280
+
281
+ if (fh.flags) {
282
+ error_setg(errp, "Flags for extension feature are unsupported");
283
+ goto fail;
284
+ }
285
+
286
+ if (fh.data_size > remaining) {
287
+ error_setg(errp, "Feature data_size exceedes Format Extension "
288
+ "cluster");
289
+ goto fail;
290
+ }
291
+
292
+ switch (fh.magic) {
293
+ case PARALLELS_END_OF_FEATURES_MAGIC:
294
+ return 0;
295
+
296
+ case PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC:
297
+ bitmap = parallels_load_bitmap(bs, pos, fh.data_size, errp);
298
+ if (!bitmap) {
299
+ goto fail;
300
+ }
301
+ bitmaps = g_slist_append(bitmaps, bitmap);
302
+ break;
303
+
304
+ default:
305
+ error_setg(errp, "Unknown feature: 0x%" PRIu64, fh.magic);
306
+ goto fail;
307
+ }
308
+
309
+ pos = ext_cluster + QEMU_ALIGN_UP(pos + fh.data_size - ext_cluster, 8);
310
+ }
311
+
312
+fail:
313
+ for (el = bitmaps; el; el = el->next) {
314
+ bdrv_release_dirty_bitmap(el->data);
315
+ }
316
+ g_slist_free(bitmaps);
317
+
318
+ return -EINVAL;
319
+}
320
+
321
+int parallels_read_format_extension(BlockDriverState *bs,
322
+ int64_t ext_off, Error **errp)
323
+{
324
+ BDRVParallelsState *s = bs->opaque;
325
+ int ret;
326
+ uint8_t *ext_cluster = qemu_blockalign(bs, s->cluster_size);
327
+
328
+ assert(ext_off > 0);
329
+
330
+ ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size);
331
+ if (ret < 0) {
332
+ error_setg_errno(errp, -ret, "Failed to read Format Extension cluster");
333
+ goto out;
334
+ }
335
+
336
+ ret = parallels_parse_format_extension(bs, ext_cluster, errp);
337
+
338
+out:
339
+ qemu_vfree(ext_cluster);
340
+
341
+ return ret;
342
+}
177
diff --git a/block/parallels.c b/block/parallels.c
343
diff --git a/block/parallels.c b/block/parallels.c
178
index XXXXXXX..XXXXXXX 100644
344
index XXXXXXX..XXXXXXX 100644
179
--- a/block/parallels.c
345
--- a/block/parallels.c
180
+++ b/block/parallels.c
346
+++ b/block/parallels.c
181
@@ -XXX,XX +XXX,XX @@
347
@@ -XXX,XX +XXX,XX @@
348
*/
349
182
#include "qemu/osdep.h"
350
#include "qemu/osdep.h"
351
+#include "qemu/error-report.h"
183
#include "qapi/error.h"
352
#include "qapi/error.h"
184
#include "block/block_int.h"
353
#include "block/block_int.h"
185
+#include "block/qdict.h"
354
#include "block/qdict.h"
186
#include "sysemu/block-backend.h"
355
@@ -XXX,XX +XXX,XX @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
187
#include "qemu/module.h"
356
goto fail_options;
188
#include "qemu/option.h"
357
}
189
diff --git a/block/qcow.c b/block/qcow.c
358
359
+ if (ph.ext_off) {
360
+ if (flags & BDRV_O_RDWR) {
361
+ /*
362
+ * It's unsafe to open image RW if there is an extension (as we
363
+ * don't support it). But parallels driver in QEMU historically
364
+ * ignores the extension, so print warning and don't care.
365
+ */
366
+ warn_report("Format Extension ignored in RW mode");
367
+ } else {
368
+ ret = parallels_read_format_extension(
369
+ bs, le64_to_cpu(ph.ext_off) << BDRV_SECTOR_BITS, errp);
370
+ if (ret < 0) {
371
+ goto fail;
372
+ }
373
+ }
374
+ }
375
+
376
if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) {
377
s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC);
378
ret = parallels_update_header(bs);
379
diff --git a/block/meson.build b/block/meson.build
190
index XXXXXXX..XXXXXXX 100644
380
index XXXXXXX..XXXXXXX 100644
191
--- a/block/qcow.c
381
--- a/block/meson.build
192
+++ b/block/qcow.c
382
+++ b/block/meson.build
193
@@ -XXX,XX +XXX,XX @@
383
@@ -XXX,XX +XXX,XX @@ block_ss.add(when: 'CONFIG_QED', if_true: files(
194
#include "qapi/error.h"
384
'qed-table.c',
195
#include "qemu/error-report.h"
385
'qed.c',
196
#include "block/block_int.h"
386
))
197
+#include "block/qdict.h"
387
-block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'], if_true: files('parallels.c'))
198
#include "sysemu/block-backend.h"
388
+block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'],
199
#include "qemu/module.h"
389
+ if_true: files('parallels.c', 'parallels-ext.c'))
200
#include "qemu/option.h"
390
block_ss.add(when: 'CONFIG_WIN32', if_true: files('file-win32.c', 'win32-aio.c'))
201
diff --git a/block/qcow2.c b/block/qcow2.c
391
block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit])
202
index XXXXXXX..XXXXXXX 100644
392
block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c'))
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
--
393
--
404
2.13.6
394
2.29.2
405
395
406
396
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
These point to the job versions now, not the blockjob versions which
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
don't really exist anymore.
4
Message-Id: <20210224104707.88430-6-vsementsov@virtuozzo.com>
5
5
Reviewed-by: Denis V. Lunev <den@openvz.org>
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>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
7
---
14
qapi/job.json | 12 ++++++------
8
tests/qemu-iotests/iotests.py | 10 ++++++++++
15
1 file changed, 6 insertions(+), 6 deletions(-)
9
1 file changed, 10 insertions(+)
16
10
17
diff --git a/qapi/job.json b/qapi/job.json
11
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
18
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
19
--- a/qapi/job.json
13
--- a/tests/qemu-iotests/iotests.py
20
+++ b/qapi/job.json
14
+++ b/tests/qemu-iotests/iotests.py
21
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@
22
#
16
#
23
# Represents command verbs that can be applied to a job.
17
24
#
18
import atexit
25
-# @cancel: see @block-job-cancel
19
+import bz2
26
+# @cancel: see @job-cancel
20
from collections import OrderedDict
27
#
21
import faulthandler
28
-# @pause: see @block-job-pause
22
import io
29
+# @pause: see @job-pause
23
@@ -XXX,XX +XXX,XX @@
30
#
24
import logging
31
-# @resume: see @block-job-resume
25
import os
32
+# @resume: see @job-resume
26
import re
33
#
27
+import shutil
34
# @set-speed: see @block-job-set-speed
28
import signal
35
#
29
import struct
36
-# @complete: see @block-job-complete
30
import subprocess
37
+# @complete: see @job-complete
31
@@ -XXX,XX +XXX,XX @@
38
#
32
os.environ.get('IMGKEYSECRET', '')
39
-# @dismiss: see @block-job-dismiss
33
luks_default_key_secret_opt = 'key-secret=keysec0'
40
+# @dismiss: see @job-dismiss
34
41
#
35
+sample_img_dir = os.environ['SAMPLE_IMG_DIR']
42
-# @finalize: see @block-job-finalize
36
+
43
+# @finalize: see @job-finalize
37
+
44
#
38
+def unarchive_sample_image(sample, fname):
45
# Since: 2.12
39
+ sample_fname = os.path.join(sample_img_dir, sample + '.bz2')
46
##
40
+ with bz2.open(sample_fname) as f_in, open(fname, 'wb') as f_out:
41
+ shutil.copyfileobj(f_in, f_out)
42
+
43
44
def qemu_tool_pipe_and_status(tool: str, args: Sequence[str],
45
connect_stderr: bool = True) -> Tuple[str, int]:
47
--
46
--
48
2.13.6
47
2.29.2
49
48
50
49
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
Although qemu-img creates aligned files (by rounding up), it
3
Test support for reading bitmap from parallels image format.
4
must also gracefully handle files that are not sector-aligned.
4
parallels-with-bitmap.bz2 is generated on Virtuozzo by
5
Test that the bug fixed in the previous patch does not recur.
5
parallels-with-bitmap.sh
6
6
7
It's a bit annoying that we can see the (implicit) hole past
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
the end of the file on to the next sector boundary, so if we
8
Message-Id: <20210224104707.88430-7-vsementsov@virtuozzo.com>
9
ever reach the point where we report a byte-accurate size rather
9
Reviewed-by: Denis V. Lunev <den@openvz.org>
10
than our current behavior of always rounding up, this test will
11
probably need a slight modification.
12
13
Signed-off-by: Eric Blake <eblake@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
11
---
16
tests/qemu-iotests/221 | 60 ++++++++++++++++++++++++++++++++++++++++++++++
12
.../sample_images/parallels-with-bitmap.bz2 | Bin 0 -> 203 bytes
17
tests/qemu-iotests/221.out | 16 +++++++++++++
13
.../sample_images/parallels-with-bitmap.sh | 51 ++++++++++++++++
18
tests/qemu-iotests/group | 1 +
14
.../qemu-iotests/tests/parallels-read-bitmap | 55 ++++++++++++++++++
19
3 files changed, 77 insertions(+)
15
.../tests/parallels-read-bitmap.out | 6 ++
20
create mode 100755 tests/qemu-iotests/221
16
4 files changed, 112 insertions(+)
21
create mode 100644 tests/qemu-iotests/221.out
17
create mode 100644 tests/qemu-iotests/sample_images/parallels-with-bitmap.bz2
18
create mode 100755 tests/qemu-iotests/sample_images/parallels-with-bitmap.sh
19
create mode 100755 tests/qemu-iotests/tests/parallels-read-bitmap
20
create mode 100644 tests/qemu-iotests/tests/parallels-read-bitmap.out
22
21
23
diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221
22
diff --git a/tests/qemu-iotests/sample_images/parallels-with-bitmap.bz2 b/tests/qemu-iotests/sample_images/parallels-with-bitmap.bz2
23
new file mode 100644
24
index XXXXXXX..XXXXXXX
25
GIT binary patch
26
literal 203
27
zcmV;+05tzXT4*^jL0KkKS@=;0bpT+Hf7|^?Km<xfFyKQJ7=Y^F-%vt;00~Ysa6|-=
28
zk&7Szk`SoS002EkfMftPG<ipnsiCK}K_sNmm}me3FiZr%Oaf_u5F8kD;mB_~cxD-r
29
z5P$(X{&Tq5C`<xK02D?NNdN+t$~z$m00O|zFh^ynq*yaCtkn+NZzWom<#OEoF`?zb
30
zv(i3x^K~wt!aLPcRBP+PckUsIh6*LgjYSh0`}#7hMC9NR5D)+W0d&8Mxgwk>NPH-R
31
Fx`3oHQ9u9y
32
33
literal 0
34
HcmV?d00001
35
36
diff --git a/tests/qemu-iotests/sample_images/parallels-with-bitmap.sh b/tests/qemu-iotests/sample_images/parallels-with-bitmap.sh
24
new file mode 100755
37
new file mode 100755
25
index XXXXXXX..XXXXXXX
38
index XXXXXXX..XXXXXXX
26
--- /dev/null
39
--- /dev/null
27
+++ b/tests/qemu-iotests/221
40
+++ b/tests/qemu-iotests/sample_images/parallels-with-bitmap.sh
28
@@ -XXX,XX +XXX,XX @@
41
@@ -XXX,XX +XXX,XX @@
29
+#!/bin/bash
42
+#!/bin/bash
30
+#
43
+#
31
+# Test qemu-img vs. unaligned images
44
+# Test parallels load bitmap
32
+#
45
+#
33
+# Copyright (C) 2018 Red Hat, Inc.
46
+# Copyright (c) 2021 Virtuozzo International GmbH.
34
+#
47
+#
35
+# This program is free software; you can redistribute it and/or modify
48
+# 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
49
+# 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
50
+# the Free Software Foundation; either version 2 of the License, or
38
+# (at your option) any later version.
51
+# (at your option) any later version.
...
...
44
+#
57
+#
45
+# You should have received a copy of the GNU General Public License
58
+# 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/>.
59
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
47
+#
60
+#
48
+
61
+
49
+seq="$(basename $0)"
62
+CT=parallels-with-bitmap-ct
50
+echo "QA output created by $seq"
63
+DIR=$PWD/parallels-with-bitmap-dir
64
+IMG=$DIR/root.hds
65
+XML=$DIR/DiskDescriptor.xml
66
+TARGET=parallels-with-bitmap.bz2
51
+
67
+
52
+here="$PWD"
68
+rm -rf $DIR
53
+status=1 # failure is the default!
54
+
69
+
55
+_cleanup()
70
+prlctl create $CT --vmtype ct
56
+{
71
+prlctl set $CT --device-add hdd --image $DIR --recreate --size 2G
57
+ _cleanup_test_img
58
+}
59
+trap "_cleanup; exit \$status" 0 1 2 3 15
60
+
72
+
61
+# get standard environment, filters and checks
73
+# cleanup the image
62
+. ./common.rc
74
+qemu-img create -f parallels $IMG 64G
63
+. ./common.filter
64
+
75
+
65
+_supported_fmt raw
76
+# create bitmap
66
+_supported_proto file
77
+prlctl backup $CT
67
+_supported_os Linux
68
+
78
+
69
+echo
79
+prlctl set $CT --device-del hdd1
70
+echo "=== Check mapping of unaligned raw image ==="
80
+prlctl destroy $CT
71
+echo
72
+
81
+
73
+_make_test_img 43009 # qemu-img create rounds size up
82
+dev=$(ploop mount $XML | sed -n 's/^Adding delta dev=\(\/dev\/ploop[0-9]\+\).*/\1/p')
74
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
83
+dd if=/dev/zero of=$dev bs=64K seek=5 count=2 oflag=direct
84
+dd if=/dev/zero of=$dev bs=64K seek=30 count=1 oflag=direct
85
+dd if=/dev/zero of=$dev bs=64K seek=10 count=3 oflag=direct
86
+ploop umount $XML # bitmap name will be in the output
75
+
87
+
76
+truncate --size=43009 "$TEST_IMG" # so we resize it and check again
88
+bzip2 -z $IMG
77
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
78
+
89
+
79
+$QEMU_IO -c 'w 43008 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up
90
+mv $IMG.bz2 $TARGET
80
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
81
+
91
+
82
+truncate --size=43009 "$TEST_IMG" # so we resize it and check again
92
+rm -rf $DIR
83
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
93
diff --git a/tests/qemu-iotests/tests/parallels-read-bitmap b/tests/qemu-iotests/tests/parallels-read-bitmap
94
new file mode 100755
95
index XXXXXXX..XXXXXXX
96
--- /dev/null
97
+++ b/tests/qemu-iotests/tests/parallels-read-bitmap
98
@@ -XXX,XX +XXX,XX @@
99
+#!/usr/bin/env python3
100
+#
101
+# Test parallels load bitmap
102
+#
103
+# Copyright (c) 2021 Virtuozzo International GmbH.
104
+#
105
+# This program is free software; you can redistribute it and/or modify
106
+# it under the terms of the GNU General Public License as published by
107
+# the Free Software Foundation; either version 2 of the License, or
108
+# (at your option) any later version.
109
+#
110
+# This program is distributed in the hope that it will be useful,
111
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
112
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
113
+# GNU General Public License for more details.
114
+#
115
+# You should have received a copy of the GNU General Public License
116
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
117
+#
84
+
118
+
85
+# success, all done
119
+import json
86
+echo '*** done'
120
+import iotests
87
+rm -f $seq.full
121
+from iotests import qemu_nbd_popen, qemu_img_pipe, log, file_path
88
+status=0
122
+
89
diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out
123
+iotests.script_initialize(supported_fmts=['parallels'])
124
+
125
+nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir)
126
+disk = iotests.file_path('disk')
127
+bitmap = 'e4f2eed0-37fe-4539-b50b-85d2e7fd235f'
128
+nbd_opts = f'driver=nbd,server.type=unix,server.path={nbd_sock}' \
129
+ f',x-dirty-bitmap=qemu:dirty-bitmap:{bitmap}'
130
+
131
+
132
+iotests.unarchive_sample_image('parallels-with-bitmap', disk)
133
+
134
+
135
+with qemu_nbd_popen('--read-only', f'--socket={nbd_sock}',
136
+ f'--bitmap={bitmap}', '-f', iotests.imgfmt, disk):
137
+ out = qemu_img_pipe('map', '--output=json', '--image-opts', nbd_opts)
138
+ chunks = json.loads(out)
139
+ cluster = 64 * 1024
140
+
141
+ log('dirty clusters (cluster size is 64K):')
142
+ for c in chunks:
143
+ assert c['start'] % cluster == 0
144
+ assert c['length'] % cluster == 0
145
+ if c['data']:
146
+ continue
147
+
148
+ a = c['start'] // cluster
149
+ b = (c['start'] + c['length']) // cluster
150
+ if b - a > 1:
151
+ log(f'{a}-{b-1}')
152
+ else:
153
+ log(a)
154
diff --git a/tests/qemu-iotests/tests/parallels-read-bitmap.out b/tests/qemu-iotests/tests/parallels-read-bitmap.out
90
new file mode 100644
155
new file mode 100644
91
index XXXXXXX..XXXXXXX
156
index XXXXXXX..XXXXXXX
92
--- /dev/null
157
--- /dev/null
93
+++ b/tests/qemu-iotests/221.out
158
+++ b/tests/qemu-iotests/tests/parallels-read-bitmap.out
94
@@ -XXX,XX +XXX,XX @@
159
@@ -XXX,XX +XXX,XX @@
95
+QA output created by 221
160
+Start NBD server
96
+
161
+dirty clusters (cluster size is 64K):
97
+=== Check mapping of unaligned raw image ===
162
+5-6
98
+
163
+10-12
99
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=43009
164
+30
100
+[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
165
+Kill NBD server
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
--
166
--
121
2.13.6
167
2.29.2
122
168
123
169
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
During the design for manual completion, we decided not to use the
3
Add new parallels-ext.c and myself as co-maintainer.
4
"manual" property as a shorthand for both auto-dismiss and auto-finalize.
5
4
6
Fix the wording.
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
6
Message-Id: <20210304095151.19358-1-vsementsov@virtuozzo.com>
8
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Denis V. Lunev <den@openvz.org>
9
Reviewed-by: Jeff Cody <jcody@redhat.com>
10
Reviewed-by: Markus Armbruster <armbru@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
9
---
13
qapi/job.json | 11 ++++++-----
10
MAINTAINERS | 3 +++
14
1 file changed, 6 insertions(+), 5 deletions(-)
11
1 file changed, 3 insertions(+)
15
12
16
diff --git a/qapi/job.json b/qapi/job.json
13
diff --git a/MAINTAINERS b/MAINTAINERS
17
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
18
--- a/qapi/job.json
15
--- a/MAINTAINERS
19
+++ b/qapi/job.json
16
+++ b/MAINTAINERS
20
@@ -XXX,XX +XXX,XX @@
17
@@ -XXX,XX +XXX,XX @@ F: block/dmg.c
21
# the last job in a transaction.
18
parallels
22
#
19
M: Stefan Hajnoczi <stefanha@redhat.com>
23
# @pending: The job has finished its work, but has finalization steps that it
20
M: Denis V. Lunev <den@openvz.org>
24
-# needs to make prior to completing. These changes may require
21
+M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
25
-# manual intervention by the management process if manual was set
22
L: qemu-block@nongnu.org
26
-# to true. These changes may still fail.
23
S: Supported
27
+# needs to make prior to completing. These changes will require
24
F: block/parallels.c
28
+# manual intervention via @job-finalize if auto-finalize was set to
25
+F: block/parallels-ext.c
29
+# false. These pending changes may still fail.
26
F: docs/interop/parallels.txt
30
#
27
+T: git https://src.openvz.org/scm/~vsementsov/qemu.git parallels
31
# @aborting: The job is in the process of being aborted, and will finish with
28
32
# an error. The job will afterwards report that it is @concluded.
29
qed
33
# This status may not be visible to the management process.
30
M: Stefan Hajnoczi <stefanha@redhat.com>
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
--
31
--
44
2.13.6
32
2.29.2
45
33
46
34
diff view generated by jsdifflib
1
The -drive option addr was deprecated in QEMU 2.10. It's time to remove
1
The 'name' option for NBD exports is optional. Add a note that the
2
it.
2
default for the option is the node name (people could otherwise expect
3
that it's the empty string like for qemu-nbd).
3
4
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Markus Armbruster <armbru@redhat.com>
6
Message-Id: <20210305094856.18964-1-kwolf@redhat.com>
6
Reviewed-by: Jeff Cody <jcody@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
9
---
8
include/sysemu/blockdev.h | 1 -
10
docs/tools/qemu-storage-daemon.rst | 5 +++--
9
blockdev.c | 17 +----------------
11
1 file changed, 3 insertions(+), 2 deletions(-)
10
device-hotplug.c | 4 ----
11
qemu-doc.texi | 5 -----
12
qemu-options.hx | 5 +----
13
5 files changed, 2 insertions(+), 30 deletions(-)
14
12
15
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
13
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
16
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
17
--- a/include/sysemu/blockdev.h
15
--- a/docs/tools/qemu-storage-daemon.rst
18
+++ b/include/sysemu/blockdev.h
16
+++ b/docs/tools/qemu-storage-daemon.rst
19
@@ -XXX,XX +XXX,XX @@ typedef enum {
17
@@ -XXX,XX +XXX,XX @@ Standard options:
20
} BlockInterfaceType;
18
requests for modifying data (the default is off).
21
19
22
struct DriveInfo {
20
The ``nbd`` export type requires ``--nbd-server`` (see below). ``name`` is
23
- const char *devaddr;
21
- the NBD export name. ``bitmap`` is the name of a dirty bitmap reachable from
24
BlockInterfaceType type;
22
- the block node, so the NBD client can use NBD_OPT_SET_META_CONTEXT with the
25
int bus;
23
+ the NBD export name (if not specified, it defaults to the given
26
int unit;
24
+ ``node-name``). ``bitmap`` is the name of a dirty bitmap reachable from the
27
diff --git a/blockdev.c b/blockdev.c
25
+ block node, so the NBD client can use NBD_OPT_SET_META_CONTEXT with the
28
index XXXXXXX..XXXXXXX 100644
26
metadata context name "qemu:dirty-bitmap:BITMAP" to inspect the bitmap.
29
--- a/blockdev.c
27
30
+++ b/blockdev.c
28
The ``vhost-user-blk`` export type takes a vhost-user socket address on which
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
--
29
--
145
2.13.6
30
2.29.2
146
31
147
32
diff view generated by jsdifflib