Message ID | 20180228123110.6507-10-kraxel@redhat.com |
---|---|
State | New |
Headers | show |
Series | vfio: add display support | expand |
> -----Original Message----- > From: intel-gvt-dev [mailto:intel-gvt-dev-bounces@lists.freedesktop.org] On > Behalf Of Gerd Hoffmann > Sent: Wednesday, February 28, 2018 8:31 PM > To: qemu-devel@nongnu.org > Cc: Alex Williamson <alex.williamson@redhat.com>; Gerd Hoffmann > <kraxel@redhat.com>; intel-gvt-dev@lists.freedesktop.org; Kirti Wankhede > <kwankhede@nvidia.com>; Zhang, Tina <tina.zhang@intel.com> > Subject: [PATCH v6 9/9] vfio/display: adding dmabuf support > > Wire up dmabuf-based display. > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- Please see my comments below. Tested-by: Tina Zhang <tina.zhang@intel.com> > include/hw/vfio/vfio-common.h | 14 ++++ > hw/vfio/display.c | 166 > +++++++++++++++++++++++++++++++++++++++++- > 2 files changed, 178 insertions(+), 2 deletions(-) > > diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h > index fc8ae14fb7..994e780d51 100644 > --- a/include/hw/vfio/vfio-common.h > +++ b/include/hw/vfio/vfio-common.h > @@ -26,6 +26,7 @@ > #include "exec/memory.h" > #include "qemu/queue.h" > #include "qemu/notify.h" > +#include "ui/console.h" > #ifdef CONFIG_LINUX > #include <linux/vfio.h> > #endif > @@ -142,12 +143,25 @@ typedef struct VFIOGroup { > QLIST_ENTRY(VFIOGroup) container_next; } VFIOGroup; > > +typedef struct VFIODMABuf { > + QemuDmaBuf buf; > + uint32_t pos_x, pos_y; > + uint32_t hot_x, hot_y; > + int dmabuf_id; > + QTAILQ_ENTRY(VFIODMABuf) next; > +} VFIODMABuf; > + > typedef struct VFIODisplay { > QemuConsole *con; > struct { > VFIORegion buffer; > DisplaySurface *surface; > } region; > + struct { > + QTAILQ_HEAD(, VFIODMABuf) bufs; > + VFIODMABuf *primary; > + VFIODMABuf *cursor; > + } dmabuf; > } VFIODisplay; > > void vfio_put_base_device(VFIODevice *vbasedev); diff --git > a/hw/vfio/display.c b/hw/vfio/display.c index f6acbacc79..053d3ab67a 100644 > --- a/hw/vfio/display.c > +++ b/hw/vfio/display.c > @@ -19,6 +19,168 @@ > #include "qapi/error.h" > #include "pci.h" > > +#ifndef DRM_PLANE_TYPE_PRIMARY > +# define DRM_PLANE_TYPE_PRIMARY 1 > +# define DRM_PLANE_TYPE_CURSOR 2 > +#endif > + > +static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, > + uint32_t plane_type) { > + VFIODisplay *dpy = vdev->dpy; > + struct vfio_device_gfx_plane_info plane; > + VFIODMABuf *dmabuf; > + int fd, ret; > + > + memset(&plane, 0, sizeof(plane)); > + plane.argsz = sizeof(plane); > + plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; > + plane.drm_plane_type = plane_type; > + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); > + if (ret < 0) { > + return NULL; > + } > + if (!plane.drm_format || !plane.size) { > + return NULL; > + } > + > + QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { > + if (dmabuf->dmabuf_id == plane.dmabuf_id) { > + /* found in list, move to head, return it */ > + QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); > + QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); > + if (plane_type == DRM_PLANE_TYPE_CURSOR) { > + dmabuf->pos_x = plane.x_pos; > + dmabuf->pos_y = plane.y_pos; Better also update plane.x_hot and plane.y_hot here. The "pos_x", "pos_y", "hot_x" and "hot_y" fields would be updated with each time VFIO_DEVICE_QUERY_GFX_PLANE is invoked, although dmabuf object may be reused. BR, Tina > + } > + return dmabuf; > + } > + } > + > + fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, > &plane.dmabuf_id); > + if (fd < 0) { > + return NULL; > + } > + > + dmabuf = g_new0(VFIODMABuf, 1); > + dmabuf->dmabuf_id = plane.dmabuf_id; > + dmabuf->buf.width = plane.width; > + dmabuf->buf.height = plane.height; > + dmabuf->buf.stride = plane.stride; > + dmabuf->buf.fourcc = plane.drm_format; > + dmabuf->buf.fd = fd; > + if (plane_type == DRM_PLANE_TYPE_CURSOR) { > + dmabuf->pos_x = plane.x_pos; > + dmabuf->pos_y = plane.y_pos; > + dmabuf->hot_x = plane.x_hot; > + dmabuf->hot_y = plane.y_hot; > + } > + > + QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); > + return dmabuf; > +} > + > +static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf > +*dmabuf) { > + QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); > + dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); > + close(dmabuf->buf.fd); > + g_free(dmabuf); > +} > + > +static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) { > + VFIODisplay *dpy = vdev->dpy; > + VFIODMABuf *dmabuf, *tmp; > + uint32_t keep = 5; > + > + QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { > + if (keep > 0) { > + keep--; > + continue; > + } > + assert(dmabuf != dpy->dmabuf.primary); > + vfio_display_free_one_dmabuf(dpy, dmabuf); > + } > +} > + > +static void vfio_display_dmabuf_update(void *opaque) { > + VFIOPCIDevice *vdev = opaque; > + VFIODisplay *dpy = vdev->dpy; > + VFIODMABuf *primary, *cursor; > + bool free_bufs = false; > + > + primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); > + if (primary == NULL) { > + return; > + } > + > + if (dpy->dmabuf.primary != primary) { > + dpy->dmabuf.primary = primary; > + qemu_console_resize(dpy->con, > + primary->buf.width, primary->buf.height); > + dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); > + free_bufs = true; > + } > + > + cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); > + if (dpy->dmabuf.cursor != cursor) { > + dpy->dmabuf.cursor = cursor; > + if (cursor) { > + bool have_hot = (cursor->hot_x != 0xffffffff && > + cursor->hot_y != 0xffffffff); > + dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, > + cursor->hot_x, cursor->hot_y); > + } else { > + dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); > + } > + free_bufs = true; > + } > + if (cursor != NULL) { > + dpy_gl_cursor_position(dpy->con, > + cursor->pos_x, > + cursor->pos_y); > + } > + > + dpy_gl_update(dpy->con, 0, 0, primary->buf.width, > + primary->buf.height); > + > + if (free_bufs) { > + vfio_display_free_dmabufs(vdev); > + } > +} > + > +static const GraphicHwOps vfio_display_dmabuf_ops = { > + .gfx_update = vfio_display_dmabuf_update, }; > + > +static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) > +{ > + if (!display_opengl) { > + error_setg(errp, "vfio-display-dmabuf: opengl not available"); > + return -1; > + } > + > + vdev->dpy = g_new0(VFIODisplay, 1); > + vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, > + &vfio_display_dmabuf_ops, > + vdev); > + return 0; > +} > + > +static void vfio_display_dmabuf_exit(VFIODisplay *dpy) { > + VFIODMABuf *dmabuf; > + > + if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { > + return; > + } > + > + while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { > + vfio_display_free_one_dmabuf(dpy, dmabuf); > + } > +} > + > /* ---------------------------------------------------------------------- */ > > static void vfio_display_region_update(void *opaque) @@ -136,8 +298,7 @@ > int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) > probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | > VFIO_GFX_PLANE_TYPE_DMABUF; > ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); > if (ret == 0) { > - error_setg(errp, "vfio-display: dmabuf support not implemented yet"); > - return -1; > + return vfio_display_dmabuf_init(vdev, errp); > } > > memset(&probe, 0, sizeof(probe)); > @@ -163,6 +324,7 @@ void vfio_display_finalize(VFIOPCIDevice *vdev) > return; > > graphic_console_close(vdev->dpy->con); > + vfio_display_dmabuf_exit(vdev->dpy); > vfio_display_region_exit(vdev->dpy); > g_free(vdev->dpy); > } > -- > 2.9.3 > > _______________________________________________ > intel-gvt-dev mailing list > intel-gvt-dev@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/intel-gvt-dev
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index fc8ae14fb7..994e780d51 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -26,6 +26,7 @@ #include "exec/memory.h" #include "qemu/queue.h" #include "qemu/notify.h" +#include "ui/console.h" #ifdef CONFIG_LINUX #include <linux/vfio.h> #endif @@ -142,12 +143,25 @@ typedef struct VFIOGroup { QLIST_ENTRY(VFIOGroup) container_next; } VFIOGroup; +typedef struct VFIODMABuf { + QemuDmaBuf buf; + uint32_t pos_x, pos_y; + uint32_t hot_x, hot_y; + int dmabuf_id; + QTAILQ_ENTRY(VFIODMABuf) next; +} VFIODMABuf; + typedef struct VFIODisplay { QemuConsole *con; struct { VFIORegion buffer; DisplaySurface *surface; } region; + struct { + QTAILQ_HEAD(, VFIODMABuf) bufs; + VFIODMABuf *primary; + VFIODMABuf *cursor; + } dmabuf; } VFIODisplay; void vfio_put_base_device(VFIODevice *vbasedev); diff --git a/hw/vfio/display.c b/hw/vfio/display.c index f6acbacc79..053d3ab67a 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -19,6 +19,168 @@ #include "qapi/error.h" #include "pci.h" +#ifndef DRM_PLANE_TYPE_PRIMARY +# define DRM_PLANE_TYPE_PRIMARY 1 +# define DRM_PLANE_TYPE_CURSOR 2 +#endif + +static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, + uint32_t plane_type) +{ + VFIODisplay *dpy = vdev->dpy; + struct vfio_device_gfx_plane_info plane; + VFIODMABuf *dmabuf; + int fd, ret; + + memset(&plane, 0, sizeof(plane)); + plane.argsz = sizeof(plane); + plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; + plane.drm_plane_type = plane_type; + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); + if (ret < 0) { + return NULL; + } + if (!plane.drm_format || !plane.size) { + return NULL; + } + + QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { + if (dmabuf->dmabuf_id == plane.dmabuf_id) { + /* found in list, move to head, return it */ + QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); + QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); + if (plane_type == DRM_PLANE_TYPE_CURSOR) { + dmabuf->pos_x = plane.x_pos; + dmabuf->pos_y = plane.y_pos; + } + return dmabuf; + } + } + + fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id); + if (fd < 0) { + return NULL; + } + + dmabuf = g_new0(VFIODMABuf, 1); + dmabuf->dmabuf_id = plane.dmabuf_id; + dmabuf->buf.width = plane.width; + dmabuf->buf.height = plane.height; + dmabuf->buf.stride = plane.stride; + dmabuf->buf.fourcc = plane.drm_format; + dmabuf->buf.fd = fd; + if (plane_type == DRM_PLANE_TYPE_CURSOR) { + dmabuf->pos_x = plane.x_pos; + dmabuf->pos_y = plane.y_pos; + dmabuf->hot_x = plane.x_hot; + dmabuf->hot_y = plane.y_hot; + } + + QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); + return dmabuf; +} + +static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) +{ + QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); + dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); + close(dmabuf->buf.fd); + g_free(dmabuf); +} + +static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) +{ + VFIODisplay *dpy = vdev->dpy; + VFIODMABuf *dmabuf, *tmp; + uint32_t keep = 5; + + QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { + if (keep > 0) { + keep--; + continue; + } + assert(dmabuf != dpy->dmabuf.primary); + vfio_display_free_one_dmabuf(dpy, dmabuf); + } +} + +static void vfio_display_dmabuf_update(void *opaque) +{ + VFIOPCIDevice *vdev = opaque; + VFIODisplay *dpy = vdev->dpy; + VFIODMABuf *primary, *cursor; + bool free_bufs = false; + + primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); + if (primary == NULL) { + return; + } + + if (dpy->dmabuf.primary != primary) { + dpy->dmabuf.primary = primary; + qemu_console_resize(dpy->con, + primary->buf.width, primary->buf.height); + dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); + free_bufs = true; + } + + cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); + if (dpy->dmabuf.cursor != cursor) { + dpy->dmabuf.cursor = cursor; + if (cursor) { + bool have_hot = (cursor->hot_x != 0xffffffff && + cursor->hot_y != 0xffffffff); + dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, + cursor->hot_x, cursor->hot_y); + } else { + dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); + } + free_bufs = true; + } + if (cursor != NULL) { + dpy_gl_cursor_position(dpy->con, + cursor->pos_x, + cursor->pos_y); + } + + dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); + + if (free_bufs) { + vfio_display_free_dmabufs(vdev); + } +} + +static const GraphicHwOps vfio_display_dmabuf_ops = { + .gfx_update = vfio_display_dmabuf_update, +}; + +static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) +{ + if (!display_opengl) { + error_setg(errp, "vfio-display-dmabuf: opengl not available"); + return -1; + } + + vdev->dpy = g_new0(VFIODisplay, 1); + vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, + &vfio_display_dmabuf_ops, + vdev); + return 0; +} + +static void vfio_display_dmabuf_exit(VFIODisplay *dpy) +{ + VFIODMABuf *dmabuf; + + if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { + return; + } + + while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { + vfio_display_free_one_dmabuf(dpy, dmabuf); + } +} + /* ---------------------------------------------------------------------- */ static void vfio_display_region_update(void *opaque) @@ -136,8 +298,7 @@ int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); if (ret == 0) { - error_setg(errp, "vfio-display: dmabuf support not implemented yet"); - return -1; + return vfio_display_dmabuf_init(vdev, errp); } memset(&probe, 0, sizeof(probe)); @@ -163,6 +324,7 @@ void vfio_display_finalize(VFIOPCIDevice *vdev) return; graphic_console_close(vdev->dpy->con); + vfio_display_dmabuf_exit(vdev->dpy); vfio_display_region_exit(vdev->dpy); g_free(vdev->dpy); }
Wire up dmabuf-based display. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- include/hw/vfio/vfio-common.h | 14 ++++ hw/vfio/display.c | 166 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 178 insertions(+), 2 deletions(-)