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, &region,
                                 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



Reply via email to