From e2358af5838dc36552d71441ecdf07faad7c6446 Mon Sep 17 00:00:00 2001 From: John Levon Date: Fri, 10 Apr 2026 09:57:14 +0100 Subject: [PATCH] vfio-user: support VFIO_USER_DEVICE_FEATURE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260410085716.877185-2-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 42 +++++++++++++++++++++++++++++++++++++++ hw/vfio-user/protocol.h | 12 +++++++++++ hw/vfio-user/trace-events | 23 +++++++++++---------- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 64ef35b320..b8d2b7c1a8 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -74,6 +74,47 @@ void vfio_user_device_reset(VFIOUserProxy *proxy) } } +static int +vfio_user_device_io_device_feature(VFIODevice *vbasedev, + struct vfio_device_feature *feature) +{ + g_autofree VFIOUserDeviceFeature *msgp = NULL; + int size = sizeof(VFIOUserHdr) + feature->argsz; + VFIOUserProxy *proxy = vbasedev->proxy; + Error *local_err = NULL; + + msgp = g_malloc0(size); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_FEATURE, size, 0); + + memcpy(&msgp->argsz, &feature->argsz, feature->argsz); + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + /* + * Client expects ENOTTY for "not supported", but the protocol may + * return EINVAL (which should only occur in the case the feature isn't + * actually supported on the server). + */ + if (msgp->hdr.error_reply == EINVAL) { + return -ENOTTY; + } + + return -msgp->hdr.error_reply; + } + + memcpy(feature, &msgp->argsz, feature->argsz); + + trace_vfio_user_device_io_device_feature(msgp->argsz, msgp->flags); + + return 0; +} + static int vfio_user_get_region_info(VFIOUserProxy *proxy, struct vfio_region_info *info, VFIOUserFDs *fds) @@ -432,6 +473,7 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, * Socket-based io_ops */ VFIODeviceIOOps vfio_user_device_io_ops_sock = { + .device_feature = vfio_user_device_io_device_feature, .get_region_info = vfio_user_device_io_get_region_info, .get_irq_info = vfio_user_device_io_get_irq_info, .set_irqs = vfio_user_device_io_set_irqs, diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index 3249a4a6b6..2a0c31e7c5 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -40,6 +40,7 @@ enum vfio_user_command { VFIO_USER_DEVICE_RESET = 13, VFIO_USER_DIRTY_PAGES = 14, VFIO_USER_REGION_WRITE_MULTI = 15, + VFIO_USER_DEVICE_FEATURE = 16, VFIO_USER_MAX, }; @@ -239,4 +240,15 @@ typedef struct { VFIOUserWROne wrs[VFIO_USER_MULTI_MAX]; } VFIOUserWRMulti; +/* + * VFIO_USER_DEVICE_FEATURE + * imported from struct vfio_device_feature + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + char data[]; +} VFIOUserDeviceFeature; + #endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events index abb67f4c11..503e1c82d4 100644 --- a/hw/vfio-user/trace-events +++ b/hw/vfio-user/trace-events @@ -2,19 +2,22 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -# common.c +# container.c +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" +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" + +# device.c +vfio_user_device_io_device_feature(uint32_t argsz, uint32_t flags) " argsz 0x%x flags 0x%x" +vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d" +vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d" +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" +vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64 +vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d" + +# proxy.c 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" vfio_user_recv_read(uint16_t id, int read) " id 0x%x read 0x%x" vfio_user_recv_request(uint16_t cmd) " command 0x%x" vfio_user_send_write(uint16_t id, int wrote) " id 0x%x wrote 0x%x" vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d minor %d caps: %s" -vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d" -vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64 -vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d" -vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d" -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" vfio_user_wrmulti(const char *s, uint64_t wr_cnt) " %s count 0x%"PRIx64 - -# container.c -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" -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"