1
The following changes since commit f4c4357fbfca0fb14e477bf661ae7384b4b9b283:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-docs-20200306' into staging (2020-03-06 11:11:54 +0000)
3
Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.12-pull-request' into staging (2017-12-22 00:11:36 +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 1de6b45fb5c1489b450df7d1a4c692bba9678ce6:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
block: bdrv_reopen() with backing file in different AioContext (2020-03-06 17:34:09 +0100)
11
block: Keep nodes drained between reopen_queue/multiple (2017-12-22 15:05:32 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
16
- Add qemu-storage-daemon (still experimental)
17
- rbd: Add support for ceph namespaces
18
- Fix bdrv_reopen() with backing file in different AioContext
19
- qcow2: Fix read-write reopen with persistent dirty bitmaps
20
- qcow2: Fix alloc_cluster_abort() for pre-existing clusters
21
15
22
----------------------------------------------------------------
16
----------------------------------------------------------------
23
Florian Florensa (1):
17
Doug Gale (1):
24
block/rbd: Add support for ceph namespaces
18
nvme: Add tracing
25
19
26
Kevin Wolf (22):
20
Edgar Kaziakhmedov (1):
27
qemu-storage-daemon: Add barebone tool
21
qcow2: get rid of qcow2_backing_read1 routine
28
stubs: Add arch_type
29
block: Move system emulator QMP commands to block/qapi-sysemu.c
30
block: Move common QMP commands to block-core QAPI module
31
block: Move sysemu QMP commands to QAPI block module
32
qemu-storage-daemon: Add --blockdev option
33
qapi: Flatten object-add
34
qemu-storage-daemon: Add --object option
35
qemu-storage-daemon: Add --nbd-server option
36
blockdev-nbd: Boxed argument type for nbd-server-add
37
qemu-storage-daemon: Add --export option
38
qemu-storage-daemon: Add main loop
39
qemu-storage-daemon: Add --chardev option
40
stubs: Update monitor stubs for qemu-storage-daemon
41
qapi: Create 'pragma' module
42
monitor: Create QAPIfied monitor_init()
43
qmp: Fail gracefully if chardev is already in use
44
hmp: Fail gracefully if chardev is already in use
45
monitor: Add allow_hmp parameter to monitor_init()
46
qemu-storage-daemon: Add --monitor option
47
iotests: Refactor blockdev-reopen test for iothreads
48
block: bdrv_reopen() with backing file in different AioContext
49
22
50
Max Reitz (4):
23
Fam Zheng (2):
51
qcow2: Fix alloc_cluster_abort() for pre-existing clusters
24
block: Open backing image in force share mode for size probe
52
iotests/026: Test EIO on preallocated zero cluster
25
block: Remove unused bdrv_requests_pending
53
iotests/026: Test EIO on allocation in a data-file
54
block: Fix leak in bdrv_create_file_fallback()
55
26
56
Peter Krempa (2):
27
John Snow (1):
57
block: Introduce 'bdrv_reopen_commit_post' step
28
iotests: fix 197 for vpc
58
block/qcow2: Move bitmap reopen into bdrv_reopen_commit_post
59
29
60
qapi/block-core.json | 733 +++++++++++++++++------------------
30
Kevin Wolf (27):
61
qapi/block.json | 512 ++++++++++++++----------
31
block: Formats don't need CONSISTENT_READ with NO_IO
62
qapi/control.json | 37 ++
32
block: Make bdrv_drain_invoke() recursive
63
qapi/pragma.json | 24 ++
33
block: Call .drain_begin only once in bdrv_drain_all_begin()
64
qapi/qapi-schema.json | 25 +-
34
test-bdrv-drain: Test BlockDriver callbacks for drain
65
qapi/qom.json | 12 +-
35
block: bdrv_drain_recurse(): Remove unused begin parameter
66
qapi/transaction.json | 2 +-
36
block: Don't wait for requests in bdrv_drain*_end()
67
docs/system/deprecated.rst | 5 +
37
block: Unify order in drain functions
68
configure | 2 +-
38
block: Don't acquire AioContext in hmp_qemu_io()
69
include/block/block_int.h | 1 +
39
block: Document that x-blockdev-change breaks quorum children list
70
include/block/nbd.h | 1 +
40
block: Assert drain_all is only called from main AioContext
71
include/monitor/monitor.h | 6 +-
41
block: Make bdrv_drain() driver callbacks non-recursive
72
include/qom/object_interfaces.h | 7 +
42
test-bdrv-drain: Test callback for bdrv_drain
73
include/sysemu/arch_init.h | 2 +
43
test-bdrv-drain: Test bs->quiesce_counter
74
block.c | 44 ++-
44
blockjob: Pause job on draining any job BDS
75
block/qapi-sysemu.c | 590 ++++++++++++++++++++++++++++
45
test-bdrv-drain: Test drain vs. block jobs
76
block/qcow2-cluster.c | 2 +-
46
block: Don't block_job_pause_all() in bdrv_drain_all()
77
block/qcow2.c | 7 +-
47
block: Nested drain_end must still call callbacks
78
block/rbd.c | 44 ++-
48
test-bdrv-drain: Test nested drain sections
79
blockdev-nbd.c | 40 +-
49
block: Don't notify parents in drain call chain
80
blockdev.c | 559 --------------------------
50
block: Add bdrv_subtree_drained_begin/end()
81
chardev/char.c | 8 +-
51
test-bdrv-drain: Tests for bdrv_subtree_drain
82
gdbstub.c | 2 +-
52
test-bdrv-drain: Test behaviour in coroutine context
83
hw/block/xen-block.c | 11 +-
53
test-bdrv-drain: Recursive draining with multiple parents
84
monitor/hmp-cmds.c | 21 +-
54
block: Allow graph changes in subtree drained section
85
monitor/hmp.c | 8 +-
55
test-bdrv-drain: Test graph changes in drained section
86
monitor/misc.c | 2 +
56
commit: Simplify reopen of base
87
monitor/monitor.c | 86 ++--
57
block: Keep nodes drained between reopen_queue/multiple
88
monitor/qmp-cmds.c | 2 +-
89
monitor/qmp.c | 11 +-
90
qemu-storage-daemon.c | 340 ++++++++++++++++
91
qom/qom-qmp-cmds.c | 42 +-
92
stubs/arch_type.c | 4 +
93
stubs/monitor-core.c | 21 +
94
stubs/monitor.c | 17 +-
95
tests/test-util-sockets.c | 4 +-
96
scripts/qapi/gen.py | 5 +
97
Makefile | 37 ++
98
Makefile.objs | 9 +
99
block/Makefile.objs | 4 +-
100
monitor/Makefile.objs | 2 +
101
qapi/Makefile.objs | 7 +-
102
qom/Makefile.objs | 1 +
103
storage-daemon/Makefile.objs | 1 +
104
storage-daemon/qapi/Makefile.objs | 1 +
105
storage-daemon/qapi/qapi-schema.json | 26 ++
106
stubs/Makefile.objs | 2 +
107
tests/qemu-iotests/026 | 53 +++
108
tests/qemu-iotests/026.out | 16 +
109
tests/qemu-iotests/026.out.nocache | 16 +
110
tests/qemu-iotests/245 | 45 ++-
111
tests/qemu-iotests/245.out | 4 +-
112
52 files changed, 2157 insertions(+), 1306 deletions(-)
113
create mode 100644 qapi/pragma.json
114
create mode 100644 block/qapi-sysemu.c
115
create mode 100644 qemu-storage-daemon.c
116
create mode 100644 stubs/arch_type.c
117
create mode 100644 stubs/monitor-core.c
118
create mode 100644 storage-daemon/Makefile.objs
119
create mode 100644 storage-daemon/qapi/Makefile.objs
120
create mode 100644 storage-daemon/qapi/qapi-schema.json
121
58
59
Thomas Huth (3):
60
block: Remove the obsolete -drive boot=on|off parameter
61
block: Remove the deprecated -hdachs option
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
122
63
64
qapi/block-core.json | 4 +
65
block/qcow2.h | 3 -
66
include/block/block.h | 15 +-
67
include/block/block_int.h | 6 +-
68
block.c | 75 ++++-
69
block/commit.c | 8 +-
70
block/io.c | 164 +++++++---
71
block/qcow2.c | 51 +--
72
block/replication.c | 6 +
73
blockdev.c | 11 -
74
blockjob.c | 22 +-
75
hmp.c | 6 -
76
hw/block/nvme.c | 349 +++++++++++++++++----
77
qemu-io-cmds.c | 3 +
78
tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++
79
vl.c | 86 +-----
80
hw/block/trace-events | 93 ++++++
81
qemu-doc.texi | 29 +-
82
qemu-options.hx | 19 +-
83
tests/Makefile.include | 2 +
84
tests/qemu-iotests/197 | 4 +
85
tests/qemu-iotests/common.filter | 3 +-
86
22 files changed, 1294 insertions(+), 316 deletions(-)
87
create mode 100644 tests/test-bdrv-drain.c
88
diff view generated by jsdifflib
1
QMP commands that are related to the system emulator and don't make
1
Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently
2
sense in the context of tools such as qemu-storage-daemon should live in
2
in use as a mirror target. It is not enough for image formats, though,
3
qapi/block.json rather than qapi/block-core.json. Move them there.
3
as these still unconditionally request BLK_PERM_CONSISTENT_READ.
4
4
5
The associated data types are actually also used in code shared with the
5
As this permission is geared towards whether the guest-visible data is
6
tools, so they stay in block-core.json.
6
consistent, and has no impact on whether the metadata is sane, and
7
'qemu-img info' does not read guest-visible data (except for the raw
8
format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there
9
is not going to be any guest I/O performed, regardless of image format.
7
10
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20200224143008.13362-6-kwolf@redhat.com>
10
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
12
---
13
qapi/block-core.json | 386 -------------------------------------------
13
block.c | 6 +++++-
14
qapi/block.json | 386 +++++++++++++++++++++++++++++++++++++++++++
14
1 file changed, 5 insertions(+), 1 deletion(-)
15
monitor/qmp-cmds.c | 2 +-
16
3 files changed, 387 insertions(+), 387 deletions(-)
17
15
18
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
diff --git a/block.c b/block.c
19
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
20
--- a/qapi/block-core.json
18
--- a/block.c
21
+++ b/qapi/block-core.json
19
+++ b/block.c
22
@@ -XXX,XX +XXX,XX @@
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
23
{ 'struct': 'BlockLatencyHistogramInfo',
21
assert(role == &child_backing || role == &child_file);
24
'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } }
22
25
23
if (!backing) {
26
-##
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
27
-# @block-latency-histogram-set:
28
-#
29
-# Manage read, write and flush latency histograms for the device.
30
-#
31
-# If only @id parameter is specified, remove all present latency histograms
32
-# for the device. Otherwise, add/reset some of (or all) latency histograms.
33
-#
34
-# @id: The name or QOM path of the guest device.
35
-#
36
-# @boundaries: list of interval boundary values (see description in
37
-# BlockLatencyHistogramInfo definition). If specified, all
38
-# latency histograms are removed, and empty ones created for all
39
-# io types with intervals corresponding to @boundaries (except for
40
-# io types, for which specific boundaries are set through the
41
-# following parameters).
42
-#
43
-# @boundaries-read: list of interval boundary values for read latency
44
-# histogram. If specified, old read latency histogram is
45
-# removed, and empty one created with intervals
46
-# corresponding to @boundaries-read. The parameter has higher
47
-# priority then @boundaries.
48
-#
49
-# @boundaries-write: list of interval boundary values for write latency
50
-# histogram.
51
-#
52
-# @boundaries-flush: list of interval boundary values for flush latency
53
-# histogram.
54
-#
55
-# Returns: error if device is not found or any boundary arrays are invalid.
56
-#
57
-# Since: 4.0
58
-#
59
-# Example: set new histograms for all io types with intervals
60
-# [0, 10), [10, 50), [50, 100), [100, +inf):
61
-#
62
-# -> { "execute": "block-latency-histogram-set",
63
-# "arguments": { "id": "drive0",
64
-# "boundaries": [10, 50, 100] } }
65
-# <- { "return": {} }
66
-#
67
-# Example: set new histogram only for write, other histograms will remain
68
-# not changed (or not created):
69
-#
70
-# -> { "execute": "block-latency-histogram-set",
71
-# "arguments": { "id": "drive0",
72
-# "boundaries-write": [10, 50, 100] } }
73
-# <- { "return": {} }
74
-#
75
-# Example: set new histograms with the following intervals:
76
-# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf)
77
-# write: [0, 1000), [1000, 5000), [5000, +inf)
78
-#
79
-# -> { "execute": "block-latency-histogram-set",
80
-# "arguments": { "id": "drive0",
81
-# "boundaries": [10, 50, 100],
82
-# "boundaries-write": [1000, 5000] } }
83
-# <- { "return": {} }
84
-#
85
-# Example: remove all latency histograms:
86
-#
87
-# -> { "execute": "block-latency-histogram-set",
88
-# "arguments": { "id": "drive0" } }
89
-# <- { "return": {} }
90
-##
91
-{ 'command': 'block-latency-histogram-set',
92
- 'data': {'id': 'str',
93
- '*boundaries': ['uint64'],
94
- '*boundaries-read': ['uint64'],
95
- '*boundaries-write': ['uint64'],
96
- '*boundaries-flush': ['uint64'] } }
97
-
98
##
99
# @BlockInfo:
100
#
101
@@ -XXX,XX +XXX,XX @@
102
'*copy-mode': 'MirrorCopyMode',
103
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
104
105
-##
106
-# @block_set_io_throttle:
107
-#
108
-# Change I/O throttle limits for a block drive.
109
-#
110
-# Since QEMU 2.4, each device with I/O limits is member of a throttle
111
-# group.
112
-#
113
-# If two or more devices are members of the same group, the limits
114
-# will apply to the combined I/O of the whole group in a round-robin
115
-# fashion. Therefore, setting new I/O limits to a device will affect
116
-# the whole group.
117
-#
118
-# The name of the group can be specified using the 'group' parameter.
119
-# If the parameter is unset, it is assumed to be the current group of
120
-# that device. If it's not in any group yet, the name of the device
121
-# will be used as the name for its group.
122
-#
123
-# The 'group' parameter can also be used to move a device to a
124
-# different group. In this case the limits specified in the parameters
125
-# will be applied to the new group only.
126
-#
127
-# I/O limits can be disabled by setting all of them to 0. In this case
128
-# the device will be removed from its group and the rest of its
129
-# members will not be affected. The 'group' parameter is ignored.
130
-#
131
-# Returns: - Nothing on success
132
-# - If @device is not a valid block device, DeviceNotFound
133
-#
134
-# Since: 1.1
135
-#
136
-# Example:
137
-#
138
-# -> { "execute": "block_set_io_throttle",
139
-# "arguments": { "id": "virtio-blk-pci0/virtio-backend",
140
-# "bps": 0,
141
-# "bps_rd": 0,
142
-# "bps_wr": 0,
143
-# "iops": 512,
144
-# "iops_rd": 0,
145
-# "iops_wr": 0,
146
-# "bps_max": 0,
147
-# "bps_rd_max": 0,
148
-# "bps_wr_max": 0,
149
-# "iops_max": 0,
150
-# "iops_rd_max": 0,
151
-# "iops_wr_max": 0,
152
-# "bps_max_length": 0,
153
-# "iops_size": 0 } }
154
-# <- { "return": {} }
155
-#
156
-# -> { "execute": "block_set_io_throttle",
157
-# "arguments": { "id": "ide0-1-0",
158
-# "bps": 1000000,
159
-# "bps_rd": 0,
160
-# "bps_wr": 0,
161
-# "iops": 0,
162
-# "iops_rd": 0,
163
-# "iops_wr": 0,
164
-# "bps_max": 8000000,
165
-# "bps_rd_max": 0,
166
-# "bps_wr_max": 0,
167
-# "iops_max": 0,
168
-# "iops_rd_max": 0,
169
-# "iops_wr_max": 0,
170
-# "bps_max_length": 60,
171
-# "iops_size": 0 } }
172
-# <- { "return": {} }
173
-##
174
-{ 'command': 'block_set_io_throttle', 'boxed': true,
175
- 'data': 'BlockIOThrottle' }
176
-
177
##
178
# @BlockIOThrottle:
179
#
180
@@ -XXX,XX +XXX,XX @@
181
'data': { 'job-id': 'str',
182
'options': 'BlockdevCreateOptions' } }
183
184
-##
185
-# @blockdev-open-tray:
186
-#
187
-# Opens a block device's tray. If there is a block driver state tree inserted as
188
-# a medium, it will become inaccessible to the guest (but it will remain
189
-# associated to the block device, so closing the tray will make it accessible
190
-# again).
191
-#
192
-# If the tray was already open before, this will be a no-op.
193
-#
194
-# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in
195
-# which no such event will be generated, these include:
196
-#
197
-# - if the guest has locked the tray, @force is false and the guest does not
198
-# respond to the eject request
199
-# - if the BlockBackend denoted by @device does not have a guest device attached
200
-# to it
201
-# - if the guest device does not have an actual tray
202
-#
203
-# @device: Block device name (deprecated, use @id instead)
204
-#
205
-# @id: The name or QOM path of the guest device (since: 2.8)
206
-#
207
-# @force: if false (the default), an eject request will be sent to
208
-# the guest if it has locked the tray (and the tray will not be opened
209
-# immediately); if true, the tray will be opened regardless of whether
210
-# it is locked
211
-#
212
-# Since: 2.5
213
-#
214
-# Example:
215
-#
216
-# -> { "execute": "blockdev-open-tray",
217
-# "arguments": { "id": "ide0-1-0" } }
218
-#
219
-# <- { "timestamp": { "seconds": 1418751016,
220
-# "microseconds": 716996 },
221
-# "event": "DEVICE_TRAY_MOVED",
222
-# "data": { "device": "ide1-cd0",
223
-# "id": "ide0-1-0",
224
-# "tray-open": true } }
225
-#
226
-# <- { "return": {} }
227
-#
228
-##
229
-{ 'command': 'blockdev-open-tray',
230
- 'data': { '*device': 'str',
231
- '*id': 'str',
232
- '*force': 'bool' } }
233
-
234
-##
235
-# @blockdev-close-tray:
236
-#
237
-# Closes a block device's tray. If there is a block driver state tree associated
238
-# with the block device (which is currently ejected), that tree will be loaded
239
-# as the medium.
240
-#
241
-# If the tray was already closed before, this will be a no-op.
242
-#
243
-# @device: Block device name (deprecated, use @id instead)
244
-#
245
-# @id: The name or QOM path of the guest device (since: 2.8)
246
-#
247
-# Since: 2.5
248
-#
249
-# Example:
250
-#
251
-# -> { "execute": "blockdev-close-tray",
252
-# "arguments": { "id": "ide0-1-0" } }
253
-#
254
-# <- { "timestamp": { "seconds": 1418751345,
255
-# "microseconds": 272147 },
256
-# "event": "DEVICE_TRAY_MOVED",
257
-# "data": { "device": "ide1-cd0",
258
-# "id": "ide0-1-0",
259
-# "tray-open": false } }
260
-#
261
-# <- { "return": {} }
262
-#
263
-##
264
-{ 'command': 'blockdev-close-tray',
265
- 'data': { '*device': 'str',
266
- '*id': 'str' } }
267
-
268
-##
269
-# @blockdev-remove-medium:
270
-#
271
-# Removes a medium (a block driver state tree) from a block device. That block
272
-# device's tray must currently be open (unless there is no attached guest
273
-# device).
274
-#
275
-# If the tray is open and there is no medium inserted, this will be a no-op.
276
-#
277
-# @id: The name or QOM path of the guest device
278
-#
279
-# Since: 2.12
280
-#
281
-# Example:
282
-#
283
-# -> { "execute": "blockdev-remove-medium",
284
-# "arguments": { "id": "ide0-1-0" } }
285
-#
286
-# <- { "error": { "class": "GenericError",
287
-# "desc": "Tray of device 'ide0-1-0' is not open" } }
288
-#
289
-# -> { "execute": "blockdev-open-tray",
290
-# "arguments": { "id": "ide0-1-0" } }
291
-#
292
-# <- { "timestamp": { "seconds": 1418751627,
293
-# "microseconds": 549958 },
294
-# "event": "DEVICE_TRAY_MOVED",
295
-# "data": { "device": "ide1-cd0",
296
-# "id": "ide0-1-0",
297
-# "tray-open": true } }
298
-#
299
-# <- { "return": {} }
300
-#
301
-# -> { "execute": "blockdev-remove-medium",
302
-# "arguments": { "id": "ide0-1-0" } }
303
-#
304
-# <- { "return": {} }
305
-#
306
-##
307
-{ 'command': 'blockdev-remove-medium',
308
- 'data': { 'id': 'str' } }
309
-
310
-##
311
-# @blockdev-insert-medium:
312
-#
313
-# Inserts a medium (a block driver state tree) into a block device. That block
314
-# device's tray must currently be open (unless there is no attached guest
315
-# device) and there must be no medium inserted already.
316
-#
317
-# @id: The name or QOM path of the guest device
318
-#
319
-# @node-name: name of a node in the block driver state graph
320
-#
321
-# Since: 2.12
322
-#
323
-# Example:
324
-#
325
-# -> { "execute": "blockdev-add",
326
-# "arguments": {
327
-# "node-name": "node0",
328
-# "driver": "raw",
329
-# "file": { "driver": "file",
330
-# "filename": "fedora.iso" } } }
331
-# <- { "return": {} }
332
-#
333
-# -> { "execute": "blockdev-insert-medium",
334
-# "arguments": { "id": "ide0-1-0",
335
-# "node-name": "node0" } }
336
-#
337
-# <- { "return": {} }
338
-#
339
-##
340
-{ 'command': 'blockdev-insert-medium',
341
- 'data': { 'id': 'str',
342
- 'node-name': 'str'} }
343
-
344
-
345
-##
346
-# @BlockdevChangeReadOnlyMode:
347
-#
348
-# Specifies the new read-only mode of a block device subject to the
349
-# @blockdev-change-medium command.
350
-#
351
-# @retain: Retains the current read-only mode
352
-#
353
-# @read-only: Makes the device read-only
354
-#
355
-# @read-write: Makes the device writable
356
-#
357
-# Since: 2.3
358
-#
359
-##
360
-{ 'enum': 'BlockdevChangeReadOnlyMode',
361
- 'data': ['retain', 'read-only', 'read-write'] }
362
-
363
-
364
-##
365
-# @blockdev-change-medium:
366
-#
367
-# Changes the medium inserted into a block device by ejecting the current medium
368
-# and loading a new image file which is inserted as the new medium (this command
369
-# combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium
370
-# and blockdev-close-tray).
371
-#
372
-# @device: Block device name (deprecated, use @id instead)
373
-#
374
-# @id: The name or QOM path of the guest device
375
-# (since: 2.8)
376
-#
377
-# @filename: filename of the new image to be loaded
378
-#
379
-# @format: format to open the new image with (defaults to
380
-# the probed format)
381
-#
382
-# @read-only-mode: change the read-only mode of the device; defaults
383
-# to 'retain'
384
-#
385
-# Since: 2.5
386
-#
387
-# Examples:
388
-#
389
-# 1. Change a removable medium
390
-#
391
-# -> { "execute": "blockdev-change-medium",
392
-# "arguments": { "id": "ide0-1-0",
393
-# "filename": "/srv/images/Fedora-12-x86_64-DVD.iso",
394
-# "format": "raw" } }
395
-# <- { "return": {} }
396
-#
397
-# 2. Load a read-only medium into a writable drive
398
-#
399
-# -> { "execute": "blockdev-change-medium",
400
-# "arguments": { "id": "floppyA",
401
-# "filename": "/srv/images/ro.img",
402
-# "format": "raw",
403
-# "read-only-mode": "retain" } }
404
-#
405
-# <- { "error":
406
-# { "class": "GenericError",
407
-# "desc": "Could not open '/srv/images/ro.img': Permission denied" } }
408
-#
409
-# -> { "execute": "blockdev-change-medium",
410
-# "arguments": { "id": "floppyA",
411
-# "filename": "/srv/images/ro.img",
412
-# "format": "raw",
413
-# "read-only-mode": "read-only" } }
414
-#
415
-# <- { "return": {} }
416
-#
417
-##
418
-{ 'command': 'blockdev-change-medium',
419
- 'data': { '*device': 'str',
420
- '*id': 'str',
421
- 'filename': 'str',
422
- '*format': 'str',
423
- '*read-only-mode': 'BlockdevChangeReadOnlyMode' } }
424
-
425
-
426
##
427
# @BlockErrorAction:
428
#
429
diff --git a/qapi/block.json b/qapi/block.json
430
index XXXXXXX..XXXXXXX 100644
431
--- a/qapi/block.json
432
+++ b/qapi/block.json
433
@@ -XXX,XX +XXX,XX @@
434
'*id': 'str',
435
'*force': 'bool' } }
436
437
+##
438
+# @blockdev-open-tray:
439
+#
440
+# Opens a block device's tray. If there is a block driver state tree inserted as
441
+# a medium, it will become inaccessible to the guest (but it will remain
442
+# associated to the block device, so closing the tray will make it accessible
443
+# again).
444
+#
445
+# If the tray was already open before, this will be a no-op.
446
+#
447
+# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in
448
+# which no such event will be generated, these include:
449
+#
450
+# - if the guest has locked the tray, @force is false and the guest does not
451
+# respond to the eject request
452
+# - if the BlockBackend denoted by @device does not have a guest device attached
453
+# to it
454
+# - if the guest device does not have an actual tray
455
+#
456
+# @device: Block device name (deprecated, use @id instead)
457
+#
458
+# @id: The name or QOM path of the guest device (since: 2.8)
459
+#
460
+# @force: if false (the default), an eject request will be sent to
461
+# the guest if it has locked the tray (and the tray will not be opened
462
+# immediately); if true, the tray will be opened regardless of whether
463
+# it is locked
464
+#
465
+# Since: 2.5
466
+#
467
+# Example:
468
+#
469
+# -> { "execute": "blockdev-open-tray",
470
+# "arguments": { "id": "ide0-1-0" } }
471
+#
472
+# <- { "timestamp": { "seconds": 1418751016,
473
+# "microseconds": 716996 },
474
+# "event": "DEVICE_TRAY_MOVED",
475
+# "data": { "device": "ide1-cd0",
476
+# "id": "ide0-1-0",
477
+# "tray-open": true } }
478
+#
479
+# <- { "return": {} }
480
+#
481
+##
482
+{ 'command': 'blockdev-open-tray',
483
+ 'data': { '*device': 'str',
484
+ '*id': 'str',
485
+ '*force': 'bool' } }
486
+
25
+
487
+##
26
/* Apart from the modifications below, the same permissions are
488
+# @blockdev-close-tray:
27
* forwarded and left alone as for filters */
489
+#
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
490
+# Closes a block device's tray. If there is a block driver state tree associated
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
491
+# with the block device (which is currently ejected), that tree will be loaded
30
492
+# as the medium.
31
/* bs->file always needs to be consistent because of the metadata. We
493
+#
32
* can never allow other users to resize or write to it. */
494
+# If the tray was already closed before, this will be a no-op.
33
- perm |= BLK_PERM_CONSISTENT_READ;
495
+#
34
+ if (!(flags & BDRV_O_NO_IO)) {
496
+# @device: Block device name (deprecated, use @id instead)
35
+ perm |= BLK_PERM_CONSISTENT_READ;
497
+#
36
+ }
498
+# @id: The name or QOM path of the guest device (since: 2.8)
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
499
+#
38
} else {
500
+# Since: 2.5
39
/* We want consistent read from backing files if the parent needs it.
501
+#
502
+# Example:
503
+#
504
+# -> { "execute": "blockdev-close-tray",
505
+# "arguments": { "id": "ide0-1-0" } }
506
+#
507
+# <- { "timestamp": { "seconds": 1418751345,
508
+# "microseconds": 272147 },
509
+# "event": "DEVICE_TRAY_MOVED",
510
+# "data": { "device": "ide1-cd0",
511
+# "id": "ide0-1-0",
512
+# "tray-open": false } }
513
+#
514
+# <- { "return": {} }
515
+#
516
+##
517
+{ 'command': 'blockdev-close-tray',
518
+ 'data': { '*device': 'str',
519
+ '*id': 'str' } }
520
+
521
+##
522
+# @blockdev-remove-medium:
523
+#
524
+# Removes a medium (a block driver state tree) from a block device. That block
525
+# device's tray must currently be open (unless there is no attached guest
526
+# device).
527
+#
528
+# If the tray is open and there is no medium inserted, this will be a no-op.
529
+#
530
+# @id: The name or QOM path of the guest device
531
+#
532
+# Since: 2.12
533
+#
534
+# Example:
535
+#
536
+# -> { "execute": "blockdev-remove-medium",
537
+# "arguments": { "id": "ide0-1-0" } }
538
+#
539
+# <- { "error": { "class": "GenericError",
540
+# "desc": "Tray of device 'ide0-1-0' is not open" } }
541
+#
542
+# -> { "execute": "blockdev-open-tray",
543
+# "arguments": { "id": "ide0-1-0" } }
544
+#
545
+# <- { "timestamp": { "seconds": 1418751627,
546
+# "microseconds": 549958 },
547
+# "event": "DEVICE_TRAY_MOVED",
548
+# "data": { "device": "ide1-cd0",
549
+# "id": "ide0-1-0",
550
+# "tray-open": true } }
551
+#
552
+# <- { "return": {} }
553
+#
554
+# -> { "execute": "blockdev-remove-medium",
555
+# "arguments": { "id": "ide0-1-0" } }
556
+#
557
+# <- { "return": {} }
558
+#
559
+##
560
+{ 'command': 'blockdev-remove-medium',
561
+ 'data': { 'id': 'str' } }
562
+
563
+##
564
+# @blockdev-insert-medium:
565
+#
566
+# Inserts a medium (a block driver state tree) into a block device. That block
567
+# device's tray must currently be open (unless there is no attached guest
568
+# device) and there must be no medium inserted already.
569
+#
570
+# @id: The name or QOM path of the guest device
571
+#
572
+# @node-name: name of a node in the block driver state graph
573
+#
574
+# Since: 2.12
575
+#
576
+# Example:
577
+#
578
+# -> { "execute": "blockdev-add",
579
+# "arguments": {
580
+# "node-name": "node0",
581
+# "driver": "raw",
582
+# "file": { "driver": "file",
583
+# "filename": "fedora.iso" } } }
584
+# <- { "return": {} }
585
+#
586
+# -> { "execute": "blockdev-insert-medium",
587
+# "arguments": { "id": "ide0-1-0",
588
+# "node-name": "node0" } }
589
+#
590
+# <- { "return": {} }
591
+#
592
+##
593
+{ 'command': 'blockdev-insert-medium',
594
+ 'data': { 'id': 'str',
595
+ 'node-name': 'str'} }
596
+
597
+
598
+##
599
+# @BlockdevChangeReadOnlyMode:
600
+#
601
+# Specifies the new read-only mode of a block device subject to the
602
+# @blockdev-change-medium command.
603
+#
604
+# @retain: Retains the current read-only mode
605
+#
606
+# @read-only: Makes the device read-only
607
+#
608
+# @read-write: Makes the device writable
609
+#
610
+# Since: 2.3
611
+#
612
+##
613
+{ 'enum': 'BlockdevChangeReadOnlyMode',
614
+ 'data': ['retain', 'read-only', 'read-write'] }
615
+
616
+
617
+##
618
+# @blockdev-change-medium:
619
+#
620
+# Changes the medium inserted into a block device by ejecting the current medium
621
+# and loading a new image file which is inserted as the new medium (this command
622
+# combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium
623
+# and blockdev-close-tray).
624
+#
625
+# @device: Block device name (deprecated, use @id instead)
626
+#
627
+# @id: The name or QOM path of the guest device
628
+# (since: 2.8)
629
+#
630
+# @filename: filename of the new image to be loaded
631
+#
632
+# @format: format to open the new image with (defaults to
633
+# the probed format)
634
+#
635
+# @read-only-mode: change the read-only mode of the device; defaults
636
+# to 'retain'
637
+#
638
+# Since: 2.5
639
+#
640
+# Examples:
641
+#
642
+# 1. Change a removable medium
643
+#
644
+# -> { "execute": "blockdev-change-medium",
645
+# "arguments": { "id": "ide0-1-0",
646
+# "filename": "/srv/images/Fedora-12-x86_64-DVD.iso",
647
+# "format": "raw" } }
648
+# <- { "return": {} }
649
+#
650
+# 2. Load a read-only medium into a writable drive
651
+#
652
+# -> { "execute": "blockdev-change-medium",
653
+# "arguments": { "id": "floppyA",
654
+# "filename": "/srv/images/ro.img",
655
+# "format": "raw",
656
+# "read-only-mode": "retain" } }
657
+#
658
+# <- { "error":
659
+# { "class": "GenericError",
660
+# "desc": "Could not open '/srv/images/ro.img': Permission denied" } }
661
+#
662
+# -> { "execute": "blockdev-change-medium",
663
+# "arguments": { "id": "floppyA",
664
+# "filename": "/srv/images/ro.img",
665
+# "format": "raw",
666
+# "read-only-mode": "read-only" } }
667
+#
668
+# <- { "return": {} }
669
+#
670
+##
671
+{ 'command': 'blockdev-change-medium',
672
+ 'data': { '*device': 'str',
673
+ '*id': 'str',
674
+ 'filename': 'str',
675
+ '*format': 'str',
676
+ '*read-only-mode': 'BlockdevChangeReadOnlyMode' } }
677
+
678
+
679
##
680
# @DEVICE_TRAY_MOVED:
681
#
682
@@ -XXX,XX +XXX,XX @@
683
##
684
{ 'event': 'PR_MANAGER_STATUS_CHANGED',
685
'data': { 'id': 'str', 'connected': 'bool' } }
686
+
687
+##
688
+# @block_set_io_throttle:
689
+#
690
+# Change I/O throttle limits for a block drive.
691
+#
692
+# Since QEMU 2.4, each device with I/O limits is member of a throttle
693
+# group.
694
+#
695
+# If two or more devices are members of the same group, the limits
696
+# will apply to the combined I/O of the whole group in a round-robin
697
+# fashion. Therefore, setting new I/O limits to a device will affect
698
+# the whole group.
699
+#
700
+# The name of the group can be specified using the 'group' parameter.
701
+# If the parameter is unset, it is assumed to be the current group of
702
+# that device. If it's not in any group yet, the name of the device
703
+# will be used as the name for its group.
704
+#
705
+# The 'group' parameter can also be used to move a device to a
706
+# different group. In this case the limits specified in the parameters
707
+# will be applied to the new group only.
708
+#
709
+# I/O limits can be disabled by setting all of them to 0. In this case
710
+# the device will be removed from its group and the rest of its
711
+# members will not be affected. The 'group' parameter is ignored.
712
+#
713
+# Returns: - Nothing on success
714
+# - If @device is not a valid block device, DeviceNotFound
715
+#
716
+# Since: 1.1
717
+#
718
+# Example:
719
+#
720
+# -> { "execute": "block_set_io_throttle",
721
+# "arguments": { "id": "virtio-blk-pci0/virtio-backend",
722
+# "bps": 0,
723
+# "bps_rd": 0,
724
+# "bps_wr": 0,
725
+# "iops": 512,
726
+# "iops_rd": 0,
727
+# "iops_wr": 0,
728
+# "bps_max": 0,
729
+# "bps_rd_max": 0,
730
+# "bps_wr_max": 0,
731
+# "iops_max": 0,
732
+# "iops_rd_max": 0,
733
+# "iops_wr_max": 0,
734
+# "bps_max_length": 0,
735
+# "iops_size": 0 } }
736
+# <- { "return": {} }
737
+#
738
+# -> { "execute": "block_set_io_throttle",
739
+# "arguments": { "id": "ide0-1-0",
740
+# "bps": 1000000,
741
+# "bps_rd": 0,
742
+# "bps_wr": 0,
743
+# "iops": 0,
744
+# "iops_rd": 0,
745
+# "iops_wr": 0,
746
+# "bps_max": 8000000,
747
+# "bps_rd_max": 0,
748
+# "bps_wr_max": 0,
749
+# "iops_max": 0,
750
+# "iops_rd_max": 0,
751
+# "iops_wr_max": 0,
752
+# "bps_max_length": 60,
753
+# "iops_size": 0 } }
754
+# <- { "return": {} }
755
+##
756
+{ 'command': 'block_set_io_throttle', 'boxed': true,
757
+ 'data': 'BlockIOThrottle' }
758
+
759
+##
760
+# @block-latency-histogram-set:
761
+#
762
+# Manage read, write and flush latency histograms for the device.
763
+#
764
+# If only @id parameter is specified, remove all present latency histograms
765
+# for the device. Otherwise, add/reset some of (or all) latency histograms.
766
+#
767
+# @id: The name or QOM path of the guest device.
768
+#
769
+# @boundaries: list of interval boundary values (see description in
770
+# BlockLatencyHistogramInfo definition). If specified, all
771
+# latency histograms are removed, and empty ones created for all
772
+# io types with intervals corresponding to @boundaries (except for
773
+# io types, for which specific boundaries are set through the
774
+# following parameters).
775
+#
776
+# @boundaries-read: list of interval boundary values for read latency
777
+# histogram. If specified, old read latency histogram is
778
+# removed, and empty one created with intervals
779
+# corresponding to @boundaries-read. The parameter has higher
780
+# priority then @boundaries.
781
+#
782
+# @boundaries-write: list of interval boundary values for write latency
783
+# histogram.
784
+#
785
+# @boundaries-flush: list of interval boundary values for flush latency
786
+# histogram.
787
+#
788
+# Returns: error if device is not found or any boundary arrays are invalid.
789
+#
790
+# Since: 4.0
791
+#
792
+# Example: set new histograms for all io types with intervals
793
+# [0, 10), [10, 50), [50, 100), [100, +inf):
794
+#
795
+# -> { "execute": "block-latency-histogram-set",
796
+# "arguments": { "id": "drive0",
797
+# "boundaries": [10, 50, 100] } }
798
+# <- { "return": {} }
799
+#
800
+# Example: set new histogram only for write, other histograms will remain
801
+# not changed (or not created):
802
+#
803
+# -> { "execute": "block-latency-histogram-set",
804
+# "arguments": { "id": "drive0",
805
+# "boundaries-write": [10, 50, 100] } }
806
+# <- { "return": {} }
807
+#
808
+# Example: set new histograms with the following intervals:
809
+# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf)
810
+# write: [0, 1000), [1000, 5000), [5000, +inf)
811
+#
812
+# -> { "execute": "block-latency-histogram-set",
813
+# "arguments": { "id": "drive0",
814
+# "boundaries": [10, 50, 100],
815
+# "boundaries-write": [1000, 5000] } }
816
+# <- { "return": {} }
817
+#
818
+# Example: remove all latency histograms:
819
+#
820
+# -> { "execute": "block-latency-histogram-set",
821
+# "arguments": { "id": "drive0" } }
822
+# <- { "return": {} }
823
+##
824
+{ 'command': 'block-latency-histogram-set',
825
+ 'data': {'id': 'str',
826
+ '*boundaries': ['uint64'],
827
+ '*boundaries-read': ['uint64'],
828
+ '*boundaries-write': ['uint64'],
829
+ '*boundaries-flush': ['uint64'] } }
830
diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
831
index XXXXXXX..XXXXXXX 100644
832
--- a/monitor/qmp-cmds.c
833
+++ b/monitor/qmp-cmds.c
834
@@ -XXX,XX +XXX,XX @@
835
#include "sysemu/blockdev.h"
836
#include "sysemu/block-backend.h"
837
#include "qapi/error.h"
838
-#include "qapi/qapi-commands-block-core.h"
839
+#include "qapi/qapi-commands-block.h"
840
#include "qapi/qapi-commands-control.h"
841
#include "qapi/qapi-commands-machine.h"
842
#include "qapi/qapi-commands-misc.h"
843
--
40
--
844
2.20.1
41
2.13.6
845
42
846
43
diff view generated by jsdifflib
New patch
1
From: John Snow <jsnow@redhat.com>
1
2
3
VPC has some difficulty creating geometries of particular size.
4
However, we can indeed force it to use a literal one, so let's
5
do that for the sake of test 197, which is testing some specific
6
offsets.
7
8
Signed-off-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
13
---
14
tests/qemu-iotests/197 | 4 ++++
15
tests/qemu-iotests/common.filter | 3 ++-
16
2 files changed, 6 insertions(+), 1 deletion(-)
17
18
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/197
21
+++ b/tests/qemu-iotests/197
22
@@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ==='
23
echo
24
25
# Prep the images
26
+# VPC rounds image sizes to a specific geometry, force a specific size.
27
+if [ "$IMGFMT" = "vpc" ]; then
28
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
29
+fi
30
_make_test_img 4G
31
$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io
32
IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \
33
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
34
index XXXXXXX..XXXXXXX 100644
35
--- a/tests/qemu-iotests/common.filter
36
+++ b/tests/qemu-iotests/common.filter
37
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
38
-e "s# log_size=[0-9]\\+##g" \
39
-e "s# refcount_bits=[0-9]\\+##g" \
40
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
41
- -e "s# iter-time=[0-9]\\+##g"
42
+ -e "s# iter-time=[0-9]\\+##g" \
43
+ -e "s# force_size=\\(on\\|off\\)##g"
44
}
45
46
_filter_img_info()
47
--
48
2.13.6
49
50
diff view generated by jsdifflib
New patch
1
This change separates bdrv_drain_invoke(), which calls the BlockDriver
2
drain callbacks, from bdrv_drain_recurse(). Instead, the function
3
performs its own recursion now.
1
4
5
One reason for this is that bdrv_drain_recurse() can be called multiple
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
7
once. The separation is necessary to fix this bug.
8
9
The other reason is that we intend to go to a model where we call all
10
driver callbacks first, and only then start polling. This is not fully
11
achieved yet with this patch, as bdrv_drain_invoke() contains a
12
BDRV_POLL_WHILE() loop for the block driver callbacks, which can still
13
call callbacks for any unrelated event. It's a step in this direction
14
anyway.
15
16
Cc: qemu-stable@nongnu.org
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
---
20
block/io.c | 14 +++++++++++---
21
1 file changed, 11 insertions(+), 3 deletions(-)
22
23
diff --git a/block/io.c b/block/io.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/block/io.c
26
+++ b/block/io.c
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
28
bdrv_wakeup(bs);
29
}
30
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
33
{
34
+ BdrvChild *child, *tmp;
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
36
37
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
39
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
40
bdrv_coroutine_enter(bs, data.co);
41
BDRV_POLL_WHILE(bs, !data.done);
42
+
43
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
44
+ bdrv_drain_invoke(child->bs, begin);
45
+ }
46
}
47
48
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
49
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
50
BdrvChild *child, *tmp;
51
bool waited;
52
53
- /* Ensure any pending metadata writes are submitted to bs->file. */
54
- bdrv_drain_invoke(bs, begin);
55
-
56
/* Wait for drained requests to finish */
57
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
58
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
60
bdrv_parent_drained_begin(bs);
61
}
62
63
+ bdrv_drain_invoke(bs, true);
64
bdrv_drain_recurse(bs, true);
65
}
66
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
68
}
69
70
bdrv_parent_drained_end(bs);
71
+ bdrv_drain_invoke(bs, false);
72
bdrv_drain_recurse(bs, false);
73
aio_enable_external(bdrv_get_aio_context(bs));
74
}
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
76
aio_context_acquire(aio_context);
77
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
78
if (aio_context == bdrv_get_aio_context(bs)) {
79
+ /* FIXME Calling this multiple times is wrong */
80
+ bdrv_drain_invoke(bs, true);
81
waited |= bdrv_drain_recurse(bs, true);
82
}
83
}
84
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
85
aio_context_acquire(aio_context);
86
aio_enable_external(aio_context);
87
bdrv_parent_drained_end(bs);
88
+ bdrv_drain_invoke(bs, false);
89
bdrv_drain_recurse(bs, false);
90
aio_context_release(aio_context);
91
}
92
--
93
2.13.6
94
95
diff view generated by jsdifflib
New patch
1
bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver
2
callback inside its polling loop. This means that how many times it got
3
called for each node depended on long it had to poll the event loop.
1
4
5
This is obviously not right and results in nodes that stay drained even
6
after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per
7
node.
8
9
Fix bdrv_drain_all_begin() to call the callback only once, too.
10
11
Cc: qemu-stable@nongnu.org
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
---
15
block/io.c | 3 +--
16
1 file changed, 1 insertion(+), 2 deletions(-)
17
18
diff --git a/block/io.c b/block/io.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/io.c
21
+++ b/block/io.c
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
23
aio_context_acquire(aio_context);
24
bdrv_parent_drained_begin(bs);
25
aio_disable_external(aio_context);
26
+ bdrv_drain_invoke(bs, true);
27
aio_context_release(aio_context);
28
29
if (!g_slist_find(aio_ctxs, aio_context)) {
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
31
aio_context_acquire(aio_context);
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
33
if (aio_context == bdrv_get_aio_context(bs)) {
34
- /* FIXME Calling this multiple times is wrong */
35
- bdrv_drain_invoke(bs, true);
36
waited |= bdrv_drain_recurse(bs, true);
37
}
38
}
39
--
40
2.13.6
41
42
diff view generated by jsdifflib
1
These commands make only sense for system emulators and their
1
This adds a test case that the BlockDriver callbacks for drain are
2
implementations call functions that don't exist in tools (e.g. to
2
called in bdrv_drained_all_begin/end(), and that both of them are called
3
resolve qdev IDs). Move them out so that blockdev.c can be linked to
3
exactly once.
4
qemu-storage-daemon.
5
4
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20200224143008.13362-4-kwolf@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
8
---
11
block/qapi-sysemu.c | 590 ++++++++++++++++++++++++++++++++++++++++++++
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
12
blockdev.c | 559 -----------------------------------------
10
tests/Makefile.include | 2 +
13
block/Makefile.objs | 2 +
11
2 files changed, 139 insertions(+)
14
3 files changed, 592 insertions(+), 559 deletions(-)
12
create mode 100644 tests/test-bdrv-drain.c
15
create mode 100644 block/qapi-sysemu.c
16
13
17
diff --git a/block/qapi-sysemu.c b/block/qapi-sysemu.c
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
18
new file mode 100644
15
new file mode 100644
19
index XXXXXXX..XXXXXXX
16
index XXXXXXX..XXXXXXX
20
--- /dev/null
17
--- /dev/null
21
+++ b/block/qapi-sysemu.c
18
+++ b/tests/test-bdrv-drain.c
22
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
23
+/*
20
+/*
24
+ * QMP command handlers specific to the system emulators
21
+ * Block node draining tests
25
+ *
22
+ *
26
+ * Copyright (c) 2003-2008 Fabrice Bellard
23
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
27
+ *
28
+ * This work is licensed under the terms of the GNU GPL, version 2 or
29
+ * later. See the COPYING file in the top-level directory.
30
+ *
31
+ * This file incorporates work covered by the following copyright and
32
+ * permission notice:
33
+ *
34
+ * Copyright (c) 2003-2008 Fabrice Bellard
35
+ *
24
+ *
36
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
37
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * of this software and associated documentation files (the "Software"), to deal
38
+ * in the Software without restriction, including without limitation the rights
27
+ * in the Software without restriction, including without limitation the rights
39
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
...
...
51
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
52
+ * THE SOFTWARE.
41
+ * THE SOFTWARE.
53
+ */
42
+ */
54
+
43
+
55
+#include "qemu/osdep.h"
44
+#include "qemu/osdep.h"
45
+#include "block/block.h"
46
+#include "sysemu/block-backend.h"
47
+#include "qapi/error.h"
56
+
48
+
57
+#include "qapi/error.h"
49
+typedef struct BDRVTestState {
58
+#include "qapi/qapi-commands-block.h"
50
+ int drain_count;
59
+#include "qapi/qmp/qdict.h"
51
+} BDRVTestState;
60
+#include "sysemu/block-backend.h"
61
+#include "sysemu/blockdev.h"
62
+
52
+
63
+static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id,
53
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
64
+ Error **errp)
65
+{
54
+{
66
+ BlockBackend *blk;
55
+ BDRVTestState *s = bs->opaque;
67
+
56
+ s->drain_count++;
68
+ if (!blk_name == !qdev_id) {
69
+ error_setg(errp, "Need exactly one of 'device' and 'id'");
70
+ return NULL;
71
+ }
72
+
73
+ if (qdev_id) {
74
+ blk = blk_by_qdev_id(qdev_id, errp);
75
+ } else {
76
+ blk = blk_by_name(blk_name);
77
+ if (blk == NULL) {
78
+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
79
+ "Device '%s' not found", blk_name);
80
+ }
81
+ }
82
+
83
+ return blk;
84
+}
57
+}
85
+
58
+
86
+/*
59
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
87
+ * Attempt to open the tray of @device.
88
+ * If @force, ignore its tray lock.
89
+ * Else, if the tray is locked, don't open it, but ask the guest to open it.
90
+ * On error, store an error through @errp and return -errno.
91
+ * If @device does not exist, return -ENODEV.
92
+ * If it has no removable media, return -ENOTSUP.
93
+ * If it has no tray, return -ENOSYS.
94
+ * If the guest was asked to open the tray, return -EINPROGRESS.
95
+ * Else, return 0.
96
+ */
97
+static int do_open_tray(const char *blk_name, const char *qdev_id,
98
+ bool force, Error **errp)
99
+{
60
+{
100
+ BlockBackend *blk;
61
+ BDRVTestState *s = bs->opaque;
101
+ const char *device = qdev_id ?: blk_name;
62
+ s->drain_count--;
102
+ bool locked;
63
+}
103
+
64
+
104
+ blk = qmp_get_blk(blk_name, qdev_id, errp);
65
+static void bdrv_test_close(BlockDriverState *bs)
105
+ if (!blk) {
66
+{
106
+ return -ENODEV;
67
+ BDRVTestState *s = bs->opaque;
107
+ }
68
+ g_assert_cmpint(s->drain_count, >, 0);
69
+}
108
+
70
+
109
+ if (!blk_dev_has_removable_media(blk)) {
71
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
110
+ error_setg(errp, "Device '%s' is not removable", device);
72
+ uint64_t offset, uint64_t bytes,
111
+ return -ENOTSUP;
73
+ QEMUIOVector *qiov, int flags)
112
+ }
74
+{
113
+
75
+ /* We want this request to stay until the polling loop in drain waits for
114
+ if (!blk_dev_has_tray(blk)) {
76
+ * it to complete. We need to sleep a while as bdrv_drain_invoke() comes
115
+ error_setg(errp, "Device '%s' does not have a tray", device);
77
+ * first and polls its result, too, but it shouldn't accidentally complete
116
+ return -ENOSYS;
78
+ * this request yet. */
117
+ }
79
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
118
+
119
+ if (blk_dev_is_tray_open(blk)) {
120
+ return 0;
121
+ }
122
+
123
+ locked = blk_dev_is_medium_locked(blk);
124
+ if (locked) {
125
+ blk_dev_eject_request(blk, force);
126
+ }
127
+
128
+ if (!locked || force) {
129
+ blk_dev_change_media_cb(blk, false, &error_abort);
130
+ }
131
+
132
+ if (locked && !force) {
133
+ error_setg(errp, "Device '%s' is locked and force was not specified, "
134
+ "wait for tray to open and try again", device);
135
+ return -EINPROGRESS;
136
+ }
137
+
80
+
138
+ return 0;
81
+ return 0;
139
+}
82
+}
140
+
83
+
141
+void qmp_blockdev_open_tray(bool has_device, const char *device,
84
+static BlockDriver bdrv_test = {
142
+ bool has_id, const char *id,
85
+ .format_name = "test",
143
+ bool has_force, bool force,
86
+ .instance_size = sizeof(BDRVTestState),
144
+ Error **errp)
87
+
88
+ .bdrv_close = bdrv_test_close,
89
+ .bdrv_co_preadv = bdrv_test_co_preadv,
90
+
91
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
92
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
93
+};
94
+
95
+static void aio_ret_cb(void *opaque, int ret)
145
+{
96
+{
146
+ Error *local_err = NULL;
97
+ int *aio_ret = opaque;
147
+ int rc;
98
+ *aio_ret = ret;
148
+
149
+ if (!has_force) {
150
+ force = false;
151
+ }
152
+ rc = do_open_tray(has_device ? device : NULL,
153
+ has_id ? id : NULL,
154
+ force, &local_err);
155
+ if (rc && rc != -ENOSYS && rc != -EINPROGRESS) {
156
+ error_propagate(errp, local_err);
157
+ return;
158
+ }
159
+ error_free(local_err);
160
+}
99
+}
161
+
100
+
162
+void qmp_blockdev_close_tray(bool has_device, const char *device,
101
+static void test_drv_cb_drain_all(void)
163
+ bool has_id, const char *id,
164
+ Error **errp)
165
+{
166
+ BlockBackend *blk;
167
+ Error *local_err = NULL;
168
+
169
+ device = has_device ? device : NULL;
170
+ id = has_id ? id : NULL;
171
+
172
+ blk = qmp_get_blk(device, id, errp);
173
+ if (!blk) {
174
+ return;
175
+ }
176
+
177
+ if (!blk_dev_has_removable_media(blk)) {
178
+ error_setg(errp, "Device '%s' is not removable", device ?: id);
179
+ return;
180
+ }
181
+
182
+ if (!blk_dev_has_tray(blk)) {
183
+ /* Ignore this command on tray-less devices */
184
+ return;
185
+ }
186
+
187
+ if (!blk_dev_is_tray_open(blk)) {
188
+ return;
189
+ }
190
+
191
+ blk_dev_change_media_cb(blk, true, &local_err);
192
+ if (local_err) {
193
+ error_propagate(errp, local_err);
194
+ return;
195
+ }
196
+}
197
+
198
+static void blockdev_remove_medium(bool has_device, const char *device,
199
+ bool has_id, const char *id, Error **errp)
200
+{
102
+{
201
+ BlockBackend *blk;
103
+ BlockBackend *blk;
202
+ BlockDriverState *bs;
104
+ BlockDriverState *bs;
203
+ AioContext *aio_context;
105
+ BDRVTestState *s;
204
+ bool has_attached_device;
106
+ BlockAIOCB *acb;
107
+ int aio_ret;
205
+
108
+
206
+ device = has_device ? device : NULL;
109
+ QEMUIOVector qiov;
207
+ id = has_id ? id : NULL;
110
+ struct iovec iov = {
111
+ .iov_base = NULL,
112
+ .iov_len = 0,
113
+ };
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
208
+
115
+
209
+ blk = qmp_get_blk(device, id, errp);
116
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
210
+ if (!blk) {
117
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
211
+ return;
118
+ &error_abort);
212
+ }
119
+ s = bs->opaque;
120
+ blk_insert_bs(blk, bs, &error_abort);
213
+
121
+
214
+ /* For BBs without a device, we can exchange the BDS tree at will */
122
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
215
+ has_attached_device = blk_get_attached_dev(blk);
123
+ g_assert_cmpint(s->drain_count, ==, 0);
124
+ bdrv_drain_all_begin();
125
+ g_assert_cmpint(s->drain_count, ==, 1);
126
+ bdrv_drain_all_end();
127
+ g_assert_cmpint(s->drain_count, ==, 0);
216
+
128
+
217
+ if (has_attached_device && !blk_dev_has_removable_media(blk)) {
129
+ /* Now do the same while a request is pending */
218
+ error_setg(errp, "Device '%s' is not removable", device ?: id);
130
+ aio_ret = -EINPROGRESS;
219
+ return;
131
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
220
+ }
132
+ g_assert(acb != NULL);
133
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
221
+
134
+
222
+ if (has_attached_device && blk_dev_has_tray(blk) &&
135
+ g_assert_cmpint(s->drain_count, ==, 0);
223
+ !blk_dev_is_tray_open(blk))
136
+ bdrv_drain_all_begin();
224
+ {
137
+ g_assert_cmpint(aio_ret, ==, 0);
225
+ error_setg(errp, "Tray of device '%s' is not open", device ?: id);
138
+ g_assert_cmpint(s->drain_count, ==, 1);
226
+ return;
139
+ bdrv_drain_all_end();
227
+ }
140
+ g_assert_cmpint(s->drain_count, ==, 0);
228
+
141
+
229
+ bs = blk_bs(blk);
142
+ bdrv_unref(bs);
230
+ if (!bs) {
143
+ blk_unref(blk);
231
+ return;
232
+ }
233
+
234
+ aio_context = bdrv_get_aio_context(bs);
235
+ aio_context_acquire(aio_context);
236
+
237
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
238
+ goto out;
239
+ }
240
+
241
+ blk_remove_bs(blk);
242
+
243
+ if (!blk_dev_has_tray(blk)) {
244
+ /* For tray-less devices, blockdev-open-tray is a no-op (or may not be
245
+ * called at all); therefore, the medium needs to be ejected here.
246
+ * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
247
+ * value passed here (i.e. false). */
248
+ blk_dev_change_media_cb(blk, false, &error_abort);
249
+ }
250
+
251
+out:
252
+ aio_context_release(aio_context);
253
+}
144
+}
254
+
145
+
255
+void qmp_blockdev_remove_medium(const char *id, Error **errp)
146
+int main(int argc, char **argv)
256
+{
147
+{
257
+ blockdev_remove_medium(false, NULL, true, id, errp);
148
+ bdrv_init();
149
+ qemu_init_main_loop(&error_abort);
150
+
151
+ g_test_init(&argc, &argv, NULL);
152
+
153
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
154
+
155
+ return g_test_run();
258
+}
156
+}
259
+
157
diff --git a/tests/Makefile.include b/tests/Makefile.include
260
+static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
261
+ BlockDriverState *bs, Error **errp)
262
+{
263
+ Error *local_err = NULL;
264
+ bool has_device;
265
+ int ret;
266
+
267
+ /* For BBs without a device, we can exchange the BDS tree at will */
268
+ has_device = blk_get_attached_dev(blk);
269
+
270
+ if (has_device && !blk_dev_has_removable_media(blk)) {
271
+ error_setg(errp, "Device is not removable");
272
+ return;
273
+ }
274
+
275
+ if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
276
+ error_setg(errp, "Tray of the device is not open");
277
+ return;
278
+ }
279
+
280
+ if (blk_bs(blk)) {
281
+ error_setg(errp, "There already is a medium in the device");
282
+ return;
283
+ }
284
+
285
+ ret = blk_insert_bs(blk, bs, errp);
286
+ if (ret < 0) {
287
+ return;
288
+ }
289
+
290
+ if (!blk_dev_has_tray(blk)) {
291
+ /* For tray-less devices, blockdev-close-tray is a no-op (or may not be
292
+ * called at all); therefore, the medium needs to be pushed into the
293
+ * slot here.
294
+ * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
295
+ * value passed here (i.e. true). */
296
+ blk_dev_change_media_cb(blk, true, &local_err);
297
+ if (local_err) {
298
+ error_propagate(errp, local_err);
299
+ blk_remove_bs(blk);
300
+ return;
301
+ }
302
+ }
303
+}
304
+
305
+static void blockdev_insert_medium(bool has_device, const char *device,
306
+ bool has_id, const char *id,
307
+ const char *node_name, Error **errp)
308
+{
309
+ BlockBackend *blk;
310
+ BlockDriverState *bs;
311
+
312
+ blk = qmp_get_blk(has_device ? device : NULL,
313
+ has_id ? id : NULL,
314
+ errp);
315
+ if (!blk) {
316
+ return;
317
+ }
318
+
319
+ bs = bdrv_find_node(node_name);
320
+ if (!bs) {
321
+ error_setg(errp, "Node '%s' not found", node_name);
322
+ return;
323
+ }
324
+
325
+ if (bdrv_has_blk(bs)) {
326
+ error_setg(errp, "Node '%s' is already in use", node_name);
327
+ return;
328
+ }
329
+
330
+ qmp_blockdev_insert_anon_medium(blk, bs, errp);
331
+}
332
+
333
+void qmp_blockdev_insert_medium(const char *id, const char *node_name,
334
+ Error **errp)
335
+{
336
+ blockdev_insert_medium(false, NULL, true, id, node_name, errp);
337
+}
338
+
339
+void qmp_blockdev_change_medium(bool has_device, const char *device,
340
+ bool has_id, const char *id,
341
+ const char *filename,
342
+ bool has_format, const char *format,
343
+ bool has_read_only,
344
+ BlockdevChangeReadOnlyMode read_only,
345
+ Error **errp)
346
+{
347
+ BlockBackend *blk;
348
+ BlockDriverState *medium_bs = NULL;
349
+ int bdrv_flags;
350
+ bool detect_zeroes;
351
+ int rc;
352
+ QDict *options = NULL;
353
+ Error *err = NULL;
354
+
355
+ blk = qmp_get_blk(has_device ? device : NULL,
356
+ has_id ? id : NULL,
357
+ errp);
358
+ if (!blk) {
359
+ goto fail;
360
+ }
361
+
362
+ if (blk_bs(blk)) {
363
+ blk_update_root_state(blk);
364
+ }
365
+
366
+ bdrv_flags = blk_get_open_flags_from_root_state(blk);
367
+ bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
368
+ BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
369
+
370
+ if (!has_read_only) {
371
+ read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
372
+ }
373
+
374
+ switch (read_only) {
375
+ case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
376
+ break;
377
+
378
+ case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
379
+ bdrv_flags &= ~BDRV_O_RDWR;
380
+ break;
381
+
382
+ case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
383
+ bdrv_flags |= BDRV_O_RDWR;
384
+ break;
385
+
386
+ default:
387
+ abort();
388
+ }
389
+
390
+ options = qdict_new();
391
+ detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
392
+ qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
393
+
394
+ if (has_format) {
395
+ qdict_put_str(options, "driver", format);
396
+ }
397
+
398
+ medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
399
+ if (!medium_bs) {
400
+ goto fail;
401
+ }
402
+
403
+ rc = do_open_tray(has_device ? device : NULL,
404
+ has_id ? id : NULL,
405
+ false, &err);
406
+ if (rc && rc != -ENOSYS) {
407
+ error_propagate(errp, err);
408
+ goto fail;
409
+ }
410
+ error_free(err);
411
+ err = NULL;
412
+
413
+ blockdev_remove_medium(has_device, device, has_id, id, &err);
414
+ if (err) {
415
+ error_propagate(errp, err);
416
+ goto fail;
417
+ }
418
+
419
+ qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
420
+ if (err) {
421
+ error_propagate(errp, err);
422
+ goto fail;
423
+ }
424
+
425
+ qmp_blockdev_close_tray(has_device, device, has_id, id, errp);
426
+
427
+fail:
428
+ /* If the medium has been inserted, the device has its own reference, so
429
+ * ours must be relinquished; and if it has not been inserted successfully,
430
+ * the reference must be relinquished anyway */
431
+ bdrv_unref(medium_bs);
432
+}
433
+
434
+void qmp_eject(bool has_device, const char *device,
435
+ bool has_id, const char *id,
436
+ bool has_force, bool force, Error **errp)
437
+{
438
+ Error *local_err = NULL;
439
+ int rc;
440
+
441
+ if (!has_force) {
442
+ force = false;
443
+ }
444
+
445
+ rc = do_open_tray(has_device ? device : NULL,
446
+ has_id ? id : NULL,
447
+ force, &local_err);
448
+ if (rc && rc != -ENOSYS) {
449
+ error_propagate(errp, local_err);
450
+ return;
451
+ }
452
+ error_free(local_err);
453
+
454
+ blockdev_remove_medium(has_device, device, has_id, id, errp);
455
+}
456
+
457
+/* throttling disk I/O limits */
458
+void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
459
+{
460
+ ThrottleConfig cfg;
461
+ BlockDriverState *bs;
462
+ BlockBackend *blk;
463
+ AioContext *aio_context;
464
+
465
+ blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
466
+ arg->has_id ? arg->id : NULL,
467
+ errp);
468
+ if (!blk) {
469
+ return;
470
+ }
471
+
472
+ aio_context = blk_get_aio_context(blk);
473
+ aio_context_acquire(aio_context);
474
+
475
+ bs = blk_bs(blk);
476
+ if (!bs) {
477
+ error_setg(errp, "Device has no medium");
478
+ goto out;
479
+ }
480
+
481
+ throttle_config_init(&cfg);
482
+ cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
483
+ cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd;
484
+ cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
485
+
486
+ cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
487
+ cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd;
488
+ cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
489
+
490
+ if (arg->has_bps_max) {
491
+ cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
492
+ }
493
+ if (arg->has_bps_rd_max) {
494
+ cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
495
+ }
496
+ if (arg->has_bps_wr_max) {
497
+ cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
498
+ }
499
+ if (arg->has_iops_max) {
500
+ cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
501
+ }
502
+ if (arg->has_iops_rd_max) {
503
+ cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
504
+ }
505
+ if (arg->has_iops_wr_max) {
506
+ cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
507
+ }
508
+
509
+ if (arg->has_bps_max_length) {
510
+ cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
511
+ }
512
+ if (arg->has_bps_rd_max_length) {
513
+ cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
514
+ }
515
+ if (arg->has_bps_wr_max_length) {
516
+ cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
517
+ }
518
+ if (arg->has_iops_max_length) {
519
+ cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
520
+ }
521
+ if (arg->has_iops_rd_max_length) {
522
+ cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
523
+ }
524
+ if (arg->has_iops_wr_max_length) {
525
+ cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
526
+ }
527
+
528
+ if (arg->has_iops_size) {
529
+ cfg.op_size = arg->iops_size;
530
+ }
531
+
532
+ if (!throttle_is_valid(&cfg, errp)) {
533
+ goto out;
534
+ }
535
+
536
+ if (throttle_enabled(&cfg)) {
537
+ /* Enable I/O limits if they're not enabled yet, otherwise
538
+ * just update the throttling group. */
539
+ if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
540
+ blk_io_limits_enable(blk,
541
+ arg->has_group ? arg->group :
542
+ arg->has_device ? arg->device :
543
+ arg->id);
544
+ } else if (arg->has_group) {
545
+ blk_io_limits_update_group(blk, arg->group);
546
+ }
547
+ /* Set the new throttling configuration */
548
+ blk_set_io_limits(blk, &cfg);
549
+ } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
550
+ /* If all throttling settings are set to 0, disable I/O limits */
551
+ blk_io_limits_disable(blk);
552
+ }
553
+
554
+out:
555
+ aio_context_release(aio_context);
556
+}
557
+
558
+void qmp_block_latency_histogram_set(
559
+ const char *id,
560
+ bool has_boundaries, uint64List *boundaries,
561
+ bool has_boundaries_read, uint64List *boundaries_read,
562
+ bool has_boundaries_write, uint64List *boundaries_write,
563
+ bool has_boundaries_flush, uint64List *boundaries_flush,
564
+ Error **errp)
565
+{
566
+ BlockBackend *blk = qmp_get_blk(NULL, id, errp);
567
+ BlockAcctStats *stats;
568
+ int ret;
569
+
570
+ if (!blk) {
571
+ return;
572
+ }
573
+
574
+ stats = blk_get_stats(blk);
575
+
576
+ if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
577
+ !has_boundaries_flush)
578
+ {
579
+ block_latency_histograms_clear(stats);
580
+ return;
581
+ }
582
+
583
+ if (has_boundaries || has_boundaries_read) {
584
+ ret = block_latency_histogram_set(
585
+ stats, BLOCK_ACCT_READ,
586
+ has_boundaries_read ? boundaries_read : boundaries);
587
+ if (ret) {
588
+ error_setg(errp, "Device '%s' set read boundaries fail", id);
589
+ return;
590
+ }
591
+ }
592
+
593
+ if (has_boundaries || has_boundaries_write) {
594
+ ret = block_latency_histogram_set(
595
+ stats, BLOCK_ACCT_WRITE,
596
+ has_boundaries_write ? boundaries_write : boundaries);
597
+ if (ret) {
598
+ error_setg(errp, "Device '%s' set write boundaries fail", id);
599
+ return;
600
+ }
601
+ }
602
+
603
+ if (has_boundaries || has_boundaries_flush) {
604
+ ret = block_latency_histogram_set(
605
+ stats, BLOCK_ACCT_FLUSH,
606
+ has_boundaries_flush ? boundaries_flush : boundaries);
607
+ if (ret) {
608
+ error_setg(errp, "Device '%s' set flush boundaries fail", id);
609
+ return;
610
+ }
611
+ }
612
+}
613
diff --git a/blockdev.c b/blockdev.c
614
index XXXXXXX..XXXXXXX 100644
158
index XXXXXXX..XXXXXXX 100644
615
--- a/blockdev.c
159
--- a/tests/Makefile.include
616
+++ b/blockdev.c
160
+++ b/tests/Makefile.include
617
@@ -XXX,XX +XXX,XX @@
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
618
static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
162
gcov-files-test-hbitmap-y = util/hbitmap.c
619
QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
163
check-unit-y += tests/test-hbitmap$(EXESUF)
620
164
gcov-files-test-hbitmap-y = blockjob.c
621
-static int do_open_tray(const char *blk_name, const char *qdev_id,
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
622
- bool force, Error **errp);
166
check-unit-y += tests/test-blockjob$(EXESUF)
623
-static void blockdev_remove_medium(bool has_device, const char *device,
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
624
- bool has_id, const char *id, Error **errp);
168
check-unit-y += tests/test-x86-cpuid$(EXESUF)
625
-static void blockdev_insert_medium(bool has_device, const char *device,
169
@@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
626
- bool has_id, const char *id,
170
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
627
- const char *node_name, Error **errp);
171
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
628
-
172
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
629
static const char *const if_name[IF_COUNT] = {
173
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
630
[IF_NONE] = "none",
174
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
631
[IF_IDE] = "ide",
175
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
632
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp)
176
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
633
return bs;
634
}
635
636
-static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id,
637
- Error **errp)
638
-{
639
- BlockBackend *blk;
640
-
641
- if (!blk_name == !qdev_id) {
642
- error_setg(errp, "Need exactly one of 'device' and 'id'");
643
- return NULL;
644
- }
645
-
646
- if (qdev_id) {
647
- blk = blk_by_qdev_id(qdev_id, errp);
648
- } else {
649
- blk = blk_by_name(blk_name);
650
- if (blk == NULL) {
651
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
652
- "Device '%s' not found", blk_name);
653
- }
654
- }
655
-
656
- return blk;
657
-}
658
-
659
void hmp_commit(Monitor *mon, const QDict *qdict)
660
{
661
const char *device = qdict_get_str(qdict, "device");
662
@@ -XXX,XX +XXX,XX @@ exit:
663
job_txn_unref(block_job_txn);
664
}
665
666
-void qmp_eject(bool has_device, const char *device,
667
- bool has_id, const char *id,
668
- bool has_force, bool force, Error **errp)
669
-{
670
- Error *local_err = NULL;
671
- int rc;
672
-
673
- if (!has_force) {
674
- force = false;
675
- }
676
-
677
- rc = do_open_tray(has_device ? device : NULL,
678
- has_id ? id : NULL,
679
- force, &local_err);
680
- if (rc && rc != -ENOSYS) {
681
- error_propagate(errp, local_err);
682
- return;
683
- }
684
- error_free(local_err);
685
-
686
- blockdev_remove_medium(has_device, device, has_id, id, errp);
687
-}
688
-
689
void qmp_block_passwd(bool has_device, const char *device,
690
bool has_node_name, const char *node_name,
691
const char *password, Error **errp)
692
@@ -XXX,XX +XXX,XX @@ void qmp_block_passwd(bool has_device, const char *device,
693
"Setting block passwords directly is no longer supported");
694
}
695
696
-/*
697
- * Attempt to open the tray of @device.
698
- * If @force, ignore its tray lock.
699
- * Else, if the tray is locked, don't open it, but ask the guest to open it.
700
- * On error, store an error through @errp and return -errno.
701
- * If @device does not exist, return -ENODEV.
702
- * If it has no removable media, return -ENOTSUP.
703
- * If it has no tray, return -ENOSYS.
704
- * If the guest was asked to open the tray, return -EINPROGRESS.
705
- * Else, return 0.
706
- */
707
-static int do_open_tray(const char *blk_name, const char *qdev_id,
708
- bool force, Error **errp)
709
-{
710
- BlockBackend *blk;
711
- const char *device = qdev_id ?: blk_name;
712
- bool locked;
713
-
714
- blk = qmp_get_blk(blk_name, qdev_id, errp);
715
- if (!blk) {
716
- return -ENODEV;
717
- }
718
-
719
- if (!blk_dev_has_removable_media(blk)) {
720
- error_setg(errp, "Device '%s' is not removable", device);
721
- return -ENOTSUP;
722
- }
723
-
724
- if (!blk_dev_has_tray(blk)) {
725
- error_setg(errp, "Device '%s' does not have a tray", device);
726
- return -ENOSYS;
727
- }
728
-
729
- if (blk_dev_is_tray_open(blk)) {
730
- return 0;
731
- }
732
-
733
- locked = blk_dev_is_medium_locked(blk);
734
- if (locked) {
735
- blk_dev_eject_request(blk, force);
736
- }
737
-
738
- if (!locked || force) {
739
- blk_dev_change_media_cb(blk, false, &error_abort);
740
- }
741
-
742
- if (locked && !force) {
743
- error_setg(errp, "Device '%s' is locked and force was not specified, "
744
- "wait for tray to open and try again", device);
745
- return -EINPROGRESS;
746
- }
747
-
748
- return 0;
749
-}
750
-
751
-void qmp_blockdev_open_tray(bool has_device, const char *device,
752
- bool has_id, const char *id,
753
- bool has_force, bool force,
754
- Error **errp)
755
-{
756
- Error *local_err = NULL;
757
- int rc;
758
-
759
- if (!has_force) {
760
- force = false;
761
- }
762
- rc = do_open_tray(has_device ? device : NULL,
763
- has_id ? id : NULL,
764
- force, &local_err);
765
- if (rc && rc != -ENOSYS && rc != -EINPROGRESS) {
766
- error_propagate(errp, local_err);
767
- return;
768
- }
769
- error_free(local_err);
770
-}
771
-
772
-void qmp_blockdev_close_tray(bool has_device, const char *device,
773
- bool has_id, const char *id,
774
- Error **errp)
775
-{
776
- BlockBackend *blk;
777
- Error *local_err = NULL;
778
-
779
- device = has_device ? device : NULL;
780
- id = has_id ? id : NULL;
781
-
782
- blk = qmp_get_blk(device, id, errp);
783
- if (!blk) {
784
- return;
785
- }
786
-
787
- if (!blk_dev_has_removable_media(blk)) {
788
- error_setg(errp, "Device '%s' is not removable", device ?: id);
789
- return;
790
- }
791
-
792
- if (!blk_dev_has_tray(blk)) {
793
- /* Ignore this command on tray-less devices */
794
- return;
795
- }
796
-
797
- if (!blk_dev_is_tray_open(blk)) {
798
- return;
799
- }
800
-
801
- blk_dev_change_media_cb(blk, true, &local_err);
802
- if (local_err) {
803
- error_propagate(errp, local_err);
804
- return;
805
- }
806
-}
807
-
808
-static void blockdev_remove_medium(bool has_device, const char *device,
809
- bool has_id, const char *id, Error **errp)
810
-{
811
- BlockBackend *blk;
812
- BlockDriverState *bs;
813
- AioContext *aio_context;
814
- bool has_attached_device;
815
-
816
- device = has_device ? device : NULL;
817
- id = has_id ? id : NULL;
818
-
819
- blk = qmp_get_blk(device, id, errp);
820
- if (!blk) {
821
- return;
822
- }
823
-
824
- /* For BBs without a device, we can exchange the BDS tree at will */
825
- has_attached_device = blk_get_attached_dev(blk);
826
-
827
- if (has_attached_device && !blk_dev_has_removable_media(blk)) {
828
- error_setg(errp, "Device '%s' is not removable", device ?: id);
829
- return;
830
- }
831
-
832
- if (has_attached_device && blk_dev_has_tray(blk) &&
833
- !blk_dev_is_tray_open(blk))
834
- {
835
- error_setg(errp, "Tray of device '%s' is not open", device ?: id);
836
- return;
837
- }
838
-
839
- bs = blk_bs(blk);
840
- if (!bs) {
841
- return;
842
- }
843
-
844
- aio_context = bdrv_get_aio_context(bs);
845
- aio_context_acquire(aio_context);
846
-
847
- if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
848
- goto out;
849
- }
850
-
851
- blk_remove_bs(blk);
852
-
853
- if (!blk_dev_has_tray(blk)) {
854
- /* For tray-less devices, blockdev-open-tray is a no-op (or may not be
855
- * called at all); therefore, the medium needs to be ejected here.
856
- * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load
857
- * value passed here (i.e. false). */
858
- blk_dev_change_media_cb(blk, false, &error_abort);
859
- }
860
-
861
-out:
862
- aio_context_release(aio_context);
863
-}
864
-
865
-void qmp_blockdev_remove_medium(const char *id, Error **errp)
866
-{
867
- blockdev_remove_medium(false, NULL, true, id, errp);
868
-}
869
-
870
-static void qmp_blockdev_insert_anon_medium(BlockBackend *blk,
871
- BlockDriverState *bs, Error **errp)
872
-{
873
- Error *local_err = NULL;
874
- bool has_device;
875
- int ret;
876
-
877
- /* For BBs without a device, we can exchange the BDS tree at will */
878
- has_device = blk_get_attached_dev(blk);
879
-
880
- if (has_device && !blk_dev_has_removable_media(blk)) {
881
- error_setg(errp, "Device is not removable");
882
- return;
883
- }
884
-
885
- if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) {
886
- error_setg(errp, "Tray of the device is not open");
887
- return;
888
- }
889
-
890
- if (blk_bs(blk)) {
891
- error_setg(errp, "There already is a medium in the device");
892
- return;
893
- }
894
-
895
- ret = blk_insert_bs(blk, bs, errp);
896
- if (ret < 0) {
897
- return;
898
- }
899
-
900
- if (!blk_dev_has_tray(blk)) {
901
- /* For tray-less devices, blockdev-close-tray is a no-op (or may not be
902
- * called at all); therefore, the medium needs to be pushed into the
903
- * slot here.
904
- * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load
905
- * value passed here (i.e. true). */
906
- blk_dev_change_media_cb(blk, true, &local_err);
907
- if (local_err) {
908
- error_propagate(errp, local_err);
909
- blk_remove_bs(blk);
910
- return;
911
- }
912
- }
913
-}
914
-
915
-static void blockdev_insert_medium(bool has_device, const char *device,
916
- bool has_id, const char *id,
917
- const char *node_name, Error **errp)
918
-{
919
- BlockBackend *blk;
920
- BlockDriverState *bs;
921
-
922
- blk = qmp_get_blk(has_device ? device : NULL,
923
- has_id ? id : NULL,
924
- errp);
925
- if (!blk) {
926
- return;
927
- }
928
-
929
- bs = bdrv_find_node(node_name);
930
- if (!bs) {
931
- error_setg(errp, "Node '%s' not found", node_name);
932
- return;
933
- }
934
-
935
- if (bdrv_has_blk(bs)) {
936
- error_setg(errp, "Node '%s' is already in use", node_name);
937
- return;
938
- }
939
-
940
- qmp_blockdev_insert_anon_medium(blk, bs, errp);
941
-}
942
-
943
-void qmp_blockdev_insert_medium(const char *id, const char *node_name,
944
- Error **errp)
945
-{
946
- blockdev_insert_medium(false, NULL, true, id, node_name, errp);
947
-}
948
-
949
-void qmp_blockdev_change_medium(bool has_device, const char *device,
950
- bool has_id, const char *id,
951
- const char *filename,
952
- bool has_format, const char *format,
953
- bool has_read_only,
954
- BlockdevChangeReadOnlyMode read_only,
955
- Error **errp)
956
-{
957
- BlockBackend *blk;
958
- BlockDriverState *medium_bs = NULL;
959
- int bdrv_flags;
960
- bool detect_zeroes;
961
- int rc;
962
- QDict *options = NULL;
963
- Error *err = NULL;
964
-
965
- blk = qmp_get_blk(has_device ? device : NULL,
966
- has_id ? id : NULL,
967
- errp);
968
- if (!blk) {
969
- goto fail;
970
- }
971
-
972
- if (blk_bs(blk)) {
973
- blk_update_root_state(blk);
974
- }
975
-
976
- bdrv_flags = blk_get_open_flags_from_root_state(blk);
977
- bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
978
- BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
979
-
980
- if (!has_read_only) {
981
- read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
982
- }
983
-
984
- switch (read_only) {
985
- case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN:
986
- break;
987
-
988
- case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY:
989
- bdrv_flags &= ~BDRV_O_RDWR;
990
- break;
991
-
992
- case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE:
993
- bdrv_flags |= BDRV_O_RDWR;
994
- break;
995
-
996
- default:
997
- abort();
998
- }
999
-
1000
- options = qdict_new();
1001
- detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
1002
- qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
1003
-
1004
- if (has_format) {
1005
- qdict_put_str(options, "driver", format);
1006
- }
1007
-
1008
- medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp);
1009
- if (!medium_bs) {
1010
- goto fail;
1011
- }
1012
-
1013
- rc = do_open_tray(has_device ? device : NULL,
1014
- has_id ? id : NULL,
1015
- false, &err);
1016
- if (rc && rc != -ENOSYS) {
1017
- error_propagate(errp, err);
1018
- goto fail;
1019
- }
1020
- error_free(err);
1021
- err = NULL;
1022
-
1023
- blockdev_remove_medium(has_device, device, has_id, id, &err);
1024
- if (err) {
1025
- error_propagate(errp, err);
1026
- goto fail;
1027
- }
1028
-
1029
- qmp_blockdev_insert_anon_medium(blk, medium_bs, &err);
1030
- if (err) {
1031
- error_propagate(errp, err);
1032
- goto fail;
1033
- }
1034
-
1035
- qmp_blockdev_close_tray(has_device, device, has_id, id, errp);
1036
-
1037
-fail:
1038
- /* If the medium has been inserted, the device has its own reference, so
1039
- * ours must be relinquished; and if it has not been inserted successfully,
1040
- * the reference must be relinquished anyway */
1041
- bdrv_unref(medium_bs);
1042
-}
1043
-
1044
-/* throttling disk I/O limits */
1045
-void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp)
1046
-{
1047
- ThrottleConfig cfg;
1048
- BlockDriverState *bs;
1049
- BlockBackend *blk;
1050
- AioContext *aio_context;
1051
-
1052
- blk = qmp_get_blk(arg->has_device ? arg->device : NULL,
1053
- arg->has_id ? arg->id : NULL,
1054
- errp);
1055
- if (!blk) {
1056
- return;
1057
- }
1058
-
1059
- aio_context = blk_get_aio_context(blk);
1060
- aio_context_acquire(aio_context);
1061
-
1062
- bs = blk_bs(blk);
1063
- if (!bs) {
1064
- error_setg(errp, "Device has no medium");
1065
- goto out;
1066
- }
1067
-
1068
- throttle_config_init(&cfg);
1069
- cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps;
1070
- cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd;
1071
- cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr;
1072
-
1073
- cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops;
1074
- cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd;
1075
- cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr;
1076
-
1077
- if (arg->has_bps_max) {
1078
- cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max;
1079
- }
1080
- if (arg->has_bps_rd_max) {
1081
- cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max;
1082
- }
1083
- if (arg->has_bps_wr_max) {
1084
- cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max;
1085
- }
1086
- if (arg->has_iops_max) {
1087
- cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max;
1088
- }
1089
- if (arg->has_iops_rd_max) {
1090
- cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max;
1091
- }
1092
- if (arg->has_iops_wr_max) {
1093
- cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max;
1094
- }
1095
-
1096
- if (arg->has_bps_max_length) {
1097
- cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length;
1098
- }
1099
- if (arg->has_bps_rd_max_length) {
1100
- cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length;
1101
- }
1102
- if (arg->has_bps_wr_max_length) {
1103
- cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length;
1104
- }
1105
- if (arg->has_iops_max_length) {
1106
- cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length;
1107
- }
1108
- if (arg->has_iops_rd_max_length) {
1109
- cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length;
1110
- }
1111
- if (arg->has_iops_wr_max_length) {
1112
- cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length;
1113
- }
1114
-
1115
- if (arg->has_iops_size) {
1116
- cfg.op_size = arg->iops_size;
1117
- }
1118
-
1119
- if (!throttle_is_valid(&cfg, errp)) {
1120
- goto out;
1121
- }
1122
-
1123
- if (throttle_enabled(&cfg)) {
1124
- /* Enable I/O limits if they're not enabled yet, otherwise
1125
- * just update the throttling group. */
1126
- if (!blk_get_public(blk)->throttle_group_member.throttle_state) {
1127
- blk_io_limits_enable(blk,
1128
- arg->has_group ? arg->group :
1129
- arg->has_device ? arg->device :
1130
- arg->id);
1131
- } else if (arg->has_group) {
1132
- blk_io_limits_update_group(blk, arg->group);
1133
- }
1134
- /* Set the new throttling configuration */
1135
- blk_set_io_limits(blk, &cfg);
1136
- } else if (blk_get_public(blk)->throttle_group_member.throttle_state) {
1137
- /* If all throttling settings are set to 0, disable I/O limits */
1138
- blk_io_limits_disable(blk);
1139
- }
1140
-
1141
-out:
1142
- aio_context_release(aio_context);
1143
-}
1144
-
1145
void qmp_block_dirty_bitmap_add(const char *node, const char *name,
1146
bool has_granularity, uint32_t granularity,
1147
bool has_persistent, bool persistent,
1148
@@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
1149
aio_context_release(old_context);
1150
}
1151
1152
-void qmp_block_latency_histogram_set(
1153
- const char *id,
1154
- bool has_boundaries, uint64List *boundaries,
1155
- bool has_boundaries_read, uint64List *boundaries_read,
1156
- bool has_boundaries_write, uint64List *boundaries_write,
1157
- bool has_boundaries_flush, uint64List *boundaries_flush,
1158
- Error **errp)
1159
-{
1160
- BlockBackend *blk = qmp_get_blk(NULL, id, errp);
1161
- BlockAcctStats *stats;
1162
- int ret;
1163
-
1164
- if (!blk) {
1165
- return;
1166
- }
1167
-
1168
- stats = blk_get_stats(blk);
1169
-
1170
- if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
1171
- !has_boundaries_flush)
1172
- {
1173
- block_latency_histograms_clear(stats);
1174
- return;
1175
- }
1176
-
1177
- if (has_boundaries || has_boundaries_read) {
1178
- ret = block_latency_histogram_set(
1179
- stats, BLOCK_ACCT_READ,
1180
- has_boundaries_read ? boundaries_read : boundaries);
1181
- if (ret) {
1182
- error_setg(errp, "Device '%s' set read boundaries fail", id);
1183
- return;
1184
- }
1185
- }
1186
-
1187
- if (has_boundaries || has_boundaries_write) {
1188
- ret = block_latency_histogram_set(
1189
- stats, BLOCK_ACCT_WRITE,
1190
- has_boundaries_write ? boundaries_write : boundaries);
1191
- if (ret) {
1192
- error_setg(errp, "Device '%s' set write boundaries fail", id);
1193
- return;
1194
- }
1195
- }
1196
-
1197
- if (has_boundaries || has_boundaries_flush) {
1198
- ret = block_latency_histogram_set(
1199
- stats, BLOCK_ACCT_FLUSH,
1200
- has_boundaries_flush ? boundaries_flush : boundaries);
1201
- if (ret) {
1202
- error_setg(errp, "Device '%s' set flush boundaries fail", id);
1203
- return;
1204
- }
1205
- }
1206
-}
1207
-
1208
QemuOptsList qemu_common_drive_opts = {
1209
.name = "drive",
1210
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
1211
diff --git a/block/Makefile.objs b/block/Makefile.objs
1212
index XXXXXXX..XXXXXXX 100644
1213
--- a/block/Makefile.objs
1214
+++ b/block/Makefile.objs
1215
@@ -XXX,XX +XXX,XX @@ block-obj-y += filter-compress.o
1216
1217
common-obj-y += stream.o
1218
1219
+common-obj-y += qapi-sysemu.o
1220
+
1221
nfs.o-libs := $(LIBNFS_LIBS)
1222
iscsi.o-cflags := $(LIBISCSI_CFLAGS)
1223
iscsi.o-libs := $(LIBISCSI_LIBS)
1224
--
177
--
1225
2.20.1
178
2.13.6
1226
179
1227
180
diff view generated by jsdifflib
1
Add a new parameter allow_hmp to monitor_init() so that the storage
1
Now that the bdrv_drain_invoke() calls are pulled up to the callers of
2
daemon can disable HMP.
2
bdrv_drain_recurse(), the 'begin' parameter isn't needed any more.
3
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Message-Id: <20200224143008.13362-20-kwolf@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
6
---
9
qapi/control.json | 3 ++-
7
block/io.c | 12 ++++++------
10
include/monitor/monitor.h | 2 +-
8
1 file changed, 6 insertions(+), 6 deletions(-)
11
monitor/monitor.c | 12 ++++++++++--
12
3 files changed, 13 insertions(+), 4 deletions(-)
13
9
14
diff --git a/qapi/control.json b/qapi/control.json
10
diff --git a/block/io.c b/block/io.c
15
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/control.json
12
--- a/block/io.c
17
+++ b/qapi/control.json
13
+++ b/block/io.c
18
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
19
#
15
}
20
# @id: Name of the monitor
21
#
22
-# @mode: Selects the monitor mode (default: readline)
23
+# @mode: Selects the monitor mode (default: readline in the system
24
+# emulator, control in qemu-storage-daemon)
25
#
26
# @pretty: Enables pretty printing (QMP only)
27
#
28
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
29
index XXXXXXX..XXXXXXX 100644
30
--- a/include/monitor/monitor.h
31
+++ b/include/monitor/monitor.h
32
@@ -XXX,XX +XXX,XX @@ void monitor_init_globals(void);
33
void monitor_init_globals_core(void);
34
void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp);
35
void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp);
36
-int monitor_init(MonitorOptions *opts, Error **errp);
37
+int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp);
38
int monitor_init_opts(QemuOpts *opts, Error **errp);
39
void monitor_cleanup(void);
40
41
diff --git a/monitor/monitor.c b/monitor/monitor.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/monitor/monitor.c
44
+++ b/monitor/monitor.c
45
@@ -XXX,XX +XXX,XX @@ void monitor_init_globals_core(void)
46
NULL);
47
}
16
}
48
17
49
-int monitor_init(MonitorOptions *opts, Error **errp)
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
50
+int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp)
19
+static bool bdrv_drain_recurse(BlockDriverState *bs)
51
{
20
{
52
Chardev *chr;
21
BdrvChild *child, *tmp;
53
Error *local_err = NULL;
22
bool waited;
54
@@ -XXX,XX +XXX,XX @@ int monitor_init(MonitorOptions *opts, Error **errp)
23
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
55
return -1;
24
*/
25
bdrv_ref(bs);
26
}
27
- waited |= bdrv_drain_recurse(bs, begin);
28
+ waited |= bdrv_drain_recurse(bs);
29
if (in_main_loop) {
30
bdrv_unref(bs);
31
}
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
56
}
33
}
57
34
58
+ if (!opts->has_mode) {
35
bdrv_drain_invoke(bs, true);
59
+ opts->mode = allow_hmp ? MONITOR_MODE_READLINE : MONITOR_MODE_CONTROL;
36
- bdrv_drain_recurse(bs, true);
60
+ }
37
+ bdrv_drain_recurse(bs);
61
+
38
}
62
switch (opts->mode) {
39
63
case MONITOR_MODE_CONTROL:
40
void bdrv_drained_end(BlockDriverState *bs)
64
monitor_init_qmp(chr, opts->pretty, &local_err);
41
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
65
break;
42
66
case MONITOR_MODE_READLINE:
43
bdrv_parent_drained_end(bs);
67
+ if (!allow_hmp) {
44
bdrv_drain_invoke(bs, false);
68
+ error_setg(errp, "Only QMP is supported");
45
- bdrv_drain_recurse(bs, false);
69
+ return -1;
46
+ bdrv_drain_recurse(bs);
70
+ }
47
aio_enable_external(bdrv_get_aio_context(bs));
71
if (opts->pretty) {
48
}
72
warn_report("'pretty' is deprecated for HMP monitors, it has no "
49
73
"effect and will be removed in future versions");
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
74
@@ -XXX,XX +XXX,XX @@ int monitor_init_opts(QemuOpts *opts, Error **errp)
51
aio_context_acquire(aio_context);
75
goto out;
52
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
53
if (aio_context == bdrv_get_aio_context(bs)) {
54
- waited |= bdrv_drain_recurse(bs, true);
55
+ waited |= bdrv_drain_recurse(bs);
56
}
57
}
58
aio_context_release(aio_context);
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
60
aio_enable_external(aio_context);
61
bdrv_parent_drained_end(bs);
62
bdrv_drain_invoke(bs, false);
63
- bdrv_drain_recurse(bs, false);
64
+ bdrv_drain_recurse(bs);
65
aio_context_release(aio_context);
76
}
66
}
77
67
78
- monitor_init(options, &local_err);
79
+ monitor_init(options, true, &local_err);
80
qapi_free_MonitorOptions(options);
81
82
out:
83
--
68
--
84
2.20.1
69
2.13.6
85
70
86
71
diff view generated by jsdifflib
1
Mapping object-add to the command line as is doesn't result in nice
1
The device is drained, so there is no point in waiting for requests at
2
syntax because of the nesting introduced with 'props'. This becomes
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
nicer and more consistent with device_add and netdev_add when we accept
3
there.
4
properties for the object on the top level instead.
5
4
6
'props' is still accepted after this patch, but marked as deprecated.
5
The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e
6
in order to call the .bdrv_co_drain_end() driver callback. This is now
7
done by a separate bdrv_drain_invoke() call.
7
8
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20200224143008.13362-8-kwolf@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
10
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
12
---
13
qapi/qom.json | 12 +++++++---
13
block/io.c | 2 --
14
docs/system/deprecated.rst | 5 ++++
14
1 file changed, 2 deletions(-)
15
include/qom/object_interfaces.h | 7 ++++++
16
hw/block/xen-block.c | 11 ++++++++-
17
monitor/misc.c | 2 ++
18
qom/qom-qmp-cmds.c | 42 +++++++++++++++++++++++++++------
19
6 files changed, 68 insertions(+), 11 deletions(-)
20
15
21
diff --git a/qapi/qom.json b/qapi/qom.json
16
diff --git a/block/io.c b/block/io.c
22
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
23
--- a/qapi/qom.json
18
--- a/block/io.c
24
+++ b/qapi/qom.json
19
+++ b/block/io.c
25
@@ -XXX,XX +XXX,XX @@
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
26
#
21
27
# @id: the name of the new object
22
bdrv_parent_drained_end(bs);
28
#
23
bdrv_drain_invoke(bs, false);
29
-# @props: a dictionary of properties to be passed to the backend
24
- bdrv_drain_recurse(bs);
30
+# @props: a dictionary of properties to be passed to the backend. Deprecated
25
aio_enable_external(bdrv_get_aio_context(bs));
31
+# since 5.0, specify the properties on the top level instead. It is an
32
+# error to specify the same option both on the top level and in @props.
33
+#
34
+# Additional arguments depend on qom-type and are passed to the backend
35
+# unchanged.
36
#
37
# Returns: Nothing on success
38
# Error if @qom-type is not a valid class name
39
@@ -XXX,XX +XXX,XX @@
40
#
41
# -> { "execute": "object-add",
42
# "arguments": { "qom-type": "rng-random", "id": "rng1",
43
-# "props": { "filename": "/dev/hwrng" } } }
44
+# "filename": "/dev/hwrng" } }
45
# <- { "return": {} }
46
#
47
##
48
{ 'command': 'object-add',
49
- 'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'} }
50
+ 'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'},
51
+ 'gen': false } # so we can get the additional arguments
52
53
##
54
# @object-del:
55
diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
56
index XXXXXXX..XXXXXXX 100644
57
--- a/docs/system/deprecated.rst
58
+++ b/docs/system/deprecated.rst
59
@@ -XXX,XX +XXX,XX @@ Use ``migrate-set-parameters`` instead.
60
61
Use ``migrate-set-parameters`` and ``query-migrate-parameters`` instead.
62
63
+``object-add`` option ``props`` (since 5.0)
64
+'''''''''''''''''''''''''''''''''''''''''''
65
+
66
+Specify the properties for the object as top-level arguments instead.
67
+
68
``query-block`` result field ``dirty-bitmaps[i].status`` (since 4.0)
69
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
70
71
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
72
index XXXXXXX..XXXXXXX 100644
73
--- a/include/qom/object_interfaces.h
74
+++ b/include/qom/object_interfaces.h
75
@@ -XXX,XX +XXX,XX @@ void user_creatable_del(const char *id, Error **errp);
76
*/
77
void user_creatable_cleanup(void);
78
79
+/**
80
+ * qmp_object_add:
81
+ *
82
+ * QMP command handler for object-add. See the QAPI schema for documentation.
83
+ */
84
+void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp);
85
+
86
#endif
87
diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/hw/block/xen-block.c
90
+++ b/hw/block/xen-block.c
91
@@ -XXX,XX +XXX,XX @@
92
#include "qapi/visitor.h"
93
#include "qapi/qmp/qdict.h"
94
#include "qapi/qmp/qstring.h"
95
+#include "qom/object_interfaces.h"
96
#include "hw/xen/xen_common.h"
97
#include "hw/block/xen_blkif.h"
98
#include "hw/qdev-properties.h"
99
@@ -XXX,XX +XXX,XX @@ static XenBlockIOThread *xen_block_iothread_create(const char *id,
100
{
101
XenBlockIOThread *iothread = g_new(XenBlockIOThread, 1);
102
Error *local_err = NULL;
103
+ QDict *opts;
104
+ QObject *ret_data;
105
106
iothread->id = g_strdup(id);
107
108
- qmp_object_add(TYPE_IOTHREAD, id, false, NULL, &local_err);
109
+ opts = qdict_new();
110
+ qdict_put_str(opts, "qom-type", TYPE_IOTHREAD);
111
+ qdict_put_str(opts, "id", id);
112
+ qmp_object_add(opts, &ret_data, &local_err);
113
+ qobject_unref(opts);
114
+ qobject_unref(ret_data);
115
+
116
if (local_err) {
117
error_propagate(errp, local_err);
118
119
diff --git a/monitor/misc.c b/monitor/misc.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/monitor/misc.c
122
+++ b/monitor/misc.c
123
@@ -XXX,XX +XXX,XX @@ static void monitor_init_qmp_commands(void)
124
QCO_NO_OPTIONS);
125
qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
126
QCO_NO_OPTIONS);
127
+ qmp_register_command(&qmp_commands, "object-add", qmp_object_add,
128
+ QCO_NO_OPTIONS);
129
130
QTAILQ_INIT(&qmp_cap_negotiation_commands);
131
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
132
diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
133
index XXXXXXX..XXXXXXX 100644
134
--- a/qom/qom-qmp-cmds.c
135
+++ b/qom/qom-qmp-cmds.c
136
@@ -XXX,XX +XXX,XX @@
137
*/
138
139
#include "qemu/osdep.h"
140
+#include "block/qdict.h"
141
#include "hw/qdev-core.h"
142
#include "qapi/error.h"
143
#include "qapi/qapi-commands-qdev.h"
144
@@ -XXX,XX +XXX,XX @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
145
return prop_list;
146
}
26
}
147
27
148
-void qmp_object_add(const char *type, const char *id,
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
149
- bool has_props, QObject *props, Error **errp)
29
aio_enable_external(aio_context);
150
+void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp)
30
bdrv_parent_drained_end(bs);
151
{
31
bdrv_drain_invoke(bs, false);
152
+ QObject *props;
32
- bdrv_drain_recurse(bs);
153
QDict *pdict;
33
aio_context_release(aio_context);
154
Visitor *v;
155
Object *obj;
156
+ const char *type;
157
+ const char *id;
158
159
+ type = qdict_get_try_str(qdict, "qom-type");
160
+ if (!type) {
161
+ error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
162
+ return;
163
+ } else {
164
+ type = g_strdup(type);
165
+ qdict_del(qdict, "qom-type");
166
+ }
167
+
168
+ id = qdict_get_try_str(qdict, "id");
169
+ if (!id) {
170
+ error_setg(errp, QERR_MISSING_PARAMETER, "id");
171
+ return;
172
+ } else {
173
+ id = g_strdup(id);
174
+ qdict_del(qdict, "id");
175
+ }
176
+
177
+ props = qdict_get(qdict, "props");
178
if (props) {
179
pdict = qobject_to(QDict, props);
180
if (!pdict) {
181
@@ -XXX,XX +XXX,XX @@ void qmp_object_add(const char *type, const char *id,
182
return;
183
}
184
qobject_ref(pdict);
185
- } else {
186
- pdict = qdict_new();
187
+ qdict_del(qdict, "props");
188
+ qdict_join(qdict, pdict, false);
189
+ if (qdict_size(pdict) != 0) {
190
+ error_setg(errp, "Option in 'props' conflicts with top level");
191
+ qobject_unref(pdict);
192
+ return;
193
+ }
194
+ qobject_unref(pdict);
195
}
34
}
196
35
197
- v = qobject_input_visitor_new(QOBJECT(pdict));
198
- obj = user_creatable_add_type(type, id, pdict, v, errp);
199
+ v = qobject_input_visitor_new(QOBJECT(qdict));
200
+ obj = user_creatable_add_type(type, id, qdict, v, errp);
201
visit_free(v);
202
if (obj) {
203
object_unref(obj);
204
}
205
- qobject_unref(pdict);
206
+ *ret_data = QOBJECT(qdict_new());
207
}
208
209
void qmp_object_del(const char *id, Error **errp)
210
--
36
--
211
2.20.1
37
2.13.6
212
38
213
39
diff view generated by jsdifflib
1
Move the arguments of nbd-server-add to a new struct BlockExportNbd and
1
Drain requests are propagated to child nodes, parent nodes and directly
2
convert the command to 'boxed': true. This makes it easier to share code
2
to the AioContext. The order in which this happened was different
3
with the storage daemon.
3
between all combinations of drain/drain_all and begin/end.
4
5
The correct order is to keep children only drained when their parents
6
are also drained. This means that at the start of a drained section, the
7
AioContext needs to be drained first, the parents second and only then
8
the children. The correct order for the end of a drained section is the
9
opposite.
10
11
This patch changes the three other functions to follow the example of
12
bdrv_drained_begin(), which is the only one that got it right.
4
13
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20200224143008.13362-11-kwolf@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
16
---
10
qapi/block-core.json | 18 ++++++++++++++----
17
block/io.c | 12 ++++++++----
11
blockdev-nbd.c | 35 ++++++++++++++++-------------------
18
1 file changed, 8 insertions(+), 4 deletions(-)
12
monitor/hmp-cmds.c | 21 +++++++++++++++++----
13
3 files changed, 47 insertions(+), 27 deletions(-)
14
19
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
20
diff --git a/block/io.c b/block/io.c
16
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
17
--- a/qapi/block-core.json
22
--- a/block/io.c
18
+++ b/qapi/block-core.json
23
+++ b/block/io.c
19
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
20
'*tls-authz': 'str'} }
21
22
##
23
-# @nbd-server-add:
24
+# @BlockExportNbd:
25
#
26
-# Export a block node to QEMU's embedded NBD server.
27
+# An NBD block export.
28
#
29
# @device: The device name or node name of the node to be exported
30
#
31
@@ -XXX,XX +XXX,XX @@
32
# NBD client can use NBD_OPT_SET_META_CONTEXT with
33
# "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0)
34
#
35
+# Since: 5.0
36
+##
37
+{ 'struct': 'BlockExportNbd',
38
+ 'data': {'device': 'str', '*name': 'str', '*description': 'str',
39
+ '*writable': 'bool', '*bitmap': 'str' } }
40
+
41
+##
42
+# @nbd-server-add:
43
+#
44
+# Export a block node to QEMU's embedded NBD server.
45
+#
46
# Returns: error if the server is not running, or export with the same name
47
# already exists.
48
#
49
# Since: 1.3.0
50
##
51
{ 'command': 'nbd-server-add',
52
- 'data': {'device': 'str', '*name': 'str', '*description': 'str',
53
- '*writable': 'bool', '*bitmap': 'str' } }
54
+ 'data': 'BlockExportNbd', 'boxed': true }
55
56
##
57
# @NbdServerRemoveMode:
58
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
59
index XXXXXXX..XXXXXXX 100644
60
--- a/blockdev-nbd.c
61
+++ b/blockdev-nbd.c
62
@@ -XXX,XX +XXX,XX @@ void qmp_nbd_server_start(SocketAddressLegacy *addr,
63
qapi_free_SocketAddress(addr_flat);
64
}
65
66
-void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
67
- bool has_description, const char *description,
68
- bool has_writable, bool writable,
69
- bool has_bitmap, const char *bitmap, Error **errp)
70
+void qmp_nbd_server_add(BlockExportNbd *arg, Error **errp)
71
{
72
BlockDriverState *bs = NULL;
73
BlockBackend *on_eject_blk;
74
@@ -XXX,XX +XXX,XX @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
75
return;
25
return;
76
}
26
}
77
27
78
- if (!has_name) {
28
+ /* Stop things in parent-to-child order */
79
- name = device;
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
80
+ if (!arg->has_name) {
30
aio_disable_external(bdrv_get_aio_context(bs));
81
+ arg->name = arg->device;
31
bdrv_parent_drained_begin(bs);
82
}
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
83
84
- if (strlen(name) > NBD_MAX_STRING_SIZE) {
85
- error_setg(errp, "export name '%s' too long", name);
86
+ if (strlen(arg->name) > NBD_MAX_STRING_SIZE) {
87
+ error_setg(errp, "export name '%s' too long", arg->name);
88
return;
33
return;
89
}
34
}
90
35
91
- if (has_description && strlen(description) > NBD_MAX_STRING_SIZE) {
36
- bdrv_parent_drained_end(bs);
92
- error_setg(errp, "description '%s' too long", description);
37
+ /* Re-enable things in child-to-parent order */
93
+ if (arg->description && strlen(arg->description) > NBD_MAX_STRING_SIZE) {
38
bdrv_drain_invoke(bs, false);
94
+ error_setg(errp, "description '%s' too long", arg->description);
39
+ bdrv_parent_drained_end(bs);
95
return;
40
aio_enable_external(bdrv_get_aio_context(bs));
41
}
42
43
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
44
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
45
AioContext *aio_context = bdrv_get_aio_context(bs);
46
47
+ /* Stop things in parent-to-child order */
48
aio_context_acquire(aio_context);
49
- bdrv_parent_drained_begin(bs);
50
aio_disable_external(aio_context);
51
+ bdrv_parent_drained_begin(bs);
52
bdrv_drain_invoke(bs, true);
53
aio_context_release(aio_context);
54
55
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
56
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
57
AioContext *aio_context = bdrv_get_aio_context(bs);
58
59
+ /* Re-enable things in child-to-parent order */
60
aio_context_acquire(aio_context);
61
- aio_enable_external(aio_context);
62
- bdrv_parent_drained_end(bs);
63
bdrv_drain_invoke(bs, false);
64
+ bdrv_parent_drained_end(bs);
65
+ aio_enable_external(aio_context);
66
aio_context_release(aio_context);
96
}
67
}
97
68
98
- if (nbd_export_find(name)) {
99
- error_setg(errp, "NBD server already has export named '%s'", name);
100
+ if (nbd_export_find(arg->name)) {
101
+ error_setg(errp, "NBD server already has export named '%s'", arg->name);
102
return;
103
}
104
105
- on_eject_blk = blk_by_name(device);
106
+ on_eject_blk = blk_by_name(arg->device);
107
108
- bs = bdrv_lookup_bs(device, device, errp);
109
+ bs = bdrv_lookup_bs(arg->device, arg->device, errp);
110
if (!bs) {
111
return;
112
}
113
@@ -XXX,XX +XXX,XX @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name,
114
goto out;
115
}
116
117
- if (!has_writable) {
118
- writable = false;
119
+ if (!arg->has_writable) {
120
+ arg->writable = false;
121
}
122
if (bdrv_is_read_only(bs)) {
123
- writable = false;
124
+ arg->writable = false;
125
}
126
127
- exp = nbd_export_new(bs, 0, len, name, description, bitmap,
128
- !writable, !writable,
129
+ exp = nbd_export_new(bs, 0, len, arg->name, arg->description, arg->bitmap,
130
+ !arg->writable, !arg->writable,
131
NULL, false, on_eject_blk, errp);
132
if (!exp) {
133
goto out;
134
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
135
index XXXXXXX..XXXXXXX 100644
136
--- a/monitor/hmp-cmds.c
137
+++ b/monitor/hmp-cmds.c
138
@@ -XXX,XX +XXX,XX @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
139
Error *local_err = NULL;
140
BlockInfoList *block_list, *info;
141
SocketAddress *addr;
142
+ BlockExportNbd export;
143
144
if (writable && !all) {
145
error_setg(&local_err, "-w only valid together with -a");
146
@@ -XXX,XX +XXX,XX @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
147
continue;
148
}
149
150
- qmp_nbd_server_add(info->value->device, false, NULL, false, NULL,
151
- true, writable, false, NULL, &local_err);
152
+ export = (BlockExportNbd) {
153
+ .device = info->value->device,
154
+ .has_writable = true,
155
+ .writable = writable,
156
+ };
157
+
158
+ qmp_nbd_server_add(&export, &local_err);
159
160
if (local_err != NULL) {
161
qmp_nbd_server_stop(NULL);
162
@@ -XXX,XX +XXX,XX @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
163
bool writable = qdict_get_try_bool(qdict, "writable", false);
164
Error *local_err = NULL;
165
166
- qmp_nbd_server_add(device, !!name, name, false, NULL, true, writable,
167
- false, NULL, &local_err);
168
+ BlockExportNbd export = {
169
+ .device = (char *) device,
170
+ .has_name = !!name,
171
+ .name = (char *) name,
172
+ .has_writable = true,
173
+ .writable = writable,
174
+ };
175
+
176
+ qmp_nbd_server_add(&export, &local_err);
177
hmp_handle_error(mon, local_err);
178
}
179
180
--
69
--
181
2.20.1
70
2.13.6
182
71
183
72
diff view generated by jsdifflib
1
We'll want to test more than one successful case in the future, so
1
Commit 15afd94a047 added code to acquire and release the AioContext in
2
prepare the test for that by a refactoring that runs each scenario in a
2
qemuio_command(). This means that the lock is taken twice now in the
3
separate VM.
3
call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for
4
any requests issued to nodes in a non-mainloop AioContext.
4
5
5
test_iothreads_switch_{backing,overlay} currently produce errors, but
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
6
these are cases that should actually work, by switching either the
7
backing file node or the overlay node to the AioContext of the other
8
node.
9
7
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Tested-by: Peter Krempa <pkrempa@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Message-Id: <20200306141413.30705-2-kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
10
---
15
tests/qemu-iotests/245 | 47 ++++++++++++++++++++++++++++++--------
11
hmp.c | 6 ------
16
tests/qemu-iotests/245.out | 4 ++--
12
1 file changed, 6 deletions(-)
17
2 files changed, 39 insertions(+), 12 deletions(-)
18
13
19
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
14
diff --git a/hmp.c b/hmp.c
20
index XXXXXXX..XXXXXXX 100755
15
index XXXXXXX..XXXXXXX 100644
21
--- a/tests/qemu-iotests/245
16
--- a/hmp.c
22
+++ b/tests/qemu-iotests/245
17
+++ b/hmp.c
23
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
18
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
24
self.assertEqual(self.get_node('hd1'), None)
19
{
25
self.assert_qmp(self.get_node('hd2'), 'ro', True)
20
BlockBackend *blk;
26
21
BlockBackend *local_blk = NULL;
27
- # We don't allow setting a backing file that uses a different AioContext
22
- AioContext *aio_context;
28
- def test_iothreads(self):
23
const char* device = qdict_get_str(qdict, "device");
29
+ def run_test_iothreads(self, iothread_a, iothread_b, errmsg = None):
24
const char* command = qdict_get_str(qdict, "command");
30
opts = hd_opts(0)
25
Error *err = NULL;
31
result = self.vm.qmp('blockdev-add', conv_keys = False, **opts)
26
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
32
self.assert_qmp(result, 'return', {})
27
}
33
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
28
}
34
result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1')
29
35
self.assert_qmp(result, 'return', {})
30
- aio_context = blk_get_aio_context(blk);
36
31
- aio_context_acquire(aio_context);
37
- result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd0', iothread='iothread0')
38
+ result = self.vm.qmp('device_add', driver='virtio-scsi', id='scsi0',
39
+ iothread=iothread_a)
40
self.assert_qmp(result, 'return', {})
41
42
- self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
43
-
32
-
44
- result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread1')
33
/*
45
+ result = self.vm.qmp('device_add', driver='virtio-scsi', id='scsi1',
34
* Notably absent: Proper permission management. This is sad, but it seems
46
+ iothread=iothread_b)
35
* almost impossible to achieve without changing the semantics and thereby
47
self.assert_qmp(result, 'return', {})
36
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
48
37
*/
49
- self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext")
38
qemuio_command(blk, command);
50
+ if iothread_a:
39
51
+ result = self.vm.qmp('device_add', driver='scsi-hd', drive='hd0',
40
- aio_context_release(aio_context);
52
+ share_rw=True, bus="scsi0.0")
41
-
53
+ self.assert_qmp(result, 'return', {})
42
fail:
54
43
blk_unref(local_blk);
55
- result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread0')
44
hmp_handle_error(mon, &err);
56
- self.assert_qmp(result, 'return', {})
57
+ if iothread_b:
58
+ result = self.vm.qmp('device_add', driver='scsi-hd', drive='hd2',
59
+ share_rw=True, bus="scsi1.0")
60
+ self.assert_qmp(result, 'return', {})
61
62
- self.reopen(opts, {'backing': 'hd2'})
63
+ # Attaching the backing file may or may not work
64
+ self.reopen(opts, {'backing': 'hd2'}, errmsg)
65
+
66
+ # But removing the backing file should always work
67
+ self.reopen(opts, {'backing': None})
68
+
69
+ self.vm.shutdown()
70
+
71
+ # We don't allow setting a backing file that uses a different AioContext if
72
+ # neither of them can switch to the other AioContext
73
+ def test_iothreads_error(self):
74
+ self.run_test_iothreads('iothread0', 'iothread1',
75
+ "Cannot use a new backing file with a different AioContext")
76
+
77
+ def test_iothreads_compatible_users(self):
78
+ self.run_test_iothreads('iothread0', 'iothread0')
79
+
80
+ def test_iothreads_switch_backing(self):
81
+ self.run_test_iothreads('iothread0', None,
82
+ "Cannot use a new backing file with a different AioContext")
83
+
84
+ def test_iothreads_switch_overlay(self):
85
+ self.run_test_iothreads(None, 'iothread0',
86
+ "Cannot use a new backing file with a different AioContext")
87
88
if __name__ == '__main__':
89
iotests.main(supported_fmts=["qcow2"],
90
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
91
index XXXXXXX..XXXXXXX 100644
92
--- a/tests/qemu-iotests/245.out
93
+++ b/tests/qemu-iotests/245.out
94
@@ -XXX,XX +XXX,XX @@
95
-..................
96
+.....................
97
----------------------------------------------------------------------
98
-Ran 18 tests
99
+Ran 21 tests
100
101
OK
102
{"execute": "job-finalize", "arguments": {"id": "commit0"}}
103
--
45
--
104
2.20.1
46
2.13.6
105
47
106
48
diff view generated by jsdifflib
1
From: Peter Krempa <pkrempa@redhat.com>
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
2
2
3
The bitmap code requires writing the 'file' child when the qcow2 driver
3
Since bdrv_co_preadv does all neccessary checks including
4
is reopened in read-write mode.
4
reading after the end of the backing file, avoid duplication
5
of verification before bdrv_co_preadv call.
5
6
6
If the 'file' child is being reopened due to a permissions change, the
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
7
modification is commited yet when qcow2_reopen_commit is called. This
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
means that any attempt to write the 'file' child will end with EBADFD
9
Reviewed-by: Eric Blake <eblake@redhat.com>
9
as the original fd was already closed.
10
11
Moving bitmap reopening to the new callback which is called after
12
permission modifications are commited fixes this as the file descriptor
13
will be replaced with the correct one.
14
15
The above problem manifests itself when reopening 'qcow2' format layer
16
which uses a 'file-posix' file child which was opened with the
17
'auto-read-only' property set.
18
19
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
20
Message-Id: <db118dbafe1955afbc0a18d3dd220931074ce349.1582893284.git.pkrempa@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
11
---
23
block/qcow2.c | 7 ++++++-
12
block/qcow2.h | 3 ---
24
1 file changed, 6 insertions(+), 1 deletion(-)
13
block/qcow2.c | 51 ++++++++-------------------------------------------
14
2 files changed, 8 insertions(+), 46 deletions(-)
25
15
16
diff --git a/block/qcow2.h b/block/qcow2.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2.h
19
+++ b/block/qcow2.h
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
21
}
22
23
/* qcow2.c functions */
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
25
- int64_t sector_num, int nb_sectors);
26
-
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
28
int refcount_order, bool generous_increase,
29
uint64_t *refblock_count);
26
diff --git a/block/qcow2.c b/block/qcow2.c
30
diff --git a/block/qcow2.c b/block/qcow2.c
27
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
28
--- a/block/qcow2.c
32
--- a/block/qcow2.c
29
+++ b/block/qcow2.c
33
+++ b/block/qcow2.c
30
@@ -XXX,XX +XXX,XX @@ fail:
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
31
static void qcow2_reopen_commit(BDRVReopenState *state)
35
return status;
36
}
37
38
-/* handle reading after the end of the backing file */
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
40
- int64_t offset, int bytes)
41
-{
42
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
43
- int n1;
44
-
45
- if ((offset + bytes) <= bs_size) {
46
- return bytes;
47
- }
48
-
49
- if (offset >= bs_size) {
50
- n1 = 0;
51
- } else {
52
- n1 = bs_size - offset;
53
- }
54
-
55
- qemu_iovec_memset(qiov, n1, 0, bytes - n1);
56
-
57
- return n1;
58
-}
59
-
60
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
61
uint64_t bytes, QEMUIOVector *qiov,
62
int flags)
32
{
63
{
33
qcow2_update_options_commit(state->bs, state->opaque);
64
BDRVQcow2State *s = bs->opaque;
34
+ g_free(state->opaque);
65
- int offset_in_cluster, n1;
35
+}
66
+ int offset_in_cluster;
36
+
67
int ret;
37
+static void qcow2_reopen_commit_post(BDRVReopenState *state)
68
unsigned int cur_bytes; /* number of bytes in current iteration */
38
+{
69
uint64_t cluster_offset = 0;
39
if (state->flags & BDRV_O_RDWR) {
70
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
40
Error *local_err = NULL;
71
case QCOW2_CLUSTER_UNALLOCATED:
41
72
42
@@ -XXX,XX +XXX,XX @@ static void qcow2_reopen_commit(BDRVReopenState *state)
73
if (bs->backing) {
43
bdrv_get_node_name(state->bs));
74
- /* read from the base image */
44
}
75
- n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
45
}
76
- offset, cur_bytes);
46
- g_free(state->opaque);
77
- if (n1 > 0) {
47
}
78
- QEMUIOVector local_qiov;
48
79
-
49
static void qcow2_reopen_abort(BDRVReopenState *state)
80
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
50
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
81
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
51
.bdrv_close = qcow2_close,
82
-
52
.bdrv_reopen_prepare = qcow2_reopen_prepare,
83
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
53
.bdrv_reopen_commit = qcow2_reopen_commit,
84
- qemu_co_mutex_unlock(&s->lock);
54
+ .bdrv_reopen_commit_post = qcow2_reopen_commit_post,
85
- ret = bdrv_co_preadv(bs->backing, offset, n1,
55
.bdrv_reopen_abort = qcow2_reopen_abort,
86
- &local_qiov, 0);
56
.bdrv_join_options = qcow2_join_options,
87
- qemu_co_mutex_lock(&s->lock);
57
.bdrv_child_perm = bdrv_format_default_perms,
88
-
89
- qemu_iovec_destroy(&local_qiov);
90
-
91
- if (ret < 0) {
92
- goto fail;
93
- }
94
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
95
+ qemu_co_mutex_unlock(&s->lock);
96
+ ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
97
+ &hd_qiov, 0);
98
+ qemu_co_mutex_lock(&s->lock);
99
+ if (ret < 0) {
100
+ goto fail;
101
}
102
} else {
103
/* Note: in this case, no need to wait */
58
--
104
--
59
2.20.1
105
2.13.6
60
106
61
107
diff view generated by jsdifflib
1
block-core is for everything that isn't related to the system emulator.
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
Internal snapshots, the NBD server and quorum events make sense in the
2
driver state that cannot be recreated with create options because it
3
tools, too, so move them to block-core.
3
would require a list with gaps. This causes trouble in at least
4
.bdrv_refresh_filename().
5
6
Document this problem so that we won't accidentally mark the command
7
stable without having addressed it.
4
8
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20200224143008.13362-5-kwolf@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
7
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
11
---
10
qapi/block-core.json | 283 ++++++++++++++++++++++++++++++++++++++++++
12
qapi/block-core.json | 4 ++++
11
qapi/block.json | 284 -------------------------------------------
13
1 file changed, 4 insertions(+)
12
2 files changed, 283 insertions(+), 284 deletions(-)
13
14
14
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/block-core.json
17
--- a/qapi/block-core.json
17
+++ b/qapi/block-core.json
18
+++ b/qapi/block-core.json
18
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
19
'data' : { 'node-name': 'str',
20
# does not support all kinds of operations, all kinds of children, nor
20
'iothread': 'StrOrNull',
21
# all block drivers.
21
'*force': 'bool' } }
22
#
22
+
23
+# FIXME Removing children from a quorum node means introducing gaps in the
23
+##
24
+# child indices. This cannot be represented in the 'children' list of
24
+# @nbd-server-start:
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
25
+#
26
+#
26
+# Start an NBD server listening on the given host and port. Block
27
# Warning: The data in a new quorum child MUST be consistent with that of
27
+# devices can then be exported using @nbd-server-add. The NBD
28
# the rest of the array.
28
+# server will present them as named exports; for example, another
29
+# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
30
+#
31
+# @addr: Address on which to listen.
32
+# @tls-creds: ID of the TLS credentials object (since 2.6).
33
+# @tls-authz: ID of the QAuthZ authorization object used to validate
34
+# the client's x509 distinguished name. This object is
35
+# is only resolved at time of use, so can be deleted and
36
+# recreated on the fly while the NBD server is active.
37
+# If missing, it will default to denying access (since 4.0).
38
+#
39
+# Returns: error if the server is already running.
40
+#
41
+# Since: 1.3.0
42
+##
43
+{ 'command': 'nbd-server-start',
44
+ 'data': { 'addr': 'SocketAddressLegacy',
45
+ '*tls-creds': 'str',
46
+ '*tls-authz': 'str'} }
47
+
48
+##
49
+# @nbd-server-add:
50
+#
51
+# Export a block node to QEMU's embedded NBD server.
52
+#
53
+# @device: The device name or node name of the node to be exported
54
+#
55
+# @name: Export name. If unspecified, the @device parameter is used as the
56
+# export name. (Since 2.12)
57
+#
58
+# @description: Free-form description of the export, up to 4096 bytes.
59
+# (Since 5.0)
60
+#
61
+# @writable: Whether clients should be able to write to the device via the
62
+# NBD connection (default false).
63
+#
64
+# @bitmap: Also export the dirty bitmap reachable from @device, so the
65
+# NBD client can use NBD_OPT_SET_META_CONTEXT with
66
+# "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0)
67
+#
68
+# Returns: error if the server is not running, or export with the same name
69
+# already exists.
70
+#
71
+# Since: 1.3.0
72
+##
73
+{ 'command': 'nbd-server-add',
74
+ 'data': {'device': 'str', '*name': 'str', '*description': 'str',
75
+ '*writable': 'bool', '*bitmap': 'str' } }
76
+
77
+##
78
+# @NbdServerRemoveMode:
79
+#
80
+# Mode for removing an NBD export.
81
+#
82
+# @safe: Remove export if there are no existing connections, fail otherwise.
83
+#
84
+# @hard: Drop all connections immediately and remove export.
85
+#
86
+# Potential additional modes to be added in the future:
87
+#
88
+# hide: Just hide export from new clients, leave existing connections as is.
89
+# Remove export after all clients are disconnected.
90
+#
91
+# soft: Hide export from new clients, answer with ESHUTDOWN for all further
92
+# requests from existing clients.
93
+#
94
+# Since: 2.12
95
+##
96
+{'enum': 'NbdServerRemoveMode', 'data': ['safe', 'hard']}
97
+
98
+##
99
+# @nbd-server-remove:
100
+#
101
+# Remove NBD export by name.
102
+#
103
+# @name: Export name.
104
+#
105
+# @mode: Mode of command operation. See @NbdServerRemoveMode description.
106
+# Default is 'safe'.
107
+#
108
+# Returns: error if
109
+# - the server is not running
110
+# - export is not found
111
+# - mode is 'safe' and there are existing connections
112
+#
113
+# Since: 2.12
114
+##
115
+{ 'command': 'nbd-server-remove',
116
+ 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} }
117
+
118
+##
119
+# @nbd-server-stop:
120
+#
121
+# Stop QEMU's embedded NBD server, and unregister all devices previously
122
+# added via @nbd-server-add.
123
+#
124
+# Since: 1.3.0
125
+##
126
+{ 'command': 'nbd-server-stop' }
127
+
128
+##
129
+# @QuorumOpType:
130
+#
131
+# An enumeration of the quorum operation types
132
+#
133
+# @read: read operation
134
+#
135
+# @write: write operation
136
+#
137
+# @flush: flush operation
138
+#
139
+# Since: 2.6
140
+##
141
+{ 'enum': 'QuorumOpType',
142
+ 'data': [ 'read', 'write', 'flush' ] }
143
+
144
+##
145
+# @QUORUM_FAILURE:
146
+#
147
+# Emitted by the Quorum block driver if it fails to establish a quorum
148
+#
149
+# @reference: device name if defined else node name
150
+#
151
+# @sector-num: number of the first sector of the failed read operation
152
+#
153
+# @sectors-count: failed read operation sector count
154
+#
155
+# Note: This event is rate-limited.
156
+#
157
+# Since: 2.0
158
+#
159
+# Example:
160
+#
161
+# <- { "event": "QUORUM_FAILURE",
162
+# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
163
+# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
164
+#
165
+##
166
+{ 'event': 'QUORUM_FAILURE',
167
+ 'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
168
+
169
+##
170
+# @QUORUM_REPORT_BAD:
171
+#
172
+# Emitted to report a corruption of a Quorum file
173
+#
174
+# @type: quorum operation type (Since 2.6)
175
+#
176
+# @error: error message. Only present on failure. This field
177
+# contains a human-readable error message. There are no semantics other
178
+# than that the block layer reported an error and clients should not
179
+# try to interpret the error string.
180
+#
181
+# @node-name: the graph node name of the block driver state
182
+#
183
+# @sector-num: number of the first sector of the failed read operation
184
+#
185
+# @sectors-count: failed read operation sector count
186
+#
187
+# Note: This event is rate-limited.
188
+#
189
+# Since: 2.0
190
+#
191
+# Example:
192
+#
193
+# 1. Read operation
194
+#
195
+# { "event": "QUORUM_REPORT_BAD",
196
+# "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
197
+# "type": "read" },
198
+# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
199
+#
200
+# 2. Flush operation
201
+#
202
+# { "event": "QUORUM_REPORT_BAD",
203
+# "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
204
+# "type": "flush", "error": "Broken pipe" },
205
+# "timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
206
+#
207
+##
208
+{ 'event': 'QUORUM_REPORT_BAD',
209
+ 'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str',
210
+ 'sector-num': 'int', 'sectors-count': 'int' } }
211
+
212
+##
213
+# @BlockdevSnapshotInternal:
214
+#
215
+# @device: the device name or node-name of a root node to generate the snapshot
216
+# from
217
+#
218
+# @name: the name of the internal snapshot to be created
219
+#
220
+# Notes: In transaction, if @name is empty, or any snapshot matching @name
221
+# exists, the operation will fail. Only some image formats support it,
222
+# for example, qcow2, rbd, and sheepdog.
223
+#
224
+# Since: 1.7
225
+##
226
+{ 'struct': 'BlockdevSnapshotInternal',
227
+ 'data': { 'device': 'str', 'name': 'str' } }
228
+
229
+##
230
+# @blockdev-snapshot-internal-sync:
231
+#
232
+# Synchronously take an internal snapshot of a block device, when the
233
+# format of the image used supports it. If the name is an empty
234
+# string, or a snapshot with name already exists, the operation will
235
+# fail.
236
+#
237
+# For the arguments, see the documentation of BlockdevSnapshotInternal.
238
+#
239
+# Returns: - nothing on success
240
+# - If @device is not a valid block device, GenericError
241
+# - If any snapshot matching @name exists, or @name is empty,
242
+# GenericError
243
+# - If the format of the image used does not support it,
244
+# BlockFormatFeatureNotSupported
245
+#
246
+# Since: 1.7
247
+#
248
+# Example:
249
+#
250
+# -> { "execute": "blockdev-snapshot-internal-sync",
251
+# "arguments": { "device": "ide-hd0",
252
+# "name": "snapshot0" }
253
+# }
254
+# <- { "return": {} }
255
+#
256
+##
257
+{ 'command': 'blockdev-snapshot-internal-sync',
258
+ 'data': 'BlockdevSnapshotInternal' }
259
+
260
+##
261
+# @blockdev-snapshot-delete-internal-sync:
262
+#
263
+# Synchronously delete an internal snapshot of a block device, when the format
264
+# of the image used support it. The snapshot is identified by name or id or
265
+# both. One of the name or id is required. Return SnapshotInfo for the
266
+# successfully deleted snapshot.
267
+#
268
+# @device: the device name or node-name of a root node to delete the snapshot
269
+# from
270
+#
271
+# @id: optional the snapshot's ID to be deleted
272
+#
273
+# @name: optional the snapshot's name to be deleted
274
+#
275
+# Returns: - SnapshotInfo on success
276
+# - If @device is not a valid block device, GenericError
277
+# - If snapshot not found, GenericError
278
+# - If the format of the image used does not support it,
279
+# BlockFormatFeatureNotSupported
280
+# - If @id and @name are both not specified, GenericError
281
+#
282
+# Since: 1.7
283
+#
284
+# Example:
285
+#
286
+# -> { "execute": "blockdev-snapshot-delete-internal-sync",
287
+# "arguments": { "device": "ide-hd0",
288
+# "name": "snapshot0" }
289
+# }
290
+# <- { "return": {
291
+# "id": "1",
292
+# "name": "snapshot0",
293
+# "vm-state-size": 0,
294
+# "date-sec": 1000012,
295
+# "date-nsec": 10,
296
+# "vm-clock-sec": 100,
297
+# "vm-clock-nsec": 20
298
+# }
299
+# }
300
+#
301
+##
302
+{ 'command': 'blockdev-snapshot-delete-internal-sync',
303
+ 'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
304
+ 'returns': 'SnapshotInfo' }
305
diff --git a/qapi/block.json b/qapi/block.json
306
index XXXXXXX..XXXXXXX 100644
307
--- a/qapi/block.json
308
+++ b/qapi/block.json
309
@@ -XXX,XX +XXX,XX @@
310
{ 'enum': 'FloppyDriveType',
311
'data': ['144', '288', '120', 'none', 'auto']}
312
313
-##
314
-# @BlockdevSnapshotInternal:
315
-#
316
-# @device: the device name or node-name of a root node to generate the snapshot
317
-# from
318
-#
319
-# @name: the name of the internal snapshot to be created
320
-#
321
-# Notes: In transaction, if @name is empty, or any snapshot matching @name
322
-# exists, the operation will fail. Only some image formats support it,
323
-# for example, qcow2, rbd, and sheepdog.
324
-#
325
-# Since: 1.7
326
-##
327
-{ 'struct': 'BlockdevSnapshotInternal',
328
- 'data': { 'device': 'str', 'name': 'str' } }
329
-
330
##
331
# @PRManagerInfo:
332
#
29
#
333
@@ -XXX,XX +XXX,XX @@
334
{ 'command': 'query-pr-managers', 'returns': ['PRManagerInfo'],
335
'allow-preconfig': true }
336
337
-
338
-##
339
-# @blockdev-snapshot-internal-sync:
340
-#
341
-# Synchronously take an internal snapshot of a block device, when the
342
-# format of the image used supports it. If the name is an empty
343
-# string, or a snapshot with name already exists, the operation will
344
-# fail.
345
-#
346
-# For the arguments, see the documentation of BlockdevSnapshotInternal.
347
-#
348
-# Returns: - nothing on success
349
-# - If @device is not a valid block device, GenericError
350
-# - If any snapshot matching @name exists, or @name is empty,
351
-# GenericError
352
-# - If the format of the image used does not support it,
353
-# BlockFormatFeatureNotSupported
354
-#
355
-# Since: 1.7
356
-#
357
-# Example:
358
-#
359
-# -> { "execute": "blockdev-snapshot-internal-sync",
360
-# "arguments": { "device": "ide-hd0",
361
-# "name": "snapshot0" }
362
-# }
363
-# <- { "return": {} }
364
-#
365
-##
366
-{ 'command': 'blockdev-snapshot-internal-sync',
367
- 'data': 'BlockdevSnapshotInternal' }
368
-
369
-##
370
-# @blockdev-snapshot-delete-internal-sync:
371
-#
372
-# Synchronously delete an internal snapshot of a block device, when the format
373
-# of the image used support it. The snapshot is identified by name or id or
374
-# both. One of the name or id is required. Return SnapshotInfo for the
375
-# successfully deleted snapshot.
376
-#
377
-# @device: the device name or node-name of a root node to delete the snapshot
378
-# from
379
-#
380
-# @id: optional the snapshot's ID to be deleted
381
-#
382
-# @name: optional the snapshot's name to be deleted
383
-#
384
-# Returns: - SnapshotInfo on success
385
-# - If @device is not a valid block device, GenericError
386
-# - If snapshot not found, GenericError
387
-# - If the format of the image used does not support it,
388
-# BlockFormatFeatureNotSupported
389
-# - If @id and @name are both not specified, GenericError
390
-#
391
-# Since: 1.7
392
-#
393
-# Example:
394
-#
395
-# -> { "execute": "blockdev-snapshot-delete-internal-sync",
396
-# "arguments": { "device": "ide-hd0",
397
-# "name": "snapshot0" }
398
-# }
399
-# <- { "return": {
400
-# "id": "1",
401
-# "name": "snapshot0",
402
-# "vm-state-size": 0,
403
-# "date-sec": 1000012,
404
-# "date-nsec": 10,
405
-# "vm-clock-sec": 100,
406
-# "vm-clock-nsec": 20
407
-# }
408
-# }
409
-#
410
-##
411
-{ 'command': 'blockdev-snapshot-delete-internal-sync',
412
- 'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
413
- 'returns': 'SnapshotInfo' }
414
-
415
##
416
# @eject:
417
#
418
@@ -XXX,XX +XXX,XX @@
419
'*id': 'str',
420
'*force': 'bool' } }
421
422
-##
423
-# @nbd-server-start:
424
-#
425
-# Start an NBD server listening on the given host and port. Block
426
-# devices can then be exported using @nbd-server-add. The NBD
427
-# server will present them as named exports; for example, another
428
-# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
429
-#
430
-# @addr: Address on which to listen.
431
-# @tls-creds: ID of the TLS credentials object (since 2.6).
432
-# @tls-authz: ID of the QAuthZ authorization object used to validate
433
-# the client's x509 distinguished name. This object is
434
-# is only resolved at time of use, so can be deleted and
435
-# recreated on the fly while the NBD server is active.
436
-# If missing, it will default to denying access (since 4.0).
437
-#
438
-# Returns: error if the server is already running.
439
-#
440
-# Since: 1.3.0
441
-##
442
-{ 'command': 'nbd-server-start',
443
- 'data': { 'addr': 'SocketAddressLegacy',
444
- '*tls-creds': 'str',
445
- '*tls-authz': 'str'} }
446
-
447
-##
448
-# @nbd-server-add:
449
-#
450
-# Export a block node to QEMU's embedded NBD server.
451
-#
452
-# @device: The device name or node name of the node to be exported
453
-#
454
-# @name: Export name. If unspecified, the @device parameter is used as the
455
-# export name. (Since 2.12)
456
-#
457
-# @description: Free-form description of the export, up to 4096 bytes.
458
-# (Since 5.0)
459
-#
460
-# @writable: Whether clients should be able to write to the device via the
461
-# NBD connection (default false).
462
-#
463
-# @bitmap: Also export the dirty bitmap reachable from @device, so the
464
-# NBD client can use NBD_OPT_SET_META_CONTEXT with
465
-# "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0)
466
-#
467
-# Returns: error if the server is not running, or export with the same name
468
-# already exists.
469
-#
470
-# Since: 1.3.0
471
-##
472
-{ 'command': 'nbd-server-add',
473
- 'data': {'device': 'str', '*name': 'str', '*description': 'str',
474
- '*writable': 'bool', '*bitmap': 'str' } }
475
-
476
-##
477
-# @NbdServerRemoveMode:
478
-#
479
-# Mode for removing an NBD export.
480
-#
481
-# @safe: Remove export if there are no existing connections, fail otherwise.
482
-#
483
-# @hard: Drop all connections immediately and remove export.
484
-#
485
-# Potential additional modes to be added in the future:
486
-#
487
-# hide: Just hide export from new clients, leave existing connections as is.
488
-# Remove export after all clients are disconnected.
489
-#
490
-# soft: Hide export from new clients, answer with ESHUTDOWN for all further
491
-# requests from existing clients.
492
-#
493
-# Since: 2.12
494
-##
495
-{'enum': 'NbdServerRemoveMode', 'data': ['safe', 'hard']}
496
-
497
-##
498
-# @nbd-server-remove:
499
-#
500
-# Remove NBD export by name.
501
-#
502
-# @name: Export name.
503
-#
504
-# @mode: Mode of command operation. See @NbdServerRemoveMode description.
505
-# Default is 'safe'.
506
-#
507
-# Returns: error if
508
-# - the server is not running
509
-# - export is not found
510
-# - mode is 'safe' and there are existing connections
511
-#
512
-# Since: 2.12
513
-##
514
-{ 'command': 'nbd-server-remove',
515
- 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} }
516
-
517
-##
518
-# @nbd-server-stop:
519
-#
520
-# Stop QEMU's embedded NBD server, and unregister all devices previously
521
-# added via @nbd-server-add.
522
-#
523
-# Since: 1.3.0
524
-##
525
-{ 'command': 'nbd-server-stop' }
526
-
527
##
528
# @DEVICE_TRAY_MOVED:
529
#
530
@@ -XXX,XX +XXX,XX @@
531
##
532
{ 'event': 'PR_MANAGER_STATUS_CHANGED',
533
'data': { 'id': 'str', 'connected': 'bool' } }
534
-
535
-##
536
-# @QuorumOpType:
537
-#
538
-# An enumeration of the quorum operation types
539
-#
540
-# @read: read operation
541
-#
542
-# @write: write operation
543
-#
544
-# @flush: flush operation
545
-#
546
-# Since: 2.6
547
-##
548
-{ 'enum': 'QuorumOpType',
549
- 'data': [ 'read', 'write', 'flush' ] }
550
-
551
-##
552
-# @QUORUM_FAILURE:
553
-#
554
-# Emitted by the Quorum block driver if it fails to establish a quorum
555
-#
556
-# @reference: device name if defined else node name
557
-#
558
-# @sector-num: number of the first sector of the failed read operation
559
-#
560
-# @sectors-count: failed read operation sector count
561
-#
562
-# Note: This event is rate-limited.
563
-#
564
-# Since: 2.0
565
-#
566
-# Example:
567
-#
568
-# <- { "event": "QUORUM_FAILURE",
569
-# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
570
-# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
571
-#
572
-##
573
-{ 'event': 'QUORUM_FAILURE',
574
- 'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
575
-
576
-##
577
-# @QUORUM_REPORT_BAD:
578
-#
579
-# Emitted to report a corruption of a Quorum file
580
-#
581
-# @type: quorum operation type (Since 2.6)
582
-#
583
-# @error: error message. Only present on failure. This field
584
-# contains a human-readable error message. There are no semantics other
585
-# than that the block layer reported an error and clients should not
586
-# try to interpret the error string.
587
-#
588
-# @node-name: the graph node name of the block driver state
589
-#
590
-# @sector-num: number of the first sector of the failed read operation
591
-#
592
-# @sectors-count: failed read operation sector count
593
-#
594
-# Note: This event is rate-limited.
595
-#
596
-# Since: 2.0
597
-#
598
-# Example:
599
-#
600
-# 1. Read operation
601
-#
602
-# { "event": "QUORUM_REPORT_BAD",
603
-# "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
604
-# "type": "read" },
605
-# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
606
-#
607
-# 2. Flush operation
608
-#
609
-# { "event": "QUORUM_REPORT_BAD",
610
-# "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
611
-# "type": "flush", "error": "Broken pipe" },
612
-# "timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
613
-#
614
-##
615
-{ 'event': 'QUORUM_REPORT_BAD',
616
- 'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str',
617
- 'sector-num': 'int', 'sectors-count': 'int' } }
618
--
30
--
619
2.20.1
31
2.13.6
620
32
621
33
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Doug Gale <doug16k@gmail.com>
2
2
3
Test what happens when writing data to an external data file, where the
3
Add trace output for commands, errors, and undefined behavior.
4
write requires an L2 entry to be allocated, but the data write fails.
4
Add guest error log output for undefined behavior.
5
Report invalid undefined accesses to MMIO.
6
Annotate unlikely error checks with unlikely.
5
7
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
7
Message-Id: <20200225143130.111267-4-mreitz@redhat.com>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
12
---
10
tests/qemu-iotests/026 | 32 ++++++++++++++++++++++++++++++
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
11
tests/qemu-iotests/026.out | 6 ++++++
14
hw/block/trace-events | 93 ++++++++++++++
12
tests/qemu-iotests/026.out.nocache | 6 ++++++
15
2 files changed, 390 insertions(+), 52 deletions(-)
13
3 files changed, 44 insertions(+)
14
16
15
diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
16
index XXXXXXX..XXXXXXX 100755
18
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/026
19
--- a/hw/block/nvme.c
18
+++ b/tests/qemu-iotests/026
20
+++ b/hw/block/nvme.c
19
@@ -XXX,XX +XXX,XX @@ _cleanup()
21
@@ -XXX,XX +XXX,XX @@
22
#include "qapi/visitor.h"
23
#include "sysemu/block-backend.h"
24
25
+#include "qemu/log.h"
26
+#include "trace.h"
27
#include "nvme.h"
28
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
30
+ do { \
31
+ (trace_##trace)(__VA_ARGS__); \
32
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
33
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
34
+ } while (0)
35
+
36
static void nvme_process_sq(void *opaque);
37
38
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
39
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
20
{
40
{
21
    _cleanup_test_img
41
if (cq->irq_enabled) {
22
rm "$TEST_DIR/blkdebug.conf"
42
if (msix_enabled(&(n->parent_obj))) {
23
+ rm -f "$TEST_IMG.data_file"
43
+ trace_nvme_irq_msix(cq->vector);
24
}
44
msix_notify(&(n->parent_obj), cq->vector);
25
trap "_cleanup; exit \$status" 0 1 2 3 15
45
} else {
26
46
+ trace_nvme_irq_pin();
27
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c "write 0 $CLUSTER_SIZE" "$BLKDBG_TEST_IMG" | _filter_qemu_io
47
pci_irq_pulse(&n->parent_obj);
28
48
}
29
_check_test_img
49
+ } else {
30
50
+ trace_nvme_irq_masked();
31
+echo
51
}
32
+echo === Avoid freeing external data clusters on failure ===
52
}
33
+echo
53
34
+
54
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
35
+# Similar test as the last one, except we test what happens when there
55
trans_len = MIN(len, trans_len);
36
+# is an error when writing to an external data file instead of when
56
int num_prps = (len >> n->page_bits) + 1;
37
+# writing to a preallocated zero cluster
57
38
+_make_test_img -o "data_file=$TEST_IMG.data_file" $CLUSTER_SIZE
58
- if (!prp1) {
39
+
59
+ if (unlikely(!prp1)) {
40
+# Put blkdebug above the data-file, and a raw node on top of that so
60
+ trace_nvme_err_invalid_prp();
41
+# that blkdebug will see a write_aio event and emit an error
61
return NVME_INVALID_FIELD | NVME_DNR;
42
+$QEMU_IO -c "write 0 $CLUSTER_SIZE" \
62
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
43
+ "json:{
63
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
44
+ 'driver': 'qcow2',
64
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
45
+ 'file': { 'driver': 'file', 'filename': '$TEST_IMG' },
65
}
46
+ 'data-file': {
66
len -= trans_len;
47
+ 'driver': 'raw',
67
if (len) {
48
+ 'file': {
68
- if (!prp2) {
49
+ 'driver': 'blkdebug',
69
+ if (unlikely(!prp2)) {
50
+ 'config': '$TEST_DIR/blkdebug.conf',
70
+ trace_nvme_err_invalid_prp2_missing();
51
+ 'image': {
71
goto unmap;
52
+ 'driver': 'file',
72
}
53
+ 'filename': '$TEST_IMG.data_file'
73
if (len > n->page_size) {
54
+ }
74
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
55
+ }
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
56
+ }
76
57
+ }" \
77
if (i == n->max_prp_ents - 1 && len > n->page_size) {
58
+ | _filter_qemu_io
78
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
59
+
79
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
60
+_check_test_img
80
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
61
+
81
goto unmap;
62
# success, all done
82
}
63
echo "*** done"
83
64
rm -f $seq.full
84
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
65
diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out
85
prp_ent = le64_to_cpu(prp_list[i]);
86
}
87
88
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
89
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
90
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
91
goto unmap;
92
}
93
94
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
95
i++;
96
}
97
} else {
98
- if (prp2 & (n->page_size - 1)) {
99
+ if (unlikely(prp2 & (n->page_size - 1))) {
100
+ trace_nvme_err_invalid_prp2_align(prp2);
101
goto unmap;
102
}
103
if (qsg->nsg) {
104
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
105
QEMUIOVector iov;
106
uint16_t status = NVME_SUCCESS;
107
108
+ trace_nvme_dma_read(prp1, prp2);
109
+
110
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
111
return NVME_INVALID_FIELD | NVME_DNR;
112
}
113
if (qsg.nsg > 0) {
114
- if (dma_buf_read(ptr, len, &qsg)) {
115
+ if (unlikely(dma_buf_read(ptr, len, &qsg))) {
116
+ trace_nvme_err_invalid_dma();
117
status = NVME_INVALID_FIELD | NVME_DNR;
118
}
119
qemu_sglist_destroy(&qsg);
120
} else {
121
- if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
122
+ if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
123
+ trace_nvme_err_invalid_dma();
124
status = NVME_INVALID_FIELD | NVME_DNR;
125
}
126
qemu_iovec_destroy(&iov);
127
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
128
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
129
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
130
131
- if (slba + nlb > ns->id_ns.nsze) {
132
+ if (unlikely(slba + nlb > ns->id_ns.nsze)) {
133
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
134
return NVME_LBA_RANGE | NVME_DNR;
135
}
136
137
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
138
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
139
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
140
141
- if ((slba + nlb) > ns->id_ns.nsze) {
142
+ trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
143
+
144
+ if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
145
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
146
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
147
return NVME_LBA_RANGE | NVME_DNR;
148
}
149
150
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
151
NvmeNamespace *ns;
152
uint32_t nsid = le32_to_cpu(cmd->nsid);
153
154
- if (nsid == 0 || nsid > n->num_namespaces) {
155
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
156
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
157
return NVME_INVALID_NSID | NVME_DNR;
158
}
159
160
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
161
case NVME_CMD_READ:
162
return nvme_rw(n, ns, cmd, req);
163
default:
164
+ trace_nvme_err_invalid_opc(cmd->opcode);
165
return NVME_INVALID_OPCODE | NVME_DNR;
166
}
167
}
168
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
169
NvmeCQueue *cq;
170
uint16_t qid = le16_to_cpu(c->qid);
171
172
- if (!qid || nvme_check_sqid(n, qid)) {
173
+ if (unlikely(!qid || nvme_check_sqid(n, qid))) {
174
+ trace_nvme_err_invalid_del_sq(qid);
175
return NVME_INVALID_QID | NVME_DNR;
176
}
177
178
+ trace_nvme_del_sq(qid);
179
+
180
sq = n->sq[qid];
181
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
182
req = QTAILQ_FIRST(&sq->out_req_list);
183
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
184
uint16_t qflags = le16_to_cpu(c->sq_flags);
185
uint64_t prp1 = le64_to_cpu(c->prp1);
186
187
- if (!cqid || nvme_check_cqid(n, cqid)) {
188
+ trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags);
189
+
190
+ if (unlikely(!cqid || nvme_check_cqid(n, cqid))) {
191
+ trace_nvme_err_invalid_create_sq_cqid(cqid);
192
return NVME_INVALID_CQID | NVME_DNR;
193
}
194
- if (!sqid || !nvme_check_sqid(n, sqid)) {
195
+ if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) {
196
+ trace_nvme_err_invalid_create_sq_sqid(sqid);
197
return NVME_INVALID_QID | NVME_DNR;
198
}
199
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
200
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
201
+ trace_nvme_err_invalid_create_sq_size(qsize);
202
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
203
}
204
- if (!prp1 || prp1 & (n->page_size - 1)) {
205
+ if (unlikely(!prp1 || prp1 & (n->page_size - 1))) {
206
+ trace_nvme_err_invalid_create_sq_addr(prp1);
207
return NVME_INVALID_FIELD | NVME_DNR;
208
}
209
- if (!(NVME_SQ_FLAGS_PC(qflags))) {
210
+ if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) {
211
+ trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags));
212
return NVME_INVALID_FIELD | NVME_DNR;
213
}
214
sq = g_malloc0(sizeof(*sq));
215
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
216
NvmeCQueue *cq;
217
uint16_t qid = le16_to_cpu(c->qid);
218
219
- if (!qid || nvme_check_cqid(n, qid)) {
220
+ if (unlikely(!qid || nvme_check_cqid(n, qid))) {
221
+ trace_nvme_err_invalid_del_cq_cqid(qid);
222
return NVME_INVALID_CQID | NVME_DNR;
223
}
224
225
cq = n->cq[qid];
226
- if (!QTAILQ_EMPTY(&cq->sq_list)) {
227
+ if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) {
228
+ trace_nvme_err_invalid_del_cq_notempty(qid);
229
return NVME_INVALID_QUEUE_DEL;
230
}
231
+ trace_nvme_del_cq(qid);
232
nvme_free_cq(cq, n);
233
return NVME_SUCCESS;
234
}
235
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
236
uint16_t qflags = le16_to_cpu(c->cq_flags);
237
uint64_t prp1 = le64_to_cpu(c->prp1);
238
239
- if (!cqid || !nvme_check_cqid(n, cqid)) {
240
+ trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags,
241
+ NVME_CQ_FLAGS_IEN(qflags) != 0);
242
+
243
+ if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) {
244
+ trace_nvme_err_invalid_create_cq_cqid(cqid);
245
return NVME_INVALID_CQID | NVME_DNR;
246
}
247
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
248
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
249
+ trace_nvme_err_invalid_create_cq_size(qsize);
250
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
251
}
252
- if (!prp1) {
253
+ if (unlikely(!prp1)) {
254
+ trace_nvme_err_invalid_create_cq_addr(prp1);
255
return NVME_INVALID_FIELD | NVME_DNR;
256
}
257
- if (vector > n->num_queues) {
258
+ if (unlikely(vector > n->num_queues)) {
259
+ trace_nvme_err_invalid_create_cq_vector(vector);
260
return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
261
}
262
- if (!(NVME_CQ_FLAGS_PC(qflags))) {
263
+ if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) {
264
+ trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags));
265
return NVME_INVALID_FIELD | NVME_DNR;
266
}
267
268
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c)
269
uint64_t prp1 = le64_to_cpu(c->prp1);
270
uint64_t prp2 = le64_to_cpu(c->prp2);
271
272
+ trace_nvme_identify_ctrl();
273
+
274
return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
275
prp1, prp2);
276
}
277
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c)
278
uint64_t prp1 = le64_to_cpu(c->prp1);
279
uint64_t prp2 = le64_to_cpu(c->prp2);
280
281
- if (nsid == 0 || nsid > n->num_namespaces) {
282
+ trace_nvme_identify_ns(nsid);
283
+
284
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
285
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
286
return NVME_INVALID_NSID | NVME_DNR;
287
}
288
289
ns = &n->namespaces[nsid - 1];
290
+
291
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
292
prp1, prp2);
293
}
294
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
295
uint16_t ret;
296
int i, j = 0;
297
298
+ trace_nvme_identify_nslist(min_nsid);
299
+
300
list = g_malloc0(data_len);
301
for (i = 0; i < n->num_namespaces; i++) {
302
if (i < min_nsid) {
303
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
304
case 0x02:
305
return nvme_identify_nslist(n, c);
306
default:
307
+ trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
308
return NVME_INVALID_FIELD | NVME_DNR;
309
}
310
}
311
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
312
switch (dw10) {
313
case NVME_VOLATILE_WRITE_CACHE:
314
result = blk_enable_write_cache(n->conf.blk);
315
+ trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
316
break;
317
case NVME_NUMBER_OF_QUEUES:
318
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
319
+ trace_nvme_getfeat_numq(result);
320
break;
321
default:
322
+ trace_nvme_err_invalid_getfeat(dw10);
323
return NVME_INVALID_FIELD | NVME_DNR;
324
}
325
326
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
327
blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
328
break;
329
case NVME_NUMBER_OF_QUEUES:
330
+ trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1,
331
+ ((dw11 >> 16) & 0xFFFF) + 1,
332
+ n->num_queues - 1, n->num_queues - 1);
333
req->cqe.result =
334
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
335
break;
336
default:
337
+ trace_nvme_err_invalid_setfeat(dw10);
338
return NVME_INVALID_FIELD | NVME_DNR;
339
}
340
return NVME_SUCCESS;
341
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
342
case NVME_ADM_CMD_GET_FEATURES:
343
return nvme_get_feature(n, cmd, req);
344
default:
345
+ trace_nvme_err_invalid_admin_opc(cmd->opcode);
346
return NVME_INVALID_OPCODE | NVME_DNR;
347
}
348
}
349
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
350
uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
351
uint32_t page_size = 1 << page_bits;
352
353
- if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
354
- n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
355
- NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
356
- NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
357
- NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
358
- NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
359
- NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
360
- NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
361
- !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) {
362
+ if (unlikely(n->cq[0])) {
363
+ trace_nvme_err_startfail_cq();
364
+ return -1;
365
+ }
366
+ if (unlikely(n->sq[0])) {
367
+ trace_nvme_err_startfail_sq();
368
+ return -1;
369
+ }
370
+ if (unlikely(!n->bar.asq)) {
371
+ trace_nvme_err_startfail_nbarasq();
372
+ return -1;
373
+ }
374
+ if (unlikely(!n->bar.acq)) {
375
+ trace_nvme_err_startfail_nbaracq();
376
+ return -1;
377
+ }
378
+ if (unlikely(n->bar.asq & (page_size - 1))) {
379
+ trace_nvme_err_startfail_asq_misaligned(n->bar.asq);
380
+ return -1;
381
+ }
382
+ if (unlikely(n->bar.acq & (page_size - 1))) {
383
+ trace_nvme_err_startfail_acq_misaligned(n->bar.acq);
384
+ return -1;
385
+ }
386
+ if (unlikely(NVME_CC_MPS(n->bar.cc) <
387
+ NVME_CAP_MPSMIN(n->bar.cap))) {
388
+ trace_nvme_err_startfail_page_too_small(
389
+ NVME_CC_MPS(n->bar.cc),
390
+ NVME_CAP_MPSMIN(n->bar.cap));
391
+ return -1;
392
+ }
393
+ if (unlikely(NVME_CC_MPS(n->bar.cc) >
394
+ NVME_CAP_MPSMAX(n->bar.cap))) {
395
+ trace_nvme_err_startfail_page_too_large(
396
+ NVME_CC_MPS(n->bar.cc),
397
+ NVME_CAP_MPSMAX(n->bar.cap));
398
+ return -1;
399
+ }
400
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) <
401
+ NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) {
402
+ trace_nvme_err_startfail_cqent_too_small(
403
+ NVME_CC_IOCQES(n->bar.cc),
404
+ NVME_CTRL_CQES_MIN(n->bar.cap));
405
+ return -1;
406
+ }
407
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) >
408
+ NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) {
409
+ trace_nvme_err_startfail_cqent_too_large(
410
+ NVME_CC_IOCQES(n->bar.cc),
411
+ NVME_CTRL_CQES_MAX(n->bar.cap));
412
+ return -1;
413
+ }
414
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) <
415
+ NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) {
416
+ trace_nvme_err_startfail_sqent_too_small(
417
+ NVME_CC_IOSQES(n->bar.cc),
418
+ NVME_CTRL_SQES_MIN(n->bar.cap));
419
+ return -1;
420
+ }
421
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) >
422
+ NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) {
423
+ trace_nvme_err_startfail_sqent_too_large(
424
+ NVME_CC_IOSQES(n->bar.cc),
425
+ NVME_CTRL_SQES_MAX(n->bar.cap));
426
+ return -1;
427
+ }
428
+ if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) {
429
+ trace_nvme_err_startfail_asqent_sz_zero();
430
+ return -1;
431
+ }
432
+ if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) {
433
+ trace_nvme_err_startfail_acqent_sz_zero();
434
return -1;
435
}
436
437
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
438
static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
439
unsigned size)
440
{
441
+ if (unlikely(offset & (sizeof(uint32_t) - 1))) {
442
+ NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32,
443
+ "MMIO write not 32-bit aligned,"
444
+ " offset=0x%"PRIx64"", offset);
445
+ /* should be ignored, fall through for now */
446
+ }
447
+
448
+ if (unlikely(size < sizeof(uint32_t))) {
449
+ NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall,
450
+ "MMIO write smaller than 32-bits,"
451
+ " offset=0x%"PRIx64", size=%u",
452
+ offset, size);
453
+ /* should be ignored, fall through for now */
454
+ }
455
+
456
switch (offset) {
457
- case 0xc:
458
+ case 0xc: /* INTMS */
459
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
460
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
461
+ "undefined access to interrupt mask set"
462
+ " when MSI-X is enabled");
463
+ /* should be ignored, fall through for now */
464
+ }
465
n->bar.intms |= data & 0xffffffff;
466
n->bar.intmc = n->bar.intms;
467
+ trace_nvme_mmio_intm_set(data & 0xffffffff,
468
+ n->bar.intmc);
469
break;
470
- case 0x10:
471
+ case 0x10: /* INTMC */
472
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
473
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
474
+ "undefined access to interrupt mask clr"
475
+ " when MSI-X is enabled");
476
+ /* should be ignored, fall through for now */
477
+ }
478
n->bar.intms &= ~(data & 0xffffffff);
479
n->bar.intmc = n->bar.intms;
480
+ trace_nvme_mmio_intm_clr(data & 0xffffffff,
481
+ n->bar.intmc);
482
break;
483
- case 0x14:
484
+ case 0x14: /* CC */
485
+ trace_nvme_mmio_cfg(data & 0xffffffff);
486
/* Windows first sends data, then sends enable bit */
487
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
488
!NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
489
@@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
490
491
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
492
n->bar.cc = data;
493
- if (nvme_start_ctrl(n)) {
494
+ if (unlikely(nvme_start_ctrl(n))) {
495
+ trace_nvme_err_startfail();
496
n->bar.csts = NVME_CSTS_FAILED;
497
} else {
498
+ trace_nvme_mmio_start_success();
499
n->bar.csts = NVME_CSTS_READY;
500
}
501
} else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
502
+ trace_nvme_mmio_stopped();
503
nvme_clear_ctrl(n);
504
n->bar.csts &= ~NVME_CSTS_READY;
505
}
506
if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
507
- nvme_clear_ctrl(n);
508
- n->bar.cc = data;
509
- n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
510
+ trace_nvme_mmio_shutdown_set();
511
+ nvme_clear_ctrl(n);
512
+ n->bar.cc = data;
513
+ n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
514
} else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
515
- n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
516
- n->bar.cc = data;
517
+ trace_nvme_mmio_shutdown_cleared();
518
+ n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
519
+ n->bar.cc = data;
520
+ }
521
+ break;
522
+ case 0x1C: /* CSTS */
523
+ if (data & (1 << 4)) {
524
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported,
525
+ "attempted to W1C CSTS.NSSRO"
526
+ " but CAP.NSSRS is zero (not supported)");
527
+ } else if (data != 0) {
528
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts,
529
+ "attempted to set a read only bit"
530
+ " of controller status");
531
+ }
532
+ break;
533
+ case 0x20: /* NSSR */
534
+ if (data == 0x4E564D65) {
535
+ trace_nvme_ub_mmiowr_ssreset_unsupported();
536
+ } else {
537
+ /* The spec says that writes of other values have no effect */
538
+ return;
539
}
540
break;
541
- case 0x24:
542
+ case 0x24: /* AQA */
543
n->bar.aqa = data & 0xffffffff;
544
+ trace_nvme_mmio_aqattr(data & 0xffffffff);
545
break;
546
- case 0x28:
547
+ case 0x28: /* ASQ */
548
n->bar.asq = data;
549
+ trace_nvme_mmio_asqaddr(data);
550
break;
551
- case 0x2c:
552
+ case 0x2c: /* ASQ hi */
553
n->bar.asq |= data << 32;
554
+ trace_nvme_mmio_asqaddr_hi(data, n->bar.asq);
555
break;
556
- case 0x30:
557
+ case 0x30: /* ACQ */
558
+ trace_nvme_mmio_acqaddr(data);
559
n->bar.acq = data;
560
break;
561
- case 0x34:
562
+ case 0x34: /* ACQ hi */
563
n->bar.acq |= data << 32;
564
+ trace_nvme_mmio_acqaddr_hi(data, n->bar.acq);
565
break;
566
+ case 0x38: /* CMBLOC */
567
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved,
568
+ "invalid write to reserved CMBLOC"
569
+ " when CMBSZ is zero, ignored");
570
+ return;
571
+ case 0x3C: /* CMBSZ */
572
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly,
573
+ "invalid write to read only CMBSZ, ignored");
574
+ return;
575
default:
576
+ NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
577
+ "invalid MMIO write,"
578
+ " offset=0x%"PRIx64", data=%"PRIx64"",
579
+ offset, data);
580
break;
581
}
582
}
583
@@ -XXX,XX +XXX,XX @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
584
uint8_t *ptr = (uint8_t *)&n->bar;
585
uint64_t val = 0;
586
587
+ if (unlikely(addr & (sizeof(uint32_t) - 1))) {
588
+ NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32,
589
+ "MMIO read not 32-bit aligned,"
590
+ " offset=0x%"PRIx64"", addr);
591
+ /* should RAZ, fall through for now */
592
+ } else if (unlikely(size < sizeof(uint32_t))) {
593
+ NVME_GUEST_ERR(nvme_ub_mmiord_toosmall,
594
+ "MMIO read smaller than 32-bits,"
595
+ " offset=0x%"PRIx64"", addr);
596
+ /* should RAZ, fall through for now */
597
+ }
598
+
599
if (addr < sizeof(n->bar)) {
600
memcpy(&val, ptr + addr, size);
601
+ } else {
602
+ NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs,
603
+ "MMIO read beyond last register,"
604
+ " offset=0x%"PRIx64", returning 0", addr);
605
}
606
+
607
return val;
608
}
609
610
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
611
{
612
uint32_t qid;
613
614
- if (addr & ((1 << 2) - 1)) {
615
+ if (unlikely(addr & ((1 << 2) - 1))) {
616
+ NVME_GUEST_ERR(nvme_ub_db_wr_misaligned,
617
+ "doorbell write not 32-bit aligned,"
618
+ " offset=0x%"PRIx64", ignoring", addr);
619
return;
620
}
621
622
if (((addr - 0x1000) >> 2) & 1) {
623
+ /* Completion queue doorbell write */
624
+
625
uint16_t new_head = val & 0xffff;
626
int start_sqs;
627
NvmeCQueue *cq;
628
629
qid = (addr - (0x1000 + (1 << 2))) >> 3;
630
- if (nvme_check_cqid(n, qid)) {
631
+ if (unlikely(nvme_check_cqid(n, qid))) {
632
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq,
633
+ "completion queue doorbell write"
634
+ " for nonexistent queue,"
635
+ " sqid=%"PRIu32", ignoring", qid);
636
return;
637
}
638
639
cq = n->cq[qid];
640
- if (new_head >= cq->size) {
641
+ if (unlikely(new_head >= cq->size)) {
642
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead,
643
+ "completion queue doorbell write value"
644
+ " beyond queue size, sqid=%"PRIu32","
645
+ " new_head=%"PRIu16", ignoring",
646
+ qid, new_head);
647
return;
648
}
649
650
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
651
nvme_isr_notify(n, cq);
652
}
653
} else {
654
+ /* Submission queue doorbell write */
655
+
656
uint16_t new_tail = val & 0xffff;
657
NvmeSQueue *sq;
658
659
qid = (addr - 0x1000) >> 3;
660
- if (nvme_check_sqid(n, qid)) {
661
+ if (unlikely(nvme_check_sqid(n, qid))) {
662
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq,
663
+ "submission queue doorbell write"
664
+ " for nonexistent queue,"
665
+ " sqid=%"PRIu32", ignoring", qid);
666
return;
667
}
668
669
sq = n->sq[qid];
670
- if (new_tail >= sq->size) {
671
+ if (unlikely(new_tail >= sq->size)) {
672
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail,
673
+ "submission queue doorbell write value"
674
+ " beyond queue size, sqid=%"PRIu32","
675
+ " new_tail=%"PRIu16", ignoring",
676
+ qid, new_tail);
677
return;
678
}
679
680
diff --git a/hw/block/trace-events b/hw/block/trace-events
66
index XXXXXXX..XXXXXXX 100644
681
index XXXXXXX..XXXXXXX 100644
67
--- a/tests/qemu-iotests/026.out
682
--- a/hw/block/trace-events
68
+++ b/tests/qemu-iotests/026.out
683
+++ b/hw/block/trace-events
69
@@ -XXX,XX +XXX,XX @@ wrote 1024/1024 bytes at offset 0
684
@@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
70
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
685
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
71
write failed: Input/output error
686
hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
72
No errors were found on the image.
687
73
+
688
+# hw/block/nvme.c
74
+=== Avoid freeing external data clusters on failure ===
689
+# nvme traces for successful events
75
+
690
+nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
76
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 data_file=TEST_DIR/t.IMGFMT.data_file
691
+nvme_irq_pin(void) "pulsing IRQ pin"
77
+write failed: Input/output error
692
+nvme_irq_masked(void) "IRQ is masked"
78
+No errors were found on the image.
693
+nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
79
*** done
694
+nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64""
80
diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache
695
+nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16""
81
index XXXXXXX..XXXXXXX 100644
696
+nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d"
82
--- a/tests/qemu-iotests/026.out.nocache
697
+nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
83
+++ b/tests/qemu-iotests/026.out.nocache
698
+nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
84
@@ -XXX,XX +XXX,XX @@ wrote 1024/1024 bytes at offset 0
699
+nvme_identify_ctrl(void) "identify controller"
85
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
700
+nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
86
write failed: Input/output error
701
+nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
87
No errors were found on the image.
702
+nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
88
+
703
+nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
89
+=== Avoid freeing external data clusters on failure ===
704
+nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d"
90
+
705
+nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
91
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 data_file=TEST_DIR/t.IMGFMT.data_file
706
+nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
92
+write failed: Input/output error
707
+nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
93
+No errors were found on the image.
708
+nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
94
*** done
709
+nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
710
+nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
711
+nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
712
+nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
713
+nvme_mmio_start_success(void) "setting controller enable bit succeeded"
714
+nvme_mmio_stopped(void) "cleared controller enable bit"
715
+nvme_mmio_shutdown_set(void) "shutdown bit set"
716
+nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
717
+
718
+# nvme traces for error conditions
719
+nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
720
+nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
721
+nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
722
+nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
723
+nvme_err_invalid_field(void) "invalid field"
724
+nvme_err_invalid_prp(void) "invalid PRP"
725
+nvme_err_invalid_sgl(void) "invalid SGL"
726
+nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
727
+nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
728
+nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
729
+nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
730
+nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
731
+nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
732
+nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
733
+nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
734
+nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
735
+nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
736
+nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
737
+nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
738
+nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
739
+nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
740
+nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
741
+nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
742
+nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
743
+nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
744
+nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
745
+nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
746
+nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
747
+nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
748
+nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
749
+nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
750
+nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
751
+nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
752
+nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u"
753
+nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u"
754
+nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u"
755
+nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u"
756
+nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u"
757
+nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u"
758
+nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero"
759
+nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero"
760
+nvme_err_startfail(void) "setting controller enable bit failed"
761
+
762
+# Traces for undefined behavior
763
+nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64""
764
+nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u"
765
+nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled"
766
+nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status"
767
+nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)"
768
+nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
769
+nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
770
+nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
771
+nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
772
+nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
773
+nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
774
+nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0"
775
+nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring"
776
+nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring"
777
+nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring"
778
+nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring"
779
+nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
780
+
781
# hw/block/xen_disk.c
782
xen_disk_alloc(char *name) "%s"
783
xen_disk_init(char *name) "%s"
95
--
784
--
96
2.20.1
785
2.13.6
97
786
98
787
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
@options is leaked by the first two return statements in this function.
3
Management tools create overlays of running guests with qemu-img:
4
4
5
Note that blk_new_open() takes the reference to @options even on
5
$ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2
6
failure, so all we need to do to fix the leak is to move the QDict
7
allocation down to where we actually need it.
8
6
9
Reported-by: Coverity (CID 1419884)
7
but this doesn't work anymore due to image locking:
10
Fixes: fd17146cd93d1704cd96d7c2757b325fc7aac6fd
8
11
("block: Generic file creation fallback")
9
qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Is another process using the image?
13
Message-Id: <20200225155618.133412-1-mreitz@redhat.com>
11
Could not open backing image to determine size.
12
Use the force share option to allow this use case again.
13
14
Cc: qemu-stable@nongnu.org
15
Signed-off-by: Fam Zheng <famz@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
18
---
16
block.c | 3 ++-
19
block.c | 3 ++-
17
1 file changed, 2 insertions(+), 1 deletion(-)
20
1 file changed, 2 insertions(+), 1 deletion(-)
18
21
19
diff --git a/block.c b/block.c
22
diff --git a/block.c b/block.c
20
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
21
--- a/block.c
24
--- a/block.c
22
+++ b/block.c
25
+++ b/block.c
23
@@ -XXX,XX +XXX,XX @@ static int bdrv_create_file_fallback(const char *filename, BlockDriver *drv,
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
24
QemuOpts *opts, Error **errp)
27
back_flags = flags;
25
{
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
26
BlockBackend *blk;
29
27
- QDict *options = qdict_new();
30
+ backing_options = qdict_new();
28
+ QDict *options;
31
if (backing_fmt) {
29
int64_t size = 0;
32
- backing_options = qdict_new();
30
char *buf = NULL;
33
qdict_put_str(backing_options, "driver", backing_fmt);
31
PreallocMode prealloc;
34
}
32
@@ -XXX,XX +XXX,XX @@ static int bdrv_create_file_fallback(const char *filename, BlockDriver *drv,
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
33
return -ENOTSUP;
36
34
}
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
35
38
&local_err);
36
+ options = qdict_new();
37
qdict_put_str(options, "driver", drv->format_name);
38
39
blk = blk_new_open(filename, NULL, options,
40
--
39
--
41
2.20.1
40
2.13.6
42
41
43
42
diff view generated by jsdifflib
1
From: Florian Florensa <fflorensa@online.net>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
Starting from ceph Nautilus, RBD has support for namespaces, allowing
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
4
for finer grain ACLs on images inside a pool, and tenant isolation.
5
4
6
In the rbd cli tool documentation, the new image-spec and snap-spec are :
5
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
- [pool-name/[namespace-name/]]image-name
6
Reviewed-by: John Snow <jsnow@redhat.com>
8
- [pool-name/[namespace-name/]]image-name@snap-name
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
9
10
When using an non namespace's enabled qemu, it complains about not
11
finding the image called namespace-name/image-name, thus we only need to
12
parse the image once again to find if there is a '/' in its name, and if
13
there is, use what is before it as the name of the namespace to later
14
pass it to rados_ioctx_set_namespace.
15
rados_ioctx_set_namespace if called with en empty string or a null
16
pointer as the namespace parameters pretty much does nothing, as it then
17
defaults to the default namespace.
18
19
The namespace is extracted inside qemu_rbd_parse_filename, stored in the
20
qdict, and used in qemu_rbd_connect to make it work with both qemu-img,
21
and qemu itself.
22
23
Signed-off-by: Florian Florensa <fflorensa@online.net>
24
Message-Id: <20200110111513.321728-2-fflorensa@online.net>
25
Reviewed-by: Jason Dillaman <dillaman@redhat.com>
26
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
27
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
28
---
9
---
29
qapi/block-core.json | 3 +++
10
blockdev.c | 11 -----------
30
block/rbd.c | 44 +++++++++++++++++++++++++++++++-------------
11
qemu-doc.texi | 6 ------
31
2 files changed, 34 insertions(+), 13 deletions(-)
12
2 files changed, 17 deletions(-)
32
13
33
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/blockdev.c b/blockdev.c
34
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
35
--- a/qapi/block-core.json
16
--- a/blockdev.c
36
+++ b/qapi/block-core.json
17
+++ b/blockdev.c
37
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
38
#
19
.type = QEMU_OPT_STRING,
39
# @pool: Ceph pool name.
20
.help = "chs translation (auto, lba, none)",
40
#
21
},{
41
+# @namespace: Rados namespace name in the Ceph pool. (Since 5.0)
22
- .name = "boot",
42
+#
23
- .type = QEMU_OPT_BOOL,
43
# @image: Image name in the Ceph pool.
24
- .help = "(deprecated, ignored)",
44
#
25
- },{
45
# @conf: path to Ceph configuration file. Values
26
.name = "addr",
46
@@ -XXX,XX +XXX,XX @@
27
.type = QEMU_OPT_STRING,
47
##
28
.help = "pci address (virtio only)",
48
{ 'struct': 'BlockdevOptionsRbd',
29
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
49
'data': { 'pool': 'str',
30
goto fail;
50
+ '*namespace': 'str',
31
}
51
'image': 'str',
32
52
'*conf': 'str',
33
- /* Deprecated option boot=[on|off] */
53
'*snapshot': 'str',
34
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
54
diff --git a/block/rbd.c b/block/rbd.c
35
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
36
- "ignored. Future versions will reject this parameter. Please "
37
- "update your scripts.\n");
38
- }
39
-
40
/* Other deprecated options */
41
if (!qtest_enabled()) {
42
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
43
diff --git a/qemu-doc.texi b/qemu-doc.texi
55
index XXXXXXX..XXXXXXX 100644
44
index XXXXXXX..XXXXXXX 100644
56
--- a/block/rbd.c
45
--- a/qemu-doc.texi
57
+++ b/block/rbd.c
46
+++ b/qemu-doc.texi
58
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVRBDState {
47
@@ -XXX,XX +XXX,XX @@ deprecated.
59
rbd_image_t image;
48
60
char *image_name;
49
@section System emulator command line arguments
61
char *snap;
50
62
+ char *namespace;
51
-@subsection -drive boot=on|off (since 1.3.0)
63
uint64_t image_size;
52
-
64
} BDRVRBDState;
53
-The ``boot=on|off'' option to the ``-drive'' argument is
65
54
-ignored. Applications should use the ``bootindex=N'' parameter
66
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
55
-to set an absolute ordering between devices instead.
67
const char *start;
56
-
68
char *p, *buf;
57
@subsection -tdf (since 1.3.0)
69
QList *keypairs = NULL;
58
70
- char *found_str;
59
The ``-tdf'' argument is ignored. The behaviour implemented
71
+ char *found_str, *image_name;
72
73
if (!strstart(filename, "rbd:", &start)) {
74
error_setg(errp, "File name must start with 'rbd:'");
75
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
76
qdict_put_str(options, "pool", found_str);
77
78
if (strchr(p, '@')) {
79
- found_str = qemu_rbd_next_tok(p, '@', &p);
80
- qemu_rbd_unescape(found_str);
81
- qdict_put_str(options, "image", found_str);
82
+ image_name = qemu_rbd_next_tok(p, '@', &p);
83
84
found_str = qemu_rbd_next_tok(p, ':', &p);
85
qemu_rbd_unescape(found_str);
86
qdict_put_str(options, "snapshot", found_str);
87
} else {
88
- found_str = qemu_rbd_next_tok(p, ':', &p);
89
+ image_name = qemu_rbd_next_tok(p, ':', &p);
90
+ }
91
+ /* Check for namespace in the image_name */
92
+ if (strchr(image_name, '/')) {
93
+ found_str = qemu_rbd_next_tok(image_name, '/', &image_name);
94
qemu_rbd_unescape(found_str);
95
- qdict_put_str(options, "image", found_str);
96
+ qdict_put_str(options, "namespace", found_str);
97
+ } else {
98
+ qdict_put_str(options, "namespace", "");
99
}
100
+ qemu_rbd_unescape(image_name);
101
+ qdict_put_str(options, "image", image_name);
102
if (!p) {
103
goto done;
104
}
105
@@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = {
106
.type = QEMU_OPT_STRING,
107
.help = "Rados pool name",
108
},
109
+ {
110
+ .name = "namespace",
111
+ .type = QEMU_OPT_STRING,
112
+ .help = "Rados namespace name in the pool",
113
+ },
114
{
115
.name = "image",
116
.type = QEMU_OPT_STRING,
117
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
118
* schema, but when they come from -drive, they're all QString.
119
*/
120
loc = rbd_opts->location;
121
- loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
122
- loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
123
- loc->has_conf = !!loc->conf;
124
- loc->user = g_strdup(qdict_get_try_str(options, "user"));
125
- loc->has_user = !!loc->user;
126
- loc->image = g_strdup(qdict_get_try_str(options, "image"));
127
- keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
128
+ loc->pool = g_strdup(qdict_get_try_str(options, "pool"));
129
+ loc->conf = g_strdup(qdict_get_try_str(options, "conf"));
130
+ loc->has_conf = !!loc->conf;
131
+ loc->user = g_strdup(qdict_get_try_str(options, "user"));
132
+ loc->has_user = !!loc->user;
133
+ loc->q_namespace = g_strdup(qdict_get_try_str(options, "namespace"));
134
+ loc->image = g_strdup(qdict_get_try_str(options, "image"));
135
+ keypairs = qdict_get_try_str(options, "=keyvalue-pairs");
136
137
ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp);
138
if (ret < 0) {
139
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
140
error_setg_errno(errp, -r, "error opening pool %s", opts->pool);
141
goto failed_shutdown;
142
}
143
+ /*
144
+ * Set the namespace after opening the io context on the pool,
145
+ * if nspace == NULL or if nspace == "", it is just as we did nothing
146
+ */
147
+ rados_ioctx_set_namespace(*io_ctx, opts->q_namespace);
148
149
return 0;
150
151
--
60
--
152
2.20.1
61
2.13.6
153
62
154
63
diff view generated by jsdifflib
New patch
1
1
From: Thomas Huth <thuth@redhat.com>
2
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
complained that we should keep it, so let's remove this legacy option
5
now to simplify the code quite a bit.
6
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
vl.c | 86 ++-------------------------------------------------------
13
qemu-doc.texi | 8 ------
14
qemu-options.hx | 19 ++-----------
15
3 files changed, 4 insertions(+), 109 deletions(-)
16
17
diff --git a/vl.c b/vl.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/vl.c
20
+++ b/vl.c
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
22
const char *boot_order = NULL;
23
const char *boot_once = NULL;
24
DisplayState *ds;
25
- int cyls, heads, secs, translation;
26
QemuOpts *opts, *machine_opts;
27
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
28
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
29
QemuOptsList *olist;
30
int optind;
31
const char *optarg;
32
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
33
34
cpu_model = NULL;
35
snapshot = 0;
36
- cyls = heads = secs = 0;
37
- translation = BIOS_ATA_TRANSLATION_AUTO;
38
39
nb_nics = 0;
40
41
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
42
if (optind >= argc)
43
break;
44
if (argv[optind][0] != '-') {
45
- hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
46
+ drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
47
} else {
48
const QEMUOption *popt;
49
50
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
51
cpu_model = optarg;
52
break;
53
case QEMU_OPTION_hda:
54
- {
55
- char buf[256];
56
- if (cyls == 0)
57
- snprintf(buf, sizeof(buf), "%s", HD_OPTS);
58
- else
59
- snprintf(buf, sizeof(buf),
60
- "%s,cyls=%d,heads=%d,secs=%d%s",
61
- HD_OPTS , cyls, heads, secs,
62
- translation == BIOS_ATA_TRANSLATION_LBA ?
63
- ",trans=lba" :
64
- translation == BIOS_ATA_TRANSLATION_NONE ?
65
- ",trans=none" : "");
66
- drive_add(IF_DEFAULT, 0, optarg, buf);
67
- break;
68
- }
69
case QEMU_OPTION_hdb:
70
case QEMU_OPTION_hdc:
71
case QEMU_OPTION_hdd:
72
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
73
case QEMU_OPTION_snapshot:
74
snapshot = 1;
75
break;
76
- case QEMU_OPTION_hdachs:
77
- {
78
- const char *p;
79
- p = optarg;
80
- cyls = strtol(p, (char **)&p, 0);
81
- if (cyls < 1 || cyls > 16383)
82
- goto chs_fail;
83
- if (*p != ',')
84
- goto chs_fail;
85
- p++;
86
- heads = strtol(p, (char **)&p, 0);
87
- if (heads < 1 || heads > 16)
88
- goto chs_fail;
89
- if (*p != ',')
90
- goto chs_fail;
91
- p++;
92
- secs = strtol(p, (char **)&p, 0);
93
- if (secs < 1 || secs > 63)
94
- goto chs_fail;
95
- if (*p == ',') {
96
- p++;
97
- if (!strcmp(p, "large")) {
98
- translation = BIOS_ATA_TRANSLATION_LARGE;
99
- } else if (!strcmp(p, "rechs")) {
100
- translation = BIOS_ATA_TRANSLATION_RECHS;
101
- } else if (!strcmp(p, "none")) {
102
- translation = BIOS_ATA_TRANSLATION_NONE;
103
- } else if (!strcmp(p, "lba")) {
104
- translation = BIOS_ATA_TRANSLATION_LBA;
105
- } else if (!strcmp(p, "auto")) {
106
- translation = BIOS_ATA_TRANSLATION_AUTO;
107
- } else {
108
- goto chs_fail;
109
- }
110
- } else if (*p != '\0') {
111
- chs_fail:
112
- error_report("invalid physical CHS format");
113
- exit(1);
114
- }
115
- if (hda_opts != NULL) {
116
- qemu_opt_set_number(hda_opts, "cyls", cyls,
117
- &error_abort);
118
- qemu_opt_set_number(hda_opts, "heads", heads,
119
- &error_abort);
120
- qemu_opt_set_number(hda_opts, "secs", secs,
121
- &error_abort);
122
- if (translation == BIOS_ATA_TRANSLATION_LARGE) {
123
- qemu_opt_set(hda_opts, "trans", "large",
124
- &error_abort);
125
- } else if (translation == BIOS_ATA_TRANSLATION_RECHS) {
126
- qemu_opt_set(hda_opts, "trans", "rechs",
127
- &error_abort);
128
- } else if (translation == BIOS_ATA_TRANSLATION_LBA) {
129
- qemu_opt_set(hda_opts, "trans", "lba",
130
- &error_abort);
131
- } else if (translation == BIOS_ATA_TRANSLATION_NONE) {
132
- qemu_opt_set(hda_opts, "trans", "none",
133
- &error_abort);
134
- }
135
- }
136
- }
137
- error_report("'-hdachs' is deprecated, please use '-device"
138
- " ide-hd,cyls=c,heads=h,secs=s,...' instead");
139
- break;
140
case QEMU_OPTION_numa:
141
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
142
optarg, true);
143
diff --git a/qemu-doc.texi b/qemu-doc.texi
144
index XXXXXXX..XXXXXXX 100644
145
--- a/qemu-doc.texi
146
+++ b/qemu-doc.texi
147
@@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the
148
``-object filter-dump'' argument which works in combination
149
with the modern ``-netdev`` backends instead.
150
151
-@subsection -hdachs (since 2.10.0)
152
-
153
-The ``-hdachs'' argument is now a synonym for setting
154
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
155
-on the ``ide-hd'' device using the ``-device'' argument.
156
-The new syntax allows different settings to be provided
157
-per disk.
158
-
159
@subsection -usbdevice (since 2.10.0)
160
161
The ``-usbdevice DEV'' argument is now a synonym for setting
162
diff --git a/qemu-options.hx b/qemu-options.hx
163
index XXXXXXX..XXXXXXX 100644
164
--- a/qemu-options.hx
165
+++ b/qemu-options.hx
166
@@ -XXX,XX +XXX,XX @@ of available connectors of a given interface type.
167
@item media=@var{media}
168
This option defines the type of the media: disk or cdrom.
169
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
170
-These options have the same definition as they have in @option{-hdachs}.
171
-These parameters are deprecated, use the corresponding parameters
172
+Force disk physical geometry and the optional BIOS translation (trans=none or
173
+lba). These parameters are deprecated, use the corresponding parameters
174
of @code{-device} instead.
175
@item snapshot=@var{snapshot}
176
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
177
@@ -XXX,XX +XXX,XX @@ the raw disk image you use is not written back. You can however force
178
the write back by pressing @key{C-a s} (@pxref{disk_images}).
179
ETEXI
180
181
-DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
182
- "-hdachs c,h,s[,t]\n" \
183
- " force hard disk 0 physical geometry and the optional BIOS\n" \
184
- " translation (t=none or lba) (usually QEMU can guess them)\n",
185
- QEMU_ARCH_ALL)
186
-STEXI
187
-@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
188
-@findex -hdachs
189
-Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
190
-@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
191
-translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
192
-all those parameters. This option is deprecated, please use
193
-@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead.
194
-ETEXI
195
-
196
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
197
"-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n"
198
" [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n"
199
--
200
2.13.6
201
202
diff view generated by jsdifflib
1
We want to share the whitelists between the system emulator schema and
1
From: Thomas Huth <thuth@redhat.com>
2
the storage daemon schema, so move all the pragmas from the main schema
3
file into a separate file that can be included from both.
4
2
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Looks like we forgot to announce the deprecation of these options in
6
Message-Id: <20200224143008.13362-16-kwolf@redhat.com>
4
the corresponding chapter of the qemu-doc text, so let's do that now.
7
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
5
6
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
---
10
qapi/pragma.json | 24 ++++++++++++++++++++++++
11
qemu-doc.texi | 15 +++++++++++++++
11
qapi/qapi-schema.json | 25 +------------------------
12
1 file changed, 15 insertions(+)
12
qapi/Makefile.objs | 2 +-
13
3 files changed, 26 insertions(+), 25 deletions(-)
14
create mode 100644 qapi/pragma.json
15
13
16
diff --git a/qapi/pragma.json b/qapi/pragma.json
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
17
new file mode 100644
15
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX
16
--- a/qemu-doc.texi
19
--- /dev/null
17
+++ b/qemu-doc.texi
20
+++ b/qapi/pragma.json
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
21
@@ -XXX,XX +XXX,XX @@
19
The ``-drive if=scsi'' argument is replaced by the the
22
+{ 'pragma': { 'doc-required': true } }
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
21
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
23
+
23
+
24
+# Whitelists to permit QAPI rule violations; think twice before you
24
+The drive geometry arguments are replaced by the the geometry arguments
25
+# add to them!
25
+that can be specified with the ``-device'' parameter.
26
+{ 'pragma': {
26
+
27
+ # Commands allowed to return a non-dictionary:
27
+@subsection -drive serial=... (since 2.10.0)
28
+ 'returns-whitelist': [
28
+
29
+ 'human-monitor-command',
29
+The drive serial argument is replaced by the the serial argument
30
+ 'qom-get',
30
+that can be specified with the ``-device'' parameter.
31
+ 'query-migrate-cache-size',
31
+
32
+ 'query-tpm-models',
32
+@subsection -drive addr=... (since 2.10.0)
33
+ 'query-tpm-types',
33
+
34
+ 'ringbuf-read' ],
34
+The drive addr argument is replaced by the the addr argument
35
+ 'name-case-whitelist': [
35
+that can be specified with the ``-device'' parameter.
36
+ 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
36
+
37
+ 'CpuInfoMIPS', # PC, visible through query-cpu
37
@subsection -net dump (since 2.10.0)
38
+ 'CpuInfoTricore', # PC, visible through query-cpu
38
39
+ 'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings
39
The ``--net dump'' argument is now replaced with the
40
+ 'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings
41
+ 'QapiErrorClass', # all members, visible through errors
42
+ 'UuidInfo', # UUID, visible through query-uuid
43
+ 'X86CPURegister32', # all members, visible indirectly through qom-get
44
+ 'CpuInfo' # CPU, visible through query-cpu
45
+ ] } }
46
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
47
index XXXXXXX..XXXXXXX 100644
48
--- a/qapi/qapi-schema.json
49
+++ b/qapi/qapi-schema.json
50
@@ -XXX,XX +XXX,XX @@
51
#
52
##
53
54
-{ 'pragma': { 'doc-required': true } }
55
-
56
-# Whitelists to permit QAPI rule violations; think twice before you
57
-# add to them!
58
-{ 'pragma': {
59
- # Commands allowed to return a non-dictionary:
60
- 'returns-whitelist': [
61
- 'human-monitor-command',
62
- 'qom-get',
63
- 'query-migrate-cache-size',
64
- 'query-tpm-models',
65
- 'query-tpm-types',
66
- 'ringbuf-read' ],
67
- 'name-case-whitelist': [
68
- 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
69
- 'CpuInfoMIPS', # PC, visible through query-cpu
70
- 'CpuInfoTricore', # PC, visible through query-cpu
71
- 'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings
72
- 'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings
73
- 'QapiErrorClass', # all members, visible through errors
74
- 'UuidInfo', # UUID, visible through query-uuid
75
- 'X86CPURegister32', # all members, visible indirectly through qom-get
76
- 'CpuInfo' # CPU, visible through query-cpu
77
- ] } }
78
+{ 'include': 'pragma.json' }
79
80
# Documentation generated with qapi-gen.py is in source order, with
81
# included sub-schemas inserted at the first include directive
82
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
83
index XXXXXXX..XXXXXXX 100644
84
--- a/qapi/Makefile.objs
85
+++ b/qapi/Makefile.objs
86
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi-util.o
87
88
QAPI_COMMON_MODULES = audio authz block-core block char common control crypto
89
QAPI_COMMON_MODULES += dump error introspect job machine migration misc
90
-QAPI_COMMON_MODULES += net qdev qom rdma rocker run-state sockets tpm
91
+QAPI_COMMON_MODULES += net pragma qdev qom rdma rocker run-state sockets tpm
92
QAPI_COMMON_MODULES += trace transaction ui
93
QAPI_TARGET_MODULES = machine-target misc-target
94
QAPI_MODULES = $(QAPI_COMMON_MODULES) $(QAPI_TARGET_MODULES)
95
--
40
--
96
2.20.1
41
2.13.6
97
42
98
43
diff view generated by jsdifflib
1
Add a --nbd-server option to qemu-storage-daemon to start the built-in
1
From: Fam Zheng <famz@redhat.com>
2
NBD server right away. It maps the arguments for nbd-server-start to the
3
command line, with the exception that it uses SocketAddress instead of
4
SocketAddressLegacy: New interfaces shouldn't use legacy types, and the
5
additional nesting would be nasty on the command line.
6
2
7
Example (only with required options):
3
Signed-off-by: Fam Zheng <famz@redhat.com>
8
9
--nbd-server addr.type=inet,addr.host=localhost,addr.port=10809
10
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20200224143008.13362-10-kwolf@redhat.com>
13
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
5
---
16
qapi/block-core.json | 24 ++++++++++++++++++++++++
6
include/block/block_int.h | 1 -
17
include/block/nbd.h | 1 +
7
block/io.c | 18 ------------------
18
blockdev-nbd.c | 5 +++++
8
2 files changed, 19 deletions(-)
19
qemu-storage-daemon.c | 26 +++++++++++++++++++++++++-
20
Makefile.objs | 2 +-
21
5 files changed, 56 insertions(+), 2 deletions(-)
22
9
23
diff --git a/qapi/block-core.json b/qapi/block-core.json
10
diff --git a/include/block/block_int.h b/include/block/block_int.h
24
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
25
--- a/qapi/block-core.json
12
--- a/include/block/block_int.h
26
+++ b/qapi/block-core.json
13
+++ b/include/block/block_int.h
27
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
28
'iothread': 'StrOrNull',
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
29
'*force': 'bool' } }
16
30
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
31
+##
18
-bool bdrv_requests_pending(BlockDriverState *bs);
32
+# @NbdServerOptions:
19
33
+#
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
34
+# @addr: Address on which to listen.
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
35
+# @tls-creds: ID of the TLS credentials object (since 2.6).
22
diff --git a/block/io.c b/block/io.c
36
+# @tls-authz: ID of the QAuthZ authorization object used to validate
37
+# the client's x509 distinguished name. This object is
38
+# is only resolved at time of use, so can be deleted and
39
+# recreated on the fly while the NBD server is active.
40
+# If missing, it will default to denying access (since 4.0).
41
+#
42
+# Keep this type consistent with the nbd-server-start arguments. The only
43
+# intended difference is using SocketAddress instead of SocketAddressLegacy.
44
+#
45
+# Since: 4.2
46
+##
47
+{ 'struct': 'NbdServerOptions',
48
+ 'data': { 'addr': 'SocketAddress',
49
+ '*tls-creds': 'str',
50
+ '*tls-authz': 'str'} }
51
+
52
##
53
# @nbd-server-start:
54
#
55
@@ -XXX,XX +XXX,XX @@
56
#
57
# Returns: error if the server is already running.
58
#
59
+# Keep this type consistent with the NbdServerOptions type. The only intended
60
+# difference is using SocketAddressLegacy instead of SocketAddress.
61
+#
62
# Since: 1.3.0
63
##
64
{ 'command': 'nbd-server-start',
65
diff --git a/include/block/nbd.h b/include/block/nbd.h
66
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
67
--- a/include/block/nbd.h
24
--- a/block/io.c
68
+++ b/include/block/nbd.h
25
+++ b/block/io.c
69
@@ -XXX,XX +XXX,XX @@ void nbd_client_put(NBDClient *client);
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
70
27
assert(old >= 1);
71
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
72
const char *tls_authz, Error **errp);
73
+void nbd_server_start_options(NbdServerOptions *arg, Error **errp);
74
75
/* nbd_read
76
* Reads @size bytes from @ioc. Returns 0 on success.
77
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
78
index XXXXXXX..XXXXXXX 100644
79
--- a/blockdev-nbd.c
80
+++ b/blockdev-nbd.c
81
@@ -XXX,XX +XXX,XX @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
82
nbd_server = NULL;
83
}
28
}
84
29
85
+void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
30
-/* Check if any requests are in-flight (including throttled requests) */
86
+{
31
-bool bdrv_requests_pending(BlockDriverState *bs)
87
+ nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, errp);
32
-{
88
+}
33
- BdrvChild *child;
89
+
34
-
90
void qmp_nbd_server_start(SocketAddressLegacy *addr,
35
- if (atomic_read(&bs->in_flight)) {
91
bool has_tls_creds, const char *tls_creds,
36
- return true;
92
bool has_tls_authz, const char *tls_authz,
37
- }
93
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
38
-
94
index XXXXXXX..XXXXXXX 100644
39
- QLIST_FOREACH(child, &bs->children, next) {
95
--- a/qemu-storage-daemon.c
40
- if (bdrv_requests_pending(child->bs)) {
96
+++ b/qemu-storage-daemon.c
41
- return true;
97
@@ -XXX,XX +XXX,XX @@
42
- }
98
#include <getopt.h>
43
- }
99
44
-
100
#include "block/block.h"
45
- return false;
101
+#include "block/nbd.h"
46
-}
102
#include "crypto/init.h"
47
-
103
48
typedef struct {
104
#include "qapi/error.h"
49
Coroutine *co;
105
-#include "qapi/qapi-visit-block-core.h"
50
BlockDriverState *bs;
106
+#include "qapi/qapi-commands-block.h"
107
#include "qapi/qapi-commands-block-core.h"
108
+#include "qapi/qapi-visit-block.h"
109
+#include "qapi/qapi-visit-block-core.h"
110
#include "qapi/qmp/qdict.h"
111
#include "qapi/qobject-input-visitor.h"
112
113
@@ -XXX,XX +XXX,XX @@ static void help(void)
114
" [,driver specific parameters...]\n"
115
" configure a block backend\n"
116
"\n"
117
+" --nbd-server addr.type=inet,addr.host=<host>,addr.port=<port>\n"
118
+" [,tls-creds=<id>][,tls-authz=<id>]\n"
119
+" --nbd-server addr.type=unix,addr.path=<path>\n"
120
+" [,tls-creds=<id>][,tls-authz=<id>]\n"
121
+" start an NBD server for exporting block nodes\n"
122
+"\n"
123
" --object help list object types that can be added\n"
124
" --object <type>,help list properties for the given object type\n"
125
" --object <type>[,<property>=<value>...]\n"
126
@@ -XXX,XX +XXX,XX @@ QEMU_HELP_BOTTOM "\n",
127
128
enum {
129
OPTION_BLOCKDEV = 256,
130
+ OPTION_NBD_SERVER,
131
OPTION_OBJECT,
132
};
133
134
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
135
static const struct option long_options[] = {
136
{"blockdev", required_argument, NULL, OPTION_BLOCKDEV},
137
{"help", no_argument, NULL, 'h'},
138
+ {"nbd-server", required_argument, NULL, OPTION_NBD_SERVER},
139
{"object", required_argument, NULL, OPTION_OBJECT},
140
{"trace", required_argument, NULL, 'T'},
141
{"version", no_argument, NULL, 'V'},
142
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
143
qapi_free_BlockdevOptions(options);
144
break;
145
}
146
+ case OPTION_NBD_SERVER:
147
+ {
148
+ Visitor *v;
149
+ NbdServerOptions *options;
150
+
151
+ v = qobject_input_visitor_new_str(optarg, NULL, &error_fatal);
152
+ visit_type_NbdServerOptions(v, NULL, &options, &error_fatal);
153
+ visit_free(v);
154
+
155
+ nbd_server_start_options(options, &error_fatal);
156
+ qapi_free_NbdServerOptions(options);
157
+ break;
158
+ }
159
case OPTION_OBJECT:
160
{
161
QemuOpts *opts;
162
diff --git a/Makefile.objs b/Makefile.objs
163
index XXXXXXX..XXXXXXX 100644
164
--- a/Makefile.objs
165
+++ b/Makefile.objs
166
@@ -XXX,XX +XXX,XX @@ endif # CONFIG_SOFTMMU or CONFIG_TOOLS
167
# used for system emulation, too, but specified separately there)
168
169
storage-daemon-obj-y = block/ qom/
170
-storage-daemon-obj-y += blockdev.o iothread.o
171
+storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o
172
173
######################################################################
174
# Target independent part of system emulation. The long term path is to
175
--
51
--
176
2.20.1
52
2.13.6
177
53
178
54
diff view generated by jsdifflib
New patch
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Fam Zheng <famz@redhat.com>
3
---
4
block/io.c | 6 ++++++
5
1 file changed, 6 insertions(+)
1
6
7
diff --git a/block/io.c b/block/io.c
8
index XXXXXXX..XXXXXXX 100644
9
--- a/block/io.c
10
+++ b/block/io.c
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
12
BdrvNextIterator it;
13
GSList *aio_ctxs = NULL, *ctx;
14
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
17
+ * nodes in several different AioContexts, so make sure we're in the main
18
+ * context. */
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
20
+
21
block_job_pause_all();
22
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
24
--
25
2.13.6
26
27
diff view generated by jsdifflib
1
This adds a new QAPI-based monitor_init() function. The existing
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
monitor_init_opts() is rewritten to simply put its QemuOpts parameter
2
and also doesn't notify other parent nodes of children, which both means
3
into a visitor and pass the resulting QAPI object to monitor_init().
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
is providing useful functionality only on a single node.
4
5
5
This will cause some change in those error messages for the monitor
6
To keep things consistent, we also shouldn't call the block driver
6
options in the system emulator that are now generated by the visitor
7
callbacks recursively.
7
rather than explicitly checked in monitor_init_opts().
8
9
A proper recursive drain version that provides an actually working
10
drained section for child nodes will be introduced later.
8
11
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20200224143008.13362-17-kwolf@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
11
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
---
14
qapi/control.json | 36 ++++++++++++++++++
15
block/io.c | 16 +++++++++-------
15
include/monitor/monitor.h | 2 +
16
1 file changed, 9 insertions(+), 7 deletions(-)
16
monitor/monitor.c | 77 +++++++++++++++++++++------------------
17
3 files changed, 80 insertions(+), 35 deletions(-)
18
17
19
diff --git a/qapi/control.json b/qapi/control.json
18
diff --git a/block/io.c b/block/io.c
20
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
21
--- a/qapi/control.json
20
--- a/block/io.c
22
+++ b/qapi/control.json
21
+++ b/block/io.c
23
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
24
# <- { "return": {} }
25
##
26
{ 'command': 'quit' }
27
+
28
+##
29
+# @MonitorMode:
30
+#
31
+# An enumeration of monitor modes.
32
+#
33
+# @readline: HMP monitor (human-oriented command line interface)
34
+#
35
+# @control: QMP monitor (JSON-based machine interface)
36
+#
37
+# Since: 5.0
38
+##
39
+{ 'enum': 'MonitorMode', 'data': [ 'readline', 'control' ] }
40
+
41
+##
42
+# @MonitorOptions:
43
+#
44
+# Options to be used for adding a new monitor.
45
+#
46
+# @id: Name of the monitor
47
+#
48
+# @mode: Selects the monitor mode (default: readline)
49
+#
50
+# @pretty: Enables pretty printing (QMP only)
51
+#
52
+# @chardev: Name of a character device to expose the monitor on
53
+#
54
+# Since: 5.0
55
+##
56
+{ 'struct': 'MonitorOptions',
57
+ 'data': {
58
+ '*id': 'str',
59
+ '*mode': 'MonitorMode',
60
+ '*pretty': 'bool',
61
+ 'chardev': 'str'
62
+ } }
63
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
64
index XXXXXXX..XXXXXXX 100644
65
--- a/include/monitor/monitor.h
66
+++ b/include/monitor/monitor.h
67
@@ -XXX,XX +XXX,XX @@
68
69
extern __thread Monitor *cur_mon;
70
typedef struct MonitorHMP MonitorHMP;
71
+typedef struct MonitorOptions MonitorOptions;
72
73
#define QMP_REQ_QUEUE_LEN_MAX 8
74
75
@@ -XXX,XX +XXX,XX @@ void monitor_init_globals(void);
76
void monitor_init_globals_core(void);
77
void monitor_init_qmp(Chardev *chr, bool pretty);
78
void monitor_init_hmp(Chardev *chr, bool use_readline);
79
+int monitor_init(MonitorOptions *opts, Error **errp);
80
int monitor_init_opts(QemuOpts *opts, Error **errp);
81
void monitor_cleanup(void);
82
83
diff --git a/monitor/monitor.c b/monitor/monitor.c
84
index XXXXXXX..XXXXXXX 100644
85
--- a/monitor/monitor.c
86
+++ b/monitor/monitor.c
87
@@ -XXX,XX +XXX,XX @@
88
#include "qemu/osdep.h"
89
#include "monitor-internal.h"
90
#include "qapi/error.h"
91
+#include "qapi/opts-visitor.h"
92
#include "qapi/qapi-emit-events.h"
93
+#include "qapi/qapi-visit-control.h"
94
#include "qapi/qmp/qdict.h"
95
#include "qapi/qmp/qstring.h"
96
#include "qemu/error-report.h"
97
@@ -XXX,XX +XXX,XX @@ void monitor_init_globals_core(void)
98
NULL);
99
}
23
}
100
24
101
-int monitor_init_opts(QemuOpts *opts, Error **errp)
25
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
102
+int monitor_init(MonitorOptions *opts, Error **errp)
26
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
27
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
103
{
28
{
104
Chardev *chr;
29
BdrvChild *child, *tmp;
105
- bool qmp;
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
106
- bool pretty = false;
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
107
- const char *chardev;
32
bdrv_coroutine_enter(bs, data.co);
108
- const char *mode;
33
BDRV_POLL_WHILE(bs, !data.done);
109
-
34
110
- mode = qemu_opt_get(opts, "mode");
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
111
- if (mode == NULL) {
36
- bdrv_drain_invoke(child->bs, begin);
112
- mode = "readline";
37
+ if (recursive) {
113
- }
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
114
- if (strcmp(mode, "readline") == 0) {
39
+ bdrv_drain_invoke(child->bs, begin, true);
115
- qmp = false;
40
+ }
116
- } else if (strcmp(mode, "control") == 0) {
117
- qmp = true;
118
- } else {
119
- error_setg(errp, "unknown monitor mode \"%s\"", mode);
120
+
121
+ chr = qemu_chr_find(opts->chardev);
122
+ if (chr == NULL) {
123
+ error_setg(errp, "chardev \"%s\" not found", opts->chardev);
124
return -1;
125
}
41
}
126
42
}
127
- if (!qmp && qemu_opt_get(opts, "pretty")) {
43
128
- warn_report("'pretty' is deprecated for HMP monitors, it has no effect "
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
129
- "and will be removed in future versions");
45
bdrv_parent_drained_begin(bs);
130
- }
131
- if (qemu_opt_get_bool(opts, "pretty", 0)) {
132
- pretty = true;
133
+ switch (opts->mode) {
134
+ case MONITOR_MODE_CONTROL:
135
+ monitor_init_qmp(chr, opts->pretty);
136
+ break;
137
+ case MONITOR_MODE_READLINE:
138
+ if (opts->pretty) {
139
+ warn_report("'pretty' is deprecated for HMP monitors, it has no "
140
+ "effect and will be removed in future versions");
141
+ }
142
+ monitor_init_hmp(chr, true);
143
+ break;
144
+ default:
145
+ g_assert_not_reached();
146
}
46
}
147
47
148
- chardev = qemu_opt_get(opts, "chardev");
48
- bdrv_drain_invoke(bs, true);
149
- if (!chardev) {
49
+ bdrv_drain_invoke(bs, true, false);
150
- error_report("chardev is required");
50
bdrv_drain_recurse(bs);
151
- exit(1);
51
}
152
- }
52
153
- chr = qemu_chr_find(chardev);
53
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
154
- if (chr == NULL) {
155
- error_setg(errp, "chardev \"%s\" not found", chardev);
156
- return -1;
157
+ return 0;
158
+}
159
+
160
+int monitor_init_opts(QemuOpts *opts, Error **errp)
161
+{
162
+ Visitor *v;
163
+ MonitorOptions *options;
164
+ Error *local_err = NULL;
165
+
166
+ v = opts_visitor_new(opts);
167
+ visit_type_MonitorOptions(v, NULL, &options, &local_err);
168
+ visit_free(v);
169
+
170
+ if (local_err) {
171
+ goto out;
172
}
54
}
173
55
174
- if (qmp) {
56
/* Re-enable things in child-to-parent order */
175
- monitor_init_qmp(chr, pretty);
57
- bdrv_drain_invoke(bs, false);
176
- } else {
58
+ bdrv_drain_invoke(bs, false, false);
177
- monitor_init_hmp(chr, true);
59
bdrv_parent_drained_end(bs);
178
+ monitor_init(options, &local_err);
60
aio_enable_external(bdrv_get_aio_context(bs));
179
+ qapi_free_MonitorOptions(options);
180
+
181
+out:
182
+ if (local_err) {
183
+ error_propagate(errp, local_err);
184
+ return -1;
185
}
186
return 0;
187
}
61
}
62
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
63
aio_context_acquire(aio_context);
64
aio_disable_external(aio_context);
65
bdrv_parent_drained_begin(bs);
66
- bdrv_drain_invoke(bs, true);
67
+ bdrv_drain_invoke(bs, true, true);
68
aio_context_release(aio_context);
69
70
if (!g_slist_find(aio_ctxs, aio_context)) {
71
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
72
73
/* Re-enable things in child-to-parent order */
74
aio_context_acquire(aio_context);
75
- bdrv_drain_invoke(bs, false);
76
+ bdrv_drain_invoke(bs, false, true);
77
bdrv_parent_drained_end(bs);
78
aio_enable_external(aio_context);
79
aio_context_release(aio_context);
188
--
80
--
189
2.20.1
81
2.13.6
190
82
191
83
diff view generated by jsdifflib
1
Add a --export option to qemu-storage-daemon to export a block node. For
1
The existing test is for bdrv_drain_all_begin/end() only. Generalise the
2
now, only NBD exports are implemented. Apart from the 'type' option
2
test case so that it can be run for the other variants as well. At the
3
(which is the implied key), it maps the arguments for nbd-server-add to
3
moment this is only bdrv_drain_begin/end(), but in a while, we'll add
4
the command line. Example:
4
another one.
5
5
6
--export nbd,device=disk,name=test-export,writable=on
6
Also, add a backing file to the test node to test whether the operations
7
work recursively.
7
8
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20200224143008.13362-12-kwolf@redhat.com>
10
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
10
---
13
qapi/block-core.json | 27 +++++++++++++++++++++++++++
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
14
qemu-storage-daemon.c | 31 +++++++++++++++++++++++++++++++
12
1 file changed, 62 insertions(+), 7 deletions(-)
15
2 files changed, 58 insertions(+)
16
13
17
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
18
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
19
--- a/qapi/block-core.json
16
--- a/tests/test-bdrv-drain.c
20
+++ b/qapi/block-core.json
17
+++ b/tests/test-bdrv-drain.c
21
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
22
##
19
23
{ 'command': 'nbd-server-stop' }
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
24
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
25
+##
26
+# @BlockExportType:
27
+#
28
+# An enumeration of block export types
29
+#
30
+# @nbd: NBD export
31
+#
32
+# Since: 4.2
33
+##
34
+{ 'enum': 'BlockExportType',
35
+ 'data': [ 'nbd' ] }
36
+
22
+
37
+##
23
+ .bdrv_child_perm = bdrv_format_default_perms,
38
+# @BlockExport:
24
};
39
+#
25
40
+# Describes a block export, i.e. how single node should be exported on an
26
static void aio_ret_cb(void *opaque, int ret)
41
+# external interface.
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
42
+#
28
*aio_ret = ret;
43
+# Since: 4.2
29
}
44
+##
30
45
+{ 'union': 'BlockExport',
31
-static void test_drv_cb_drain_all(void)
46
+ 'base': { 'type': 'BlockExportType' },
32
+enum drain_type {
47
+ 'discriminator': 'type',
33
+ BDRV_DRAIN_ALL,
48
+ 'data': {
34
+ BDRV_DRAIN,
49
+ 'nbd': 'BlockExportNbd'
35
+};
50
+ } }
51
+
36
+
52
##
37
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
53
# @QuorumOpType:
54
#
55
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
56
index XXXXXXX..XXXXXXX 100644
57
--- a/qemu-storage-daemon.c
58
+++ b/qemu-storage-daemon.c
59
@@ -XXX,XX +XXX,XX @@ static void help(void)
60
" [,driver specific parameters...]\n"
61
" configure a block backend\n"
62
"\n"
63
+" --export [type=]nbd,device=<node-name>[,name=<export-name>]\n"
64
+" [,writable=on|off][,bitmap=<name>]\n"
65
+" export the specified block node over NBD\n"
66
+" (requires --nbd-server)\n"
67
+"\n"
68
" --nbd-server addr.type=inet,addr.host=<host>,addr.port=<port>\n"
69
" [,tls-creds=<id>][,tls-authz=<id>]\n"
70
" --nbd-server addr.type=unix,addr.path=<path>\n"
71
@@ -XXX,XX +XXX,XX @@ QEMU_HELP_BOTTOM "\n",
72
73
enum {
74
OPTION_BLOCKDEV = 256,
75
+ OPTION_EXPORT,
76
OPTION_NBD_SERVER,
77
OPTION_OBJECT,
78
};
79
@@ -XXX,XX +XXX,XX @@ static QemuOptsList qemu_object_opts = {
80
},
81
};
82
83
+static void init_export(BlockExport *export, Error **errp)
84
+{
38
+{
85
+ switch (export->type) {
39
+ switch (drain_type) {
86
+ case BLOCK_EXPORT_TYPE_NBD:
40
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
87
+ qmp_nbd_server_add(&export->u.nbd, errp);
41
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
88
+ break;
42
+ default: g_assert_not_reached();
89
+ default:
90
+ g_assert_not_reached();
91
+ }
43
+ }
92
+}
44
+}
93
+
45
+
94
static void process_options(int argc, char *argv[])
46
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
47
+{
48
+ switch (drain_type) {
49
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
50
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
51
+ default: g_assert_not_reached();
52
+ }
53
+}
54
+
55
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
95
{
56
{
96
int c;
57
BlockBackend *blk;
97
58
- BlockDriverState *bs;
98
static const struct option long_options[] = {
59
- BDRVTestState *s;
99
{"blockdev", required_argument, NULL, OPTION_BLOCKDEV},
60
+ BlockDriverState *bs, *backing;
100
+ {"export", required_argument, NULL, OPTION_EXPORT},
61
+ BDRVTestState *s, *backing_s;
101
{"help", no_argument, NULL, 'h'},
62
BlockAIOCB *acb;
102
{"nbd-server", required_argument, NULL, OPTION_NBD_SERVER},
63
int aio_ret;
103
{"object", required_argument, NULL, OPTION_OBJECT},
64
104
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
65
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
105
qapi_free_BlockdevOptions(options);
66
s = bs->opaque;
106
break;
67
blk_insert_bs(blk, bs, &error_abort);
107
}
68
108
+ case OPTION_EXPORT:
69
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
109
+ {
70
+ backing_s = backing->opaque;
110
+ Visitor *v;
71
+ bdrv_set_backing_hd(bs, backing, &error_abort);
111
+ BlockExport *export;
112
+
72
+
113
+ v = qobject_input_visitor_new_str(optarg, "type", &error_fatal);
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
114
+ visit_type_BlockExport(v, NULL, &export, &error_fatal);
74
g_assert_cmpint(s->drain_count, ==, 0);
115
+ visit_free(v);
75
- bdrv_drain_all_begin();
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
116
+
77
+
117
+ init_export(export, &error_fatal);
78
+ do_drain_begin(drain_type, bs);
118
+ qapi_free_BlockExport(export);
79
+
119
+ break;
80
g_assert_cmpint(s->drain_count, ==, 1);
120
+ }
81
- bdrv_drain_all_end();
121
case OPTION_NBD_SERVER:
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
122
{
83
+
123
Visitor *v;
84
+ do_drain_end(drain_type, bs);
85
+
86
g_assert_cmpint(s->drain_count, ==, 0);
87
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
88
89
/* Now do the same while a request is pending */
90
aio_ret = -EINPROGRESS;
91
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
92
g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
93
94
g_assert_cmpint(s->drain_count, ==, 0);
95
- bdrv_drain_all_begin();
96
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
97
+
98
+ do_drain_begin(drain_type, bs);
99
+
100
g_assert_cmpint(aio_ret, ==, 0);
101
g_assert_cmpint(s->drain_count, ==, 1);
102
- bdrv_drain_all_end();
103
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
104
+
105
+ do_drain_end(drain_type, bs);
106
+
107
g_assert_cmpint(s->drain_count, ==, 0);
108
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
109
110
+ bdrv_unref(backing);
111
bdrv_unref(bs);
112
blk_unref(blk);
113
}
114
115
+static void test_drv_cb_drain_all(void)
116
+{
117
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
118
+}
119
+
120
+static void test_drv_cb_drain(void)
121
+{
122
+ test_drv_cb_common(BDRV_DRAIN, false);
123
+}
124
+
125
int main(int argc, char **argv)
126
{
127
bdrv_init();
128
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
129
g_test_init(&argc, &argv, NULL);
130
131
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
132
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
133
134
return g_test_run();
135
}
124
--
136
--
125
2.20.1
137
2.13.6
126
138
127
139
diff view generated by jsdifflib
1
This adds and parses the --monitor option, so that a QMP monitor can be
1
This is currently only working correctly for bdrv_drain(), not for
2
used in the storage daemon. The monitor offers commands defined in the
2
bdrv_drain_all(). Leave a comment for the drain_all case, we'll address
3
QAPI schema at storage-daemon/qapi/qapi-schema.json.
3
it later.
4
5
The --monitor options currently allows to create multiple monitors with
6
the same ID. This part of the interface is considered unstable. We will
7
reject such configurations as soon as we have a design for the monitor
8
subsystem to perform these checks. (In the system emulator, we depend on
9
QemuOpts rejecting duplicate IDs.)
10
4
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20200224143008.13362-21-kwolf@redhat.com>
13
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
6
---
16
qapi/transaction.json | 2 +-
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
17
qemu-storage-daemon.c | 47 ++++++++++++++++++++++++++--
8
1 file changed, 45 insertions(+)
18
scripts/qapi/gen.py | 5 +++
19
Makefile | 33 +++++++++++++++++++
20
Makefile.objs | 4 +--
21
monitor/Makefile.objs | 2 ++
22
qapi/Makefile.objs | 5 +++
23
storage-daemon/Makefile.objs | 1 +
24
storage-daemon/qapi/Makefile.objs | 1 +
25
storage-daemon/qapi/qapi-schema.json | 26 +++++++++++++++
26
10 files changed, 121 insertions(+), 5 deletions(-)
27
create mode 100644 storage-daemon/Makefile.objs
28
create mode 100644 storage-daemon/qapi/Makefile.objs
29
create mode 100644 storage-daemon/qapi/qapi-schema.json
30
9
31
diff --git a/qapi/transaction.json b/qapi/transaction.json
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
32
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
33
--- a/qapi/transaction.json
12
--- a/tests/test-bdrv-drain.c
34
+++ b/qapi/transaction.json
13
+++ b/tests/test-bdrv-drain.c
35
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
36
# = Transactions
15
test_drv_cb_common(BDRV_DRAIN, false);
37
##
16
}
38
17
39
-{ 'include': 'block.json' }
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
40
+{ 'include': 'block-core.json' }
19
+{
41
20
+ BlockBackend *blk;
42
##
21
+ BlockDriverState *bs, *backing;
43
# @Abort:
44
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/qemu-storage-daemon.c
47
+++ b/qemu-storage-daemon.c
48
@@ -XXX,XX +XXX,XX @@
49
#include "block/nbd.h"
50
#include "chardev/char.h"
51
#include "crypto/init.h"
52
+#include "monitor/monitor.h"
53
+#include "monitor/monitor-internal.h"
54
55
#include "qapi/error.h"
56
-#include "qapi/qapi-commands-block.h"
57
-#include "qapi/qapi-commands-block-core.h"
58
#include "qapi/qapi-visit-block.h"
59
#include "qapi/qapi-visit-block-core.h"
60
+#include "qapi/qapi-visit-control.h"
61
#include "qapi/qmp/qdict.h"
62
+#include "qapi/qmp/qstring.h"
63
#include "qapi/qobject-input-visitor.h"
64
65
#include "qemu-common.h"
66
@@ -XXX,XX +XXX,XX @@
67
#include "qemu/option.h"
68
#include "qom/object_interfaces.h"
69
70
+#include "storage-daemon/qapi/qapi-commands.h"
71
+#include "storage-daemon/qapi/qapi-init-commands.h"
72
+
22
+
73
#include "sysemu/runstate.h"
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
74
#include "trace/control.h"
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
75
25
+ &error_abort);
76
@@ -XXX,XX +XXX,XX @@ void qemu_system_killed(int signal, pid_t pid)
26
+ blk_insert_bs(blk, bs, &error_abort);
77
exit_requested = true;
27
+
78
}
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
79
29
+ bdrv_set_backing_hd(bs, backing, &error_abort);
80
+void qmp_quit(Error **errp)
30
+
81
+{
31
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
82
+ exit_requested = true;
32
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
33
+
34
+ do_drain_begin(drain_type, bs);
35
+
36
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
37
+ g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
38
+
39
+ do_drain_end(drain_type, bs);
40
+
41
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
43
+
44
+ bdrv_unref(backing);
45
+ bdrv_unref(bs);
46
+ blk_unref(blk);
83
+}
47
+}
84
+
48
+
85
static void help(void)
49
+static void test_quiesce_drain_all(void)
86
{
87
printf(
88
@@ -XXX,XX +XXX,XX @@ static void help(void)
89
" export the specified block node over NBD\n"
90
" (requires --nbd-server)\n"
91
"\n"
92
+" --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n"
93
+" configure a QMP monitor\n"
94
+"\n"
95
" --nbd-server addr.type=inet,addr.host=<host>,addr.port=<port>\n"
96
" [,tls-creds=<id>][,tls-authz=<id>]\n"
97
" --nbd-server addr.type=unix,addr.path=<path>\n"
98
@@ -XXX,XX +XXX,XX @@ enum {
99
OPTION_BLOCKDEV = 256,
100
OPTION_CHARDEV,
101
OPTION_EXPORT,
102
+ OPTION_MONITOR,
103
OPTION_NBD_SERVER,
104
OPTION_OBJECT,
105
};
106
@@ -XXX,XX +XXX,XX @@ static QemuOptsList qemu_object_opts = {
107
},
108
};
109
110
+static void init_qmp_commands(void)
111
+{
50
+{
112
+ qmp_init_marshal(&qmp_commands);
51
+ // XXX drain_all doesn't quiesce
113
+ qmp_register_command(&qmp_commands, "query-qmp-schema",
52
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
114
+ qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG);
115
+
116
+ QTAILQ_INIT(&qmp_cap_negotiation_commands);
117
+ qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
118
+ qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
119
+}
53
+}
120
+
54
+
121
static void init_export(BlockExport *export, Error **errp)
55
+static void test_quiesce_drain(void)
56
+{
57
+ test_quiesce_common(BDRV_DRAIN, false);
58
+}
59
+
60
int main(int argc, char **argv)
122
{
61
{
123
switch (export->type) {
62
bdrv_init();
124
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
63
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
125
{"chardev", required_argument, NULL, OPTION_CHARDEV},
64
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
126
{"export", required_argument, NULL, OPTION_EXPORT},
65
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
127
{"help", no_argument, NULL, 'h'},
66
128
+ {"monitor", required_argument, NULL, OPTION_MONITOR},
67
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
129
{"nbd-server", required_argument, NULL, OPTION_NBD_SERVER},
68
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
130
{"object", required_argument, NULL, OPTION_OBJECT},
131
{"trace", required_argument, NULL, 'T'},
132
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
133
qapi_free_BlockExport(export);
134
break;
135
}
136
+ case OPTION_MONITOR:
137
+ {
138
+ Visitor *v;
139
+ MonitorOptions *monitor;
140
+
69
+
141
+ v = qobject_input_visitor_new_str(optarg, "chardev",
70
return g_test_run();
142
+ &error_fatal);
71
}
143
+ visit_type_MonitorOptions(v, NULL, &monitor, &error_fatal);
144
+ visit_free(v);
145
+
146
+ /* TODO Catch duplicate monitor IDs */
147
+ monitor_init(monitor, false, &error_fatal);
148
+ qapi_free_MonitorOptions(monitor);
149
+ break;
150
+ }
151
case OPTION_NBD_SERVER:
152
{
153
Visitor *v;
154
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
155
qemu_add_opts(&qemu_trace_opts);
156
qcrypto_init(&error_fatal);
157
bdrv_init();
158
+ monitor_init_globals_core();
159
+ init_qmp_commands();
160
161
if (!trace_init_backends()) {
162
return EXIT_FAILURE;
163
diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py
164
index XXXXXXX..XXXXXXX 100644
165
--- a/scripts/qapi/gen.py
166
+++ b/scripts/qapi/gen.py
167
@@ -XXX,XX +XXX,XX @@ class QAPIGen:
168
return ''
169
170
def write(self, output_dir):
171
+ # Include paths starting with ../ are used to reuse modules of the main
172
+ # schema in specialised schemas. Don't overwrite the files that are
173
+ # already generated for the main schema.
174
+ if self.fname.startswith('../'):
175
+ return
176
pathname = os.path.join(output_dir, self.fname)
177
odir = os.path.dirname(pathname)
178
if odir:
179
diff --git a/Makefile b/Makefile
180
index XXXXXXX..XXXXXXX 100644
181
--- a/Makefile
182
+++ b/Makefile
183
@@ -XXX,XX +XXX,XX @@ GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
184
GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
185
GENERATED_QAPI_FILES += qapi/qapi-doc.texi
186
187
+# The following list considers only the storage daemon main module. All other
188
+# modules are currently shared with the main schema, so we don't actually
189
+# generate additional files.
190
+
191
+GENERATED_STORAGE_DAEMON_QAPI_FILES = storage-daemon/qapi/qapi-commands.h
192
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-commands.c
193
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.h
194
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.c
195
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.h
196
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.c
197
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.h
198
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.c
199
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.h
200
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.c
201
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.h
202
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.c
203
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.h
204
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.c
205
+GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-doc.texi
206
+
207
generated-files-y += $(GENERATED_QAPI_FILES)
208
+generated-files-y += $(GENERATED_STORAGE_DAEMON_QAPI_FILES)
209
210
generated-files-y += trace/generated-tcg-tracers.h
211
212
@@ -XXX,XX +XXX,XX @@ qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
213
        "GEN","$(@:%-timestamp=%)")
214
    @>$@
215
216
+qapi-modules-storage-daemon = \
217
+    $(SRC_PATH)/storage-daemon/qapi/qapi-schema.json \
218
+ $(QAPI_MODULES_STORAGE_DAEMON:%=$(SRC_PATH)/qapi/%.json)
219
+
220
+$(GENERATED_STORAGE_DAEMON_QAPI_FILES): storage-daemon/qapi/qapi-gen-timestamp ;
221
+storage-daemon/qapi/qapi-gen-timestamp: $(qapi-modules-storage-daemon) $(qapi-py)
222
+    $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
223
+        -o "storage-daemon/qapi" $<, \
224
+        "GEN","$(@:%-timestamp=%)")
225
+    @>$@
226
+
227
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h qga-qapi-init-commands.h)
228
$(qga-obj-y): $(QGALIB_GEN)
229
230
@@ -XXX,XX +XXX,XX @@ clean: recurse-clean
231
    rm -f trace/generated-tracers-dtrace.h*
232
    rm -f $(foreach f,$(generated-files-y),$(f) $(f)-timestamp)
233
    rm -f qapi-gen-timestamp
234
+    rm -f storage-daemon/qapi/qapi-gen-timestamp
235
    rm -rf qga/qapi-generated
236
    rm -f config-all-devices.mak
237
238
diff --git a/Makefile.objs b/Makefile.objs
239
index XXXXXXX..XXXXXXX 100644
240
--- a/Makefile.objs
241
+++ b/Makefile.objs
242
@@ -XXX,XX +XXX,XX @@ endif # CONFIG_SOFTMMU or CONFIG_TOOLS
243
# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are
244
# used for system emulation, too, but specified separately there)
245
246
-storage-daemon-obj-y = block/ qom/
247
-storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o
248
+storage-daemon-obj-y = block/ monitor/ qapi/ qom/ storage-daemon/
249
+storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o job-qmp.o
250
storage-daemon-obj-$(CONFIG_WIN32) += os-win32.o
251
storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o
252
253
diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs
254
index XXXXXXX..XXXXXXX 100644
255
--- a/monitor/Makefile.objs
256
+++ b/monitor/Makefile.objs
257
@@ -XXX,XX +XXX,XX @@ obj-y += misc.o
258
common-obj-y += monitor.o qmp.o hmp.o
259
common-obj-y += qmp-cmds.o qmp-cmds-control.o
260
common-obj-y += hmp-cmds.o
261
+
262
+storage-daemon-obj-y += monitor.o qmp.o qmp-cmds-control.o
263
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
264
index XXXXXXX..XXXXXXX 100644
265
--- a/qapi/Makefile.objs
266
+++ b/qapi/Makefile.objs
267
@@ -XXX,XX +XXX,XX @@ obj-y += qapi-events.o
268
obj-y += $(QAPI_TARGET_MODULES:%=qapi-commands-%.o)
269
obj-y += qapi-commands.o
270
obj-y += qapi-init-commands.o
271
+
272
+QAPI_MODULES_STORAGE_DAEMON = block-core char common control crypto
273
+QAPI_MODULES_STORAGE_DAEMON += introspect job qom sockets pragma transaction
274
+
275
+storage-daemon-obj-y += $(QAPI_MODULES_STORAGE_DAEMON:%=qapi-commands-%.o)
276
diff --git a/storage-daemon/Makefile.objs b/storage-daemon/Makefile.objs
277
new file mode 100644
278
index XXXXXXX..XXXXXXX
279
--- /dev/null
280
+++ b/storage-daemon/Makefile.objs
281
@@ -0,0 +1 @@
282
+storage-daemon-obj-y += qapi/
283
diff --git a/storage-daemon/qapi/Makefile.objs b/storage-daemon/qapi/Makefile.objs
284
new file mode 100644
285
index XXXXXXX..XXXXXXX
286
--- /dev/null
287
+++ b/storage-daemon/qapi/Makefile.objs
288
@@ -0,0 +1 @@
289
+storage-daemon-obj-y += qapi-commands.o qapi-init-commands.o qapi-introspect.o
290
diff --git a/storage-daemon/qapi/qapi-schema.json b/storage-daemon/qapi/qapi-schema.json
291
new file mode 100644
292
index XXXXXXX..XXXXXXX
293
--- /dev/null
294
+++ b/storage-daemon/qapi/qapi-schema.json
295
@@ -XXX,XX +XXX,XX @@
296
+# -*- Mode: Python -*-
297
+
298
+# Note that modules are shared with the QEMU main schema under the assumption
299
+# that the storage daemon schema is a subset of the main schema. For the shared
300
+# modules, no code is generated here, but we reuse the code files generated
301
+# from the main schema.
302
+#
303
+# If you wish to extend the storage daemon schema to contain things that are
304
+# not in the main schema, be aware that array types of types defined in shared
305
+# modules are only generated if an array of the respective type is already used
306
+# in the main schema. Therefore, if you use such arrays, you may need to define
307
+# the array type in the main schema, even if it is unused outside of the
308
+# storage daemon.
309
+
310
+{ 'include': '../../qapi/pragma.json' }
311
+
312
+{ 'include': '../../qapi/block-core.json' }
313
+{ 'include': '../../qapi/char.json' }
314
+{ 'include': '../../qapi/common.json' }
315
+{ 'include': '../../qapi/control.json' }
316
+{ 'include': '../../qapi/crypto.json' }
317
+{ 'include': '../../qapi/introspect.json' }
318
+{ 'include': '../../qapi/job.json' }
319
+{ 'include': '../../qapi/qom.json' }
320
+{ 'include': '../../qapi/sockets.json' }
321
+{ 'include': '../../qapi/transaction.json' }
322
--
72
--
323
2.20.1
73
2.13.6
324
74
325
75
diff view generated by jsdifflib
1
Trying to attach a HMP monitor to a chardev that is already in use
1
Block jobs already paused themselves when their main BlockBackend
2
results in a crash because monitor_init_hmp() passes &error_abort to
2
entered a drained section. This is not good enough: We also want to
3
qemu_chr_fe_init():
3
pause a block job and may not submit new requests if, for example, the
4
mirror target node should be drained.
4
5
5
$ ./x86_64-softmmu/qemu-system-x86_64 --chardev stdio,id=foo --mon foo --mon foo
6
This implements .drained_begin/end callbacks in child_job in order to
6
QEMU 4.2.50 monitor - type 'help' for more information
7
consider all block nodes related to the job, and removes the
7
(qemu) Unexpected error in qemu_chr_fe_init() at chardev/char-fe.c:220:
8
BlockBackend callbacks which are unnecessary now because the root of the
8
qemu-system-x86_64: --mon foo: Device 'foo' is in use
9
job main BlockBackend is always referenced with a child_job, too.
9
Abgebrochen (Speicherabzug geschrieben)
10
11
Fix this by allowing monitor_init_hmp() to return an error and passing
12
any error in qemu_chr_fe_init() to its caller instead of aborting.
13
10
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Message-Id: <20200224143008.13362-19-kwolf@redhat.com>
16
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
12
---
19
include/monitor/monitor.h | 2 +-
13
blockjob.c | 22 +++++++++-------------
20
chardev/char.c | 8 +++++++-
14
1 file changed, 9 insertions(+), 13 deletions(-)
21
gdbstub.c | 2 +-
22
monitor/hmp.c | 8 ++++++--
23
monitor/monitor.c | 2 +-
24
stubs/monitor.c | 2 +-
25
tests/test-util-sockets.c | 2 +-
26
7 files changed, 18 insertions(+), 8 deletions(-)
27
15
28
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
16
diff --git a/blockjob.c b/blockjob.c
29
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
30
--- a/include/monitor/monitor.h
18
--- a/blockjob.c
31
+++ b/include/monitor/monitor.h
19
+++ b/blockjob.c
32
@@ -XXX,XX +XXX,XX @@ bool monitor_cur_is_qmp(void);
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
33
void monitor_init_globals(void);
21
job->id);
34
void monitor_init_globals_core(void);
35
void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp);
36
-void monitor_init_hmp(Chardev *chr, bool use_readline);
37
+void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp);
38
int monitor_init(MonitorOptions *opts, Error **errp);
39
int monitor_init_opts(QemuOpts *opts, Error **errp);
40
void monitor_cleanup(void);
41
diff --git a/chardev/char.c b/chardev/char.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/chardev/char.c
44
+++ b/chardev/char.c
45
@@ -XXX,XX +XXX,XX @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
46
47
if (qemu_opt_get_bool(opts, "mux", 0)) {
48
assert(permit_mux_mon);
49
- monitor_init_hmp(chr, true);
50
+ monitor_init_hmp(chr, true, &err);
51
+ if (err) {
52
+ error_report_err(err);
53
+ object_unparent(OBJECT(chr));
54
+ chr = NULL;
55
+ goto out;
56
+ }
57
}
58
59
out:
60
diff --git a/gdbstub.c b/gdbstub.c
61
index XXXXXXX..XXXXXXX 100644
62
--- a/gdbstub.c
63
+++ b/gdbstub.c
64
@@ -XXX,XX +XXX,XX @@ int gdbserver_start(const char *device)
65
/* Initialize a monitor terminal for gdb */
66
mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
67
NULL, NULL, &error_abort);
68
- monitor_init_hmp(mon_chr, false);
69
+ monitor_init_hmp(mon_chr, false, &error_abort);
70
} else {
71
qemu_chr_fe_deinit(&s->chr, true);
72
mon_chr = s->mon_chr;
73
diff --git a/monitor/hmp.c b/monitor/hmp.c
74
index XXXXXXX..XXXXXXX 100644
75
--- a/monitor/hmp.c
76
+++ b/monitor/hmp.c
77
@@ -XXX,XX +XXX,XX @@ static void monitor_readline_flush(void *opaque)
78
monitor_flush(&mon->common);
79
}
22
}
80
23
81
-void monitor_init_hmp(Chardev *chr, bool use_readline)
24
-static const BdrvChildRole child_job = {
82
+void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp)
25
- .get_parent_desc = child_job_get_parent_desc,
26
- .stay_at_node = true,
27
-};
28
-
29
-static void block_job_drained_begin(void *opaque)
30
+static void child_job_drained_begin(BdrvChild *c)
83
{
31
{
84
MonitorHMP *mon = g_new0(MonitorHMP, 1);
32
- BlockJob *job = opaque;
85
33
+ BlockJob *job = c->opaque;
86
+ if (!qemu_chr_fe_init(&mon->common.chr, chr, errp)) {
34
block_job_pause(job);
87
+ g_free(mon);
88
+ return;
89
+ }
90
+
91
monitor_data_init(&mon->common, false, false, false);
92
- qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
93
94
mon->use_readline = use_readline;
95
if (mon->use_readline) {
96
diff --git a/monitor/monitor.c b/monitor/monitor.c
97
index XXXXXXX..XXXXXXX 100644
98
--- a/monitor/monitor.c
99
+++ b/monitor/monitor.c
100
@@ -XXX,XX +XXX,XX @@ int monitor_init(MonitorOptions *opts, Error **errp)
101
warn_report("'pretty' is deprecated for HMP monitors, it has no "
102
"effect and will be removed in future versions");
103
}
104
- monitor_init_hmp(chr, true);
105
+ monitor_init_hmp(chr, true, &local_err);
106
break;
107
default:
108
g_assert_not_reached();
109
diff --git a/stubs/monitor.c b/stubs/monitor.c
110
index XXXXXXX..XXXXXXX 100644
111
--- a/stubs/monitor.c
112
+++ b/stubs/monitor.c
113
@@ -XXX,XX +XXX,XX @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
114
return -1;
115
}
35
}
116
36
117
-void monitor_init_hmp(Chardev *chr, bool use_readline)
37
-static void block_job_drained_end(void *opaque)
118
+void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp)
38
+static void child_job_drained_end(BdrvChild *c)
119
{
39
{
40
- BlockJob *job = opaque;
41
+ BlockJob *job = c->opaque;
42
block_job_resume(job);
120
}
43
}
121
44
122
diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c
45
-static const BlockDevOps block_job_dev_ops = {
123
index XXXXXXX..XXXXXXX 100644
46
- .drained_begin = block_job_drained_begin,
124
--- a/tests/test-util-sockets.c
47
- .drained_end = block_job_drained_end,
125
+++ b/tests/test-util-sockets.c
48
+static const BdrvChildRole child_job = {
126
@@ -XXX,XX +XXX,XX @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
49
+ .get_parent_desc = child_job_get_parent_desc,
127
__thread Monitor *cur_mon;
50
+ .drained_begin = child_job_drained_begin,
128
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); }
51
+ .drained_end = child_job_drained_end,
129
void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) {}
52
+ .stay_at_node = true,
130
-void monitor_init_hmp(Chardev *chr, bool use_readline) {}
53
};
131
+void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp) {}
54
132
55
void block_job_remove_all_bdrv(BlockJob *job)
133
56
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
134
static void test_socket_fd_pass_name_good(void)
57
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
58
bs->job = job;
59
60
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
61
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
62
63
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
135
--
64
--
136
2.20.1
65
2.13.6
137
66
138
67
diff view generated by jsdifflib
1
Add a command line option to create user-creatable QOM objects.
1
Block jobs must be paused if any of the involved nodes are drained.
2
2
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Message-Id: <20200224143008.13362-9-kwolf@redhat.com>
5
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
4
---
8
qemu-storage-daemon.c | 47 +++++++++++++++++++++++++++++++++++++++++++
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
9
Makefile.objs | 2 +-
6
1 file changed, 121 insertions(+)
10
qom/Makefile.objs | 1 +
11
3 files changed, 49 insertions(+), 1 deletion(-)
12
7
13
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
14
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
15
--- a/qemu-storage-daemon.c
10
--- a/tests/test-bdrv-drain.c
16
+++ b/qemu-storage-daemon.c
11
+++ b/tests/test-bdrv-drain.c
17
@@ -XXX,XX +XXX,XX @@
12
@@ -XXX,XX +XXX,XX @@
13
14
#include "qemu/osdep.h"
15
#include "block/block.h"
16
+#include "block/blockjob_int.h"
17
#include "sysemu/block-backend.h"
18
#include "qapi/error.h"
18
#include "qapi/error.h"
19
#include "qapi/qapi-visit-block-core.h"
19
20
#include "qapi/qapi-commands-block-core.h"
20
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
21
+#include "qapi/qmp/qdict.h"
21
test_quiesce_common(BDRV_DRAIN, false);
22
#include "qapi/qobject-input-visitor.h"
23
24
#include "qemu-common.h"
25
#include "qemu-version.h"
26
#include "qemu/config-file.h"
27
#include "qemu/error-report.h"
28
+#include "qemu/help_option.h"
29
#include "qemu/log.h"
30
#include "qemu/main-loop.h"
31
#include "qemu/module.h"
32
+#include "qemu/option.h"
33
+#include "qom/object_interfaces.h"
34
35
#include "trace/control.h"
36
37
@@ -XXX,XX +XXX,XX @@ static void help(void)
38
" [,driver specific parameters...]\n"
39
" configure a block backend\n"
40
"\n"
41
+" --object help list object types that can be added\n"
42
+" --object <type>,help list properties for the given object type\n"
43
+" --object <type>[,<property>=<value>...]\n"
44
+" create a new object of type <type>, setting\n"
45
+" properties in the order they are specified. Note\n"
46
+" that the 'id' property must be set.\n"
47
+" See the qemu(1) man page for documentation of the\n"
48
+" objects that can be added.\n"
49
+"\n"
50
QEMU_HELP_BOTTOM "\n",
51
error_get_progname());
52
}
22
}
53
23
54
enum {
24
+
55
OPTION_BLOCKDEV = 256,
25
+typedef struct TestBlockJob {
56
+ OPTION_OBJECT,
26
+ BlockJob common;
27
+ bool should_complete;
28
+} TestBlockJob;
29
+
30
+static void test_job_completed(BlockJob *job, void *opaque)
31
+{
32
+ block_job_completed(job, 0);
33
+}
34
+
35
+static void coroutine_fn test_job_start(void *opaque)
36
+{
37
+ TestBlockJob *s = opaque;
38
+
39
+ while (!s->should_complete) {
40
+ block_job_sleep_ns(&s->common, 100000);
41
+ }
42
+
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
44
+}
45
+
46
+static void test_job_complete(BlockJob *job, Error **errp)
47
+{
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
49
+ s->should_complete = true;
50
+}
51
+
52
+BlockJobDriver test_job_driver = {
53
+ .instance_size = sizeof(TestBlockJob),
54
+ .start = test_job_start,
55
+ .complete = test_job_complete,
57
+};
56
+};
58
+
57
+
59
+static QemuOptsList qemu_object_opts = {
58
+static void test_blockjob_common(enum drain_type drain_type)
60
+ .name = "object",
59
+{
61
+ .implied_opt_name = "qom-type",
60
+ BlockBackend *blk_src, *blk_target;
62
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
61
+ BlockDriverState *src, *target;
63
+ .desc = {
62
+ BlockJob *job;
64
+ { }
63
+ int ret;
65
+ },
66
};
67
68
static void process_options(int argc, char *argv[])
69
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
70
static const struct option long_options[] = {
71
{"blockdev", required_argument, NULL, OPTION_BLOCKDEV},
72
{"help", no_argument, NULL, 'h'},
73
+ {"object", required_argument, NULL, OPTION_OBJECT},
74
{"trace", required_argument, NULL, 'T'},
75
{"version", no_argument, NULL, 'V'},
76
{0, 0, 0, 0}
77
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
78
qapi_free_BlockdevOptions(options);
79
break;
80
}
81
+ case OPTION_OBJECT:
82
+ {
83
+ QemuOpts *opts;
84
+ const char *type;
85
+ QDict *args;
86
+ QObject *ret_data = NULL;
87
+
64
+
88
+ /* FIXME The keyval parser rejects 'help' arguments, so we must
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
89
+ * unconditionall try QemuOpts first. */
66
+ &error_abort);
90
+ opts = qemu_opts_parse(&qemu_object_opts,
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
91
+ optarg, true, &error_fatal);
68
+ blk_insert_bs(blk_src, src, &error_abort);
92
+ type = qemu_opt_get(opts, "qom-type");
93
+ if (type && user_creatable_print_help(type, opts)) {
94
+ exit(EXIT_SUCCESS);
95
+ }
96
+ qemu_opts_del(opts);
97
+
69
+
98
+ args = keyval_parse(optarg, "qom-type", &error_fatal);
70
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
99
+ qmp_object_add(args, &ret_data, &error_fatal);
71
+ &error_abort);
100
+ qobject_unref(args);
72
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
101
+ qobject_unref(ret_data);
73
+ blk_insert_bs(blk_target, target, &error_abort);
102
+ break;
74
+
103
+ }
75
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
104
default:
76
+ 0, NULL, NULL, &error_abort);
105
g_assert_not_reached();
77
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
106
}
78
+ block_job_start(job);
107
diff --git a/Makefile.objs b/Makefile.objs
79
+
108
index XXXXXXX..XXXXXXX 100644
80
+ g_assert_cmpint(job->pause_count, ==, 0);
109
--- a/Makefile.objs
81
+ g_assert_false(job->paused);
110
+++ b/Makefile.objs
82
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
111
@@ -XXX,XX +XXX,XX @@ endif # CONFIG_SOFTMMU or CONFIG_TOOLS
83
+
112
# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are
84
+ do_drain_begin(drain_type, src);
113
# used for system emulation, too, but specified separately there)
85
+
114
86
+ if (drain_type == BDRV_DRAIN_ALL) {
115
-storage-daemon-obj-y = block/
87
+ /* bdrv_drain_all() drains both src and target, and involves an
116
+storage-daemon-obj-y = block/ qom/
88
+ * additional block_job_pause_all() */
117
storage-daemon-obj-y += blockdev.o iothread.o
89
+ g_assert_cmpint(job->pause_count, ==, 3);
118
90
+ } else {
119
######################################################################
91
+ g_assert_cmpint(job->pause_count, ==, 1);
120
diff --git a/qom/Makefile.objs b/qom/Makefile.objs
92
+ }
121
index XXXXXXX..XXXXXXX 100644
93
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
122
--- a/qom/Makefile.objs
94
+ /* g_assert_true(job->paused); */
123
+++ b/qom/Makefile.objs
95
+ g_assert_false(job->busy); /* The job is paused */
124
@@ -XXX,XX +XXX,XX @@ qom-obj-y = object.o container.o qom-qobject.o
96
+
125
qom-obj-y += object_interfaces.o
97
+ do_drain_end(drain_type, src);
126
98
+
127
common-obj-$(CONFIG_SOFTMMU) += qom-hmp-cmds.o qom-qmp-cmds.o
99
+ g_assert_cmpint(job->pause_count, ==, 0);
128
+storage-daemon-obj-y += qom-qmp-cmds.o
100
+ g_assert_false(job->paused);
101
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
102
+
103
+ do_drain_begin(drain_type, target);
104
+
105
+ if (drain_type == BDRV_DRAIN_ALL) {
106
+ /* bdrv_drain_all() drains both src and target, and involves an
107
+ * additional block_job_pause_all() */
108
+ g_assert_cmpint(job->pause_count, ==, 3);
109
+ } else {
110
+ g_assert_cmpint(job->pause_count, ==, 1);
111
+ }
112
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
113
+ /* g_assert_true(job->paused); */
114
+ g_assert_false(job->busy); /* The job is paused */
115
+
116
+ do_drain_end(drain_type, target);
117
+
118
+ g_assert_cmpint(job->pause_count, ==, 0);
119
+ g_assert_false(job->paused);
120
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
121
+
122
+ ret = block_job_complete_sync(job, &error_abort);
123
+ g_assert_cmpint(ret, ==, 0);
124
+
125
+ blk_unref(blk_src);
126
+ blk_unref(blk_target);
127
+ bdrv_unref(src);
128
+ bdrv_unref(target);
129
+}
130
+
131
+static void test_blockjob_drain_all(void)
132
+{
133
+ test_blockjob_common(BDRV_DRAIN_ALL);
134
+}
135
+
136
+static void test_blockjob_drain(void)
137
+{
138
+ test_blockjob_common(BDRV_DRAIN);
139
+}
140
+
141
int main(int argc, char **argv)
142
{
143
bdrv_init();
144
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
145
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
146
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
147
148
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
149
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
150
+
151
return g_test_run();
152
}
129
--
153
--
130
2.20.1
154
2.13.6
131
155
132
156
diff view generated by jsdifflib
1
This adds a --blockdev option to the storage daemon that works the same
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
as the -blockdev option of the system emulator.
2
so we don't need an additional block_job_pause_all() call.
3
4
In order to be able to link with blockdev.o, we also need to change
5
stream.o from common-obj to block-obj, which is where all other block
6
jobs already are.
7
8
In contrast to the system emulator, qemu-storage-daemon options will be
9
processed in the order they are given. The user needs to take care to
10
refer to other objects only after defining them.
11
3
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Message-Id: <20200224143008.13362-7-kwolf@redhat.com>
14
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
5
---
17
qemu-storage-daemon.c | 31 +++++++++++++++++++++++++++++++
6
block/io.c | 4 ----
18
Makefile | 5 ++++-
7
tests/test-bdrv-drain.c | 10 ++++------
19
Makefile.objs | 7 +++++++
8
2 files changed, 4 insertions(+), 10 deletions(-)
20
block/Makefile.objs | 2 +-
21
4 files changed, 43 insertions(+), 2 deletions(-)
22
9
23
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
10
diff --git a/block/io.c b/block/io.c
24
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
25
--- a/qemu-storage-daemon.c
12
--- a/block/io.c
26
+++ b/qemu-storage-daemon.c
13
+++ b/block/io.c
27
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
28
#include "crypto/init.h"
15
* context. */
29
16
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
30
#include "qapi/error.h"
17
31
+#include "qapi/qapi-visit-block-core.h"
18
- block_job_pause_all();
32
+#include "qapi/qapi-commands-block-core.h"
19
-
33
+#include "qapi/qobject-input-visitor.h"
20
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
34
+
21
AioContext *aio_context = bdrv_get_aio_context(bs);
35
#include "qemu-common.h"
22
36
#include "qemu-version.h"
23
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
37
#include "qemu/config-file.h"
24
aio_enable_external(aio_context);
38
@@ -XXX,XX +XXX,XX @@ static void help(void)
25
aio_context_release(aio_context);
39
" specify tracing options\n"
26
}
40
" -V, --version output version information and exit\n"
27
-
41
"\n"
28
- block_job_resume_all();
42
+" --blockdev [driver=]<driver>[,node-name=<N>][,discard=ignore|unmap]\n"
43
+" [,cache.direct=on|off][,cache.no-flush=on|off]\n"
44
+" [,read-only=on|off][,auto-read-only=on|off]\n"
45
+" [,force-share=on|off][,detect-zeroes=on|off|unmap]\n"
46
+" [,driver specific parameters...]\n"
47
+" configure a block backend\n"
48
+"\n"
49
QEMU_HELP_BOTTOM "\n",
50
error_get_progname());
51
}
29
}
52
30
53
+enum {
31
void bdrv_drain_all(void)
54
+ OPTION_BLOCKDEV = 256,
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
55
+};
56
+
57
static void process_options(int argc, char *argv[])
58
{
59
int c;
60
61
static const struct option long_options[] = {
62
+ {"blockdev", required_argument, NULL, OPTION_BLOCKDEV},
63
{"help", no_argument, NULL, 'h'},
64
{"trace", required_argument, NULL, 'T'},
65
{"version", no_argument, NULL, 'V'},
66
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
67
printf("qemu-storage-daemon version "
68
QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n");
69
exit(EXIT_SUCCESS);
70
+ case OPTION_BLOCKDEV:
71
+ {
72
+ Visitor *v;
73
+ BlockdevOptions *options;
74
+
75
+ v = qobject_input_visitor_new_str(optarg, "driver",
76
+ &error_fatal);
77
+
78
+ visit_type_BlockdevOptions(v, NULL, &options, &error_fatal);
79
+ visit_free(v);
80
+
81
+ qmp_blockdev_add(options, &error_fatal);
82
+ qapi_free_BlockdevOptions(options);
83
+ break;
84
+ }
85
default:
86
g_assert_not_reached();
87
}
88
diff --git a/Makefile b/Makefile
89
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
90
--- a/Makefile
34
--- a/tests/test-bdrv-drain.c
91
+++ b/Makefile
35
+++ b/tests/test-bdrv-drain.c
92
@@ -XXX,XX +XXX,XX @@ dummy := $(call unnest-vars,, \
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
93
qga-vss-dll-obj-y \
37
do_drain_begin(drain_type, src);
94
block-obj-y \
38
95
block-obj-m \
39
if (drain_type == BDRV_DRAIN_ALL) {
96
+ storage-daemon-obj-y \
40
- /* bdrv_drain_all() drains both src and target, and involves an
97
+ storage-daemon-obj-m \
41
- * additional block_job_pause_all() */
98
crypto-obj-y \
42
- g_assert_cmpint(job->pause_count, ==, 3);
99
qom-obj-y \
43
+ /* bdrv_drain_all() drains both src and target */
100
io-obj-y \
44
+ g_assert_cmpint(job->pause_count, ==, 2);
101
@@ -XXX,XX +XXX,XX @@ TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(T
45
} else {
102
SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
46
g_assert_cmpint(job->pause_count, ==, 1);
103
$(SOFTMMU_ALL_RULES): $(authz-obj-y)
47
}
104
$(SOFTMMU_ALL_RULES): $(block-obj-y)
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
105
+$(SOFTMMU_ALL_RULES): $(storage-daemon-obj-y)
49
do_drain_begin(drain_type, target);
106
$(SOFTMMU_ALL_RULES): $(chardev-obj-y)
50
107
$(SOFTMMU_ALL_RULES): $(crypto-obj-y)
51
if (drain_type == BDRV_DRAIN_ALL) {
108
$(SOFTMMU_ALL_RULES): $(io-obj-y)
52
- /* bdrv_drain_all() drains both src and target, and involves an
109
@@ -XXX,XX +XXX,XX @@ qemu-img.o: qemu-img-cmds.h
53
- * additional block_job_pause_all() */
110
qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
54
- g_assert_cmpint(job->pause_count, ==, 3);
111
qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
55
+ /* bdrv_drain_all() drains both src and target */
112
qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
56
+ g_assert_cmpint(job->pause_count, ==, 2);
113
-qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
57
} else {
114
+qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS)
58
g_assert_cmpint(job->pause_count, ==, 1);
115
59
}
116
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
117
118
diff --git a/Makefile.objs b/Makefile.objs
119
index XXXXXXX..XXXXXXX 100644
120
--- a/Makefile.objs
121
+++ b/Makefile.objs
122
@@ -XXX,XX +XXX,XX @@ io-obj-y = io/
123
124
endif # CONFIG_SOFTMMU or CONFIG_TOOLS
125
126
+#######################################################################
127
+# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are
128
+# used for system emulation, too, but specified separately there)
129
+
130
+storage-daemon-obj-y = block/
131
+storage-daemon-obj-y += blockdev.o iothread.o
132
+
133
######################################################################
134
# Target independent part of system emulation. The long term path is to
135
# suppress *all* target specific code in case of system emulation, i.e. a
136
diff --git a/block/Makefile.objs b/block/Makefile.objs
137
index XXXXXXX..XXXXXXX 100644
138
--- a/block/Makefile.objs
139
+++ b/block/Makefile.objs
140
@@ -XXX,XX +XXX,XX @@ block-obj-y += aio_task.o
141
block-obj-y += backup-top.o
142
block-obj-y += filter-compress.o
143
144
-common-obj-y += stream.o
145
+block-obj-y += stream.o
146
147
common-obj-y += qapi-sysemu.o
148
149
--
60
--
150
2.20.1
61
2.13.6
151
62
152
63
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
aio_disable_external() to the outermost drain section, but the block
3
driver callbacks are always called. bdrv_do_drained_end() must match
4
this behaviour, otherwise nodes stay drained even if begin/end calls
5
were balanced.
2
6
3
Test what happens when writing data to a preallocated zero cluster, but
4
the data write fails.
5
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20200225143130.111267-3-mreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
8
---
10
tests/qemu-iotests/026 | 21 +++++++++++++++++++++
9
block/io.c | 12 +++++++-----
11
tests/qemu-iotests/026.out | 10 ++++++++++
10
1 file changed, 7 insertions(+), 5 deletions(-)
12
tests/qemu-iotests/026.out.nocache | 10 ++++++++++
13
3 files changed, 41 insertions(+)
14
11
15
diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026
12
diff --git a/block/io.c b/block/io.c
16
index XXXXXXX..XXXXXXX 100755
13
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/026
14
--- a/block/io.c
18
+++ b/tests/qemu-iotests/026
15
+++ b/block/io.c
19
@@ -XXX,XX +XXX,XX @@ _make_test_img 64M
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
20
$QEMU_IO -c "write 0 1M" -c "write 0 1M" "$BLKDBG_TEST_IMG" | _filter_qemu_io
17
21
_check_test_img
18
void bdrv_drained_end(BlockDriverState *bs)
22
19
{
23
+echo
20
+ int old_quiesce_counter;
24
+echo === Avoid freeing preallocated zero clusters on failure ===
25
+echo
26
+
21
+
27
+cat > "$TEST_DIR/blkdebug.conf" <<EOF
22
if (qemu_in_coroutine()) {
28
+[inject-error]
23
bdrv_co_yield_to_drain(bs, false);
29
+event = "write_aio"
24
return;
30
+errno = "5"
25
}
31
+once = "on"
26
assert(bs->quiesce_counter > 0);
32
+EOF
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
33
+
28
- return;
34
+_make_test_img $CLUSTER_SIZE
29
- }
35
+# Create a preallocated zero cluster
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
36
+$QEMU_IO -c "write 0 $CLUSTER_SIZE" -c "write -z 0 $CLUSTER_SIZE" "$TEST_IMG" \
31
37
+ | _filter_qemu_io
32
/* Re-enable things in child-to-parent order */
38
+# Try to overwrite it (prompting an I/O error from blkdebug), thus
33
bdrv_drain_invoke(bs, false, false);
39
+# triggering the alloc abort code
34
- bdrv_parent_drained_end(bs);
40
+$QEMU_IO -c "write 0 $CLUSTER_SIZE" "$BLKDBG_TEST_IMG" | _filter_qemu_io
35
- aio_enable_external(bdrv_get_aio_context(bs));
41
+
36
+ if (old_quiesce_counter == 1) {
42
+_check_test_img
37
+ bdrv_parent_drained_end(bs);
43
+
38
+ aio_enable_external(bdrv_get_aio_context(bs));
44
# success, all done
39
+ }
45
echo "*** done"
40
}
46
rm -f $seq.full
41
47
diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out
42
/*
48
index XXXXXXX..XXXXXXX 100644
49
--- a/tests/qemu-iotests/026.out
50
+++ b/tests/qemu-iotests/026.out
51
@@ -XXX,XX +XXX,XX @@ write failed: Input/output error
52
wrote 1048576/1048576 bytes at offset 0
53
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
54
No errors were found on the image.
55
+
56
+=== Avoid freeing preallocated zero clusters on failure ===
57
+
58
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024
59
+wrote 1024/1024 bytes at offset 0
60
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
61
+wrote 1024/1024 bytes at offset 0
62
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
63
+write failed: Input/output error
64
+No errors were found on the image.
65
*** done
66
diff --git a/tests/qemu-iotests/026.out.nocache b/tests/qemu-iotests/026.out.nocache
67
index XXXXXXX..XXXXXXX 100644
68
--- a/tests/qemu-iotests/026.out.nocache
69
+++ b/tests/qemu-iotests/026.out.nocache
70
@@ -XXX,XX +XXX,XX @@ write failed: Input/output error
71
wrote 1048576/1048576 bytes at offset 0
72
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
73
No errors were found on the image.
74
+
75
+=== Avoid freeing preallocated zero clusters on failure ===
76
+
77
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024
78
+wrote 1024/1024 bytes at offset 0
79
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
80
+wrote 1024/1024 bytes at offset 0
81
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
82
+write failed: Input/output error
83
+No errors were found on the image.
84
*** done
85
--
43
--
86
2.20.1
44
2.13.6
87
45
88
46
diff view generated by jsdifflib
1
This adds a --chardev option to the storage daemon that works the same
2
as the -chardev option of the system emulator.
3
4
The syntax of the --chardev option is still considered unstable. We want
5
to QAPIfy it and will potentially make changes to its syntax while
6
converting it. However, we haven't decided yet on a design for the
7
QAPIfication, so QemuOpts will have to do for now.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20200224143008.13362-14-kwolf@redhat.com>
11
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
2
---
14
qemu-storage-daemon.c | 24 ++++++++++++++++++++++++
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
15
Makefile | 2 +-
4
1 file changed, 57 insertions(+)
16
2 files changed, 25 insertions(+), 1 deletion(-)
17
5
18
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
19
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
20
--- a/qemu-storage-daemon.c
8
--- a/tests/test-bdrv-drain.c
21
+++ b/qemu-storage-daemon.c
9
+++ b/tests/test-bdrv-drain.c
22
@@ -XXX,XX +XXX,XX @@
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
23
11
enum drain_type {
24
#include "block/block.h"
12
BDRV_DRAIN_ALL,
25
#include "block/nbd.h"
13
BDRV_DRAIN,
26
+#include "chardev/char.h"
14
+ DRAIN_TYPE_MAX,
27
#include "crypto/init.h"
28
29
#include "qapi/error.h"
30
@@ -XXX,XX +XXX,XX @@ static void help(void)
31
" [,driver specific parameters...]\n"
32
" configure a block backend\n"
33
"\n"
34
+" --chardev <options> configure a character device backend\n"
35
+" (see the qemu(1) man page for possible options)\n"
36
+"\n"
37
" --export [type=]nbd,device=<node-name>[,name=<export-name>]\n"
38
" [,writable=on|off][,bitmap=<name>]\n"
39
" export the specified block node over NBD\n"
40
@@ -XXX,XX +XXX,XX @@ QEMU_HELP_BOTTOM "\n",
41
42
enum {
43
OPTION_BLOCKDEV = 256,
44
+ OPTION_CHARDEV,
45
OPTION_EXPORT,
46
OPTION_NBD_SERVER,
47
OPTION_OBJECT,
48
};
15
};
49
16
50
+extern QemuOptsList qemu_chardev_opts;
17
static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
18
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
19
test_quiesce_common(BDRV_DRAIN, false);
20
}
21
22
+static void test_nested(void)
23
+{
24
+ BlockBackend *blk;
25
+ BlockDriverState *bs, *backing;
26
+ BDRVTestState *s, *backing_s;
27
+ enum drain_type outer, inner;
51
+
28
+
52
static QemuOptsList qemu_object_opts = {
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
53
.name = "object",
30
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
54
.implied_opt_name = "qom-type",
31
+ &error_abort);
55
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
32
+ s = bs->opaque;
56
33
+ blk_insert_bs(blk, bs, &error_abort);
57
static const struct option long_options[] = {
58
{"blockdev", required_argument, NULL, OPTION_BLOCKDEV},
59
+ {"chardev", required_argument, NULL, OPTION_CHARDEV},
60
{"export", required_argument, NULL, OPTION_EXPORT},
61
{"help", no_argument, NULL, 'h'},
62
{"nbd-server", required_argument, NULL, OPTION_NBD_SERVER},
63
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
64
qapi_free_BlockdevOptions(options);
65
break;
66
}
67
+ case OPTION_CHARDEV:
68
+ {
69
+ /* TODO This interface is not stable until we QAPIfy it */
70
+ QemuOpts *opts = qemu_opts_parse_noisily(&qemu_chardev_opts,
71
+ optarg, true);
72
+ if (opts == NULL) {
73
+ exit(EXIT_FAILURE);
74
+ }
75
+
34
+
76
+ if (!qemu_chr_new_from_opts(opts, NULL, &error_fatal)) {
35
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
77
+ /* No error, but NULL returned means help was printed */
36
+ backing_s = backing->opaque;
78
+ exit(EXIT_SUCCESS);
37
+ bdrv_set_backing_hd(bs, backing, &error_abort);
79
+ }
38
+
80
+ qemu_opts_del(opts);
39
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
81
+ break;
40
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
82
+ }
41
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
83
case OPTION_EXPORT:
42
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
84
{
43
+ (inner != BDRV_DRAIN_ALL);
85
Visitor *v;
44
+ int backing_quiesce = 0;
86
diff --git a/Makefile b/Makefile
45
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
87
index XXXXXXX..XXXXXXX 100644
46
+ (inner != BDRV_DRAIN);
88
--- a/Makefile
47
+
89
+++ b/Makefile
48
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
90
@@ -XXX,XX +XXX,XX @@ qemu-img.o: qemu-img-cmds.h
49
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
91
qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
50
+ g_assert_cmpint(s->drain_count, ==, 0);
92
qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
51
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
93
qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
52
+
94
-qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS)
53
+ do_drain_begin(outer, bs);
95
+qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(chardev-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS)
54
+ do_drain_begin(inner, bs);
96
55
+
97
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
56
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
57
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
58
+ g_assert_cmpint(s->drain_count, ==, 2);
59
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
60
+
61
+ do_drain_end(inner, bs);
62
+ do_drain_end(outer, bs);
63
+
64
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
65
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
66
+ g_assert_cmpint(s->drain_count, ==, 0);
67
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
68
+ }
69
+ }
70
+
71
+ bdrv_unref(backing);
72
+ bdrv_unref(bs);
73
+ blk_unref(blk);
74
+}
75
+
76
77
typedef struct TestBlockJob {
78
BlockJob common;
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
82
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
84
+
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
98
87
99
--
88
--
100
2.20.1
89
2.13.6
101
90
102
91
diff view generated by jsdifflib
New patch
1
1
This is in preparation for subtree drains, i.e. drained sections that
2
affect not only a single node, but recursively all child nodes, too.
3
4
Calling the parent callbacks for drain is pointless when we just came
5
from that parent node recursively and leads to multiple increases of
6
bs->quiesce_counter in a single drain call. Don't do it.
7
8
In order for this to work correctly, the parent callback must be called
9
for every bdrv_drain_begin/end() call, not only for the outermost one:
10
11
If we have a node N with two parents A and B, recursive draining of A
12
should cause the quiesce_counter of B to increase because its child N is
13
drained independently of B. If now B is recursively drained, too, A must
14
increase its quiesce_counter because N is drained independently of A
15
only now, even if N is going from quiesce_counter 1 to 2.
16
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
19
include/block/block.h | 4 ++--
20
block.c | 13 +++++++++----
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
22
3 files changed, 45 insertions(+), 19 deletions(-)
23
24
diff --git a/include/block/block.h b/include/block/block.h
25
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/block.h
27
+++ b/include/block/block.h
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
29
* Begin a quiesced section of all users of @bs. This is part of
30
* bdrv_drained_begin.
31
*/
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
34
35
/**
36
* bdrv_parent_drained_end:
37
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs);
38
* End a quiesced section of all users of @bs. This is part of
39
* bdrv_drained_end.
40
*/
41
-void bdrv_parent_drained_end(BlockDriverState *bs);
42
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
43
44
/**
45
* bdrv_drained_begin:
46
diff --git a/block.c b/block.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/block.c
49
+++ b/block.c
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
51
BlockDriverState *new_bs)
52
{
53
BlockDriverState *old_bs = child->bs;
54
+ int i;
55
56
if (old_bs && new_bs) {
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
58
}
59
if (old_bs) {
60
if (old_bs->quiesce_counter && child->role->drained_end) {
61
- child->role->drained_end(child);
62
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
63
+ child->role->drained_end(child);
64
+ }
65
}
66
if (child->role->detach) {
67
child->role->detach(child);
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
69
if (new_bs) {
70
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
71
if (new_bs->quiesce_counter && child->role->drained_begin) {
72
- child->role->drained_begin(child);
73
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
74
+ child->role->drained_begin(child);
75
+ }
76
}
77
78
if (child->role->attach) {
79
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
80
AioContext *ctx = bdrv_get_aio_context(bs);
81
82
aio_disable_external(ctx);
83
- bdrv_parent_drained_begin(bs);
84
+ bdrv_parent_drained_begin(bs, NULL);
85
bdrv_drain(bs); /* ensure there are no in-flight requests */
86
87
while (aio_poll(ctx, false)) {
88
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
89
*/
90
aio_context_acquire(new_context);
91
bdrv_attach_aio_context(bs, new_context);
92
- bdrv_parent_drained_end(bs);
93
+ bdrv_parent_drained_end(bs, NULL);
94
aio_enable_external(ctx);
95
aio_context_release(new_context);
96
}
97
diff --git a/block/io.c b/block/io.c
98
index XXXXXXX..XXXXXXX 100644
99
--- a/block/io.c
100
+++ b/block/io.c
101
@@ -XXX,XX +XXX,XX @@
102
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
103
int64_t offset, int bytes, BdrvRequestFlags flags);
104
105
-void bdrv_parent_drained_begin(BlockDriverState *bs)
106
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
107
{
108
BdrvChild *c, *next;
109
110
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
111
+ if (c == ignore) {
112
+ continue;
113
+ }
114
if (c->role->drained_begin) {
115
c->role->drained_begin(c);
116
}
117
}
118
}
119
120
-void bdrv_parent_drained_end(BlockDriverState *bs)
121
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
122
{
123
BdrvChild *c, *next;
124
125
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
126
+ if (c == ignore) {
127
+ continue;
128
+ }
129
if (c->role->drained_end) {
130
c->role->drained_end(c);
131
}
132
@@ -XXX,XX +XXX,XX @@ typedef struct {
133
BlockDriverState *bs;
134
bool done;
135
bool begin;
136
+ BdrvChild *parent;
137
} BdrvCoDrainData;
138
139
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
140
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
141
return waited;
142
}
143
144
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
145
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
146
+
147
static void bdrv_co_drain_bh_cb(void *opaque)
148
{
149
BdrvCoDrainData *data = opaque;
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
151
152
bdrv_dec_in_flight(bs);
153
if (data->begin) {
154
- bdrv_drained_begin(bs);
155
+ bdrv_do_drained_begin(bs, data->parent);
156
} else {
157
- bdrv_drained_end(bs);
158
+ bdrv_do_drained_end(bs, data->parent);
159
}
160
161
data->done = true;
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
163
}
164
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
166
- bool begin)
167
+ bool begin, BdrvChild *parent)
168
{
169
BdrvCoDrainData data;
170
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
172
.bs = bs,
173
.done = false,
174
.begin = begin,
175
+ .parent = parent,
176
};
177
bdrv_inc_in_flight(bs);
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
180
assert(data.done);
181
}
182
183
-void bdrv_drained_begin(BlockDriverState *bs)
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
185
{
186
if (qemu_in_coroutine()) {
187
- bdrv_co_yield_to_drain(bs, true);
188
+ bdrv_co_yield_to_drain(bs, true, parent);
189
return;
190
}
191
192
/* Stop things in parent-to-child order */
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
194
aio_disable_external(bdrv_get_aio_context(bs));
195
- bdrv_parent_drained_begin(bs);
196
}
197
198
+ bdrv_parent_drained_begin(bs, parent);
199
bdrv_drain_invoke(bs, true, false);
200
bdrv_drain_recurse(bs);
201
}
202
203
-void bdrv_drained_end(BlockDriverState *bs)
204
+void bdrv_drained_begin(BlockDriverState *bs)
205
+{
206
+ bdrv_do_drained_begin(bs, NULL);
207
+}
208
+
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
210
{
211
int old_quiesce_counter;
212
213
if (qemu_in_coroutine()) {
214
- bdrv_co_yield_to_drain(bs, false);
215
+ bdrv_co_yield_to_drain(bs, false, parent);
216
return;
217
}
218
assert(bs->quiesce_counter > 0);
219
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
220
221
/* Re-enable things in child-to-parent order */
222
bdrv_drain_invoke(bs, false, false);
223
+ bdrv_parent_drained_end(bs, parent);
224
if (old_quiesce_counter == 1) {
225
- bdrv_parent_drained_end(bs);
226
aio_enable_external(bdrv_get_aio_context(bs));
227
}
228
}
229
230
+void bdrv_drained_end(BlockDriverState *bs)
231
+{
232
+ bdrv_do_drained_end(bs, NULL);
233
+}
234
+
235
/*
236
* Wait for pending requests to complete on a single BlockDriverState subtree,
237
* and suspend block driver's internal I/O until next request arrives.
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
239
/* Stop things in parent-to-child order */
240
aio_context_acquire(aio_context);
241
aio_disable_external(aio_context);
242
- bdrv_parent_drained_begin(bs);
243
+ bdrv_parent_drained_begin(bs, NULL);
244
bdrv_drain_invoke(bs, true, true);
245
aio_context_release(aio_context);
246
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
248
/* Re-enable things in child-to-parent order */
249
aio_context_acquire(aio_context);
250
bdrv_drain_invoke(bs, false, true);
251
- bdrv_parent_drained_end(bs);
252
+ bdrv_parent_drained_end(bs, NULL);
253
aio_enable_external(aio_context);
254
aio_context_release(aio_context);
255
}
256
--
257
2.13.6
258
259
diff view generated by jsdifflib
1
Trying to attach a QMP monitor to a chardev that is already in use
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
results in a crash because monitor_init_qmp() passes &error_abort to
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
qemu_chr_fe_init():
3
until bdrv_drained_end().
4
4
5
$ ./x86_64-softmmu/qemu-system-x86_64 --chardev stdio,id=foo --mon foo,mode=control --mon foo,mode=control
5
Add a version that keeps the whole subtree drained. As of this commit,
6
Unexpected error in qemu_chr_fe_init() at chardev/char-fe.c:220:
6
graph changes cannot be allowed during a subtree drained section, but
7
qemu-system-x86_64: --mon foo,mode=control: Device 'foo' is in use
7
this will be fixed soon.
8
Abgebrochen (Speicherabzug geschrieben)
9
10
Fix this by allowing monitor_init_qmp() to return an error and passing
11
any error in qemu_chr_fe_init() to its caller instead of aborting.
12
8
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Message-Id: <20200224143008.13362-18-kwolf@redhat.com>
15
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
10
---
18
include/monitor/monitor.h | 2 +-
11
include/block/block.h | 13 +++++++++++++
19
monitor/monitor.c | 7 ++++++-
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
20
monitor/qmp.c | 11 +++++++----
13
2 files changed, 56 insertions(+), 11 deletions(-)
21
stubs/monitor-core.c | 2 +-
22
tests/test-util-sockets.c | 2 +-
23
5 files changed, 16 insertions(+), 8 deletions(-)
24
14
25
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
15
diff --git a/include/block/block.h b/include/block/block.h
26
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
27
--- a/include/monitor/monitor.h
17
--- a/include/block/block.h
28
+++ b/include/monitor/monitor.h
18
+++ b/include/block/block.h
29
@@ -XXX,XX +XXX,XX @@ bool monitor_cur_is_qmp(void);
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
30
20
void bdrv_drained_begin(BlockDriverState *bs);
31
void monitor_init_globals(void);
21
32
void monitor_init_globals_core(void);
22
/**
33
-void monitor_init_qmp(Chardev *chr, bool pretty);
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
34
+void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp);
24
+ * exclusive access to all child nodes as well.
35
void monitor_init_hmp(Chardev *chr, bool use_readline);
25
+ *
36
int monitor_init(MonitorOptions *opts, Error **errp);
26
+ * Graph changes are not allowed during a subtree drain section.
37
int monitor_init_opts(QemuOpts *opts, Error **errp);
27
+ */
38
diff --git a/monitor/monitor.c b/monitor/monitor.c
28
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
29
+
30
+/**
31
* bdrv_drained_end:
32
*
33
* End a quiescent section started by bdrv_drained_begin().
34
*/
35
void bdrv_drained_end(BlockDriverState *bs);
36
37
+/**
38
+ * End a quiescent section started by bdrv_subtree_drained_begin().
39
+ */
40
+void bdrv_subtree_drained_end(BlockDriverState *bs);
41
+
42
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
43
Error **errp);
44
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
45
diff --git a/block/io.c b/block/io.c
39
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
40
--- a/monitor/monitor.c
47
--- a/block/io.c
41
+++ b/monitor/monitor.c
48
+++ b/block/io.c
42
@@ -XXX,XX +XXX,XX @@ void monitor_init_globals_core(void)
49
@@ -XXX,XX +XXX,XX @@ typedef struct {
43
int monitor_init(MonitorOptions *opts, Error **errp)
50
BlockDriverState *bs;
51
bool done;
52
bool begin;
53
+ bool recursive;
54
BdrvChild *parent;
55
} BdrvCoDrainData;
56
57
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
58
return waited;
59
}
60
61
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
62
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
63
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
64
+ BdrvChild *parent);
65
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
66
+ BdrvChild *parent);
67
68
static void bdrv_co_drain_bh_cb(void *opaque)
44
{
69
{
45
Chardev *chr;
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
46
+ Error *local_err = NULL;
71
47
72
bdrv_dec_in_flight(bs);
48
chr = qemu_chr_find(opts->chardev);
73
if (data->begin) {
49
if (chr == NULL) {
74
- bdrv_do_drained_begin(bs, data->parent);
50
@@ -XXX,XX +XXX,XX @@ int monitor_init(MonitorOptions *opts, Error **errp)
75
+ bdrv_do_drained_begin(bs, data->recursive, data->parent);
51
76
} else {
52
switch (opts->mode) {
77
- bdrv_do_drained_end(bs, data->parent);
53
case MONITOR_MODE_CONTROL:
78
+ bdrv_do_drained_end(bs, data->recursive, data->parent);
54
- monitor_init_qmp(chr, opts->pretty);
55
+ monitor_init_qmp(chr, opts->pretty, &local_err);
56
break;
57
case MONITOR_MODE_READLINE:
58
if (opts->pretty) {
59
@@ -XXX,XX +XXX,XX @@ int monitor_init(MonitorOptions *opts, Error **errp)
60
g_assert_not_reached();
61
}
79
}
62
80
63
+ if (local_err) {
81
data->done = true;
64
+ error_propagate(errp, local_err);
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
65
+ return -1;
83
}
84
85
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
86
- bool begin, BdrvChild *parent)
87
+ bool begin, bool recursive,
88
+ BdrvChild *parent)
89
{
90
BdrvCoDrainData data;
91
92
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
93
.bs = bs,
94
.done = false,
95
.begin = begin,
96
+ .recursive = recursive,
97
.parent = parent,
98
};
99
bdrv_inc_in_flight(bs);
100
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
101
assert(data.done);
102
}
103
104
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
105
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
106
+ BdrvChild *parent)
107
{
108
+ BdrvChild *child, *next;
109
+
110
if (qemu_in_coroutine()) {
111
- bdrv_co_yield_to_drain(bs, true, parent);
112
+ bdrv_co_yield_to_drain(bs, true, recursive, parent);
113
return;
114
}
115
116
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
117
bdrv_parent_drained_begin(bs, parent);
118
bdrv_drain_invoke(bs, true, false);
119
bdrv_drain_recurse(bs);
120
+
121
+ if (recursive) {
122
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
123
+ bdrv_do_drained_begin(child->bs, true, child);
124
+ }
66
+ }
125
+ }
67
return 0;
68
}
126
}
69
127
70
diff --git a/monitor/qmp.c b/monitor/qmp.c
128
void bdrv_drained_begin(BlockDriverState *bs)
71
index XXXXXXX..XXXXXXX 100644
129
{
72
--- a/monitor/qmp.c
130
- bdrv_do_drained_begin(bs, NULL);
73
+++ b/monitor/qmp.c
131
+ bdrv_do_drained_begin(bs, false, NULL);
74
@@ -XXX,XX +XXX,XX @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
132
+}
75
monitor_list_append(&mon->common);
133
+
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
135
+{
136
+ bdrv_do_drained_begin(bs, true, NULL);
76
}
137
}
77
138
78
-void monitor_init_qmp(Chardev *chr, bool pretty)
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
79
+void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
141
+ BdrvChild *parent)
80
{
142
{
81
MonitorQMP *mon = g_new0(MonitorQMP, 1);
143
+ BdrvChild *child, *next;
82
144
int old_quiesce_counter;
83
+ if (!qemu_chr_fe_init(&mon->common.chr, chr, errp)) {
145
84
+ g_free(mon);
146
if (qemu_in_coroutine()) {
85
+ return;
147
- bdrv_co_yield_to_drain(bs, false, parent);
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
149
return;
150
}
151
assert(bs->quiesce_counter > 0);
152
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
153
if (old_quiesce_counter == 1) {
154
aio_enable_external(bdrv_get_aio_context(bs));
155
}
156
+
157
+ if (recursive) {
158
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
159
+ bdrv_do_drained_end(child->bs, true, child);
160
+ }
86
+ }
161
+ }
87
+ qemu_chr_fe_set_echo(&mon->common.chr, true);
162
}
163
164
void bdrv_drained_end(BlockDriverState *bs)
165
{
166
- bdrv_do_drained_end(bs, NULL);
167
+ bdrv_do_drained_end(bs, false, NULL);
168
+}
88
+
169
+
89
/* Note: we run QMP monitor in I/O thread when @chr supports that */
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
90
monitor_data_init(&mon->common, true, false,
171
+{
91
qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
172
+ bdrv_do_drained_end(bs, true, NULL);
92
@@ -XXX,XX +XXX,XX @@ void monitor_init_qmp(Chardev *chr, bool pretty)
93
qemu_mutex_init(&mon->qmp_queue_lock);
94
mon->qmp_requests = g_queue_new();
95
96
- qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
97
- qemu_chr_fe_set_echo(&mon->common.chr, true);
98
-
99
json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
100
if (mon->common.use_io_thread) {
101
/*
102
diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c
103
index XXXXXXX..XXXXXXX 100644
104
--- a/stubs/monitor-core.c
105
+++ b/stubs/monitor-core.c
106
@@ -XXX,XX +XXX,XX @@
107
108
__thread Monitor *cur_mon;
109
110
-void monitor_init_qmp(Chardev *chr, bool pretty)
111
+void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
112
{
113
}
173
}
114
174
115
diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c
175
/*
116
index XXXXXXX..XXXXXXX 100644
117
--- a/tests/test-util-sockets.c
118
+++ b/tests/test-util-sockets.c
119
@@ -XXX,XX +XXX,XX @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
120
*/
121
__thread Monitor *cur_mon;
122
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); }
123
-void monitor_init_qmp(Chardev *chr, bool pretty) {}
124
+void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) {}
125
void monitor_init_hmp(Chardev *chr, bool use_readline) {}
126
127
128
--
176
--
129
2.20.1
177
2.13.6
130
178
131
179
diff view generated by jsdifflib
1
Before we can add the monitor to qemu-storage-daemon, we need to add a
1
Add a subtree drain version to the existing test cases.
2
stubs for monitor_fdsets_cleanup().
3
4
We also need to make sure that stubs that are actually implemented in
5
the monitor core aren't linked to qemu-storage-daemon so that we don't
6
get linker errors because of duplicate symbols. This is achieved by
7
moving the stubs in question to a new file stubs/monitor-core.c.
8
2
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Message-Id: <20200224143008.13362-15-kwolf@redhat.com>
11
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
4
---
14
stubs/monitor-core.c | 21 +++++++++++++++++++++
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
15
stubs/monitor.c | 15 ++-------------
6
1 file changed, 26 insertions(+), 1 deletion(-)
16
stubs/Makefile.objs | 1 +
17
3 files changed, 24 insertions(+), 13 deletions(-)
18
create mode 100644 stubs/monitor-core.c
19
7
20
diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
21
new file mode 100644
9
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX
10
--- a/tests/test-bdrv-drain.c
23
--- /dev/null
11
+++ b/tests/test-bdrv-drain.c
24
+++ b/stubs/monitor-core.c
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
25
@@ -XXX,XX +XXX,XX @@
13
enum drain_type {
26
+#include "qemu/osdep.h"
14
BDRV_DRAIN_ALL,
27
+#include "monitor/monitor.h"
15
BDRV_DRAIN,
28
+#include "qemu-common.h"
16
+ BDRV_SUBTREE_DRAIN,
29
+#include "qapi/qapi-emit-events.h"
17
DRAIN_TYPE_MAX,
30
+
18
};
31
+__thread Monitor *cur_mon;
19
32
+
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
33
+void monitor_init_qmp(Chardev *chr, bool pretty)
21
switch (drain_type) {
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
25
default: g_assert_not_reached();
26
}
27
}
28
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
29
switch (drain_type) {
30
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
31
case BDRV_DRAIN: bdrv_drained_end(bs); break;
32
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
33
default: g_assert_not_reached();
34
}
35
}
36
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
37
test_drv_cb_common(BDRV_DRAIN, false);
38
}
39
40
+static void test_drv_cb_drain_subtree(void)
34
+{
41
+{
42
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
35
+}
43
+}
36
+
44
+
37
+void qapi_event_emit(QAPIEvent event, QDict *qdict)
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
46
{
47
BlockBackend *blk;
48
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
49
test_quiesce_common(BDRV_DRAIN, false);
50
}
51
52
+static void test_quiesce_drain_subtree(void)
38
+{
53
+{
54
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
39
+}
55
+}
40
+
56
+
41
+int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
57
static void test_nested(void)
58
{
59
BlockBackend *blk;
60
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
61
/* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
62
int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
63
(inner != BDRV_DRAIN_ALL);
64
- int backing_quiesce = 0;
65
+ int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) +
66
+ (inner == BDRV_SUBTREE_DRAIN);
67
int backing_cb_cnt = (outer != BDRV_DRAIN) +
68
(inner != BDRV_DRAIN);
69
70
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_drain(void)
71
test_blockjob_common(BDRV_DRAIN);
72
}
73
74
+static void test_blockjob_drain_subtree(void)
42
+{
75
+{
43
+ abort();
76
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
44
+}
77
+}
45
+
78
+
46
+
79
int main(int argc, char **argv)
47
diff --git a/stubs/monitor.c b/stubs/monitor.c
48
index XXXXXXX..XXXXXXX 100644
49
--- a/stubs/monitor.c
50
+++ b/stubs/monitor.c
51
@@ -XXX,XX +XXX,XX @@
52
#include "qemu/osdep.h"
53
#include "qapi/error.h"
54
-#include "qapi/qapi-emit-events.h"
55
#include "monitor/monitor.h"
56
-
57
-__thread Monitor *cur_mon;
58
-
59
-int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
60
-{
61
- abort();
62
-}
63
+#include "../monitor/monitor-internal.h"
64
65
int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
66
{
80
{
67
@@ -XXX,XX +XXX,XX @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
81
bdrv_init();
68
return -1;
82
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
83
84
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
85
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
86
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
87
+ test_drv_cb_drain_subtree);
88
89
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
90
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
91
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
92
+ test_quiesce_drain_subtree);
93
94
g_test_add_func("/bdrv-drain/nested", test_nested);
95
96
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
97
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
98
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
99
+ test_blockjob_drain_subtree);
100
101
return g_test_run();
69
}
102
}
70
71
-void monitor_init_qmp(Chardev *chr, bool pretty)
72
-{
73
-}
74
-
75
void monitor_init_hmp(Chardev *chr, bool use_readline)
76
{
77
}
78
79
-void qapi_event_emit(QAPIEvent event, QDict *qdict)
80
+void monitor_fdsets_cleanup(void)
81
{
82
}
83
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
84
index XXXXXXX..XXXXXXX 100644
85
--- a/stubs/Makefile.objs
86
+++ b/stubs/Makefile.objs
87
@@ -XXX,XX +XXX,XX @@ stub-obj-y += machine-init-done.o
88
stub-obj-y += migr-blocker.o
89
stub-obj-y += change-state-handler.o
90
stub-obj-y += monitor.o
91
+stub-obj-y += monitor-core.o
92
stub-obj-y += notify-event.o
93
stub-obj-y += qtest.o
94
stub-obj-y += replay.o
95
--
103
--
96
2.20.1
104
2.13.6
97
105
98
106
diff view generated by jsdifflib
1
This adds a new binary qemu-storage-daemon that doesn't yet do more than
1
If bdrv_do_drained_begin/end() are called in coroutine context, they
2
some typical initialisation for tools and parsing the basic command
2
first use a BH to get out of the coroutine context. Call some existing
3
options --version, --help and --trace.
3
tests again from a coroutine to cover this code path.
4
5
Even though this doesn't add any options yet that create things (like
6
--object or --blockdev), already document that we're planning to process
7
them in the order they are given on the command line rather than trying
8
(and failing, like vl.c) to resolve dependencies between options
9
automatically.
10
4
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Message-Id: <20200224143008.13362-2-kwolf@redhat.com>
13
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
6
---
16
configure | 2 +-
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
17
qemu-storage-daemon.c | 127 ++++++++++++++++++++++++++++++++++++++++++
8
1 file changed, 59 insertions(+)
18
Makefile | 1 +
19
3 files changed, 129 insertions(+), 1 deletion(-)
20
create mode 100644 qemu-storage-daemon.c
21
9
22
diff --git a/configure b/configure
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
23
index XXXXXXX..XXXXXXX 100755
11
index XXXXXXX..XXXXXXX 100644
24
--- a/configure
12
--- a/tests/test-bdrv-drain.c
25
+++ b/configure
13
+++ b/tests/test-bdrv-drain.c
26
@@ -XXX,XX +XXX,XX @@ tools=""
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
27
if test "$want_tools" = "yes" ; then
15
*aio_ret = ret;
28
tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) qemu-edid\$(EXESUF) $tools"
16
}
29
if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
17
30
- tools="qemu-nbd\$(EXESUF) $tools"
18
+typedef struct CallInCoroutineData {
31
+ tools="qemu-nbd\$(EXESUF) qemu-storage-daemon\$(EXESUF) $tools"
19
+ void (*entry)(void);
32
fi
20
+ bool done;
33
if [ "$ivshmem" = "yes" ]; then
21
+} CallInCoroutineData;
34
tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools"
35
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
36
new file mode 100644
37
index XXXXXXX..XXXXXXX
38
--- /dev/null
39
+++ b/qemu-storage-daemon.c
40
@@ -XXX,XX +XXX,XX @@
41
+/*
42
+ * QEMU storage daemon
43
+ *
44
+ * Copyright (c) 2003-2008 Fabrice Bellard
45
+ * Copyright (c) 2019 Kevin Wolf <kwolf@redhat.com>
46
+ *
47
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
48
+ * of this software and associated documentation files (the "Software"), to deal
49
+ * in the Software without restriction, including without limitation the rights
50
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
51
+ * copies of the Software, and to permit persons to whom the Software is
52
+ * furnished to do so, subject to the following conditions:
53
+ *
54
+ * The above copyright notice and this permission notice shall be included in
55
+ * all copies or substantial portions of the Software.
56
+ *
57
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
60
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
61
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
62
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
63
+ * THE SOFTWARE.
64
+ */
65
+
22
+
66
+#include "qemu/osdep.h"
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
24
+{
25
+ CallInCoroutineData *data = opaque;
67
+
26
+
68
+#include <getopt.h>
27
+ data->entry();
69
+
28
+ data->done = true;
70
+#include "block/block.h"
71
+#include "crypto/init.h"
72
+
73
+#include "qapi/error.h"
74
+#include "qemu-common.h"
75
+#include "qemu-version.h"
76
+#include "qemu/config-file.h"
77
+#include "qemu/error-report.h"
78
+#include "qemu/log.h"
79
+#include "qemu/main-loop.h"
80
+#include "qemu/module.h"
81
+
82
+#include "trace/control.h"
83
+
84
+static void help(void)
85
+{
86
+ printf(
87
+"Usage: %s [options]\n"
88
+"QEMU storage daemon\n"
89
+"\n"
90
+" -h, --help display this help and exit\n"
91
+" -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
92
+" specify tracing options\n"
93
+" -V, --version output version information and exit\n"
94
+"\n"
95
+QEMU_HELP_BOTTOM "\n",
96
+ error_get_progname());
97
+}
29
+}
98
+
30
+
99
+static void process_options(int argc, char *argv[])
31
+static void call_in_coroutine(void (*entry)(void))
100
+{
32
+{
101
+ int c;
33
+ Coroutine *co;
102
+
34
+ CallInCoroutineData data = {
103
+ static const struct option long_options[] = {
35
+ .entry = entry,
104
+ {"help", no_argument, NULL, 'h'},
36
+ .done = false,
105
+ {"trace", required_argument, NULL, 'T'},
106
+ {"version", no_argument, NULL, 'V'},
107
+ {0, 0, 0, 0}
108
+ };
37
+ };
109
+
38
+
110
+ /*
39
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
111
+ * In contrast to the system emulator, options are processed in the order
40
+ qemu_coroutine_enter(co);
112
+ * they are given on the command lines. This means that things must be
41
+ while (!data.done) {
113
+ * defined first before they can be referenced in another option.
42
+ aio_poll(qemu_get_aio_context(), true);
114
+ */
115
+ while ((c = getopt_long(argc, argv, "hT:V", long_options, NULL)) != -1) {
116
+ switch (c) {
117
+ case '?':
118
+ exit(EXIT_FAILURE);
119
+ case 'h':
120
+ help();
121
+ exit(EXIT_SUCCESS);
122
+ case 'T':
123
+ {
124
+ char *trace_file = trace_opt_parse(optarg);
125
+ trace_init_file(trace_file);
126
+ g_free(trace_file);
127
+ break;
128
+ }
129
+ case 'V':
130
+ printf("qemu-storage-daemon version "
131
+ QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n");
132
+ exit(EXIT_SUCCESS);
133
+ default:
134
+ g_assert_not_reached();
135
+ }
136
+ }
137
+ if (optind != argc) {
138
+ error_report("Unexpected argument: %s", argv[optind]);
139
+ exit(EXIT_FAILURE);
140
+ }
43
+ }
141
+}
44
+}
142
+
45
+
143
+int main(int argc, char *argv[])
46
enum drain_type {
47
BDRV_DRAIN_ALL,
48
BDRV_DRAIN,
49
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_subtree(void)
50
test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
51
}
52
53
+static void test_drv_cb_co_drain(void)
144
+{
54
+{
145
+#ifdef CONFIG_POSIX
55
+ call_in_coroutine(test_drv_cb_drain);
146
+ signal(SIGPIPE, SIG_IGN);
56
+}
147
+#endif
148
+
57
+
149
+ error_init(argv[0]);
58
+static void test_drv_cb_co_drain_subtree(void)
150
+ qemu_init_exec_dir(argv[0]);
59
+{
60
+ call_in_coroutine(test_drv_cb_drain_subtree);
61
+}
151
+
62
+
152
+ module_call_init(MODULE_INIT_QOM);
63
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
153
+ module_call_init(MODULE_INIT_TRACE);
64
{
154
+ qemu_add_opts(&qemu_trace_opts);
65
BlockBackend *blk;
155
+ qcrypto_init(&error_fatal);
66
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void)
156
+ bdrv_init();
67
test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
68
}
69
70
+static void test_quiesce_co_drain(void)
71
+{
72
+ call_in_coroutine(test_quiesce_drain);
73
+}
157
+
74
+
158
+ if (!trace_init_backends()) {
75
+static void test_quiesce_co_drain_subtree(void)
159
+ return EXIT_FAILURE;
76
+{
160
+ }
77
+ call_in_coroutine(test_quiesce_drain_subtree);
161
+ qemu_set_log(LOG_TRACE);
78
+}
162
+
79
+
163
+ qemu_init_main_loop(&error_fatal);
80
static void test_nested(void)
164
+ process_options(argc, argv);
81
{
82
BlockBackend *blk;
83
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
84
g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
85
test_drv_cb_drain_subtree);
86
87
+ // XXX bdrv_drain_all() doesn't work in coroutine context
88
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
89
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
90
+ test_drv_cb_co_drain_subtree);
165
+
91
+
166
+ return EXIT_SUCCESS;
92
+
167
+}
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
168
diff --git a/Makefile b/Makefile
94
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
169
index XXXXXXX..XXXXXXX 100644
95
g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
170
--- a/Makefile
96
test_quiesce_drain_subtree);
171
+++ b/Makefile
97
172
@@ -XXX,XX +XXX,XX @@ qemu-img.o: qemu-img-cmds.h
98
+ // XXX bdrv_drain_all() doesn't work in coroutine context
173
qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
99
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
174
qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
100
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
175
qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
101
+ test_quiesce_co_drain_subtree);
176
+qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
102
+
177
103
g_test_add_func("/bdrv-drain/nested", test_nested);
178
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
104
179
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
180
--
106
--
181
2.20.1
107
2.13.6
182
108
183
109
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
Test that drain sections are correctly propagated through the graph.
2
2
3
handle_alloc() reuses preallocated zero clusters. If anything goes
4
wrong during the data write, we do not change their L2 entry, so we
5
must not let qcow2_alloc_cluster_abort() free them.
6
7
Fixes: 8b24cd141549b5b264baeddd4e72902cfb5de23b
8
Cc: qemu-stable@nongnu.org
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Message-Id: <20200225143130.111267-2-mreitz@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
4
---
13
block/qcow2-cluster.c | 2 +-
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
14
1 file changed, 1 insertion(+), 1 deletion(-)
6
1 file changed, 74 insertions(+)
15
7
16
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
17
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2-cluster.c
10
--- a/tests/test-bdrv-drain.c
19
+++ b/block/qcow2-cluster.c
11
+++ b/tests/test-bdrv-drain.c
20
@@ -XXX,XX +XXX,XX @@ err:
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
21
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
13
blk_unref(blk);
22
{
14
}
23
BDRVQcow2State *s = bs->opaque;
15
24
- if (!has_data_file(bs)) {
16
+static void test_multiparent(void)
25
+ if (!has_data_file(bs) && !m->keep_old_clusters) {
17
+{
26
qcow2_free_clusters(bs, m->alloc_offset,
18
+ BlockBackend *blk_a, *blk_b;
27
m->nb_clusters << s->cluster_bits,
19
+ BlockDriverState *bs_a, *bs_b, *backing;
28
QCOW2_DISCARD_NEVER);
20
+ BDRVTestState *a_s, *b_s, *backing_s;
21
+
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
23
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
24
+ &error_abort);
25
+ a_s = bs_a->opaque;
26
+ blk_insert_bs(blk_a, bs_a, &error_abort);
27
+
28
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
29
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
30
+ &error_abort);
31
+ b_s = bs_b->opaque;
32
+ blk_insert_bs(blk_b, bs_b, &error_abort);
33
+
34
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
35
+ backing_s = backing->opaque;
36
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
37
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
38
+
39
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
40
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
41
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(a_s->drain_count, ==, 0);
43
+ g_assert_cmpint(b_s->drain_count, ==, 0);
44
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
45
+
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
47
+
48
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
49
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
50
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
51
+ g_assert_cmpint(a_s->drain_count, ==, 1);
52
+ g_assert_cmpint(b_s->drain_count, ==, 1);
53
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
54
+
55
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
56
+
57
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
58
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
59
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(a_s->drain_count, ==, 2);
61
+ g_assert_cmpint(b_s->drain_count, ==, 2);
62
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
63
+
64
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
65
+
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
69
+ g_assert_cmpint(a_s->drain_count, ==, 1);
70
+ g_assert_cmpint(b_s->drain_count, ==, 1);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
74
+
75
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
76
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
77
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
78
+ g_assert_cmpint(a_s->drain_count, ==, 0);
79
+ g_assert_cmpint(b_s->drain_count, ==, 0);
80
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
81
+
82
+ bdrv_unref(backing);
83
+ bdrv_unref(bs_a);
84
+ bdrv_unref(bs_b);
85
+ blk_unref(blk_a);
86
+ blk_unref(blk_b);
87
+}
88
+
89
90
typedef struct TestBlockJob {
91
BlockJob common;
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
93
test_quiesce_co_drain_subtree);
94
95
g_test_add_func("/bdrv-drain/nested", test_nested);
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
97
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
29
--
100
--
30
2.20.1
101
2.13.6
31
102
32
103
diff view generated by jsdifflib
1
From: Peter Krempa <pkrempa@redhat.com>
1
We need to remember how many of the drain sections in which a node is
2
2
were recursive (i.e. subtree drain rather than node drain), so that they
3
Add another step in the reopen process where driver can execute code
3
can be correctly applied when children are added or removed during the
4
after permission changes are comitted.
4
drained section.
5
5
6
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
6
With this change, it is safe to modify the graph even inside a
7
Message-Id: <adc02cf591c3cb34e98e33518eb1c540a0f27db1.1582893284.git.pkrempa@redhat.com>
7
bdrv_subtree_drained_begin/end() section.
8
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
---
10
include/block/block_int.h | 1 +
11
include/block/block.h | 2 --
11
block.c | 9 +++++++++
12
include/block/block_int.h | 5 +++++
12
2 files changed, 10 insertions(+)
13
block.c | 32 +++++++++++++++++++++++++++++---
13
14
block/io.c | 28 ++++++++++++++++++++++++----
15
4 files changed, 58 insertions(+), 9 deletions(-)
16
17
diff --git a/include/block/block.h b/include/block/block.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/block.h
20
+++ b/include/block/block.h
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
22
/**
23
* Like bdrv_drained_begin, but recursively begins a quiesced section for
24
* exclusive access to all child nodes as well.
25
- *
26
- * Graph changes are not allowed during a subtree drain section.
27
*/
28
void bdrv_subtree_drained_begin(BlockDriverState *bs);
29
14
diff --git a/include/block/block_int.h b/include/block/block_int.h
30
diff --git a/include/block/block_int.h b/include/block/block_int.h
15
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/block_int.h
32
--- a/include/block/block_int.h
17
+++ b/include/block/block_int.h
33
+++ b/include/block/block_int.h
18
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
19
int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state,
35
20
BlockReopenQueue *queue, Error **errp);
36
/* Accessed with atomic ops. */
21
void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state);
37
int quiesce_counter;
22
+ void (*bdrv_reopen_commit_post)(BDRVReopenState *reopen_state);
38
+ int recursive_quiesce_counter;
23
void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state);
39
+
24
void (*bdrv_join_options)(QDict *options, QDict *old_options);
40
unsigned int write_gen; /* Current data generation */
25
41
42
/* Protected by reqs_lock. */
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
44
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
45
BdrvRequestFlags flags);
46
47
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
48
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
49
+
50
int get_tmp_filename(char *filename, int size);
51
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
52
const char *filename);
26
diff --git a/block.c b/block.c
53
diff --git a/block.c b/block.c
27
index XXXXXXX..XXXXXXX 100644
54
index XXXXXXX..XXXXXXX 100644
28
--- a/block.c
55
--- a/block.c
29
+++ b/block.c
56
+++ b/block.c
30
@@ -XXX,XX +XXX,XX @@ cleanup_perm:
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
58
bdrv_drained_end(bs);
59
}
60
61
+static void bdrv_child_cb_attach(BdrvChild *child)
62
+{
63
+ BlockDriverState *bs = child->opaque;
64
+ bdrv_apply_subtree_drain(child, bs);
65
+}
66
+
67
+static void bdrv_child_cb_detach(BdrvChild *child)
68
+{
69
+ BlockDriverState *bs = child->opaque;
70
+ bdrv_unapply_subtree_drain(child, bs);
71
+}
72
+
73
static int bdrv_child_cb_inactivate(BdrvChild *child)
74
{
75
BlockDriverState *bs = child->opaque;
76
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_file = {
77
.inherit_options = bdrv_inherited_options,
78
.drained_begin = bdrv_child_cb_drained_begin,
79
.drained_end = bdrv_child_cb_drained_end,
80
+ .attach = bdrv_child_cb_attach,
81
+ .detach = bdrv_child_cb_detach,
82
.inactivate = bdrv_child_cb_inactivate,
83
};
84
85
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = {
86
.inherit_options = bdrv_inherited_fmt_options,
87
.drained_begin = bdrv_child_cb_drained_begin,
88
.drained_end = bdrv_child_cb_drained_end,
89
+ .attach = bdrv_child_cb_attach,
90
+ .detach = bdrv_child_cb_detach,
91
.inactivate = bdrv_child_cb_inactivate,
92
};
93
94
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_attach(BdrvChild *c)
95
parent->backing_blocker);
96
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
97
parent->backing_blocker);
98
+
99
+ bdrv_child_cb_attach(c);
100
}
101
102
static void bdrv_backing_detach(BdrvChild *c)
103
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_detach(BdrvChild *c)
104
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
105
error_free(parent->backing_blocker);
106
parent->backing_blocker = NULL;
107
+
108
+ bdrv_child_cb_detach(c);
109
}
110
111
/*
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
114
}
115
if (old_bs) {
116
+ /* Detach first so that the recursive drain sections coming from @child
117
+ * are already gone and we only end the drain sections that came from
118
+ * elsewhere. */
119
+ if (child->role->detach) {
120
+ child->role->detach(child);
121
+ }
122
if (old_bs->quiesce_counter && child->role->drained_end) {
123
for (i = 0; i < old_bs->quiesce_counter; i++) {
124
child->role->drained_end(child);
31
}
125
}
32
}
126
}
127
- if (child->role->detach) {
128
- child->role->detach(child);
129
- }
130
QLIST_REMOVE(child, next_parent);
33
}
131
}
34
+
132
35
+ if (ret == 0) {
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
36
+ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
134
}
37
+ BlockDriverState *bs = bs_entry->state.bs;
135
}
38
+
136
39
+ if (bs->drv->bdrv_reopen_commit_post)
137
+ /* Attach only after starting new drained sections, so that recursive
40
+ bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
138
+ * drain sections coming from @child don't get an extra .drained_begin
41
+ }
139
+ * callback. */
140
if (child->role->attach) {
141
child->role->attach(child);
142
}
143
diff --git a/block/io.c b/block/io.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/io.c
146
+++ b/block/io.c
147
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
148
assert(data.done);
149
}
150
151
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
152
- BdrvChild *parent)
153
+void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
154
+ BdrvChild *parent)
155
{
156
BdrvChild *child, *next;
157
158
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
159
bdrv_drain_recurse(bs);
160
161
if (recursive) {
162
+ bs->recursive_quiesce_counter++;
163
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
164
bdrv_do_drained_begin(child->bs, true, child);
165
}
166
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
167
bdrv_do_drained_begin(bs, true, NULL);
168
}
169
170
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
171
- BdrvChild *parent)
172
+void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
173
+ BdrvChild *parent)
174
{
175
BdrvChild *child, *next;
176
int old_quiesce_counter;
177
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
178
}
179
180
if (recursive) {
181
+ bs->recursive_quiesce_counter--;
182
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
183
bdrv_do_drained_end(child->bs, true, child);
184
}
185
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs)
186
bdrv_do_drained_end(bs, true, NULL);
187
}
188
189
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
190
+{
191
+ int i;
192
+
193
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
194
+ bdrv_do_drained_begin(child->bs, true, child);
42
+ }
195
+ }
43
cleanup:
196
+}
44
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
197
+
45
if (ret) {
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
199
+{
200
+ int i;
201
+
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
203
+ bdrv_do_drained_end(child->bs, true, child);
204
+ }
205
+}
206
+
207
/*
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
209
* and suspend block driver's internal I/O until next request arrives.
46
--
210
--
47
2.20.1
211
2.13.6
48
212
49
213
diff view generated by jsdifflib
1
Instead of exiting after processing all command line options, start a
2
main loop and keep processing events until exit is requested with a
3
signal (e.g. SIGINT).
4
5
Now qemu-storage-daemon can be used as an alternative for qemu-nbd that
6
provides a few features that were previously only available from QMP,
7
such as access to options only available with -blockdev and the socket
8
types 'vsock' and 'fd'.
9
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20200224143008.13362-13-kwolf@redhat.com>
12
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
2
---
15
qemu-storage-daemon.c | 13 +++++++++++++
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
16
Makefile.objs | 2 ++
4
1 file changed, 80 insertions(+)
17
2 files changed, 15 insertions(+)
18
5
19
diff --git a/qemu-storage-daemon.c b/qemu-storage-daemon.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
20
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
21
--- a/qemu-storage-daemon.c
8
--- a/tests/test-bdrv-drain.c
22
+++ b/qemu-storage-daemon.c
9
+++ b/tests/test-bdrv-drain.c
23
@@ -XXX,XX +XXX,XX @@
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
24
#include "qemu/option.h"
11
blk_unref(blk_b);
25
#include "qom/object_interfaces.h"
12
}
26
13
27
+#include "sysemu/runstate.h"
14
+static void test_graph_change(void)
28
#include "trace/control.h"
15
+{
29
16
+ BlockBackend *blk_a, *blk_b;
30
+static volatile bool exit_requested = false;
17
+ BlockDriverState *bs_a, *bs_b, *backing;
18
+ BDRVTestState *a_s, *b_s, *backing_s;
31
+
19
+
32
+void qemu_system_killed(int signal, pid_t pid)
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
33
+{
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
34
+ exit_requested = true;
22
+ &error_abort);
23
+ a_s = bs_a->opaque;
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
25
+
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
28
+ &error_abort);
29
+ b_s = bs_b->opaque;
30
+ blk_insert_bs(blk_b, bs_b, &error_abort);
31
+
32
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
33
+ backing_s = backing->opaque;
34
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
35
+
36
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
37
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
38
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
39
+ g_assert_cmpint(a_s->drain_count, ==, 0);
40
+ g_assert_cmpint(b_s->drain_count, ==, 0);
41
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
42
+
43
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
44
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
45
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
47
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
48
+
49
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
50
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
51
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
52
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
53
+ g_assert_cmpint(a_s->drain_count, ==, 5);
54
+ g_assert_cmpint(b_s->drain_count, ==, 5);
55
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
56
+
57
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
58
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
59
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
61
+ g_assert_cmpint(a_s->drain_count, ==, 3);
62
+ g_assert_cmpint(b_s->drain_count, ==, 2);
63
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
64
+
65
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
69
+ g_assert_cmpint(a_s->drain_count, ==, 5);
70
+ g_assert_cmpint(b_s->drain_count, ==, 5);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
74
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
75
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
76
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
77
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
78
+
79
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
80
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
81
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
82
+ g_assert_cmpint(a_s->drain_count, ==, 0);
83
+ g_assert_cmpint(b_s->drain_count, ==, 0);
84
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
85
+
86
+ bdrv_unref(backing);
87
+ bdrv_unref(bs_a);
88
+ bdrv_unref(bs_b);
89
+ blk_unref(blk_a);
90
+ blk_unref(blk_b);
35
+}
91
+}
36
+
92
+
37
static void help(void)
93
38
{
94
typedef struct TestBlockJob {
39
printf(
95
BlockJob common;
40
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
41
97
42
error_init(argv[0]);
98
g_test_add_func("/bdrv-drain/nested", test_nested);
43
qemu_init_exec_dir(argv[0]);
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
44
+ os_setup_signal_handling();
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
45
101
46
module_call_init(MODULE_INIT_QOM);
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
47
module_call_init(MODULE_INIT_TRACE);
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
48
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
49
qemu_init_main_loop(&error_fatal);
50
process_options(argc, argv);
51
52
+ while (!exit_requested) {
53
+ main_loop_wait(false);
54
+ }
55
+
56
return EXIT_SUCCESS;
57
}
58
diff --git a/Makefile.objs b/Makefile.objs
59
index XXXXXXX..XXXXXXX 100644
60
--- a/Makefile.objs
61
+++ b/Makefile.objs
62
@@ -XXX,XX +XXX,XX @@ endif # CONFIG_SOFTMMU or CONFIG_TOOLS
63
64
storage-daemon-obj-y = block/ qom/
65
storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o
66
+storage-daemon-obj-$(CONFIG_WIN32) += os-win32.o
67
+storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o
68
69
######################################################################
70
# Target independent part of system emulation. The long term path is to
71
--
104
--
72
2.20.1
105
2.13.6
73
106
74
107
diff view generated by jsdifflib
1
blockdev.c uses the arch_type constant, so before we can use the file in
1
Since commit bde70715, base is the only node that is reopened in
2
tools (i.e. outside of the system emulator), we need to add a stub for
2
commit_start(). This means that the code, which still involves an
3
it. A new QEMU_ARCH_NONE is introduced for this case.
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
4
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Message-Id: <20200224143008.13362-3-kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
7
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
7
---
10
include/sysemu/arch_init.h | 2 ++
8
block/commit.c | 8 +-------
11
stubs/arch_type.c | 4 ++++
9
1 file changed, 1 insertion(+), 7 deletions(-)
12
stubs/Makefile.objs | 1 +
13
3 files changed, 7 insertions(+)
14
create mode 100644 stubs/arch_type.c
15
10
16
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
11
diff --git a/block/commit.c b/block/commit.c
17
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
18
--- a/include/sysemu/arch_init.h
13
--- a/block/commit.c
19
+++ b/include/sysemu/arch_init.h
14
+++ b/block/commit.c
20
@@ -XXX,XX +XXX,XX @@ enum {
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
21
QEMU_ARCH_NIOS2 = (1 << 17),
16
const char *filter_node_name, Error **errp)
22
QEMU_ARCH_HPPA = (1 << 18),
17
{
23
QEMU_ARCH_RISCV = (1 << 19),
18
CommitBlockJob *s;
24
+
19
- BlockReopenQueue *reopen_queue = NULL;
25
+ QEMU_ARCH_NONE = (1 << 31),
20
int orig_base_flags;
26
};
21
BlockDriverState *iter;
27
22
BlockDriverState *commit_top_bs = NULL;
28
extern const uint32_t arch_type;
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
29
diff --git a/stubs/arch_type.c b/stubs/arch_type.c
24
/* convert base to r/w, if necessary */
30
new file mode 100644
25
orig_base_flags = bdrv_get_flags(base);
31
index XXXXXXX..XXXXXXX
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
32
--- /dev/null
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
33
+++ b/stubs/arch_type.c
28
- orig_base_flags | BDRV_O_RDWR);
34
@@ -XXX,XX +XXX,XX @@
29
- }
35
+#include "qemu/osdep.h"
30
-
36
+#include "sysemu/arch_init.h"
31
- if (reopen_queue) {
37
+
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
38
+const uint32_t arch_type = QEMU_ARCH_NONE;
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
39
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
34
if (local_err != NULL) {
40
index XXXXXXX..XXXXXXX 100644
35
error_propagate(errp, local_err);
41
--- a/stubs/Makefile.objs
36
goto fail;
42
+++ b/stubs/Makefile.objs
43
@@ -XXX,XX +XXX,XX @@
44
+stub-obj-y += arch_type.o
45
stub-obj-y += bdrv-next-monitor-owned.o
46
stub-obj-y += blk-commit-all.o
47
stub-obj-y += blockdev-close-all-bdrv-states.o
48
--
37
--
49
2.20.1
38
2.13.6
50
39
51
40
diff view generated by jsdifflib
1
This patch allows bdrv_reopen() (and therefore the x-blockdev-reopen QMP
1
The bdrv_reopen*() implementation doesn't like it if the graph is
2
command) to attach a node as the new backing file even if the node is in
2
changed between queuing nodes for reopen and actually reopening them
3
a different AioContext than the parent if one of both nodes can be moved
3
(one of the reasons is that queuing can be recursive).
4
to the AioContext of the other node.
4
5
So instead of draining the device only in bdrv_reopen_multiple(),
6
require that callers already drained all affected nodes, and assert this
7
in bdrv_reopen_queue().
5
8
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Tested-by: Peter Krempa <pkrempa@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
8
Message-Id: <20200306141413.30705-3-kwolf@redhat.com>
9
Reviewed-by: Alberto Garcia <berto@igalia.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
11
---
12
block.c | 32 ++++++++++++++++++++++++++------
12
block.c | 23 ++++++++++++++++-------
13
tests/qemu-iotests/245 | 8 +++-----
13
block/replication.c | 6 ++++++
14
2 files changed, 29 insertions(+), 11 deletions(-)
14
qemu-io-cmds.c | 3 +++
15
3 files changed, 25 insertions(+), 7 deletions(-)
15
16
16
diff --git a/block.c b/block.c
17
diff --git a/block.c b/block.c
17
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
19
--- a/block.c
19
+++ b/block.c
20
+++ b/block.c
20
@@ -XXX,XX +XXX,XX @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
21
*shared = cumulative_shared_perms;
22
* returns a pointer to bs_queue, which is either the newly allocated
23
* bs_queue, or the existing bs_queue being used.
24
*
25
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
26
*/
27
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
28
BlockDriverState *bs,
29
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
30
BdrvChild *child;
31
QDict *old_options, *explicit_options;
32
33
+ /* Make sure that the caller remembered to use a drained section. This is
34
+ * important to avoid graph changes between the recursive queuing here and
35
+ * bdrv_reopen_multiple(). */
36
+ assert(bs->quiesce_counter > 0);
37
+
38
if (bs_queue == NULL) {
39
bs_queue = g_new0(BlockReopenQueue, 1);
40
QSIMPLEQ_INIT(bs_queue);
41
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
42
* If all devices prepare successfully, then the changes are committed
43
* to all devices.
44
*
45
+ * All affected nodes must be drained between bdrv_reopen_queue() and
46
+ * bdrv_reopen_multiple().
47
*/
48
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
49
{
50
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
51
52
assert(bs_queue != NULL);
53
54
- aio_context_release(ctx);
55
- bdrv_drain_all_begin();
56
- aio_context_acquire(ctx);
57
-
58
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
59
+ assert(bs_entry->state.bs->quiesce_counter > 0);
60
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
61
error_propagate(errp, local_err);
62
goto cleanup;
63
@@ -XXX,XX +XXX,XX @@ cleanup:
64
}
65
g_free(bs_queue);
66
67
- bdrv_drain_all_end();
68
-
69
return ret;
22
}
70
}
23
71
24
+static bool bdrv_reopen_can_attach(BlockDriverState *parent,
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
25
+ BdrvChild *child,
73
{
26
+ BlockDriverState *new_child,
74
int ret = -1;
27
+ Error **errp)
75
Error *local_err = NULL;
28
+{
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
29
+ AioContext *parent_ctx = bdrv_get_aio_context(parent);
77
+ BlockReopenQueue *queue;
30
+ AioContext *child_ctx = bdrv_get_aio_context(new_child);
78
31
+ GSList *ignore;
79
+ bdrv_subtree_drained_begin(bs);
32
+ bool ret;
33
+
80
+
34
+ ignore = g_slist_prepend(NULL, child);
81
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
35
+ ret = bdrv_can_set_aio_context(new_child, parent_ctx, &ignore, NULL);
82
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
36
+ g_slist_free(ignore);
83
if (local_err != NULL) {
37
+ if (ret) {
84
error_propagate(errp, local_err);
38
+ return ret;
85
}
39
+ }
40
+
86
+
41
+ ignore = g_slist_prepend(NULL, child);
87
+ bdrv_subtree_drained_end(bs);
42
+ ret = bdrv_can_set_aio_context(parent, child_ctx, &ignore, errp);
43
+ g_slist_free(ignore);
44
+ return ret;
45
+}
46
+
88
+
47
/*
89
return ret;
48
* Take a BDRVReopenState and check if the value of 'backing' in the
90
}
49
* reopen_state->options QDict is valid or not.
91
50
@@ -XXX,XX +XXX,XX @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
92
diff --git a/block/replication.c b/block/replication.c
93
index XXXXXXX..XXXXXXX 100644
94
--- a/block/replication.c
95
+++ b/block/replication.c
96
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
97
new_secondary_flags = s->orig_secondary_flags;
51
}
98
}
52
99
53
/*
100
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
54
- * TODO: before removing the x- prefix from x-blockdev-reopen we
101
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
55
- * should move the new backing file into the right AioContext
102
+
56
- * instead of returning an error.
103
if (orig_hidden_flags != new_hidden_flags) {
57
+ * Check AioContext compatibility so that the bdrv_set_backing_hd() call in
104
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
58
+ * bdrv_reopen_commit() won't fail.
105
new_hidden_flags);
59
*/
106
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
60
if (new_backing_bs) {
107
reopen_queue, &local_err);
61
- if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
108
error_propagate(errp, local_err);
62
- error_setg(errp, "Cannot use a new backing file "
63
- "with a different AioContext");
64
+ if (!bdrv_reopen_can_attach(bs, bs->backing, new_backing_bs, errp)) {
65
return -EINVAL;
66
}
67
}
109
}
68
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
110
+
69
index XXXXXXX..XXXXXXX 100755
111
+ bdrv_subtree_drained_end(s->hidden_disk->bs);
70
--- a/tests/qemu-iotests/245
112
+ bdrv_subtree_drained_end(s->secondary_disk->bs);
71
+++ b/tests/qemu-iotests/245
113
}
72
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
114
73
# neither of them can switch to the other AioContext
115
static void backup_job_cleanup(BlockDriverState *bs)
74
def test_iothreads_error(self):
116
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
75
self.run_test_iothreads('iothread0', 'iothread1',
117
index XXXXXXX..XXXXXXX 100644
76
- "Cannot use a new backing file with a different AioContext")
118
--- a/qemu-io-cmds.c
77
+ "Cannot change iothread of active block backend")
119
+++ b/qemu-io-cmds.c
78
120
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
79
def test_iothreads_compatible_users(self):
121
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
80
self.run_test_iothreads('iothread0', 'iothread0')
122
qemu_opts_reset(&reopen_opts);
81
123
82
def test_iothreads_switch_backing(self):
124
+ bdrv_subtree_drained_begin(bs);
83
- self.run_test_iothreads('iothread0', None,
125
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
84
- "Cannot use a new backing file with a different AioContext")
126
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
85
+ self.run_test_iothreads('iothread0', None)
127
+ bdrv_subtree_drained_end(bs);
86
128
+
87
def test_iothreads_switch_overlay(self):
129
if (local_err) {
88
- self.run_test_iothreads(None, 'iothread0',
130
error_report_err(local_err);
89
- "Cannot use a new backing file with a different AioContext")
131
} else {
90
+ self.run_test_iothreads(None, 'iothread0')
91
92
if __name__ == '__main__':
93
iotests.main(supported_fmts=["qcow2"],
94
--
132
--
95
2.20.1
133
2.13.6
96
134
97
135
diff view generated by jsdifflib