If a dmabuf can be created using Udmabuf driver for non-Virgil rendered
resources, then this should be preferred over pixman. If a dmabuf cannot
be created then we can fallback to pixman.
The dmabuf create and release functions are inspired by similar
functions in vfio/display.c.
Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
---
hw/display/virtio-gpu.c | 133 +++++++++++++++++++++++++++++++++
include/hw/virtio/virtio-gpu.h | 12 +++
2 files changed, 145 insertions(+)
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 2e4a9822b6..399d46eac3 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -18,6 +18,7 @@
#include "trace.h"
#include "sysemu/dma.h"
#include "sysemu/sysemu.h"
+#include "exec/ramblock.h"
#include "hw/virtio/virtio.h"
#include "migration/qemu-file-types.h"
#include "hw/virtio/virtio-gpu.h"
@@ -30,9 +31,14 @@
#include "qemu/module.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
+#include "standard-headers/drm/drm_fourcc.h"
+#include <sys/ioctl.h>
+
+#include <linux/udmabuf.h>
#define VIRTIO_GPU_VM_VERSION 1
+static int udmabuf_fd;
static struct virtio_gpu_simple_resource*
virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
@@ -519,6 +525,119 @@ static void virtio_unref_resource(pixman_image_t *image, void *data)
pixman_image_unref(data);
}
+static VGPUDMABuf *virtio_gpu_get_dmabuf(VirtIOGPU *g,
+ struct virtio_gpu_simple_resource *res)
+{
+ VGPUDMABuf *dmabuf;
+ RAMBlock *rb;
+ ram_addr_t offset;
+ struct udmabuf_create_list *create;
+ uint32_t modifier_hi, modifier_lo;
+ uint64_t modifier;
+ static uint64_t ids = 1;
+ int i, dmabuf_fd;
+
+ create = g_malloc0(sizeof(*create) +
+ res->iov_cnt * sizeof (struct udmabuf_create_item));
+ if (!create)
+ return NULL;
Pointless allocation check (g_malloc will abort if it failed to allocate)
+
+ create->count = res->iov_cnt;
+ create->flags = UDMABUF_FLAGS_CLOEXEC;
+ 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) {
+ g_free(create);
+ return NULL;
+ }
+
+ create->list[i].memfd = rb->fd;
+ create->list[i].__pad = 0;
+ create->list[i].offset = offset;
+ create->list[i].size = res->iov[i].iov_len;
+ }
+
+ dmabuf_fd = ioctl(udmabuf_fd, UDMABUF_CREATE_LIST, create);
+ if (dmabuf_fd < 0) {
It would be useful to print the error with errno.
+ g_free(create);
(we now like new code to use g_auto)
+ return NULL;
+ }
+
+ /* FIXME: We should get the modifier and format info with blob resources */
+ modifier_hi = fourcc_mod_code(INTEL, I915_FORMAT_MOD_X_TILED) >> 32;
+ modifier_lo = fourcc_mod_code(INTEL,I915_FORMAT_MOD_X_TILED) & 0xFFFFFFFF;
I have no idea if this format is going to work with various drivers and matches the underlying memory representation.
+ modifier = ((uint64_t)modifier_hi << 32) | modifier_lo;
+
+ dmabuf = g_new0(VGPUDMABuf, 1);
+ dmabuf->dmabuf_id = ids++;
+ dmabuf->buf.width = res->width;
+ dmabuf->buf.height = res->height;
+ dmabuf->buf.stride = pixman_image_get_stride(res->image);
+ dmabuf->buf.fourcc = DRM_FORMAT_XRGB8888;
+ dmabuf->buf.modifier = modifier;
+ dmabuf->buf.fd = dmabuf_fd;
+
+ QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next);
+ g_free(create);
+
+ return dmabuf;
+}
+
+static void virtio_gpu_free_one_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf,
+ struct virtio_gpu_scanout *scanout)
+{
+ QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next);
+ dpy_gl_release_dmabuf(scanout->con, &dmabuf->buf);
+
+ close(dmabuf->buf.fd);
+ g_free(dmabuf);
+}
+
+static void virtio_gpu_free_dmabufs(VirtIOGPU *g,
+ struct virtio_gpu_scanout *scanout)
+{
+ VGPUDMABuf *dmabuf, *tmp;
+ uint32_t keep = 1;
+
+ QTAILQ_FOREACH_SAFE(dmabuf, &g->dmabuf.bufs, next, tmp) {
+ if (keep > 0) {
+ keep--;
+ continue;
+ }
+ assert(dmabuf != g->dmabuf.primary);
+ virtio_gpu_free_one_dmabuf(g, dmabuf, scanout);
+ }
+}
+
+static int virtio_gpu_dmabuf_update(VirtIOGPU *g,
+ struct virtio_gpu_simple_resource *res,
+ struct virtio_gpu_scanout *scanout)
+{
+ VGPUDMABuf *primary;
+ bool free_bufs = false;
+
+ primary = virtio_gpu_get_dmabuf(g, res);
+ if (!primary) {
+ return -EINVAL;
+ }
+
+ if (g->dmabuf.primary != primary) {
+ g->dmabuf.primary = primary;
+ qemu_console_resize(scanout->con,
+ primary->buf.width, primary->buf.height);
+ dpy_gl_scanout_dmabuf(scanout->con, &primary->buf);
+ free_bufs = true;
+ }
+
+ dpy_gl_update(scanout->con, 0, 0, primary->buf.width, primary->buf.height);
+
+ if (free_bufs) {
+ virtio_gpu_free_dmabufs(g, scanout);
+ }
+
+ return 0;
+}
+
static void virtio_gpu_set_scanout(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
@@ -528,6 +647,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
uint32_t offset;
int bpp;
struct virtio_gpu_set_scanout ss;
+ int ret;
VIRTIO_GPU_FILL_CMD(ss);
virtio_gpu_bswap_32(&ss, sizeof(ss));
@@ -574,6 +694,12 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
scanout = &g->parent_obj.scanout[ss.scanout_id];
+ if (udmabuf_fd > 0) {
+ ret = virtio_gpu_dmabuf_update(g, res, scanout);
+ if (!ret)
+ return;
+ }
+
format = pixman_image_get_format(res->image);
bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8);
offset = (ss.r.x * bpp) + ss.r.y * pixman_image_get_stride(res->image);
@@ -1139,6 +1265,13 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
return;
}
+ udmabuf_fd = open("/dev/udmabuf", O_RDWR);
If udmabuf_fd is already opened, this will leak fds.
+ if (udmabuf_fd < 0) {
+ error_setg_errno(errp, errno,
+ "udmabuf: failed to open vhost device");
+ return;
The fallback path should keep working
+ }
+
g->ctrl_vq = virtio_get_queue(vdev, 0);
g->cursor_vq = virtio_get_queue(vdev, 1);
g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index fae149235c..153f3364a9 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -131,6 +131,13 @@ struct VirtIOGPUBaseClass {
DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1024), \
DEFINE_PROP_UINT32("yres", _state, _conf.yres, 768)
+typedef struct VGPUDMABuf {
+ QemuDmaBuf buf;
+ uint32_t x, y;
+ uint64_t dmabuf_id;
+ QTAILQ_ENTRY(VGPUDMABuf) next;
+} VGPUDMABuf;
+
struct VirtIOGPU {
VirtIOGPUBase parent_obj;
@@ -161,6 +168,11 @@ struct VirtIOGPU {
uint32_t req_3d;
uint32_t bytes_3d;
} stats;
+
+ struct {
+ QTAILQ_HEAD(, VGPUDMABuf) bufs;
+ VGPUDMABuf *primary;
+ } dmabuf;
};
struct VhostUserGPU {
--
2.26.2
It would be worth doing a similar change in vhost-user-gpu.