Message ID | 20210302080358.3095748-2-vivek.kasireddy@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Use dmabufs for display updates instead of pixman | expand |
Hi (adding the maintainer, Gerd, in CC) On Tue, Mar 2, 2021 at 12:17 PM Vivek Kasireddy <vivek.kasireddy@intel.com> wrote: > 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.
Hi Marc-Andre, <snip> + 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) [Kasireddy, Vivek] Ok, will remove it in v2. + + 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. [Kasireddy, Vivek] Sure. + 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. [Kasireddy, Vivek] No, it won’t work with other drivers. The modifier information should be obtained from the Guest that allocates the buffers. I just added it as a temporary hack for testing with a Intel GPU. + 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; +} + udmabuf_fd = open("/dev/udmabuf", O_RDWR); If udmabuf_fd is already opened, this will leak fds. [Kasireddy, Vivek] Yup, will take care of it in v2. + if (udmabuf_fd < 0) { + error_setg_errno(errp, errno, + "udmabuf: failed to open vhost device"); + return; The fallback path should keep working [Kasireddy, Vivek] Makes sense; will fix it in v2. It would be worth doing a similar change in vhost-user-gpu. [Kasireddy, Vivek] Sure, will look into it after understanding the deltas between vhost-user and the in-qemu implementations. Thanks, Vivek -- Marc-André Lureau
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; + + 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) { + g_free(create); + 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; + 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 < 0) { + error_setg_errno(errp, errno, + "udmabuf: failed to open vhost device"); + return; + } + 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 {
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(+)