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