@samitouri / QOSamiQemu / commits / 1f8d06ef42

block: add x-qemu-io QMP command

Add an x-qemu-io QMP command that runs qemu-io commands on block devices. The command accepts a device name (block backend name, node-name, or qdev ID) and a qemu-io command string. Refactor hmp_qemu_io() to be a thin wrapper around the new QMP command, following the standard HMP-over-QMP pattern used by other block commands. This change is also required for the qtest qemu-io command in the following patch. Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260828-qemu-no-hmp-v5-22-9227de146347@redhat.com>

Marc-André Lureau committed Aug 28, 2026 at 16:04 UTC 1f8d06ef42a70c34eb931f573b5e7ecdb663d6ba
4 files changed +117 -57
block/monitor/block-hmp-cmds.c
+3 -57
@@ -56,7 +56,6 @@
56 #include "block/qapi.h"
57 #include "block/block_int.h"
58 #include "block/block-hmp-cmds.h"
59 -#include "qemu-io.h"
59
60 static void hmp_drive_add_node(Monitor *mon, const char *optstr)
61 {
@@ -541,67 +540,14 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
540
541 void hmp_qemu_io(Monitor *mon, const QDict *qdict)
542 {
544 - BlockBackend *blk = NULL;
545 - BlockDriverState *bs = NULL;
546 - BlockBackend *local_blk = NULL;
543 bool qdev = qdict_get_try_bool(qdict, "qdev", false);
544 const char *device = qdict_get_str(qdict, "device");
545 const char *command = qdict_get_str(qdict, "command");
546 Error *err = NULL;
551 - int ret;
552 -
553 - if (qdev) {
554 - blk = blk_by_qdev_id(device, &err);
555 - if (!blk) {
556 - goto fail;
557 - }
558 - } else {
559 - blk = blk_by_name(device);
560 - if (!blk) {
561 - bs = bdrv_lookup_bs(NULL, device, &err);
562 - if (!bs) {
563 - goto fail;
564 - }
565 - }
566 - }
567 -
568 - if (bs) {
569 - blk = local_blk = blk_new(bdrv_get_aio_context(bs), 0, BLK_PERM_ALL);
570 - ret = blk_insert_bs(blk, bs, &err);
571 - if (ret < 0) {
572 - goto fail;
573 - }
574 - }
575 -
576 - /*
577 - * Notably absent: Proper permission management. This is sad, but it seems
578 - * almost impossible to achieve without changing the semantics and thereby
579 - * limiting the use cases of the qemu-io HMP command.
580 - *
581 - * In an ideal world we would unconditionally create a new BlockBackend for
582 - * qemuio_command(), but we have commands like 'reopen' and want them to
583 - * take effect on the exact BlockBackend whose name the user passed instead
584 - * of just on a temporary copy of it.
585 - *
586 - * Another problem is that deleting the temporary BlockBackend involves
587 - * draining all requests on it first, but some qemu-iotests cases want to
588 - * issue multiple aio_read/write requests and expect them to complete in
589 - * the background while the monitor has already returned.
590 - *
591 - * This is also what prevents us from saving the original permissions and
592 - * restoring them later: We can't revoke permissions until all requests
593 - * have completed, and we don't know when that is nor can we really let
594 - * anything else run before we have revoken them to avoid race conditions.
595 - *
596 - * What happens now is that command() in qemu-io-cmds.c can extend the
597 - * permissions if necessary for the qemu-io command. And they simply stay
598 - * extended, possibly resulting in a read-only guest device keeping write
599 - * permissions. Ugly, but it appears to be the lesser evil.
600 - */
601 - qemuio_command(blk, command, &err);
547
603 -fail:
604 - blk_unref(local_blk);
548 + qmp_x_qemu_io(qdev ? NULL : device,
549 + qdev ? device : NULL,
550 + command, &err);
551 hmp_handle_error(mon, err);
552 }
553
block/monitor/meson.build
+1
@@ -1,2 +1,3 @@
1 system_ss.add(files('block-hmp-cmds.c'))
2 block_ss.add(files('bitmap-qmp-cmds.c'))
3 +system_ss.add(files('qmp-cmds.c'))
block/monitor/qmp-cmds.c new
+79
@@ -0,0 +1,79 @@
1 +/* SPDX-License-Identifier: GPL-2.0-or-later */
2 +#include "qemu/osdep.h"
3 +
4 +#include "system/block-backend.h"
5 +#include "block/block_int.h"
6 +#include "qapi/qapi-commands-block.h"
7 +#include "qapi/error.h"
8 +#include "qemu-io.h"
9 +
10 +void qmp_x_qemu_io(const char *device, const char *qdev,
11 + const char *command, Error **errp)
12 +{
13 + BlockBackend *blk = NULL;
14 + BlockBackend *local_blk = NULL;
15 + BlockDriverState *bs = NULL;
16 + int ret;
17 +
18 + if (!device && !qdev) {
19 + error_setg(errp, "Must specify either device or qdev");
20 + return;
21 + }
22 + if (qdev && device) {
23 + error_setg(errp, "Cannot specify both qdev and device");
24 + return;
25 + }
26 +
27 + if (qdev) {
28 + blk = blk_by_qdev_id(qdev, errp);
29 + if (!blk) {
30 + return;
31 + }
32 + } else {
33 + blk = blk_by_name(device);
34 + if (!blk) {
35 + bs = bdrv_lookup_bs(NULL, device, errp);
36 + if (!bs) {
37 + return;
38 + }
39 + }
40 + }
41 +
42 + if (bs) {
43 + blk = local_blk = blk_new(bdrv_get_aio_context(bs), 0, BLK_PERM_ALL);
44 + ret = blk_insert_bs(blk, bs, errp);
45 + if (ret < 0) {
46 + goto fail;
47 + }
48 + }
49 +
50 + /*
51 + * Notably absent: Proper permission management. This is sad, but it seems
52 + * almost impossible to achieve without changing the semantics and thereby
53 + * limiting the use cases of the qemu-io command.
54 + *
55 + * In an ideal world we would unconditionally create a new BlockBackend for
56 + * qemuio_command(), but we have commands like 'reopen' and want them to
57 + * take effect on the exact BlockBackend whose name the user passed instead
58 + * of just on a temporary copy of it.
59 + *
60 + * Another problem is that deleting the temporary BlockBackend involves
61 + * draining all requests on it first, but some qemu-iotests cases want to
62 + * issue multiple aio_read/write requests and expect them to complete in
63 + * the background while the monitor has already returned.
64 + *
65 + * This is also what prevents us from saving the original permissions and
66 + * restoring them later: We can't revoke permissions until all requests
67 + * have completed, and we don't know when that is nor can we really let
68 + * anything else run before we have revoken them to avoid race conditions.
69 + *
70 + * What happens now is that command() in qemu-io-cmds.c can extend the
71 + * permissions if necessary for the qemu-io command. And they simply stay
72 + * extended, possibly resulting in a read-only guest device keeping write
73 + * permissions. Ugly, but it appears to be the lesser evil.
74 + */
75 + qemuio_command(blk, command, errp);
76 +
77 +fail:
78 + blk_unref(local_blk);
79 +}
qapi/block.json
+34
@@ -603,3 +603,37 @@
603 '*boundaries-zap': ['uint64'],
604 '*boundaries-flush': ['uint64'] },
605 'allow-preconfig': true }
606 +
607 +##
608 +# @x-qemu-io:
609 +#
610 +# Run a qemu-io command on a block device. Take either a block
611 +# backend name or a qdev ID to identify the device.
612 +#
613 +# @device: the block backend name, node-name to run the
614 +# command on.
615 +#
616 +# @qdev: the qdev ID of the block device to run the
617 +# command on.
618 +#
619 +# @command: the qemu-io command string to execute.
620 +#
621 +# Features:
622 +#
623 +# @unstable: This command is for testing only.
624 +#
625 +# Since: 11.2
626 +#
627 +# .. qmp-example::
628 +#
629 +# -> { "execute": "x-qemu-io",
630 +# "arguments": { "device": "virtio0",
631 +# "command": "read 0 512" } }
632 +# <- { "return": {} }
633 +##
634 +{ 'command': 'x-qemu-io',
635 + 'data': { '*device': 'str',
636 + '*qdev': 'str',
637 + 'command': 'str' },
638 + 'features': [ 'unstable' ],
639 + 'allow-preconfig': true }