diff mbox series

[v6,9/9] vfio/display: adding dmabuf support

Message ID 20180228123110.6507-10-kraxel@redhat.com
State New
Headers show
Series vfio: add display support | expand

Commit Message

Gerd Hoffmann Feb. 28, 2018, 12:31 p.m. UTC
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(-)

Comments

Tina Zhang March 1, 2018, 8:59 a.m. UTC | #1
> -----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 mbox series

Patch

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);
 }