Hello !
[ ... ]
Choosing between qemu_log_mask(LOG_GUEST_ERROR, ...) and
error_report_err() is also a machine decision. The caller needs to
distinguish incompatible backing from other failures to select the
reporting path; human-readable attribution in Error * is insufficient.
The -EINVAL ambiguity therefore remains. vfio_device_get_feature()
also returns -EINVAL when the backend lacks the operation. If we
propagate that unchanged, your diagram classifies it as incompatible
guest memory. Using -EINVAL for that classification requires an
explicit mapping that keeps these other failures distinct.
Regarding -ENODEV, virtio_gpu_device_realize() already requires
virtio_gpu_have_udmabuf() for this blob path. udmabuf_fd() caches the
opened fd, so the availability-related -ENODEV should not occur
after successful realization.
You raise valid points :
* -EINVAL from vfio_device_get_feature(): You're right. That needs a
remap to avoid the -EINVAL collision.
* -ENODEV: Correct, I'll drop that suggestion.
So, we just need the one-line remap above to avoid the -EINVAL collision.
See a proposal here :
https://github.com/legoater/qemu/commits/virtio-gpu-dmabuf
Fixup patch :
https://github.com/legoater/qemu/commit/653c41e89468f82bbdfcaeafe75473b84f5a1e49
Also below,
Thanks,
C.
From 653c41e89468f82bbdfcaeafe75473b84f5a1e49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <[email protected]>
Date: Wed, 14 Oct 2026 00:00:00 +0200
Subject: [PATCH] vfio, virtio-gpu-dmabuf: Replace custom error enum with
standard -errno
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Drop the VFIO_DMABUF_CREATE_ERR_{INVALID_IOV,UNSPEC} enum from
vfio-device.h and use standard negative errno values instead:
- -EINVAL: iov addresses are incompatible with this dmabuf backend
- -ENODEV: required backend (udmabuf device) is unavailable
- -ENOMEM: allocation failure
- -EIO: VFIO feature call failed
- -ENOTSUP: VFIO not compiled in
- other -errno: forwarded from kernel VFIO ioctl
Remap -EINVAL from vfio_device_get_feature() to -EIO so that a
"feature not supported" error is not misclassified by the caller
as "addresses incompatible with this backend".
The virtio-gpu fallback logic (try udmabuf, then VFIO) can now check
-EINVAL.
Also fix several issues in the same code:
- vfio_device_lookup: use global_next (not next) for vfio_device_list
- Move vfio_device_create_dmabuf_fd/vfio_device_mmap_dmabuf
declarations outside #ifdef CONFIG_VFIO guard so prototypes are
visible to the stubs (fixes MinGW build)
- Remove trailing \n and trailing space from error_setg messages
Signed-off-by: Cédric Le Goater <[email protected]>
---
include/hw/vfio/vfio-device.h | 17 +++++++----------
hw/display/virtio-gpu-dmabuf.c | 22 ++++++++++++----------
hw/vfio/device.c | 21 +++++++++++----------
hw/vfio/dmabuf-stubs.c | 2 +-
4 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h
index 4b8fb6205604..473623240877 100644
--- a/include/hw/vfio/vfio-device.h
+++ b/include/hw/vfio/vfio-device.h
@@ -41,13 +41,6 @@ enum {
VFIO_DEVICE_TYPE_AP = 3,
};
-enum {
- /* The Guest passed addresses stored in IOV are invalid */
- VFIO_DMABUF_CREATE_ERR_INVALID_IOV = -1,
- /* Guest or Host may be at fault for this type of error */
- VFIO_DMABUF_CREATE_ERR_UNSPEC = -2,
-};
-
typedef struct VFIODeviceOps VFIODeviceOps;
typedef struct VFIODeviceIOOps VFIODeviceIOOps;
typedef struct VFIOMigration VFIOMigration;
@@ -331,6 +324,8 @@ bool vfio_device_has_region_cap(VFIODevice *vbasedev, int
region, uint16_t cap_t
int vfio_device_get_irq_info(VFIODevice *vbasedev, int index,
struct vfio_irq_info *info);
+#endif
+
/**
* Create a dmabuf fd by first translating the addresses in the
* iovec array into VFIO region offsets and then invoking the
@@ -340,8 +335,11 @@ int vfio_device_get_irq_info(VFIODevice *vbasedev, int
index,
* @iov_cnt: number of entries in the iovec array
* @errp: pointer to Error*, to store an error if it happens.
*
- * Returns the created dmabuf fd or either VFIO_DMABUF_CREATE_ERR_UNSPEC
- * or VFIO_DMABUF_CREATE_ERR_INVALID_IOV on error.
+ * Returns the dmabuf fd (>= 0) on success, or a negative errno:
+ * - -EINVAL: iov addresses do not belong to a VFIO device
+ * - -EIO: VFIO feature call failed
+ * - -ENOTSUP: VFIO not compiled in
+ * - other negative errno from the kernel VFIO ioctl
*/
int vfio_device_create_dmabuf_fd(struct iovec *iov, unsigned int iov_cnt,
Error **errp);
@@ -360,7 +358,6 @@ int vfio_device_create_dmabuf_fd(struct iovec *iov,
unsigned int iov_cnt,
*/
void *vfio_device_mmap_dmabuf(struct iovec *iov, unsigned int iov_cnt,
Error **errp);
-#endif
/* Returns 0 on success, or a negative errno. */
bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp);
diff --git a/hw/display/virtio-gpu-dmabuf.c b/hw/display/virtio-gpu-dmabuf.c
index f59a5e76ff3c..82769eed442a 100644
--- a/hw/display/virtio-gpu-dmabuf.c
+++ b/hw/display/virtio-gpu-dmabuf.c
@@ -39,21 +39,21 @@ static int virtio_gpu_create_udmabuf(struct
virtio_gpu_simple_resource *res,
udmabuf = udmabuf_fd();
if (udmabuf < 0) {
error_setg(errp, "udmabuf device not available or enabled");
- return VFIO_DMABUF_CREATE_ERR_UNSPEC;
+ return -ENODEV;
}
list = g_try_malloc0(sizeof(struct udmabuf_create_list) +
sizeof(struct udmabuf_create_item) * res->iov_cnt);
if (!list) {
error_setg(errp, "failed to allocate udmabuf create list");
- return VFIO_DMABUF_CREATE_ERR_UNSPEC;
+ return -ENOMEM;
}
for (i = 0; i < res->iov_cnt; i++) {
rb = qemu_ram_block_from_host(res->iov[i].iov_base, false, &offset);
if (!rb || rb->fd < 0) {
- error_setg(errp, "IOV memory address incompatible with udmabuf ");
- return VFIO_DMABUF_CREATE_ERR_INVALID_IOV;
+ error_setg(errp, "IOV memory address incompatible with udmabuf");
+ return -EINVAL;
}
list->list[i].memfd = rb->fd;
@@ -66,11 +66,13 @@ static int virtio_gpu_create_udmabuf(struct
virtio_gpu_simple_resource *res,
fd = ioctl(udmabuf, UDMABUF_CREATE_LIST, list);
if (fd < 0) {
- error_setg_errno(errp, errno, "UDMABUF_CREATE_LIST: ioctl failed");
- if (errno == EINVAL || errno == EBADFD) {
- return VFIO_DMABUF_CREATE_ERR_INVALID_IOV;
+ int saved_errno = errno;
+
+ error_setg_errno(errp, saved_errno, "UDMABUF_CREATE_LIST: ioctl
failed");
+ if (saved_errno == EINVAL || saved_errno == EBADFD) {
+ return -EINVAL;
}
- return VFIO_DMABUF_CREATE_ERR_UNSPEC;
+ return -saved_errno;
}
return fd;
}
@@ -150,13 +152,13 @@ bool virtio_gpu_init_dmabuf(struct
virtio_gpu_simple_resource *res)
pdata = res->iov[0].iov_base;
} else if (res->blob_size) {
res->dmabuf_fd = virtio_gpu_create_udmabuf(res, &local_err);
- if (res->dmabuf_fd == VFIO_DMABUF_CREATE_ERR_INVALID_IOV) {
+ if (res->dmabuf_fd == -EINVAL) {
error_free_or_abort(&local_err);
res->dmabuf_fd = vfio_device_create_dmabuf_fd(res->iov,
res->iov_cnt,
&local_err);
- if (res->dmabuf_fd == VFIO_DMABUF_CREATE_ERR_INVALID_IOV) {
+ if (res->dmabuf_fd == -EINVAL) {
error_free_or_abort(&local_err);
qemu_log_mask(LOG_GUEST_ERROR,
"Cannot create dmabuf: incompatible memory\n");
diff --git a/hw/vfio/device.c b/hw/vfio/device.c
index 5bc044376441..28cb23f3df6a 100644
--- a/hw/vfio/device.c
+++ b/hw/vfio/device.c
@@ -686,18 +686,18 @@ static bool vfio_device_lookup(struct iovec *iov,
VFIODevice **vbasedevp,
first_rb = qemu_ram_block_from_host(iov[0].iov_base, false, &offset);
if (!first_rb) {
- error_setg(errp, "Could not find first ramblock\n");
+ error_setg(errp, "Could not find first ramblock");
return false;
}
*first_rbp = first_rb;
- QLIST_FOREACH(vbasedev, &vfio_device_list, next) {
+ QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) {
if (vbasedev->dev == first_rb->mr->dev) {
*vbasedevp = vbasedev;
return true;
}
}
- error_setg(errp, "No VFIO device found to create dmabuf from\n");
+ error_setg(errp, "No VFIO device found to create dmabuf from");
return false;
}
@@ -716,7 +716,7 @@ static bool vfio_region_lookup(void *addr, VFIORegion **regionp,
rb = qemu_ram_block_from_host(addr, false, offsetp);
if (!rb || rb != first_rb) {
- error_setg(errp, "Dmabuf segments must belong to the same region\n");
+ error_setg(errp, "Dmabuf segments must belong to the same region");
return false;
}
@@ -725,7 +725,7 @@ static bool vfio_region_lookup(void *addr, VFIORegion **regionp,
*regionp = region;
return true;
}
- error_setg(errp, "Could not find valid region for dmabuf segment\n");
+ error_setg(errp, "Could not find valid region for dmabuf segment");
return false;
}
@@ -742,7 +742,7 @@ int vfio_device_create_dmabuf_fd(struct iovec *iov, unsigned int iov_cnt,
int i, ret;
if (!vfio_device_lookup(iov, &vbasedev, &first_rb, errp)) {
- return VFIO_DMABUF_CREATE_ERR_INVALID_IOV;
+ return -EINVAL;
}
argsz = sizeof(*feature) + sizeof (*dma_buf) +
@@ -757,7 +757,7 @@ int vfio_device_create_dmabuf_fd(struct iovec *iov,
unsigned int iov_cnt,
for (i = 0; i < iov_cnt; i++) {
if (!vfio_region_lookup(iov[i].iov_base, ®ion,
first_rb, &offset, errp)) {
- return VFIO_DMABUF_CREATE_ERR_INVALID_IOV;
+ return -EINVAL;
}
dma_buf->region_index = region->nr;
@@ -772,7 +772,8 @@ int vfio_device_create_dmabuf_fd(struct iovec *iov,
unsigned int iov_cnt,
if (ret < 0) {
error_setg_errno(errp, -ret,
"Could not create dmabuf fd via VFIO device");
- return VFIO_DMABUF_CREATE_ERR_UNSPEC;
+ /* Remap -EINVAL: caller uses it to mean "addresses incompatible" */
+ return (ret == -EINVAL) ? -EIO : ret;
}
return ret;
}
@@ -811,7 +812,7 @@ void *vfio_device_mmap_dmabuf(struct iovec *iov, unsigned
int iov_cnt,
}
if (vfio_device_get_region_info(vbasedev, region->nr, &info)) {
- error_setg(errp, "Cannot find region info for dmabuf segment\n");
+ error_setg(errp, "Cannot find region info for dmabuf segment");
goto err;
}
@@ -819,7 +820,7 @@ void *vfio_device_mmap_dmabuf(struct iovec *iov, unsigned int iov_cnt,
MAP_SHARED | MAP_FIXED, vbasedev->fd,
info->offset + offset);
if (submap == MAP_FAILED) {
- error_setg_errno(errp, errno, "Could not mmap dmabuf segment\n");
+ error_setg_errno(errp, errno, "Could not mmap dmabuf segment");
goto err;
}
len += iov[i].iov_len;
diff --git a/hw/vfio/dmabuf-stubs.c b/hw/vfio/dmabuf-stubs.c
index b9e03a162a75..241a9761ef5e 100644
--- a/hw/vfio/dmabuf-stubs.c
+++ b/hw/vfio/dmabuf-stubs.c
@@ -13,7 +13,7 @@ int vfio_device_create_dmabuf_fd(struct iovec *iov, unsigned
int iov_cnt,
Error **errp)
{
error_setg(errp, "VFIO dmabuf support is not enabled");
- return VFIO_DMABUF_CREATE_ERR_UNSPEC;
+ return -ENOTSUP;
}
void *vfio_device_mmap_dmabuf(struct iovec *iov, unsigned int iov_cnt,
--
2.56.0