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 }