vfio-user: support VFIO_USER_DEVICE_FEATURE
Plumb through vfio_device_get_feature to the vfio-user server. Note that we translate EINVAL into ENOTTY, as the existing generic vfio code is expecting the latter to mean "unsupported". As part of adding a trace point, clean up the trace file. Signed-off-by: John Levon <john.levon@nutanix.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260410085716.877185-2-john.levon@nutanix.com Signed-off-by: Cédric Le Goater <clg@redhat.com>
John Levon committed
Apr 10, 2026 at 09:57 UTC
e2358af5838dc36552d71441ecdf07faad7c6446
3 files changed
+67
-10
hw/vfio-user/device.c
+42
@@ -74,6 +74,47 @@ void vfio_user_device_reset(VFIOUserProxy *proxy)
74
}
75
}
76
77
+static int
78
+vfio_user_device_io_device_feature(VFIODevice *vbasedev,
79
+ struct vfio_device_feature *feature)
80
+{
81
+ g_autofree VFIOUserDeviceFeature *msgp = NULL;
82
+ int size = sizeof(VFIOUserHdr) + feature->argsz;
83
+ VFIOUserProxy *proxy = vbasedev->proxy;
84
+ Error *local_err = NULL;
85
+
86
+ msgp = g_malloc0(size);
87
+
88
+ vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_FEATURE, size, 0);
89
+
90
+ memcpy(&msgp->argsz, &feature->argsz, feature->argsz);
91
+
92
+ if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) {
93
+ error_prepend(&local_err, "%s: ", __func__);
94
+ error_report_err(local_err);
95
+ return -EFAULT;
96
+ }
97
+
98
+ if (msgp->hdr.flags & VFIO_USER_ERROR) {
99
+ /*
100
+ * Client expects ENOTTY for "not supported", but the protocol may
101
+ * return EINVAL (which should only occur in the case the feature isn't
102
+ * actually supported on the server).
103
+ */
104
+ if (msgp->hdr.error_reply == EINVAL) {
105
+ return -ENOTTY;
106
+ }
107
+
108
+ return -msgp->hdr.error_reply;
109
+ }
110
+
111
+ memcpy(feature, &msgp->argsz, feature->argsz);
112
+
113
+ trace_vfio_user_device_io_device_feature(msgp->argsz, msgp->flags);
114
+
115
+ return 0;
116
+}
117
+
118
static int vfio_user_get_region_info(VFIOUserProxy *proxy,
119
struct vfio_region_info *info,
120
VFIOUserFDs *fds)
@@ -432,6 +473,7 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
473
* Socket-based io_ops
474
*/
475
VFIODeviceIOOps vfio_user_device_io_ops_sock = {
476
+ .device_feature = vfio_user_device_io_device_feature,
477
.get_region_info = vfio_user_device_io_get_region_info,
478
.get_irq_info = vfio_user_device_io_get_irq_info,
479
.set_irqs = vfio_user_device_io_set_irqs,
hw/vfio-user/protocol.h
+12
@@ -40,6 +40,7 @@ enum vfio_user_command {
40
VFIO_USER_DEVICE_RESET = 13,
41
VFIO_USER_DIRTY_PAGES = 14,
42
VFIO_USER_REGION_WRITE_MULTI = 15,
43
+ VFIO_USER_DEVICE_FEATURE = 16,
44
VFIO_USER_MAX,
45
};
46
@@ -239,4 +240,15 @@ typedef struct {
240
VFIOUserWROne wrs[VFIO_USER_MULTI_MAX];
241
} VFIOUserWRMulti;
242
243
+/*
244
+ * VFIO_USER_DEVICE_FEATURE
245
+ * imported from struct vfio_device_feature
246
+ */
247
+typedef struct {
248
+ VFIOUserHdr hdr;
249
+ uint32_t argsz;
250
+ uint32_t flags;
251
+ char data[];
252
+} VFIOUserDeviceFeature;
253
+
254
#endif /* VFIO_USER_PROTOCOL_H */
hw/vfio-user/trace-events
+13
-10
@@ -2,19 +2,22 @@
2
#
3
# SPDX-License-Identifier: GPL-2.0-or-later
4
5
-# common.c
5
+# container.c
6
+vfio_user_dma_map(uint64_t iova, uint64_t size, uint64_t off, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" off 0x%"PRIx64" flags 0x%x async_ops %d"
7
+vfio_user_dma_unmap(uint64_t iova, uint64_t size, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" flags 0x%x async_ops %d"
8
+
9
+# device.c
10
+vfio_user_device_io_device_feature(uint32_t argsz, uint32_t flags) " argsz 0x%x flags 0x%x"
11
+vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d"
12
+vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d"
13
+vfio_user_set_irqs(uint32_t index, uint32_t start, uint32_t count, uint32_t flags) " index %d start %d count %d flags 0x%x"
14
+vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64
15
+vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d"
16
+
17
+# proxy.c
18
vfio_user_recv_hdr(const char *name, uint16_t id, uint16_t cmd, uint32_t size, uint32_t flags) " (%s) id 0x%x cmd 0x%x size 0x%x flags 0x%x"
19
vfio_user_recv_read(uint16_t id, int read) " id 0x%x read 0x%x"
20
vfio_user_recv_request(uint16_t cmd) " command 0x%x"
21
vfio_user_send_write(uint16_t id, int wrote) " id 0x%x wrote 0x%x"
22
vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d minor %d caps: %s"
11
-vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d"
12
-vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64
13
-vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d"
14
-vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d"
15
-vfio_user_set_irqs(uint32_t index, uint32_t start, uint32_t count, uint32_t flags) " index %d start %d count %d flags 0x%x"
23
vfio_user_wrmulti(const char *s, uint64_t wr_cnt) " %s count 0x%"PRIx64
17
-
18
-# container.c
19
-vfio_user_dma_map(uint64_t iova, uint64_t size, uint64_t off, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" off 0x%"PRIx64" flags 0x%x async_ops %d"
20
-vfio_user_dma_unmap(uint64_t iova, uint64_t size, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" flags 0x%x async_ops %d"