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