]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Blockdev HMP commands | |
3 | * | |
4 | * Authors: | |
5 | * Anthony Liguori <aliguori@us.ibm.com> | |
6 | * | |
7 | * Copyright (c) 2003-2008 Fabrice Bellard | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2. | |
10 | * See the COPYING file in the top-level directory. | |
11 | * Contributions after 2012-01-13 are licensed under the terms of the | |
12 | * GNU GPL, version 2 or (at your option) any later version. | |
13 | * | |
14 | * This file incorporates work covered by the following copyright and | |
15 | * permission notice: | |
16 | * | |
17 | * Copyright (c) 2003-2008 Fabrice Bellard | |
18 | * | |
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
20 | * of this software and associated documentation files (the "Software"), to deal | |
21 | * in the Software without restriction, including without limitation the rights | |
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
23 | * copies of the Software, and to permit persons to whom the Software is | |
24 | * furnished to do so, subject to the following conditions: | |
25 | * | |
26 | * The above copyright notice and this permission notice shall be included in | |
27 | * all copies or substantial portions of the Software. | |
28 | * | |
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
35 | * THE SOFTWARE. | |
36 | */ | |
37 | ||
38 | #include "qemu/osdep.h" | |
39 | #include "hw/boards.h" | |
40 | #include "sysemu/block-backend.h" | |
41 | #include "sysemu/blockdev.h" | |
42 | #include "qapi/qapi-commands-block.h" | |
43 | #include "qapi/qapi-commands-block-export.h" | |
44 | #include "qapi/qmp/qdict.h" | |
45 | #include "qapi/error.h" | |
46 | #include "qapi/qmp/qerror.h" | |
47 | #include "qemu/config-file.h" | |
48 | #include "qemu/option.h" | |
49 | #include "qemu/sockets.h" | |
50 | #include "qemu/cutils.h" | |
51 | #include "qemu/error-report.h" | |
52 | #include "sysemu/sysemu.h" | |
53 | #include "monitor/monitor.h" | |
54 | #include "monitor/hmp.h" | |
55 | #include "block/nbd.h" | |
56 | #include "block/qapi.h" | |
57 | #include "block/block_int.h" | |
58 | #include "block/block-hmp-cmds.h" | |
59 | #include "qemu-io.h" | |
60 | ||
61 | static void hmp_drive_add_node(Monitor *mon, const char *optstr) | |
62 | { | |
63 | QemuOpts *opts; | |
64 | QDict *qdict; | |
65 | Error *local_err = NULL; | |
66 | ||
67 | opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false); | |
68 | if (!opts) { | |
69 | return; | |
70 | } | |
71 | ||
72 | qdict = qemu_opts_to_qdict(opts, NULL); | |
73 | ||
74 | if (!qdict_get_try_str(qdict, "node-name")) { | |
75 | qobject_unref(qdict); | |
76 | error_report("'node-name' needs to be specified"); | |
77 | goto out; | |
78 | } | |
79 | ||
80 | BlockDriverState *bs = bds_tree_init(qdict, &local_err); | |
81 | if (!bs) { | |
82 | error_report_err(local_err); | |
83 | goto out; | |
84 | } | |
85 | ||
86 | bdrv_set_monitor_owned(bs); | |
87 | out: | |
88 | qemu_opts_del(opts); | |
89 | } | |
90 | ||
91 | void hmp_drive_add(Monitor *mon, const QDict *qdict) | |
92 | { | |
93 | Error *err = NULL; | |
94 | DriveInfo *dinfo; | |
95 | QemuOpts *opts; | |
96 | MachineClass *mc; | |
97 | const char *optstr = qdict_get_str(qdict, "opts"); | |
98 | bool node = qdict_get_try_bool(qdict, "node", false); | |
99 | ||
100 | if (node) { | |
101 | hmp_drive_add_node(mon, optstr); | |
102 | return; | |
103 | } | |
104 | ||
105 | opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false); | |
106 | if (!opts) | |
107 | return; | |
108 | ||
109 | mc = MACHINE_GET_CLASS(current_machine); | |
110 | dinfo = drive_new(opts, mc->block_default_type, &err); | |
111 | if (err) { | |
112 | error_report_err(err); | |
113 | qemu_opts_del(opts); | |
114 | goto err; | |
115 | } | |
116 | ||
117 | if (!dinfo) { | |
118 | return; | |
119 | } | |
120 | ||
121 | switch (dinfo->type) { | |
122 | case IF_NONE: | |
123 | monitor_printf(mon, "OK\n"); | |
124 | break; | |
125 | default: | |
126 | monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); | |
127 | goto err; | |
128 | } | |
129 | return; | |
130 | ||
131 | err: | |
132 | if (dinfo) { | |
133 | BlockBackend *blk = blk_by_legacy_dinfo(dinfo); | |
134 | monitor_remove_blk(blk); | |
135 | blk_unref(blk); | |
136 | } | |
137 | } | |
138 | ||
139 | void hmp_drive_del(Monitor *mon, const QDict *qdict) | |
140 | { | |
141 | const char *id = qdict_get_str(qdict, "id"); | |
142 | BlockBackend *blk; | |
143 | BlockDriverState *bs; | |
144 | AioContext *aio_context; | |
145 | Error *local_err = NULL; | |
146 | ||
147 | bs = bdrv_find_node(id); | |
148 | if (bs) { | |
149 | qmp_blockdev_del(id, &local_err); | |
150 | if (local_err) { | |
151 | error_report_err(local_err); | |
152 | } | |
153 | return; | |
154 | } | |
155 | ||
156 | blk = blk_by_name(id); | |
157 | if (!blk) { | |
158 | error_report("Device '%s' not found", id); | |
159 | return; | |
160 | } | |
161 | ||
162 | if (!blk_legacy_dinfo(blk)) { | |
163 | error_report("Deleting device added with blockdev-add" | |
164 | " is not supported"); | |
165 | return; | |
166 | } | |
167 | ||
168 | aio_context = blk_get_aio_context(blk); | |
169 | aio_context_acquire(aio_context); | |
170 | ||
171 | bs = blk_bs(blk); | |
172 | if (bs) { | |
173 | if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { | |
174 | error_report_err(local_err); | |
175 | aio_context_release(aio_context); | |
176 | return; | |
177 | } | |
178 | ||
179 | blk_remove_bs(blk); | |
180 | } | |
181 | ||
182 | /* Make the BlockBackend and the attached BlockDriverState anonymous */ | |
183 | monitor_remove_blk(blk); | |
184 | ||
185 | /* | |
186 | * If this BlockBackend has a device attached to it, its refcount will be | |
187 | * decremented when the device is removed; otherwise we have to do so here. | |
188 | */ | |
189 | if (blk_get_attached_dev(blk)) { | |
190 | /* Further I/O must not pause the guest */ | |
191 | blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, | |
192 | BLOCKDEV_ON_ERROR_REPORT); | |
193 | } else { | |
194 | blk_unref(blk); | |
195 | } | |
196 | ||
197 | aio_context_release(aio_context); | |
198 | } | |
199 | ||
200 | void hmp_commit(Monitor *mon, const QDict *qdict) | |
201 | { | |
202 | const char *device = qdict_get_str(qdict, "device"); | |
203 | BlockBackend *blk; | |
204 | int ret; | |
205 | ||
206 | if (!strcmp(device, "all")) { | |
207 | ret = blk_commit_all(); | |
208 | } else { | |
209 | BlockDriverState *bs; | |
210 | AioContext *aio_context; | |
211 | ||
212 | blk = blk_by_name(device); | |
213 | if (!blk) { | |
214 | error_report("Device '%s' not found", device); | |
215 | return; | |
216 | } | |
217 | ||
218 | bs = bdrv_skip_implicit_filters(blk_bs(blk)); | |
219 | aio_context = bdrv_get_aio_context(bs); | |
220 | aio_context_acquire(aio_context); | |
221 | ||
222 | if (!blk_is_available(blk)) { | |
223 | error_report("Device '%s' has no medium", device); | |
224 | aio_context_release(aio_context); | |
225 | return; | |
226 | } | |
227 | ||
228 | ret = bdrv_commit(bs); | |
229 | ||
230 | aio_context_release(aio_context); | |
231 | } | |
232 | if (ret < 0) { | |
233 | error_report("'commit' error for '%s': %s", device, strerror(-ret)); | |
234 | } | |
235 | } | |
236 | ||
237 | void hmp_drive_mirror(Monitor *mon, const QDict *qdict) | |
238 | { | |
239 | const char *filename = qdict_get_str(qdict, "target"); | |
240 | const char *format = qdict_get_try_str(qdict, "format"); | |
241 | bool reuse = qdict_get_try_bool(qdict, "reuse", false); | |
242 | bool full = qdict_get_try_bool(qdict, "full", false); | |
243 | Error *err = NULL; | |
244 | DriveMirror mirror = { | |
245 | .device = (char *)qdict_get_str(qdict, "device"), | |
246 | .target = (char *)filename, | |
247 | .format = (char *)format, | |
248 | .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, | |
249 | .has_mode = true, | |
250 | .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, | |
251 | .unmap = true, | |
252 | }; | |
253 | ||
254 | if (!filename) { | |
255 | error_setg(&err, QERR_MISSING_PARAMETER, "target"); | |
256 | goto end; | |
257 | } | |
258 | qmp_drive_mirror(&mirror, &err); | |
259 | end: | |
260 | hmp_handle_error(mon, err); | |
261 | } | |
262 | ||
263 | void hmp_drive_backup(Monitor *mon, const QDict *qdict) | |
264 | { | |
265 | const char *device = qdict_get_str(qdict, "device"); | |
266 | const char *filename = qdict_get_str(qdict, "target"); | |
267 | const char *format = qdict_get_try_str(qdict, "format"); | |
268 | bool reuse = qdict_get_try_bool(qdict, "reuse", false); | |
269 | bool full = qdict_get_try_bool(qdict, "full", false); | |
270 | bool compress = qdict_get_try_bool(qdict, "compress", false); | |
271 | Error *err = NULL; | |
272 | DriveBackup backup = { | |
273 | .device = (char *)device, | |
274 | .target = (char *)filename, | |
275 | .format = (char *)format, | |
276 | .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, | |
277 | .has_mode = true, | |
278 | .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, | |
279 | .has_compress = !!compress, | |
280 | .compress = compress, | |
281 | }; | |
282 | ||
283 | if (!filename) { | |
284 | error_setg(&err, QERR_MISSING_PARAMETER, "target"); | |
285 | goto end; | |
286 | } | |
287 | ||
288 | qmp_drive_backup(&backup, &err); | |
289 | end: | |
290 | hmp_handle_error(mon, err); | |
291 | } | |
292 | ||
293 | void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) | |
294 | { | |
295 | Error *error = NULL; | |
296 | const char *device = qdict_get_str(qdict, "device"); | |
297 | int64_t value = qdict_get_int(qdict, "speed"); | |
298 | ||
299 | qmp_block_job_set_speed(device, value, &error); | |
300 | ||
301 | hmp_handle_error(mon, error); | |
302 | } | |
303 | ||
304 | void hmp_block_job_cancel(Monitor *mon, const QDict *qdict) | |
305 | { | |
306 | Error *error = NULL; | |
307 | const char *device = qdict_get_str(qdict, "device"); | |
308 | bool force = qdict_get_try_bool(qdict, "force", false); | |
309 | ||
310 | qmp_block_job_cancel(device, true, force, &error); | |
311 | ||
312 | hmp_handle_error(mon, error); | |
313 | } | |
314 | ||
315 | void hmp_block_job_pause(Monitor *mon, const QDict *qdict) | |
316 | { | |
317 | Error *error = NULL; | |
318 | const char *device = qdict_get_str(qdict, "device"); | |
319 | ||
320 | qmp_block_job_pause(device, &error); | |
321 | ||
322 | hmp_handle_error(mon, error); | |
323 | } | |
324 | ||
325 | void hmp_block_job_resume(Monitor *mon, const QDict *qdict) | |
326 | { | |
327 | Error *error = NULL; | |
328 | const char *device = qdict_get_str(qdict, "device"); | |
329 | ||
330 | qmp_block_job_resume(device, &error); | |
331 | ||
332 | hmp_handle_error(mon, error); | |
333 | } | |
334 | ||
335 | void hmp_block_job_complete(Monitor *mon, const QDict *qdict) | |
336 | { | |
337 | Error *error = NULL; | |
338 | const char *device = qdict_get_str(qdict, "device"); | |
339 | ||
340 | qmp_block_job_complete(device, &error); | |
341 | ||
342 | hmp_handle_error(mon, error); | |
343 | } | |
344 | ||
345 | void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) | |
346 | { | |
347 | const char *device = qdict_get_str(qdict, "device"); | |
348 | const char *filename = qdict_get_try_str(qdict, "snapshot-file"); | |
349 | const char *format = qdict_get_try_str(qdict, "format"); | |
350 | bool reuse = qdict_get_try_bool(qdict, "reuse", false); | |
351 | enum NewImageMode mode; | |
352 | Error *err = NULL; | |
353 | ||
354 | if (!filename) { | |
355 | /* | |
356 | * In the future, if 'snapshot-file' is not specified, the snapshot | |
357 | * will be taken internally. Today it's actually required. | |
358 | */ | |
359 | error_setg(&err, QERR_MISSING_PARAMETER, "snapshot-file"); | |
360 | goto end; | |
361 | } | |
362 | ||
363 | mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS; | |
364 | qmp_blockdev_snapshot_sync(device, NULL, filename, NULL, format, | |
365 | true, mode, &err); | |
366 | end: | |
367 | hmp_handle_error(mon, err); | |
368 | } | |
369 | ||
370 | void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict) | |
371 | { | |
372 | const char *device = qdict_get_str(qdict, "device"); | |
373 | const char *name = qdict_get_str(qdict, "name"); | |
374 | Error *err = NULL; | |
375 | ||
376 | qmp_blockdev_snapshot_internal_sync(device, name, &err); | |
377 | hmp_handle_error(mon, err); | |
378 | } | |
379 | ||
380 | void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict) | |
381 | { | |
382 | const char *device = qdict_get_str(qdict, "device"); | |
383 | const char *name = qdict_get_str(qdict, "name"); | |
384 | const char *id = qdict_get_try_str(qdict, "id"); | |
385 | Error *err = NULL; | |
386 | ||
387 | qmp_blockdev_snapshot_delete_internal_sync(device, id, name, &err); | |
388 | hmp_handle_error(mon, err); | |
389 | } | |
390 | ||
391 | void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) | |
392 | { | |
393 | const char *uri = qdict_get_str(qdict, "uri"); | |
394 | bool writable = qdict_get_try_bool(qdict, "writable", false); | |
395 | bool all = qdict_get_try_bool(qdict, "all", false); | |
396 | Error *local_err = NULL; | |
397 | BlockInfoList *block_list, *info; | |
398 | SocketAddress *addr; | |
399 | NbdServerAddOptions export; | |
400 | ||
401 | if (writable && !all) { | |
402 | error_setg(&local_err, "-w only valid together with -a"); | |
403 | goto exit; | |
404 | } | |
405 | ||
406 | /* First check if the address is valid and start the server. */ | |
407 | addr = socket_parse(uri, &local_err); | |
408 | if (local_err != NULL) { | |
409 | goto exit; | |
410 | } | |
411 | ||
412 | nbd_server_start(addr, NULL, NULL, 0, &local_err); | |
413 | qapi_free_SocketAddress(addr); | |
414 | if (local_err != NULL) { | |
415 | goto exit; | |
416 | } | |
417 | ||
418 | if (!all) { | |
419 | return; | |
420 | } | |
421 | ||
422 | /* Then try adding all block devices. If one fails, close all and | |
423 | * exit. | |
424 | */ | |
425 | block_list = qmp_query_block(NULL); | |
426 | ||
427 | for (info = block_list; info; info = info->next) { | |
428 | if (!info->value->inserted) { | |
429 | continue; | |
430 | } | |
431 | ||
432 | export = (NbdServerAddOptions) { | |
433 | .device = info->value->device, | |
434 | .has_writable = true, | |
435 | .writable = writable, | |
436 | }; | |
437 | ||
438 | qmp_nbd_server_add(&export, &local_err); | |
439 | ||
440 | if (local_err != NULL) { | |
441 | qmp_nbd_server_stop(NULL); | |
442 | break; | |
443 | } | |
444 | } | |
445 | ||
446 | qapi_free_BlockInfoList(block_list); | |
447 | ||
448 | exit: | |
449 | hmp_handle_error(mon, local_err); | |
450 | } | |
451 | ||
452 | void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) | |
453 | { | |
454 | const char *device = qdict_get_str(qdict, "device"); | |
455 | const char *name = qdict_get_try_str(qdict, "name"); | |
456 | bool writable = qdict_get_try_bool(qdict, "writable", false); | |
457 | Error *local_err = NULL; | |
458 | ||
459 | NbdServerAddOptions export = { | |
460 | .device = (char *) device, | |
461 | .name = (char *) name, | |
462 | .has_writable = true, | |
463 | .writable = writable, | |
464 | }; | |
465 | ||
466 | qmp_nbd_server_add(&export, &local_err); | |
467 | hmp_handle_error(mon, local_err); | |
468 | } | |
469 | ||
470 | void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict) | |
471 | { | |
472 | const char *name = qdict_get_str(qdict, "name"); | |
473 | bool force = qdict_get_try_bool(qdict, "force", false); | |
474 | Error *err = NULL; | |
475 | ||
476 | /* Rely on BLOCK_EXPORT_REMOVE_MODE_SAFE being the default */ | |
477 | qmp_nbd_server_remove(name, force, BLOCK_EXPORT_REMOVE_MODE_HARD, &err); | |
478 | hmp_handle_error(mon, err); | |
479 | } | |
480 | ||
481 | void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) | |
482 | { | |
483 | Error *err = NULL; | |
484 | ||
485 | qmp_nbd_server_stop(&err); | |
486 | hmp_handle_error(mon, err); | |
487 | } | |
488 | ||
489 | void coroutine_fn hmp_block_resize(Monitor *mon, const QDict *qdict) | |
490 | { | |
491 | const char *device = qdict_get_str(qdict, "device"); | |
492 | int64_t size = qdict_get_int(qdict, "size"); | |
493 | Error *err = NULL; | |
494 | ||
495 | qmp_block_resize(device, NULL, size, &err); | |
496 | hmp_handle_error(mon, err); | |
497 | } | |
498 | ||
499 | void hmp_block_stream(Monitor *mon, const QDict *qdict) | |
500 | { | |
501 | Error *error = NULL; | |
502 | const char *device = qdict_get_str(qdict, "device"); | |
503 | const char *base = qdict_get_try_str(qdict, "base"); | |
504 | int64_t speed = qdict_get_try_int(qdict, "speed", 0); | |
505 | ||
506 | qmp_block_stream(device, device, base, NULL, NULL, NULL, | |
507 | qdict_haskey(qdict, "speed"), speed, | |
508 | true, BLOCKDEV_ON_ERROR_REPORT, NULL, | |
509 | false, false, false, false, &error); | |
510 | ||
511 | hmp_handle_error(mon, error); | |
512 | } | |
513 | ||
514 | void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) | |
515 | { | |
516 | Error *err = NULL; | |
517 | char *device = (char *) qdict_get_str(qdict, "device"); | |
518 | BlockIOThrottle throttle = { | |
519 | .bps = qdict_get_int(qdict, "bps"), | |
520 | .bps_rd = qdict_get_int(qdict, "bps_rd"), | |
521 | .bps_wr = qdict_get_int(qdict, "bps_wr"), | |
522 | .iops = qdict_get_int(qdict, "iops"), | |
523 | .iops_rd = qdict_get_int(qdict, "iops_rd"), | |
524 | .iops_wr = qdict_get_int(qdict, "iops_wr"), | |
525 | }; | |
526 | ||
527 | /* | |
528 | * qmp_block_set_io_throttle has separate parameters for the | |
529 | * (deprecated) block device name and the qdev ID but the HMP | |
530 | * version has only one, so we must decide which one to pass. | |
531 | */ | |
532 | if (blk_by_name(device)) { | |
533 | throttle.device = device; | |
534 | } else { | |
535 | throttle.id = device; | |
536 | } | |
537 | ||
538 | qmp_block_set_io_throttle(&throttle, &err); | |
539 | hmp_handle_error(mon, err); | |
540 | } | |
541 | ||
542 | void hmp_eject(Monitor *mon, const QDict *qdict) | |
543 | { | |
544 | bool force = qdict_get_try_bool(qdict, "force", false); | |
545 | const char *device = qdict_get_str(qdict, "device"); | |
546 | Error *err = NULL; | |
547 | ||
548 | qmp_eject(device, NULL, true, force, &err); | |
549 | hmp_handle_error(mon, err); | |
550 | } | |
551 | ||
552 | void hmp_qemu_io(Monitor *mon, const QDict *qdict) | |
553 | { | |
554 | BlockBackend *blk = NULL; | |
555 | BlockDriverState *bs = NULL; | |
556 | BlockBackend *local_blk = NULL; | |
557 | AioContext *ctx = NULL; | |
558 | bool qdev = qdict_get_try_bool(qdict, "qdev", false); | |
559 | const char *device = qdict_get_str(qdict, "device"); | |
560 | const char *command = qdict_get_str(qdict, "command"); | |
561 | Error *err = NULL; | |
562 | int ret; | |
563 | ||
564 | if (qdev) { | |
565 | blk = blk_by_qdev_id(device, &err); | |
566 | if (!blk) { | |
567 | goto fail; | |
568 | } | |
569 | } else { | |
570 | blk = blk_by_name(device); | |
571 | if (!blk) { | |
572 | bs = bdrv_lookup_bs(NULL, device, &err); | |
573 | if (!bs) { | |
574 | goto fail; | |
575 | } | |
576 | } | |
577 | } | |
578 | ||
579 | ctx = blk ? blk_get_aio_context(blk) : bdrv_get_aio_context(bs); | |
580 | aio_context_acquire(ctx); | |
581 | ||
582 | if (bs) { | |
583 | blk = local_blk = blk_new(bdrv_get_aio_context(bs), 0, BLK_PERM_ALL); | |
584 | ret = blk_insert_bs(blk, bs, &err); | |
585 | if (ret < 0) { | |
586 | goto fail; | |
587 | } | |
588 | } | |
589 | ||
590 | /* | |
591 | * Notably absent: Proper permission management. This is sad, but it seems | |
592 | * almost impossible to achieve without changing the semantics and thereby | |
593 | * limiting the use cases of the qemu-io HMP command. | |
594 | * | |
595 | * In an ideal world we would unconditionally create a new BlockBackend for | |
596 | * qemuio_command(), but we have commands like 'reopen' and want them to | |
597 | * take effect on the exact BlockBackend whose name the user passed instead | |
598 | * of just on a temporary copy of it. | |
599 | * | |
600 | * Another problem is that deleting the temporary BlockBackend involves | |
601 | * draining all requests on it first, but some qemu-iotests cases want to | |
602 | * issue multiple aio_read/write requests and expect them to complete in | |
603 | * the background while the monitor has already returned. | |
604 | * | |
605 | * This is also what prevents us from saving the original permissions and | |
606 | * restoring them later: We can't revoke permissions until all requests | |
607 | * have completed, and we don't know when that is nor can we really let | |
608 | * anything else run before we have revoken them to avoid race conditions. | |
609 | * | |
610 | * What happens now is that command() in qemu-io-cmds.c can extend the | |
611 | * permissions if necessary for the qemu-io command. And they simply stay | |
612 | * extended, possibly resulting in a read-only guest device keeping write | |
613 | * permissions. Ugly, but it appears to be the lesser evil. | |
614 | */ | |
615 | qemuio_command(blk, command); | |
616 | ||
617 | fail: | |
618 | blk_unref(local_blk); | |
619 | ||
620 | if (ctx) { | |
621 | aio_context_release(ctx); | |
622 | } | |
623 | ||
624 | hmp_handle_error(mon, err); | |
625 | } | |
626 | ||
627 | static void print_block_info(Monitor *mon, BlockInfo *info, | |
628 | BlockDeviceInfo *inserted, bool verbose) | |
629 | { | |
630 | ImageInfo *image_info; | |
631 | ||
632 | assert(!info || !info->inserted || info->inserted == inserted); | |
633 | ||
634 | if (info && *info->device) { | |
635 | monitor_puts(mon, info->device); | |
636 | if (inserted && inserted->node_name) { | |
637 | monitor_printf(mon, " (%s)", inserted->node_name); | |
638 | } | |
639 | } else { | |
640 | assert(info || inserted); | |
641 | monitor_puts(mon, | |
642 | inserted && inserted->node_name ? inserted->node_name | |
643 | : info && info->qdev ? info->qdev | |
644 | : "<anonymous>"); | |
645 | } | |
646 | ||
647 | if (inserted) { | |
648 | monitor_printf(mon, ": %s (%s%s%s)\n", | |
649 | inserted->file, | |
650 | inserted->drv, | |
651 | inserted->ro ? ", read-only" : "", | |
652 | inserted->encrypted ? ", encrypted" : ""); | |
653 | } else { | |
654 | monitor_printf(mon, ": [not inserted]\n"); | |
655 | } | |
656 | ||
657 | if (info) { | |
658 | if (info->qdev) { | |
659 | monitor_printf(mon, " Attached to: %s\n", info->qdev); | |
660 | } | |
661 | if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) { | |
662 | monitor_printf(mon, " I/O status: %s\n", | |
663 | BlockDeviceIoStatus_str(info->io_status)); | |
664 | } | |
665 | ||
666 | if (info->removable) { | |
667 | monitor_printf(mon, " Removable device: %slocked, tray %s\n", | |
668 | info->locked ? "" : "not ", | |
669 | info->tray_open ? "open" : "closed"); | |
670 | } | |
671 | } | |
672 | ||
673 | ||
674 | if (!inserted) { | |
675 | return; | |
676 | } | |
677 | ||
678 | monitor_printf(mon, " Cache mode: %s%s%s\n", | |
679 | inserted->cache->writeback ? "writeback" : "writethrough", | |
680 | inserted->cache->direct ? ", direct" : "", | |
681 | inserted->cache->no_flush ? ", ignore flushes" : ""); | |
682 | ||
683 | if (inserted->backing_file) { | |
684 | monitor_printf(mon, | |
685 | " Backing file: %s " | |
686 | "(chain depth: %" PRId64 ")\n", | |
687 | inserted->backing_file, | |
688 | inserted->backing_file_depth); | |
689 | } | |
690 | ||
691 | if (inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) { | |
692 | monitor_printf(mon, " Detect zeroes: %s\n", | |
693 | BlockdevDetectZeroesOptions_str(inserted->detect_zeroes)); | |
694 | } | |
695 | ||
696 | if (inserted->bps || inserted->bps_rd || inserted->bps_wr || | |
697 | inserted->iops || inserted->iops_rd || inserted->iops_wr) | |
698 | { | |
699 | monitor_printf(mon, " I/O throttling: bps=%" PRId64 | |
700 | " bps_rd=%" PRId64 " bps_wr=%" PRId64 | |
701 | " bps_max=%" PRId64 | |
702 | " bps_rd_max=%" PRId64 | |
703 | " bps_wr_max=%" PRId64 | |
704 | " iops=%" PRId64 " iops_rd=%" PRId64 | |
705 | " iops_wr=%" PRId64 | |
706 | " iops_max=%" PRId64 | |
707 | " iops_rd_max=%" PRId64 | |
708 | " iops_wr_max=%" PRId64 | |
709 | " iops_size=%" PRId64 | |
710 | " group=%s\n", | |
711 | inserted->bps, | |
712 | inserted->bps_rd, | |
713 | inserted->bps_wr, | |
714 | inserted->bps_max, | |
715 | inserted->bps_rd_max, | |
716 | inserted->bps_wr_max, | |
717 | inserted->iops, | |
718 | inserted->iops_rd, | |
719 | inserted->iops_wr, | |
720 | inserted->iops_max, | |
721 | inserted->iops_rd_max, | |
722 | inserted->iops_wr_max, | |
723 | inserted->iops_size, | |
724 | inserted->group); | |
725 | } | |
726 | ||
727 | if (verbose) { | |
728 | monitor_printf(mon, "\nImages:\n"); | |
729 | image_info = inserted->image; | |
730 | while (1) { | |
731 | bdrv_node_info_dump(qapi_ImageInfo_base(image_info), 0, false); | |
732 | if (image_info->backing_image) { | |
733 | image_info = image_info->backing_image; | |
734 | } else { | |
735 | break; | |
736 | } | |
737 | } | |
738 | } | |
739 | } | |
740 | ||
741 | void hmp_info_block(Monitor *mon, const QDict *qdict) | |
742 | { | |
743 | BlockInfoList *block_list, *info; | |
744 | BlockDeviceInfoList *blockdev_list, *blockdev; | |
745 | const char *device = qdict_get_try_str(qdict, "device"); | |
746 | bool verbose = qdict_get_try_bool(qdict, "verbose", false); | |
747 | bool nodes = qdict_get_try_bool(qdict, "nodes", false); | |
748 | bool printed = false; | |
749 | ||
750 | /* Print BlockBackend information */ | |
751 | if (!nodes) { | |
752 | block_list = qmp_query_block(NULL); | |
753 | } else { | |
754 | block_list = NULL; | |
755 | } | |
756 | ||
757 | for (info = block_list; info; info = info->next) { | |
758 | if (device && strcmp(device, info->value->device)) { | |
759 | continue; | |
760 | } | |
761 | ||
762 | if (info != block_list) { | |
763 | monitor_printf(mon, "\n"); | |
764 | } | |
765 | ||
766 | print_block_info(mon, info->value, info->value->inserted, | |
767 | verbose); | |
768 | printed = true; | |
769 | } | |
770 | ||
771 | qapi_free_BlockInfoList(block_list); | |
772 | ||
773 | if ((!device && !nodes) || printed) { | |
774 | return; | |
775 | } | |
776 | ||
777 | /* Print node information */ | |
778 | blockdev_list = qmp_query_named_block_nodes(false, false, NULL); | |
779 | for (blockdev = blockdev_list; blockdev; blockdev = blockdev->next) { | |
780 | assert(blockdev->value->node_name); | |
781 | if (device && strcmp(device, blockdev->value->node_name)) { | |
782 | continue; | |
783 | } | |
784 | ||
785 | if (blockdev != blockdev_list) { | |
786 | monitor_printf(mon, "\n"); | |
787 | } | |
788 | ||
789 | print_block_info(mon, NULL, blockdev->value, verbose); | |
790 | } | |
791 | qapi_free_BlockDeviceInfoList(blockdev_list); | |
792 | } | |
793 | ||
794 | void hmp_info_blockstats(Monitor *mon, const QDict *qdict) | |
795 | { | |
796 | BlockStatsList *stats_list, *stats; | |
797 | ||
798 | stats_list = qmp_query_blockstats(false, false, NULL); | |
799 | ||
800 | for (stats = stats_list; stats; stats = stats->next) { | |
801 | if (!stats->value->device) { | |
802 | continue; | |
803 | } | |
804 | ||
805 | monitor_printf(mon, "%s:", stats->value->device); | |
806 | monitor_printf(mon, " rd_bytes=%" PRId64 | |
807 | " wr_bytes=%" PRId64 | |
808 | " rd_operations=%" PRId64 | |
809 | " wr_operations=%" PRId64 | |
810 | " flush_operations=%" PRId64 | |
811 | " wr_total_time_ns=%" PRId64 | |
812 | " rd_total_time_ns=%" PRId64 | |
813 | " flush_total_time_ns=%" PRId64 | |
814 | " rd_merged=%" PRId64 | |
815 | " wr_merged=%" PRId64 | |
816 | " idle_time_ns=%" PRId64 | |
817 | "\n", | |
818 | stats->value->stats->rd_bytes, | |
819 | stats->value->stats->wr_bytes, | |
820 | stats->value->stats->rd_operations, | |
821 | stats->value->stats->wr_operations, | |
822 | stats->value->stats->flush_operations, | |
823 | stats->value->stats->wr_total_time_ns, | |
824 | stats->value->stats->rd_total_time_ns, | |
825 | stats->value->stats->flush_total_time_ns, | |
826 | stats->value->stats->rd_merged, | |
827 | stats->value->stats->wr_merged, | |
828 | stats->value->stats->idle_time_ns); | |
829 | } | |
830 | ||
831 | qapi_free_BlockStatsList(stats_list); | |
832 | } | |
833 | ||
834 | void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) | |
835 | { | |
836 | BlockJobInfoList *list; | |
837 | ||
838 | list = qmp_query_block_jobs(&error_abort); | |
839 | ||
840 | if (!list) { | |
841 | monitor_printf(mon, "No active jobs\n"); | |
842 | return; | |
843 | } | |
844 | ||
845 | while (list) { | |
846 | if (strcmp(list->value->type, "stream") == 0) { | |
847 | monitor_printf(mon, "Streaming device %s: Completed %" PRId64 | |
848 | " of %" PRId64 " bytes, speed limit %" PRId64 | |
849 | " bytes/s\n", | |
850 | list->value->device, | |
851 | list->value->offset, | |
852 | list->value->len, | |
853 | list->value->speed); | |
854 | } else { | |
855 | monitor_printf(mon, "Type %s, device %s: Completed %" PRId64 | |
856 | " of %" PRId64 " bytes, speed limit %" PRId64 | |
857 | " bytes/s\n", | |
858 | list->value->type, | |
859 | list->value->device, | |
860 | list->value->offset, | |
861 | list->value->len, | |
862 | list->value->speed); | |
863 | } | |
864 | list = list->next; | |
865 | } | |
866 | ||
867 | qapi_free_BlockJobInfoList(list); | |
868 | } | |
869 | ||
870 | void hmp_info_snapshots(Monitor *mon, const QDict *qdict) | |
871 | { | |
872 | BlockDriverState *bs, *bs1; | |
873 | BdrvNextIterator it1; | |
874 | QEMUSnapshotInfo *sn_tab, *sn; | |
875 | bool no_snapshot = true; | |
876 | int nb_sns, i; | |
877 | int total; | |
878 | int *global_snapshots; | |
879 | AioContext *aio_context; | |
880 | ||
881 | typedef struct SnapshotEntry { | |
882 | QEMUSnapshotInfo sn; | |
883 | QTAILQ_ENTRY(SnapshotEntry) next; | |
884 | } SnapshotEntry; | |
885 | ||
886 | typedef struct ImageEntry { | |
887 | const char *imagename; | |
888 | QTAILQ_ENTRY(ImageEntry) next; | |
889 | QTAILQ_HEAD(, SnapshotEntry) snapshots; | |
890 | } ImageEntry; | |
891 | ||
892 | QTAILQ_HEAD(, ImageEntry) image_list = | |
893 | QTAILQ_HEAD_INITIALIZER(image_list); | |
894 | ||
895 | ImageEntry *image_entry, *next_ie; | |
896 | SnapshotEntry *snapshot_entry; | |
897 | Error *err = NULL; | |
898 | ||
899 | GRAPH_RDLOCK_GUARD_MAINLOOP(); | |
900 | ||
901 | bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, &err); | |
902 | if (!bs) { | |
903 | error_report_err(err); | |
904 | return; | |
905 | } | |
906 | aio_context = bdrv_get_aio_context(bs); | |
907 | ||
908 | aio_context_acquire(aio_context); | |
909 | nb_sns = bdrv_snapshot_list(bs, &sn_tab); | |
910 | aio_context_release(aio_context); | |
911 | ||
912 | if (nb_sns < 0) { | |
913 | monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); | |
914 | return; | |
915 | } | |
916 | ||
917 | for (bs1 = bdrv_first(&it1); bs1; bs1 = bdrv_next(&it1)) { | |
918 | int bs1_nb_sns = 0; | |
919 | ImageEntry *ie; | |
920 | SnapshotEntry *se; | |
921 | AioContext *ctx = bdrv_get_aio_context(bs1); | |
922 | ||
923 | aio_context_acquire(ctx); | |
924 | if (bdrv_can_snapshot(bs1)) { | |
925 | sn = NULL; | |
926 | bs1_nb_sns = bdrv_snapshot_list(bs1, &sn); | |
927 | if (bs1_nb_sns > 0) { | |
928 | no_snapshot = false; | |
929 | ie = g_new0(ImageEntry, 1); | |
930 | ie->imagename = bdrv_get_device_name(bs1); | |
931 | QTAILQ_INIT(&ie->snapshots); | |
932 | QTAILQ_INSERT_TAIL(&image_list, ie, next); | |
933 | for (i = 0; i < bs1_nb_sns; i++) { | |
934 | se = g_new0(SnapshotEntry, 1); | |
935 | se->sn = sn[i]; | |
936 | QTAILQ_INSERT_TAIL(&ie->snapshots, se, next); | |
937 | } | |
938 | } | |
939 | g_free(sn); | |
940 | } | |
941 | aio_context_release(ctx); | |
942 | } | |
943 | ||
944 | if (no_snapshot) { | |
945 | monitor_printf(mon, "There is no snapshot available.\n"); | |
946 | return; | |
947 | } | |
948 | ||
949 | global_snapshots = g_new0(int, nb_sns); | |
950 | total = 0; | |
951 | for (i = 0; i < nb_sns; i++) { | |
952 | SnapshotEntry *next_sn; | |
953 | if (bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL) == 1) { | |
954 | global_snapshots[total] = i; | |
955 | total++; | |
956 | QTAILQ_FOREACH(image_entry, &image_list, next) { | |
957 | QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, | |
958 | next, next_sn) { | |
959 | if (!strcmp(sn_tab[i].name, snapshot_entry->sn.name)) { | |
960 | QTAILQ_REMOVE(&image_entry->snapshots, snapshot_entry, | |
961 | next); | |
962 | g_free(snapshot_entry); | |
963 | } | |
964 | } | |
965 | } | |
966 | } | |
967 | } | |
968 | monitor_printf(mon, "List of snapshots present on all disks:\n"); | |
969 | ||
970 | if (total > 0) { | |
971 | bdrv_snapshot_dump(NULL); | |
972 | monitor_printf(mon, "\n"); | |
973 | for (i = 0; i < total; i++) { | |
974 | sn = &sn_tab[global_snapshots[i]]; | |
975 | /* | |
976 | * The ID is not guaranteed to be the same on all images, so | |
977 | * overwrite it. | |
978 | */ | |
979 | pstrcpy(sn->id_str, sizeof(sn->id_str), "--"); | |
980 | bdrv_snapshot_dump(sn); | |
981 | monitor_printf(mon, "\n"); | |
982 | } | |
983 | } else { | |
984 | monitor_printf(mon, "None\n"); | |
985 | } | |
986 | ||
987 | QTAILQ_FOREACH(image_entry, &image_list, next) { | |
988 | if (QTAILQ_EMPTY(&image_entry->snapshots)) { | |
989 | continue; | |
990 | } | |
991 | monitor_printf(mon, | |
992 | "\nList of partial (non-loadable) snapshots on '%s':\n", | |
993 | image_entry->imagename); | |
994 | bdrv_snapshot_dump(NULL); | |
995 | monitor_printf(mon, "\n"); | |
996 | QTAILQ_FOREACH(snapshot_entry, &image_entry->snapshots, next) { | |
997 | bdrv_snapshot_dump(&snapshot_entry->sn); | |
998 | monitor_printf(mon, "\n"); | |
999 | } | |
1000 | } | |
1001 | ||
1002 | QTAILQ_FOREACH_SAFE(image_entry, &image_list, next, next_ie) { | |
1003 | SnapshotEntry *next_sn; | |
1004 | QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, next, | |
1005 | next_sn) { | |
1006 | g_free(snapshot_entry); | |
1007 | } | |
1008 | g_free(image_entry); | |
1009 | } | |
1010 | g_free(sn_tab); | |
1011 | g_free(global_snapshots); | |
1012 | } | |
1013 | ||
1014 | void hmp_change_medium(Monitor *mon, const char *device, const char *target, | |
1015 | const char *arg, const char *read_only, bool force, | |
1016 | Error **errp) | |
1017 | { | |
1018 | ERRP_GUARD(); | |
1019 | BlockdevChangeReadOnlyMode read_only_mode = 0; | |
1020 | ||
1021 | if (read_only) { | |
1022 | read_only_mode = | |
1023 | qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup, | |
1024 | read_only, | |
1025 | BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, errp); | |
1026 | if (*errp) { | |
1027 | return; | |
1028 | } | |
1029 | } | |
1030 | ||
1031 | qmp_blockdev_change_medium(device, NULL, target, arg, true, force, | |
1032 | !!read_only, read_only_mode, errp); | |
1033 | } |