All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
@ 2018-07-13 13:08 Marc-André Lureau
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 01/29] chardev: avoid crash if no associated address Marc-André Lureau
                   ` (31 more replies)
  0 siblings, 32 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Hi,

vhost-user allows to drive a virtio device in a seperate
process. After vhost-user-net, we have seen
vhost-user-{scsi,blk,crypto} added more recently.

This series, initially proposed 2 years ago
(https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01905.html)
contributes with vhost-user-input and vhost-user-gpu.

Additionally, to factor out common code and ease the usage, a
vhost-user-backend is introduced as an intermediary object between the
backend and the qemu device.

You may start a vhost-user-gpu with virgl rendering in a separate
process like this:

$ ./vhost-user-gpu --virgl -s vgpu.sock &
$ qemu...
  -chardev socket,id=chr,path=vgpu.sock
  -object vhost-user-backend,id=vug,chardev=chr
  -device vhost-user-vga,vhost-user=vug

You may also specify the backend command and the arguments as part of
vhost-user-backend qemu arguments. For example, to start a
vhost-user-input backend on input device /dev/input/event19:

-object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event19"
-device virtio-input-host-pci,vhost-user=vuid

The vhost-user-gpu backend requires virgl from git.

The libvirt support is on-going work:
https://github.com/elmarco/libvirt/commits/vhost-user-gpu

The GPU benchmarks are encouraging, giving up to x5 performance on
Unigine Heaven 4.0.

Feedback welcome,

v4:
 - move qemu_write_pidfile() in util, improve it a bit
 - add --pid and --fd arguments to vhost-user to help with libvirt support
 - various bug fixes for synchronization, and tearing down

v3: deal with most comments from rfcv2 and various improvements
 - "vhost-user-backend: allow to specify binary to execute" as seperate
  patch, not for inclusion since Daniel as concerned about parsing
  shell strings with glib
 - use dmabuf to share 2d rendering result (with intel gem only atm)
 - document the vhost-user-gpu protocol
 - make vhost-user-gpu-pci and vhost-user-vga seperate devices (instead
   of adding vhost-user support to existing devices)
 - allow to specify virgl rendering, and rendernode
 - let's promote out of RFC status :)

RFCv2: (addressing some of Gerd comments digged in the archives)
 - rebased, clean ups, various small fixes, update commit messages
 - teach the vhost-user-backend to take a chardev
 - add vhost-user-input-pci, instead of adding vhost code to virtio-input-host-pci

Marc-André Lureau (29):
  chardev: avoid crash if no associated address
  chardev: remove qemu_chr_fe_read_all() counter
  chardev: unref if underlying chardev has no parent
  dmabuf: add y0_top, pass it to spice
  vhost-user: simplify vhost_user_init/vhost_user_cleanup
  libvhost-user: exit by default on VHOST_USER_NONE
  vhost-user: wrap some read/write with retry handling
  Add vhost-user-backend
  qio: add qio_channel_command_new_spawn_with_pre_exec()
  HACK: vhost-user-backend: allow to specify binary to execute
  vhost-user: split vhost_user_read()
  vhost-user: add vhost_user_input_get_config()
  libvhost-user: export vug_source_new()
  contrib: add vhost-user-input
  Add vhost-user-input-pci
  vhost-user: add vhost_user_gpu_set_socket()
  vhost-user: add vhost_user_gpu_get_num_capsets()
  virtio: add virtio-gpu bswap helpers header
  util: promote qemu_egl_rendernode_open() to libqemuutil
  util: add qemu_write_pidfile()
  util: use fcntl() for qemu_write_pidfile() locking
  contrib: add vhost-user-gpu
  virtio-gpu: remove unused qdev
  virtio-gpu: remove unused config_size
  virtio-gpu: block both 2d and 3d rendering
  virtio-gpu: remove useless 'waiting' field
  virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
  virtio-gpu: split virtio-gpu-pci & virtio-vga
  hw/display: add vhost-user-vga & gpu-pci

 contrib/libvhost-user/libvhost-user-glib.h |    3 +
 contrib/libvhost-user/libvhost-user.h      |    3 +
 contrib/vhost-user-gpu/drm.h               |   63 ++
 contrib/vhost-user-gpu/virgl.h             |   25 +
 contrib/vhost-user-gpu/vugpu.h             |  168 +++
 hw/display/virtio-vga.h                    |   22 +
 hw/virtio/virtio-pci.h                     |   27 +-
 include/hw/virtio/vhost-backend.h          |    6 +
 include/hw/virtio/vhost-user-blk.h         |    2 +-
 include/hw/virtio/vhost-user-scsi.h        |    2 +-
 include/hw/virtio/vhost-user.h             |    2 +-
 include/hw/virtio/virtio-gpu-bswap.h       |   61 +
 include/hw/virtio/virtio-gpu.h             |   90 +-
 include/hw/virtio/virtio-input.h           |   14 +
 include/io/channel-command.h               |   18 +
 include/qemu/drm.h                         |    6 +
 include/qemu/osdep.h                       |    3 +-
 include/sysemu/vhost-user-backend.h        |   60 +
 include/ui/console.h                       |    1 +
 backends/cryptodev-vhost-user.c            |   18 +-
 backends/vhost-user.c                      |  320 ++++++
 chardev/char-fe.c                          |   13 +-
 chardev/char-socket.c                      |    8 +-
 contrib/libvhost-user/libvhost-user-glib.c |   15 +-
 contrib/libvhost-user/libvhost-user.c      |    3 +-
 contrib/vhost-user-gpu/drm.c               |  190 ++++
 contrib/vhost-user-gpu/main.c              | 1164 ++++++++++++++++++++
 contrib/vhost-user-gpu/virgl.c             |  579 ++++++++++
 contrib/vhost-user-input/main.c            |  379 +++++++
 hw/block/vhost-user-blk.c                  |   22 +-
 hw/display/vhost-user-gpu-pci.c            |   51 +
 hw/display/vhost-user-gpu.c                |  524 +++++++++
 hw/display/vhost-user-vga.c                |   52 +
 hw/display/virtio-gpu-3d.c                 |   70 +-
 hw/display/virtio-gpu-base.c               |  292 +++++
 hw/display/virtio-gpu-pci.c                |   39 +-
 hw/display/virtio-gpu.c                    |  368 ++-----
 hw/display/virtio-vga.c                    |  127 ++-
 hw/input/vhost-user-input.c                |  110 ++
 hw/scsi/vhost-user-scsi.c                  |   20 +-
 hw/virtio/vhost-stub.c                     |    4 +-
 hw/virtio/vhost-user.c                     |  143 ++-
 hw/virtio/virtio-pci.c                     |   20 +
 io/channel-command.c                       |   33 +-
 net/vhost-user.c                           |   13 +-
 os-posix.c                                 |   24 -
 os-win32.c                                 |   25 -
 qga/main.c                                 |   54 +-
 scsi/qemu-pr-helper.c                      |   40 +-
 ui/egl-helpers.c                           |   51 +-
 ui/spice-display.c                         |    3 +-
 util/drm.c                                 |   66 ++
 util/oslib-posix.c                         |   39 +
 util/oslib-win32.c                         |   27 +
 vl.c                                       |    8 +-
 MAINTAINERS                                |   15 +-
 Makefile                                   |    6 +
 Makefile.objs                              |    2 +
 backends/Makefile.objs                     |    3 +-
 configure                                  |   35 +
 contrib/vhost-user-gpu/Makefile.objs       |   10 +
 contrib/vhost-user-input/Makefile.objs     |    1 +
 docs/interop/vhost-user-gpu.rst            |  236 ++++
 docs/interop/vhost-user.txt                |   24 +
 hw/display/Makefile.objs                   |    5 +-
 hw/input/Makefile.objs                     |    1 +
 qemu-options.hx                            |   24 +
 util/Makefile.objs                         |    1 +
 68 files changed, 5130 insertions(+), 723 deletions(-)
 create mode 100644 contrib/vhost-user-gpu/drm.h
 create mode 100644 contrib/vhost-user-gpu/virgl.h
 create mode 100644 contrib/vhost-user-gpu/vugpu.h
 create mode 100644 hw/display/virtio-vga.h
 create mode 100644 include/hw/virtio/virtio-gpu-bswap.h
 create mode 100644 include/qemu/drm.h
 create mode 100644 include/sysemu/vhost-user-backend.h
 create mode 100644 backends/vhost-user.c
 create mode 100644 contrib/vhost-user-gpu/drm.c
 create mode 100644 contrib/vhost-user-gpu/main.c
 create mode 100644 contrib/vhost-user-gpu/virgl.c
 create mode 100644 contrib/vhost-user-input/main.c
 create mode 100644 hw/display/vhost-user-gpu-pci.c
 create mode 100644 hw/display/vhost-user-gpu.c
 create mode 100644 hw/display/vhost-user-vga.c
 create mode 100644 hw/display/virtio-gpu-base.c
 create mode 100644 hw/input/vhost-user-input.c
 create mode 100644 util/drm.c
 create mode 100644 contrib/vhost-user-gpu/Makefile.objs
 create mode 100644 contrib/vhost-user-input/Makefile.objs
 create mode 100644 docs/interop/vhost-user-gpu.rst

-- 
2.18.0.129.ge3331758f1

^ permalink raw reply	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 01/29] chardev: avoid crash if no associated address
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 02/29] chardev: remove qemu_chr_fe_read_all() counter Marc-André Lureau
                   ` (30 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

A socket chardev may not have associated address (when adding client
fd manually for example). But on disconnect, updating socket filename
expects an address and may lead to this crash:

  Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
  0x0000555555d8c70c in SocketAddress_to_str (prefix=0x555556043062 "disconnected:", addr=0x0, is_listen=false, is_telnet=false) at /home/elmarco/src/qq/chardev/char-socket.c:388
  388	    switch (addr->type) {
  (gdb) bt
  #0  0x0000555555d8c70c in SocketAddress_to_str (prefix=0x555556043062 "disconnected:", addr=0x0, is_listen=false, is_telnet=false) at /home/elmarco/src/qq/chardev/char-socket.c:388
  #1  0x0000555555d8c8aa in update_disconnected_filename (s=0x555556b1ed00) at /home/elmarco/src/qq/chardev/char-socket.c:419
  #2  0x0000555555d8c959 in tcp_chr_disconnect (chr=0x555556b1ed00) at /home/elmarco/src/qq/chardev/char-socket.c:438
  #3  0x0000555555d8cba1 in tcp_chr_hup (channel=0x555556b75690, cond=G_IO_HUP, opaque=0x555556b1ed00) at /home/elmarco/src/qq/chardev/char-socket.c:482
  #4  0x0000555555da596e in qio_channel_fd_source_dispatch (source=0x555556bb68b0, callback=0x555555d8cb58 <tcp_chr_hup>, user_data=0x555556b1ed00) at /home/elmarco/src/qq/io/channel-watch.c:84

Replace filename with a generic "disconnected:socket" in this case.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 chardev/char-socket.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index efbad6ee7c..fa5bfb3b0e 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -419,8 +419,12 @@ static void update_disconnected_filename(SocketChardev *s)
     Chardev *chr = CHARDEV(s);
 
     g_free(chr->filename);
-    chr->filename = SocketAddress_to_str("disconnected:", s->addr,
-                                         s->is_listen, s->is_telnet);
+    if (s->addr) {
+        chr->filename = SocketAddress_to_str("disconnected:", s->addr,
+                                             s->is_listen, s->is_telnet);
+    } else {
+        chr->filename = g_strdup("disconnected:socket");
+    }
 }
 
 /* NB may be called even if tcp_chr_connect has not been
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 02/29] chardev: remove qemu_chr_fe_read_all() counter
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 01/29] chardev: avoid crash if no associated address Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-08-28 15:05   ` Daniel P. Berrangé
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 03/29] chardev: unref if underlying chardev has no parent Marc-André Lureau
                   ` (29 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

There is no obvious reason to have a loop counter. This limits from
reading several megabytes large buffers in one go, since socket
read/write usually have a limit.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
---
 chardev/char-fe.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index b1f228e8b5..f158f158f8 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -56,7 +56,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
 int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
 {
     Chardev *s = be->chr;
-    int offset = 0, counter = 10;
+    int offset = 0;
     int res;
 
     if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
@@ -88,10 +88,6 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
         }
 
         offset += res;
-
-        if (!counter--) {
-            break;
-        }
     }
 
     if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 03/29] chardev: unref if underlying chardev has no parent
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 01/29] chardev: avoid crash if no associated address Marc-André Lureau
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 02/29] chardev: remove qemu_chr_fe_read_all() counter Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-08-28 15:06   ` Daniel P. Berrangé
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 04/29] dmabuf: add y0_top, pass it to spice Marc-André Lureau
                   ` (28 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

It's possible to write code creating a chardev backend that is not
registered. When it is not user-created, it makes sense to keep it
hidden. Let the associated frontend destroy it also in this case.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 chardev/char-fe.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index f158f158f8..a8931f7afd 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -235,7 +235,12 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del)
             d->backends[b->tag] = NULL;
         }
         if (del) {
-            object_unparent(OBJECT(b->chr));
+            Object *obj = OBJECT(b->chr);
+            if (obj->parent) {
+                object_unparent(obj);
+            } else {
+                object_unref(obj);
+            }
         }
         b->chr = NULL;
     }
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 04/29] dmabuf: add y0_top, pass it to spice
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (2 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 03/29] chardev: unref if underlying chardev has no parent Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-08-21  6:25   ` Gerd Hoffmann
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 05/29] vhost-user: simplify vhost_user_init/vhost_user_cleanup Marc-André Lureau
                   ` (27 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Some scanouts during boot are top-down without it.

y0_top is set from VHOST_USER_GPU_DMABUF_SCANOUT code path in the last
patch of this series.

In current QEMU code base, only vfio/display uses dmabuf API. But the
VFIO query interface doesn't provide or need that detail so far.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/console.h | 1 +
 ui/spice-display.c   | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/ui/console.h b/include/ui/console.h
index 981b519dde..fb969caf70 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -186,6 +186,7 @@ struct QemuDmaBuf {
     uint32_t  stride;
     uint32_t  fourcc;
     uint32_t  texture;
+    bool      y0_top;
 };
 
 typedef struct DisplayChangeListenerOps {
diff --git a/ui/spice-display.c b/ui/spice-display.c
index fe734821dd..81f08a85bc 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -1048,7 +1048,8 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
             /* note: spice server will close the fd, so hand over a dup */
             spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd),
                                  dmabuf->width, dmabuf->height,
-                                 dmabuf->stride, dmabuf->fourcc, false);
+                                 dmabuf->stride, dmabuf->fourcc,
+                                 dmabuf->y0_top);
         }
         qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height);
         ssd->guest_dmabuf_refresh = false;
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 05/29] vhost-user: simplify vhost_user_init/vhost_user_cleanup
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (3 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 04/29] dmabuf: add y0_top, pass it to spice Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 06/29] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
                   ` (26 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Take a VhostUserState* that can be pre-allocated, and initialize it
with the associated chardev.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Tiwei Bie <tiwei.bie@intel.com>
---
 include/hw/virtio/vhost-user-blk.h  |  2 +-
 include/hw/virtio/vhost-user-scsi.h |  2 +-
 include/hw/virtio/vhost-user.h      |  2 +-
 backends/cryptodev-vhost-user.c     | 18 ++++--------------
 hw/block/vhost-user-blk.c           | 22 ++++------------------
 hw/scsi/vhost-user-scsi.c           | 20 ++++----------------
 hw/virtio/vhost-stub.c              |  4 ++--
 hw/virtio/vhost-user.c              | 16 ++++++++++++----
 net/vhost-user.c                    | 13 ++++---------
 9 files changed, 33 insertions(+), 66 deletions(-)

diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h
index d52944aeeb..a8a106eecb 100644
--- a/include/hw/virtio/vhost-user-blk.h
+++ b/include/hw/virtio/vhost-user-blk.h
@@ -36,7 +36,7 @@ typedef struct VHostUserBlk {
     uint32_t queue_size;
     uint32_t config_wce;
     struct vhost_dev dev;
-    VhostUserState *vhost_user;
+    VhostUserState vhost_user;
 } VHostUserBlk;
 
 #endif
diff --git a/include/hw/virtio/vhost-user-scsi.h b/include/hw/virtio/vhost-user-scsi.h
index 3ec34ae867..7c22bdc957 100644
--- a/include/hw/virtio/vhost-user-scsi.h
+++ b/include/hw/virtio/vhost-user-scsi.h
@@ -31,7 +31,7 @@
 typedef struct VHostUserSCSI {
     VHostSCSICommon parent_obj;
     uint64_t host_features;
-    VhostUserState *vhost_user;
+    VhostUserState vhost_user;
 } VHostUserSCSI;
 
 #endif /* VHOST_USER_SCSI_H */
diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h
index fd660393a0..811e325f42 100644
--- a/include/hw/virtio/vhost-user.h
+++ b/include/hw/virtio/vhost-user.h
@@ -22,7 +22,7 @@ typedef struct VhostUserState {
     VhostUserHostNotifier notifier[VIRTIO_QUEUE_MAX];
 } VhostUserState;
 
-VhostUserState *vhost_user_init(void);
+bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp);
 void vhost_user_cleanup(VhostUserState *user);
 
 #endif
diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index d52daccfcd..63573e211e 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -47,7 +47,7 @@
 typedef struct CryptoDevBackendVhostUser {
     CryptoDevBackend parent_obj;
 
-    VhostUserState *vhost_user;
+    VhostUserState vhost_user;
     CharBackend chr;
     char *chr_name;
     bool opened;
@@ -104,7 +104,7 @@ cryptodev_vhost_user_start(int queues,
             continue;
         }
 
-        options.opaque = s->vhost_user;
+        options.opaque = &s->vhost_user;
         options.backend_type = VHOST_BACKEND_TYPE_USER;
         options.cc = b->conf.peers.ccs[i];
         s->vhost_crypto[i] = cryptodev_vhost_init(&options);
@@ -187,7 +187,6 @@ static void cryptodev_vhost_user_init(
     size_t i;
     Error *local_err = NULL;
     Chardev *chr;
-    VhostUserState *user;
     CryptoDevBackendClient *cc;
     CryptoDevBackendVhostUser *s =
                       CRYPTODEV_BACKEND_VHOST_USER(backend);
@@ -218,15 +217,10 @@ static void cryptodev_vhost_user_init(
         }
     }
 
-    user = vhost_user_init();
-    if (!user) {
-        error_setg(errp, "Failed to init vhost_user");
+    if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) {
         return;
     }
 
-    user->chr = &s->chr;
-    s->vhost_user = user;
-
     qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
                      cryptodev_vhost_user_event, NULL, s, NULL, true);
 
@@ -312,11 +306,7 @@ static void cryptodev_vhost_user_cleanup(
         }
     }
 
-    if (s->vhost_user) {
-        vhost_user_cleanup(s->vhost_user);
-        g_free(s->vhost_user);
-        s->vhost_user = NULL;
-    }
+    vhost_user_cleanup(&s->vhost_user);
 }
 
 static void cryptodev_vhost_user_set_chardev(Object *obj,
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index d755223643..5a2c6fe5f3 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -224,7 +224,6 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
 {
     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
     VHostUserBlk *s = VHOST_USER_BLK(vdev);
-    VhostUserState *user;
     int i, ret;
 
     if (!s->chardev.chr) {
@@ -242,15 +241,10 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
         return;
     }
 
-    user = vhost_user_init();
-    if (!user) {
-        error_setg(errp, "vhost-user-blk: failed to init vhost_user");
+    if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
         return;
     }
 
-    user->chr = &s->chardev;
-    s->vhost_user = user;
-
     virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
                 sizeof(struct virtio_blk_config));
 
@@ -266,7 +260,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
 
     vhost_dev_set_config_notifier(&s->dev, &blk_ops);
 
-    ret = vhost_dev_init(&s->dev, s->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
+    ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
     if (ret < 0) {
         error_setg(errp, "vhost-user-blk: vhost initialization failed: %s",
                    strerror(-ret));
@@ -291,10 +285,7 @@ vhost_err:
 virtio_err:
     g_free(s->dev.vqs);
     virtio_cleanup(vdev);
-
-    vhost_user_cleanup(user);
-    g_free(user);
-    s->vhost_user = NULL;
+    vhost_user_cleanup(&s->vhost_user);
 }
 
 static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
@@ -306,12 +297,7 @@ static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
     vhost_dev_cleanup(&s->dev);
     g_free(s->dev.vqs);
     virtio_cleanup(vdev);
-
-    if (s->vhost_user) {
-        vhost_user_cleanup(s->vhost_user);
-        g_free(s->vhost_user);
-        s->vhost_user = NULL;
-    }
+    vhost_user_cleanup(&s->vhost_user);
 }
 
 static void vhost_user_blk_instance_init(Object *obj)
diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c
index 9355cfdf07..b620725bc2 100644
--- a/hw/scsi/vhost-user-scsi.c
+++ b/hw/scsi/vhost-user-scsi.c
@@ -69,7 +69,6 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
     VHostUserSCSI *s = VHOST_USER_SCSI(dev);
     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
-    VhostUserState *user;
     Error *err = NULL;
     int ret;
 
@@ -86,30 +85,24 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
         return;
     }
 
-    user = vhost_user_init();
-    if (!user) {
-        error_setg(errp, "vhost-user-scsi: failed to init vhost_user");
+    if (!vhost_user_init(&s->vhost_user, &vs->conf.chardev, errp)) {
         return;
     }
-    user->chr = &vs->conf.chardev;
 
     vsc->dev.nvqs = 2 + vs->conf.num_queues;
     vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs);
     vsc->dev.vq_index = 0;
     vsc->dev.backend_features = 0;
 
-    ret = vhost_dev_init(&vsc->dev, user,
+    ret = vhost_dev_init(&vsc->dev, &s->vhost_user,
                          VHOST_BACKEND_TYPE_USER, 0);
     if (ret < 0) {
         error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s",
                    strerror(-ret));
-        vhost_user_cleanup(user);
-        g_free(user);
+        vhost_user_cleanup(&s->vhost_user);
         return;
     }
 
-    s->vhost_user = user;
-
     /* Channel and lun both are 0 for bootable vhost-user-scsi disk */
     vsc->channel = 0;
     vsc->lun = 0;
@@ -129,12 +122,7 @@ static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp)
     g_free(vsc->dev.vqs);
 
     virtio_scsi_common_unrealize(dev, errp);
-
-    if (s->vhost_user) {
-        vhost_user_cleanup(s->vhost_user);
-        g_free(s->vhost_user);
-        s->vhost_user = NULL;
-    }
+    vhost_user_cleanup(&s->vhost_user);
 }
 
 static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev,
diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c
index 049089b5e2..c175148fce 100644
--- a/hw/virtio/vhost-stub.c
+++ b/hw/virtio/vhost-stub.c
@@ -7,9 +7,9 @@ bool vhost_has_free_slot(void)
     return true;
 }
 
-VhostUserState *vhost_user_init(void)
+bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp)
 {
-    return NULL;
+    return false;
 }
 
 void vhost_user_cleanup(VhostUserState *user)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index b041343632..44795880d6 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -1743,17 +1743,24 @@ static bool vhost_user_mem_section_filter(struct vhost_dev *dev,
     return result;
 }
 
-VhostUserState *vhost_user_init(void)
+bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp)
 {
-    VhostUserState *user = g_new0(struct VhostUserState, 1);
-
-    return user;
+    if (user->chr) {
+        error_setg(errp, "Cannot initialize vhost-user state");
+        return false;
+    }
+    user->chr = chr;
+    return true;
 }
 
 void vhost_user_cleanup(VhostUserState *user)
 {
     int i;
 
+    if (!user->chr) {
+        return;
+    }
+
     for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
         if (user->notifier[i].addr) {
             object_unparent(OBJECT(&user->notifier[i].mr));
@@ -1761,6 +1768,7 @@ void vhost_user_cleanup(VhostUserState *user)
             user->notifier[i].addr = NULL;
         }
     }
+    user->chr = NULL;
 }
 
 const VhostOps user_ops = {
diff --git a/net/vhost-user.c b/net/vhost-user.c
index a39f9c9974..cea78b81f6 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -291,19 +291,14 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
 {
     Error *err = NULL;
     NetClientState *nc, *nc0 = NULL;
-    VhostUserState *user = NULL;
     NetVhostUserState *s = NULL;
+    VhostUserState *user;
     int i;
 
     assert(name);
     assert(queues > 0);
 
-    user = vhost_user_init();
-    if (!user) {
-        error_report("failed to init vhost_user");
-        goto err;
-    }
-
+    user = g_new0(struct VhostUserState, 1);
     for (i = 0; i < queues; i++) {
         nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
         snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
@@ -312,11 +307,11 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
         if (!nc0) {
             nc0 = nc;
             s = DO_UPCAST(NetVhostUserState, nc, nc);
-            if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
+            if (!qemu_chr_fe_init(&s->chr, chr, &err) ||
+                !vhost_user_init(user, &s->chr, &err)) {
                 error_report_err(err);
                 goto err;
             }
-            user->chr = &s->chr;
         }
         s = DO_UPCAST(NetVhostUserState, nc, nc);
         s->vhost_user = user;
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 06/29] libvhost-user: exit by default on VHOST_USER_NONE
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (4 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 05/29] vhost-user: simplify vhost_user_init/vhost_user_cleanup Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-08-28 13:12   ` Jens Freimann
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 07/29] vhost-user: wrap some read/write with retry handling Marc-André Lureau
                   ` (25 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Since commit 2566378d6d13bf4d28c7770bdbda5f7682594bbe, libvhost-user
no longer panics on disconnect (rc == 0), and instead silently ignores
an invalid VHOST_USER_NONE message.

Without extra work from the API user, this will simply busy-loop on
HUP events. The obvious thing to do is to exit(0) instead, while
additional or different work can be done by overriding
iface->process_msg().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/libvhost-user/libvhost-user.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c
index a6b46cdc03..41470daf57 100644
--- a/contrib/libvhost-user/libvhost-user.c
+++ b/contrib/libvhost-user/libvhost-user.c
@@ -1285,7 +1285,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
     case VHOST_USER_SET_CONFIG:
         return vu_set_config(dev, vmsg);
     case VHOST_USER_NONE:
-        break;
+        /* if you need processing before exit, override iface->process_msg */
+        exit(0);
     case VHOST_USER_POSTCOPY_ADVISE:
         return vu_set_postcopy_advise(dev, vmsg);
     case VHOST_USER_POSTCOPY_LISTEN:
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 07/29] vhost-user: wrap some read/write with retry handling
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (5 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 06/29] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 08/29] Add vhost-user-backend Marc-André Lureau
                   ` (24 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
 hw/virtio/vhost-user.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 44795880d6..5b4188bc27 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -975,7 +975,10 @@ static void slave_read(void *opaque)
     iov.iov_base = &hdr;
     iov.iov_len = VHOST_USER_HDR_SIZE;
 
-    size = recvmsg(u->slave_fd, &msgh, 0);
+    do {
+        size = recvmsg(u->slave_fd, &msgh, 0);
+    } while (size < 0 && (errno == EINTR || errno == EAGAIN));
+
     if (size != VHOST_USER_HDR_SIZE) {
         error_report("Failed to read from slave.");
         goto err;
@@ -1004,7 +1007,10 @@ static void slave_read(void *opaque)
     }
 
     /* Read payload */
-    size = read(u->slave_fd, &payload, hdr.size);
+    do {
+        size = read(u->slave_fd, &payload, hdr.size);
+    } while (size < 0 && (errno == EINTR || errno == EAGAIN));
+
     if (size != hdr.size) {
         error_report("Failed to read payload from slave.");
         goto err;
@@ -1052,7 +1058,10 @@ static void slave_read(void *opaque)
         iovec[1].iov_base = &payload;
         iovec[1].iov_len = hdr.size;
 
-        size = writev(u->slave_fd, iovec, ARRAY_SIZE(iovec));
+        do {
+            size = writev(u->slave_fd, iovec, ARRAY_SIZE(iovec));
+        } while (size < 0 && (errno == EINTR || errno == EAGAIN));
+
         if (size != VHOST_USER_HDR_SIZE + hdr.size) {
             error_report("Failed to send msg reply to slave.");
             goto err;
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 08/29] Add vhost-user-backend
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (6 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 07/29] vhost-user: wrap some read/write with retry handling Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 09/29] qio: add qio_channel_command_new_spawn_with_pre_exec() Marc-André Lureau
                   ` (23 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Create a vhost-user-backend object that holds a connection to a
vhost-user backend and can be referenced from virtio devices that
support it. See later patches for input & gpu usage.

A chardev can be specified to communicate with the vhost-user backend,
ex: -chardev socket,id=char0,path=/tmp/foo.sock -object
vhost-user-backend,id=vuid,chardev=char0.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/sysemu/vhost-user-backend.h |  60 +++++++
 backends/vhost-user.c               | 244 ++++++++++++++++++++++++++++
 vl.c                                |   3 +-
 MAINTAINERS                         |   2 +
 backends/Makefile.objs              |   3 +-
 qemu-options.hx                     |  20 +++
 6 files changed, 330 insertions(+), 2 deletions(-)
 create mode 100644 include/sysemu/vhost-user-backend.h
 create mode 100644 backends/vhost-user.c

diff --git a/include/sysemu/vhost-user-backend.h b/include/sysemu/vhost-user-backend.h
new file mode 100644
index 0000000000..60f811cae7
--- /dev/null
+++ b/include/sysemu/vhost-user-backend.h
@@ -0,0 +1,60 @@
+/*
+ * QEMU vhost-user backend
+ *
+ * Copyright (C) 2018 Red Hat Inc
+ *
+ * Authors:
+ *  Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef QEMU_VHOST_USER_BACKEND_H
+#define QEMU_VHOST_USER_BACKEND_H
+
+#include "qom/object.h"
+#include "exec/memory.h"
+#include "qemu/option.h"
+#include "qemu/bitmap.h"
+#include "hw/virtio/vhost.h"
+#include "hw/virtio/vhost-user.h"
+#include "chardev/char-fe.h"
+#include "io/channel.h"
+
+#define TYPE_VHOST_USER_BACKEND "vhost-user-backend"
+#define VHOST_USER_BACKEND(obj) \
+    OBJECT_CHECK(VhostUserBackend, (obj), TYPE_VHOST_USER_BACKEND)
+#define VHOST_USER_BACKEND_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(VhostUserBackendClass, (obj), TYPE_VHOST_USER_BACKEND)
+#define VHOST_USER_BACKEND_CLASS(klass) \
+    OBJECT_CLASS_CHECK(VhostUserBackendClass, (klass), TYPE_VHOST_USER_BACKEND)
+
+typedef struct VhostUserBackend VhostUserBackend;
+typedef struct VhostUserBackendClass VhostUserBackendClass;
+
+struct VhostUserBackendClass {
+    ObjectClass parent_class;
+};
+
+struct VhostUserBackend {
+    /* private */
+    Object parent;
+
+    char *cmd;
+    char *chr_name;
+
+    CharBackend chr;
+    VhostUserState vhost_user;
+    struct vhost_dev dev;
+    QIOChannel *child;
+    VirtIODevice *vdev;
+    bool started;
+    bool completed;
+};
+
+int vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
+                                unsigned nvqs, Error **errp);
+void vhost_user_backend_start(VhostUserBackend *b);
+void vhost_user_backend_stop(VhostUserBackend *b);
+
+#endif
diff --git a/backends/vhost-user.c b/backends/vhost-user.c
new file mode 100644
index 0000000000..bf39c0751d
--- /dev/null
+++ b/backends/vhost-user.c
@@ -0,0 +1,244 @@
+/*
+ * QEMU vhost-user backend
+ *
+ * Copyright (C) 2018 Red Hat Inc
+ *
+ * Authors:
+ *  Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+
+#include "qemu/osdep.h"
+#include "hw/qdev.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
+#include "qom/object_interfaces.h"
+#include "sysemu/vhost-user-backend.h"
+#include "sysemu/kvm.h"
+#include "io/channel-command.h"
+#include "hw/virtio/virtio-bus.h"
+
+static bool
+ioeventfd_enabled(void)
+{
+    return kvm_enabled() && kvm_eventfds_enabled();
+}
+
+int
+vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
+                            unsigned nvqs, Error **errp)
+{
+    int ret;
+
+    assert(!b->vdev && vdev);
+
+    if (!ioeventfd_enabled()) {
+        error_setg(errp, "vhost initialization failed: requires kvm");
+        return -1;
+    }
+
+    if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) {
+        return -1;
+    }
+
+    b->vdev = vdev;
+    b->dev.nvqs = nvqs;
+    b->dev.vqs = g_new(struct vhost_virtqueue, nvqs);
+
+    ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
+    if (ret < 0) {
+        error_setg_errno(errp, -ret, "vhost initialization failed");
+        return -1;
+    }
+
+    return 0;
+}
+
+void
+vhost_user_backend_start(VhostUserBackend *b)
+{
+    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
+    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+    int ret, i ;
+
+    if (b->started) {
+        return;
+    }
+
+    if (!k->set_guest_notifiers) {
+        error_report("binding does not support guest notifiers");
+        return;
+    }
+
+    ret = vhost_dev_enable_notifiers(&b->dev, b->vdev);
+    if (ret < 0) {
+        return;
+    }
+
+    ret = k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true);
+    if (ret < 0) {
+        error_report("Error binding guest notifier");
+        goto err_host_notifiers;
+    }
+
+    b->dev.acked_features = b->vdev->guest_features;
+    ret = vhost_dev_start(&b->dev, b->vdev);
+    if (ret < 0) {
+        error_report("Error start vhost dev");
+        goto err_guest_notifiers;
+    }
+
+    /* guest_notifier_mask/pending not used yet, so just unmask
+     * everything here.  virtio-pci will do the right thing by
+     * enabling/disabling irqfd.
+     */
+    for (i = 0; i < b->dev.nvqs; i++) {
+        vhost_virtqueue_mask(&b->dev, b->vdev,
+                             b->dev.vq_index + i, false);
+    }
+
+    b->started = true;
+    return;
+
+err_guest_notifiers:
+    k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false);
+err_host_notifiers:
+    vhost_dev_disable_notifiers(&b->dev, b->vdev);
+}
+
+void
+vhost_user_backend_stop(VhostUserBackend *b)
+{
+    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
+    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+    int ret = 0;
+
+    if (!b->started) {
+        return;
+    }
+
+    vhost_dev_stop(&b->dev, b->vdev);
+
+    if (k->set_guest_notifiers) {
+        ret = k->set_guest_notifiers(qbus->parent,
+                                     b->dev.nvqs, false);
+        if (ret < 0) {
+            error_report("vhost guest notifier cleanup failed: %d", ret);
+        }
+    }
+    assert(ret >= 0);
+
+    vhost_dev_disable_notifiers(&b->dev, b->vdev);
+    b->started = false;
+}
+
+static void
+vhost_user_backend_complete(UserCreatable *uc, Error **errp)
+{
+    VhostUserBackend *b = VHOST_USER_BACKEND(uc);
+    Chardev *chr;
+
+    if (!b->chr_name) {
+        error_setg(errp, "You must specificy 'chardev'.");
+        return;
+    }
+
+    chr = qemu_chr_find(b->chr_name);
+    if (chr == NULL) {
+        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+                  "Chardev '%s' not found", b->chr_name);
+        return;
+    }
+
+    if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
+        return;
+    }
+
+    b->completed = true;
+    /* could vhost_dev_init() happen here, so early vhost-user message
+     * can be exchanged */
+}
+
+static void set_chardev(Object *obj, const char *value, Error **errp)
+{
+    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
+
+    if (b->completed) {
+        error_setg(errp, QERR_PERMISSION_DENIED);
+    } else {
+        g_free(b->chr_name);
+        b->chr_name = g_strdup(value);
+    }
+}
+
+static char *get_chardev(Object *obj, Error **errp)
+{
+    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
+    Chardev *chr = qemu_chr_fe_get_driver(&b->chr);
+
+    if (chr && chr->label) {
+        return g_strdup(chr->label);
+    }
+
+    return NULL;
+}
+
+static void vhost_user_backend_init(Object *obj)
+{
+    object_property_add_str(obj, "chardev", get_chardev, set_chardev, NULL);
+}
+
+static void vhost_user_backend_finalize(Object *obj)
+{
+    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
+
+    g_free(b->dev.vqs);
+    g_free(b->chr_name);
+
+    vhost_user_cleanup(&b->vhost_user);
+    qemu_chr_fe_deinit(&b->chr, true);
+
+    if (b->child) {
+        object_unref(OBJECT(b->child));
+    }
+}
+
+static bool
+vhost_user_backend_can_be_deleted(UserCreatable *uc)
+{
+    return true;
+}
+
+static void
+vhost_user_backend_class_init(ObjectClass *oc, void *data)
+{
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+    ucc->complete = vhost_user_backend_complete;
+    ucc->can_be_deleted = vhost_user_backend_can_be_deleted;
+}
+
+static const TypeInfo vhost_user_backend_info = {
+    .name = TYPE_VHOST_USER_BACKEND,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(VhostUserBackend),
+    .instance_init = vhost_user_backend_init,
+    .instance_finalize = vhost_user_backend_finalize,
+    .class_size = sizeof(VhostUserBackendClass),
+    .class_init = vhost_user_backend_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+static void register_types(void)
+{
+    type_register_static(&vhost_user_backend_info);
+}
+
+type_init(register_types);
diff --git a/vl.c b/vl.c
index 16b913f9d5..5272b4939f 100644
--- a/vl.c
+++ b/vl.c
@@ -2737,7 +2737,8 @@ static bool object_create_initial(const char *type)
     }
 
 #if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
-    if (g_str_equal(type, "cryptodev-vhost-user")) {
+    if (g_str_equal(type, "cryptodev-vhost-user") ||
+        g_str_equal(type, "vhost-user-backend")) {
         return false;
     }
 #endif
diff --git a/MAINTAINERS b/MAINTAINERS
index 20eef3cb61..dd8d07651f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1189,6 +1189,8 @@ M: Michael S. Tsirkin <mst@redhat.com>
 S: Supported
 F: hw/*/*vhost*
 F: docs/interop/vhost-user.txt
+F: backends/vhost-user.c
+F: include/sysemu/vhost-user-backend.h
 
 virtio
 M: Michael S. Tsirkin <mst@redhat.com>
diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index ad7c0325ed..1ffcf76bb1 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -12,7 +12,8 @@ common-obj-y += cryptodev-builtin.o
 ifeq ($(CONFIG_VIRTIO),y)
 common-obj-y += cryptodev-vhost.o
 common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
-    cryptodev-vhost-user.o
+    cryptodev-vhost-user.o \
+    vhost-user.o
 endif
 
 common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
diff --git a/qemu-options.hx b/qemu-options.hx
index 654e69cc3b..413b97d5e9 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4292,6 +4292,26 @@ secondary:
 If you want to know the detail of above command line, you can read
 the colo-compare git log.
 
+@item -object vhost-user-backend,id=id=@var{id},chardev=@var{chardevid}
+
+Create a vhost-user-backend object that holds a connection to a
+vhost-user backend and can be referenced from virtio/vhost-user
+devices that support it.
+
+The @var{id} parameter is a unique ID that will be used to reference
+this vhost-user backend from the @option{vhost-user} device. The
+@var{chardev} parameter is the unique ID of a character device backend
+that provides the connection to the vhost-user slave process. (Since 3.0)
+
+@example
+
+ # qemu-system-x86_64 \
+   [...] \
+   -object vhost-user-backend,id=vuid,chardev=char0 \
+   -device vhost-user-input-pci,vhost-user=vuid
+   [...]
+@end example
+
 @item -object cryptodev-backend-builtin,id=@var{id}[,queues=@var{queues}]
 
 Creates a cryptodev backend which executes crypto opreation from
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 09/29] qio: add qio_channel_command_new_spawn_with_pre_exec()
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (7 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 08/29] Add vhost-user-backend Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-08-28 15:09   ` Daniel P. Berrangé
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 10/29] HACK: vhost-user-backend: allow to specify binary to execute Marc-André Lureau
                   ` (22 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Add a new function to let caller do some tuning thanks to a callback
before exec().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/io/channel-command.h | 18 ++++++++++++++++++
 io/channel-command.c         | 33 ++++++++++++++++++++++++++-------
 2 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/include/io/channel-command.h b/include/io/channel-command.h
index 336d47fa5c..96c833daab 100644
--- a/include/io/channel-command.h
+++ b/include/io/channel-command.h
@@ -71,6 +71,24 @@ qio_channel_command_new_pid(int writefd,
                             int readfd,
                             pid_t pid);
 
+/**
+ * qio_channel_command_new_spawn_with_pre_exec:
+ * @argv: the NULL terminated list of command arguments
+ * @flags: the I/O mode, one of O_RDONLY, O_WRONLY, O_RDWR
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Create a channel for performing I/O with the
+ * command to be spawned with arguments @argv.
+ *
+ * Returns: the command channel object, or NULL on error
+ */
+QIOChannelCommand *
+qio_channel_command_new_spawn_with_pre_exec(const char *const argv[],
+                                            int flags,
+                                            void (*pre_exec_cb)(void *),
+                                            void *data,
+                                            Error **errp);
+
 /**
  * qio_channel_command_new_spawn:
  * @argv: the NULL terminated list of command arguments
diff --git a/io/channel-command.c b/io/channel-command.c
index 3e7eb17eff..05903ff194 100644
--- a/io/channel-command.c
+++ b/io/channel-command.c
@@ -46,9 +46,12 @@ qio_channel_command_new_pid(int writefd,
 
 #ifndef WIN32
 QIOChannelCommand *
-qio_channel_command_new_spawn(const char *const argv[],
-                              int flags,
-                              Error **errp)
+qio_channel_command_new_spawn_with_pre_exec(const char *const argv[],
+                                            int flags,
+                                            void (*pre_exec_cb)(void *),
+                                            void *data,
+                                            Error **errp)
+
 {
     pid_t pid = -1;
     int stdinfd[2] = { -1, -1 };
@@ -104,6 +107,10 @@ qio_channel_command_new_spawn(const char *const argv[],
             close(devnull);
         }
 
+        if (pre_exec_cb) {
+            pre_exec_cb(data);
+        }
+
         execv(argv[0], (char * const *)argv);
         _exit(1);
     }
@@ -139,12 +146,13 @@ qio_channel_command_new_spawn(const char *const argv[],
     }
     return NULL;
 }
-
 #else /* WIN32 */
 QIOChannelCommand *
-qio_channel_command_new_spawn(const char *const argv[],
-                              int flags,
-                              Error **errp)
+qio_channel_command_new_spawn_with_pre_exec(const char *const argv[],
+                                            int flags,
+                                            void (*pre_exec_cb)(void *),
+                                            void *data,
+                                            Error **errp)
 {
     error_setg_errno(errp, ENOSYS,
                      "Command spawn not supported on this platform");
@@ -152,6 +160,17 @@ qio_channel_command_new_spawn(const char *const argv[],
 }
 #endif /* WIN32 */
 
+
+QIOChannelCommand *
+qio_channel_command_new_spawn(const char *const argv[],
+                              int flags,
+                              Error **errp)
+{
+    return qio_channel_command_new_spawn_with_pre_exec(argv, flags,
+                                                       NULL, NULL, errp);
+}
+
+
 #ifndef WIN32
 static int qio_channel_command_abort(QIOChannelCommand *ioc,
                                      Error **errp)
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 10/29] HACK: vhost-user-backend: allow to specify binary to execute
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (8 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 09/29] qio: add qio_channel_command_new_spawn_with_pre_exec() Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-08-28 15:44   ` Daniel P. Berrangé
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 11/29] vhost-user: split vhost_user_read() Marc-André Lureau
                   ` (21 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

An executable with its arguments may be given as 'cmd' property, ex:
-object vhost-user-backend,id=vui,cmd="./vhost-user-input
/dev/input..". The executable is then spawn and, by convention, the
vhost-user socket is passed as fd=3. It may be considered a security
breach to allow creating processes that may execute arbitrary
executables, so this may be restricted to some known executables (via
signature etc) or directory.

To make the patch more acceptable, the command argument would have to
be passed via an array (probably via -object json: syntax), instead of
using g_shell_parse_argv().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/vhost-user.c | 96 ++++++++++++++++++++++++++++++++++++++-----
 qemu-options.hx       | 12 ++++--
 2 files changed, 94 insertions(+), 14 deletions(-)

diff --git a/backends/vhost-user.c b/backends/vhost-user.c
index bf39c0751d..32d3ec0e8b 100644
--- a/backends/vhost-user.c
+++ b/backends/vhost-user.c
@@ -136,31 +136,105 @@ vhost_user_backend_stop(VhostUserBackend *b)
     b->started = false;
 }
 
+static void
+pre_exec_cb(void *data)
+{
+    int *sv = data;
+    int maxfd = sysconf(_SC_OPEN_MAX);
+    int fd;
+
+    dup2(sv[1], 3);
+    for (fd = 4; fd < maxfd; fd++) {
+        close(fd);
+    }
+}
+
 static void
 vhost_user_backend_complete(UserCreatable *uc, Error **errp)
 {
     VhostUserBackend *b = VHOST_USER_BACKEND(uc);
     Chardev *chr;
+    char **argv = NULL;
 
-    if (!b->chr_name) {
-        error_setg(errp, "You must specificy 'chardev'.");
+    if (!!b->chr_name + !!b->cmd != 1) {
+        error_setg(errp, "You may specificy only one of 'chardev' or 'cmd'.");
         return;
     }
 
-    chr = qemu_chr_find(b->chr_name);
-    if (chr == NULL) {
-        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-                  "Chardev '%s' not found", b->chr_name);
-        return;
-    }
+    if (b->chr_name) {
+        chr = qemu_chr_find(b->chr_name);
+        if (chr == NULL) {
+            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+                      "Chardev '%s' not found", b->chr_name);
+            return;
+        }
 
-    if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
-        return;
+        if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
+            return;
+        }
+    } else {
+        QIOChannelCommand *child;
+        GError *err;
+        int sv[2];
+
+        if (!g_shell_parse_argv(b->cmd, NULL, &argv, &err)) {
+            error_setg(errp, "Fail to parse command: %s", err->message);
+            g_error_free(err);
+            return;
+        }
+
+        if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+            error_setg_errno(errp, errno, "socketpair() failed");
+            goto end;
+        }
+
+        chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET));
+        if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) {
+            error_setg(errp, "Failed to make socket chardev");
+            object_unref(OBJECT(chr));
+            goto end;
+        }
+
+        if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
+            goto end;
+        }
+
+        child = qio_channel_command_new_spawn_with_pre_exec(
+            (const char * const *)argv, O_RDONLY | O_WRONLY,
+            pre_exec_cb, sv, errp);
+        if (!child) {
+            goto end;
+        }
+        b->child = QIO_CHANNEL(child);
+
+        close(sv[1]);
     }
 
     b->completed = true;
     /* could vhost_dev_init() happen here, so early vhost-user message
      * can be exchanged */
+end:
+    g_strfreev(argv);
+}
+
+static char *get_cmd(Object *obj, Error **errp)
+{
+    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
+
+    return g_strdup(b->cmd);
+}
+
+static void set_cmd(Object *obj, const char *str, Error **errp)
+{
+    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
+
+    if (b->child) {
+        error_setg(errp, "cannot change property value");
+        return;
+    }
+
+    g_free(b->cmd);
+    b->cmd = g_strdup(str);
 }
 
 static void set_chardev(Object *obj, const char *value, Error **errp)
@@ -189,6 +263,7 @@ static char *get_chardev(Object *obj, Error **errp)
 
 static void vhost_user_backend_init(Object *obj)
 {
+    object_property_add_str(obj, "cmd", get_cmd, set_cmd, NULL);
     object_property_add_str(obj, "chardev", get_chardev, set_chardev, NULL);
 }
 
@@ -197,6 +272,7 @@ static void vhost_user_backend_finalize(Object *obj)
     VhostUserBackend *b = VHOST_USER_BACKEND(obj);
 
     g_free(b->dev.vqs);
+    g_free(b->cmd);
     g_free(b->chr_name);
 
     vhost_user_cleanup(&b->vhost_user);
diff --git a/qemu-options.hx b/qemu-options.hx
index 413b97d5e9..9243a5f8ab 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4292,16 +4292,20 @@ secondary:
 If you want to know the detail of above command line, you can read
 the colo-compare git log.
 
-@item -object vhost-user-backend,id=id=@var{id},chardev=@var{chardevid}
+@item -object vhost-user-backend,id=id=@var{id}[,chardev=@var{chardevid},cmd=@var{cmd}]
 
 Create a vhost-user-backend object that holds a connection to a
 vhost-user backend and can be referenced from virtio/vhost-user
 devices that support it.
 
 The @var{id} parameter is a unique ID that will be used to reference
-this vhost-user backend from the @option{vhost-user} device. The
-@var{chardev} parameter is the unique ID of a character device backend
-that provides the connection to the vhost-user slave process. (Since 3.0)
+this vhost-user backend from the @option{vhost-user} device.
+
+You must specify either @var{chardev} or @var{cmd}. The @var{chardev}
+parameter is the unique ID of a character device backend that provides
+the connection to the vhost-user slave process.  The @var{cmd}
+parameter will simplify handling of the backend, by running the given
+command and establishing the connection. (Since 3.1)
 
 @example
 
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 11/29] vhost-user: split vhost_user_read()
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (9 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 10/29] HACK: vhost-user-backend: allow to specify binary to execute Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-08-28 15:46   ` Daniel P. Berrangé
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 12/29] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
                   ` (20 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Split vhost_user_read(), so only header can be read with
vhost_user_read_header().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/virtio/vhost-user.c | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 5b4188bc27..29f8568a13 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -210,7 +210,7 @@ static bool ioeventfd_enabled(void)
     return kvm_enabled() && kvm_eventfds_enabled();
 }
 
-static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
+static int vhost_user_read_header(struct vhost_dev *dev, VhostUserMsg *msg)
 {
     struct vhost_user *u = dev->opaque;
     CharBackend *chr = u->user->chr;
@@ -221,7 +221,7 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
     if (r != size) {
         error_report("Failed to read msg header. Read %d instead of %d."
                      " Original request %d.", r, size, msg->hdr.request);
-        goto fail;
+        return -1;
     }
 
     /* validate received flags */
@@ -229,7 +229,21 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
         error_report("Failed to read msg header."
                 " Flags 0x%x instead of 0x%x.", msg->hdr.flags,
                 VHOST_USER_REPLY_MASK | VHOST_USER_VERSION);
-        goto fail;
+        return -1;
+    }
+
+    return 0;
+}
+
+static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
+{
+    struct vhost_user *u = dev->opaque;
+    CharBackend *chr = u->user->chr;
+    uint8_t *p = (uint8_t *) msg;
+    int r, size;
+
+    if (vhost_user_read_header(dev, msg) < 0) {
+        return -1;
     }
 
     /* validate message size is sane */
@@ -237,7 +251,7 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
         error_report("Failed to read msg header."
                 " Size %d exceeds the maximum %zu.", msg->hdr.size,
                 VHOST_USER_PAYLOAD_SIZE);
-        goto fail;
+        return -1;
     }
 
     if (msg->hdr.size) {
@@ -247,14 +261,11 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
         if (r != size) {
             error_report("Failed to read msg payload."
                          " Read %d instead of %d.", r, msg->hdr.size);
-            goto fail;
+            return -1;
         }
     }
 
     return 0;
-
-fail:
-    return -1;
 }
 
 static int process_message_reply(struct vhost_dev *dev,
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 12/29] vhost-user: add vhost_user_input_get_config()
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (10 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 11/29] vhost-user: split vhost_user_read() Marc-André Lureau
@ 2018-07-13 13:08 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 13/29] libvhost-user: export vug_source_new() Marc-André Lureau
                   ` (19 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:08 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Ask vhost user input backend the list of virtio_input_config.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/libvhost-user/libvhost-user.h |  1 +
 include/hw/virtio/vhost-backend.h     |  4 ++
 hw/virtio/vhost-user.c                | 59 +++++++++++++++++++++++++++
 docs/interop/vhost-user.txt           |  7 ++++
 4 files changed, 71 insertions(+)

diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index 4aa55b4d2d..a4afbc3a46 100644
--- a/contrib/libvhost-user/libvhost-user.h
+++ b/contrib/libvhost-user/libvhost-user.h
@@ -91,6 +91,7 @@ typedef enum VhostUserRequest {
     VHOST_USER_POSTCOPY_ADVISE  = 28,
     VHOST_USER_POSTCOPY_LISTEN  = 29,
     VHOST_USER_POSTCOPY_END     = 30,
+    VHOST_USER_INPUT_GET_CONFIG = 31,
     VHOST_USER_MAX
 } VhostUserRequest;
 
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index 81283ec50f..1fca321d8a 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -12,6 +12,7 @@
 #define VHOST_BACKEND_H
 
 #include "exec/memory.h"
+#include "standard-headers/linux/virtio_input.h"
 
 typedef enum VhostBackendType {
     VHOST_BACKEND_TYPE_NONE = 0,
@@ -160,4 +161,7 @@ int vhost_backend_invalidate_device_iotlb(struct vhost_dev *dev,
 int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev,
                                           struct vhost_iotlb_msg *imsg);
 
+int vhost_user_input_get_config(struct vhost_dev *dev,
+                                struct virtio_input_config **config);
+
 #endif /* VHOST_BACKEND_H */
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 29f8568a13..0c6914fab7 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -89,6 +89,7 @@ typedef enum VhostUserRequest {
     VHOST_USER_POSTCOPY_ADVISE  = 28,
     VHOST_USER_POSTCOPY_LISTEN  = 29,
     VHOST_USER_POSTCOPY_END     = 30,
+    VHOST_USER_INPUT_GET_CONFIG = 31,
     VHOST_USER_MAX
 } VhostUserRequest;
 
@@ -338,6 +339,64 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
     return 0;
 }
 
+static void *vhost_user_read_size(struct vhost_dev *dev, uint32_t size)
+{
+    struct vhost_user *u = dev->opaque;
+    CharBackend *chr = u->user->chr;
+    int r;
+    uint8_t *p = g_malloc(size);
+
+    r = qemu_chr_fe_read_all(chr, p, size);
+    if (r != size) {
+        error_report("Failed to read msg payload."
+                     " Read %d instead of %d.", r, size);
+        return NULL;
+    }
+
+    return p;
+}
+
+int vhost_user_input_get_config(struct vhost_dev *dev,
+                                struct virtio_input_config **config)
+{
+    void *p = NULL;
+    VhostUserMsg msg = {
+        .hdr.request = VHOST_USER_INPUT_GET_CONFIG,
+        .hdr.flags = VHOST_USER_VERSION,
+    };
+
+    if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+        goto err;
+    }
+
+    if (vhost_user_read_header(dev, &msg) < 0) {
+        goto err;
+    }
+
+    p = vhost_user_read_size(dev, msg.hdr.size);
+    if (!p) {
+        goto err;
+    }
+
+    if (msg.hdr.request != VHOST_USER_INPUT_GET_CONFIG) {
+        error_report("Received unexpected msg type. Expected %d received %d",
+                     VHOST_USER_INPUT_GET_CONFIG, msg.hdr.request);
+        goto err;
+    }
+
+    if (msg.hdr.size % sizeof(struct virtio_input_config)) {
+        error_report("Invalid msg size");
+        goto err;
+    }
+
+    *config = p;
+    return msg.hdr.size / sizeof(struct virtio_input_config);
+
+err:
+    g_free(p);
+    return -1;
+}
+
 static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
                                    struct vhost_log *log)
 {
diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt
index f59667f498..cba1ddde16 100644
--- a/docs/interop/vhost-user.txt
+++ b/docs/interop/vhost-user.txt
@@ -761,6 +761,13 @@ Master message types
       was previously sent.
       The value returned is an error indication; 0 is success.
 
+ * VHOST_USER_INPUT_GET_CONFIG
+      Id: 31
+      Master payload: N/A
+      Slave payload: (struct virtio_input_config)*
+
+      Ask vhost user input backend the list of virtio_input_config.
+
 Slave message types
 -------------------
 
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 13/29] libvhost-user: export vug_source_new()
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (11 preceding siblings ...)
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 12/29] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 14/29] contrib: add vhost-user-input Marc-André Lureau
                   ` (18 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Simplify the creation of FD sources for other users.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/libvhost-user/libvhost-user-glib.h |  3 +++
 contrib/libvhost-user/libvhost-user-glib.c | 15 +++++++++++----
 2 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/contrib/libvhost-user/libvhost-user-glib.h b/contrib/libvhost-user/libvhost-user-glib.h
index 6b2110b94c..d3200f3afc 100644
--- a/contrib/libvhost-user/libvhost-user-glib.h
+++ b/contrib/libvhost-user/libvhost-user-glib.h
@@ -29,4 +29,7 @@ void vug_init(VugDev *dev, int socket,
               vu_panic_cb panic, const VuDevIface *iface);
 void vug_deinit(VugDev *dev);
 
+GSource *vug_source_new(VugDev *dev, int fd, GIOCondition cond,
+                        vu_watch_cb vu_cb, gpointer data);
+
 #endif /* LIBVHOST_USER_GLIB_H */
diff --git a/contrib/libvhost-user/libvhost-user-glib.c b/contrib/libvhost-user/libvhost-user-glib.c
index 545f089587..0e2c467f28 100644
--- a/contrib/libvhost-user/libvhost-user-glib.c
+++ b/contrib/libvhost-user/libvhost-user-glib.c
@@ -69,8 +69,8 @@ static GSourceFuncs vug_src_funcs = {
 };
 
 static GSource *
-vug_source_new(VuDev *dev, int fd, GIOCondition cond,
-               vu_watch_cb vu_cb, gpointer data)
+_vug_source_new(VuDev *dev, int fd, GIOCondition cond,
+                vu_watch_cb vu_cb, gpointer data)
 {
     GSource *gsrc;
     VugSrc *src;
@@ -95,6 +95,13 @@ vug_source_new(VuDev *dev, int fd, GIOCondition cond,
     return gsrc;
 }
 
+GSource *
+vug_source_new(VugDev *dev, int fd, GIOCondition cond,
+               vu_watch_cb vu_cb, gpointer data)
+{
+    return _vug_source_new(&dev->parent, fd, cond, vu_cb, data);
+}
+
 static void
 set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb, void *pvt)
 {
@@ -106,7 +113,7 @@ set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb, void *pvt)
     g_assert(cb);
 
     dev = container_of(vu_dev, VugDev, parent);
-    src = vug_source_new(vu_dev, fd, vu_evt, cb, pvt);
+    src = _vug_source_new(vu_dev, fd, vu_evt, cb, pvt);
     g_hash_table_replace(dev->fdmap, GINT_TO_POINTER(fd), src);
 }
 
@@ -141,7 +148,7 @@ vug_init(VugDev *dev, int socket,
     dev->fdmap = g_hash_table_new_full(NULL, NULL, NULL,
                                        (GDestroyNotify) g_source_destroy);
 
-    dev->src = vug_source_new(&dev->parent, socket, G_IO_IN, vug_watch, NULL);
+    dev->src = _vug_source_new(&dev->parent, socket, G_IO_IN, vug_watch, NULL);
 }
 
 void
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 14/29] contrib: add vhost-user-input
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (12 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 13/29] libvhost-user: export vug_source_new() Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 15/29] Add vhost-user-input-pci Marc-André Lureau
                   ` (17 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Add a vhost-user input backend, based on virtio-input-host device. It
takes an evdev path as argument, and can be associated with a
vhost-user-backend object, ex:

-object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event0"

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-input/main.c        | 379 +++++++++++++++++++++++++
 MAINTAINERS                            |   1 +
 Makefile                               |   3 +
 Makefile.objs                          |   1 +
 configure                              |   3 +
 contrib/vhost-user-input/Makefile.objs |   1 +
 6 files changed, 388 insertions(+)
 create mode 100644 contrib/vhost-user-input/main.c
 create mode 100644 contrib/vhost-user-input/Makefile.objs

diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/main.c
new file mode 100644
index 0000000000..f0a58da2a8
--- /dev/null
+++ b/contrib/vhost-user-input/main.c
@@ -0,0 +1,379 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include <glib.h>
+#include <linux/input.h>
+
+#include "qemu/iov.h"
+#include "qemu/bswap.h"
+#include "contrib/libvhost-user/libvhost-user.h"
+#include "contrib/libvhost-user/libvhost-user-glib.h"
+#include "standard-headers/linux/virtio_input.h"
+#include "qapi/error.h"
+
+typedef struct virtio_input_event virtio_input_event;
+typedef struct virtio_input_config virtio_input_config;
+
+typedef struct VuInput {
+    VugDev dev;
+    GSource *evsrc;
+    int evdevfd;
+    GArray *config;
+    virtio_input_event *queue;
+    uint32_t qindex, qsize;
+} VuInput;
+
+static void vi_input_send(VuInput *vi, struct virtio_input_event *event)
+{
+    VuDev *dev = &vi->dev.parent;
+    VuVirtq *vq = vu_get_queue(dev, 0);
+    VuVirtqElement *elem;
+    unsigned have, need;
+    int i, len;
+
+    /* queue up events ... */
+    if (vi->qindex == vi->qsize) {
+        vi->qsize++;
+        vi->queue = realloc(vi->queue, vi->qsize *
+                                sizeof(virtio_input_event));
+    }
+    vi->queue[vi->qindex++] = *event;
+
+    /* ... until we see a report sync ... */
+    if (event->type != htole16(EV_SYN) ||
+        event->code != htole16(SYN_REPORT)) {
+        return;
+    }
+
+    /* ... then check available space ... */
+    need = sizeof(virtio_input_event) * vi->qindex;
+    vu_queue_get_avail_bytes(dev, vq, &have, NULL, need, 0);
+    if (have < need) {
+        vi->qindex = 0;
+        g_warning("ENOSPC in vq, dropping events");
+        return;
+    }
+
+    /* ... and finally pass them to the guest */
+    for (i = 0; i < vi->qindex; i++) {
+        elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
+        if (!elem) {
+            /* should not happen, we've checked for space beforehand */
+            g_warning("%s: Huh?  No vq elem available ...\n", __func__);
+            return;
+        }
+        len = iov_from_buf(elem->in_sg, elem->in_num,
+                           0, vi->queue + i, sizeof(virtio_input_event));
+        vu_queue_push(dev, vq, elem, len);
+        g_free(elem);
+    }
+    vu_queue_notify(&vi->dev.parent, vq);
+    vi->qindex = 0;
+}
+
+static void
+vi_evdev_watch(VuDev *dev, int condition, void *data)
+{
+    VuInput *vi = data;
+    int fd = vi->evdevfd;
+
+    g_debug("Got evdev condition %x", condition);
+
+    struct virtio_input_event virtio;
+    struct input_event evdev;
+    int rc;
+
+    for (;;) {
+        rc = read(fd, &evdev, sizeof(evdev));
+        if (rc != sizeof(evdev)) {
+            break;
+        }
+
+        g_debug("input %d %d %d", evdev.type, evdev.code, evdev.value);
+
+        virtio.type  = htole16(evdev.type);
+        virtio.code  = htole16(evdev.code);
+        virtio.value = htole32(evdev.value);
+        vi_input_send(vi, &virtio);
+    }
+}
+
+
+static void vi_handle_status(VuInput *vi, virtio_input_event *event)
+{
+    struct input_event evdev;
+    int rc;
+
+    if (gettimeofday(&evdev.time, NULL)) {
+        perror("vi_handle_status: gettimeofday");
+        return;
+    }
+
+    evdev.type = le16toh(event->type);
+    evdev.code = le16toh(event->code);
+    evdev.value = le32toh(event->value);
+
+    rc = write(vi->evdevfd, &evdev, sizeof(evdev));
+    if (rc == -1) {
+        perror("vi_host_handle_status: write");
+    }
+}
+
+static void vi_handle_sts(VuDev *dev, int qidx)
+{
+    VuInput *vi = container_of(dev, VuInput, dev.parent);
+    VuVirtq *vq = vu_get_queue(dev, qidx);
+    virtio_input_event event;
+    VuVirtqElement *elem;
+    int len;
+
+    g_debug("%s", G_STRFUNC);
+
+    for (;;) {
+        elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
+        if (!elem) {
+            break;
+        }
+
+        memset(&event, 0, sizeof(event));
+        len = iov_to_buf(elem->out_sg, elem->out_num,
+                         0, &event, sizeof(event));
+        vi_handle_status(vi, &event);
+        vu_queue_push(dev, vq, elem, len);
+        g_free(elem);
+    }
+
+    vu_queue_notify(&vi->dev.parent, vq);
+}
+
+static void
+vi_panic(VuDev *dev, const char *msg)
+{
+    g_critical("%s\n", msg);
+    exit(1);
+}
+
+static void
+vi_queue_set_started(VuDev *dev, int qidx, bool started)
+{
+    VuInput *vi = container_of(dev, VuInput, dev.parent);
+    VuVirtq *vq = vu_get_queue(dev, qidx);
+
+    g_debug("queue started %d:%d", qidx, started);
+
+    if (qidx == 0) {
+        if (started && !vi->evsrc) {
+            vi->evsrc = vug_source_new(&vi->dev, vi->evdevfd,
+                                       G_IO_IN, vi_evdev_watch, vi);
+        } else if (!started) {
+            g_source_destroy(vi->evsrc);
+            vi->evsrc = NULL;
+        }
+    } else {
+        vu_set_queue_handler(dev, vq, started ? vi_handle_sts : NULL);
+    }
+}
+
+static int
+vi_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply)
+{
+    VuInput *vi = container_of(dev, VuInput, dev.parent);
+
+    switch (vmsg->request) {
+    case VHOST_USER_INPUT_GET_CONFIG:
+        vmsg->size = vi->config->len * sizeof(virtio_input_config);
+        vmsg->data = g_memdup(vi->config->data, vmsg->size);
+        *do_reply = true;
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+static const VuDevIface vuiface = {
+    .queue_set_started = vi_queue_set_started,
+    .process_msg = vi_process_msg,
+};
+
+static void
+vi_bits_config(VuInput *vi, int type, int count)
+{
+    virtio_input_config bits;
+    int rc, i, size = 0;
+
+    memset(&bits, 0, sizeof(bits));
+    rc = ioctl(vi->evdevfd, EVIOCGBIT(type, count / 8), bits.u.bitmap);
+    if (rc < 0) {
+        return;
+    }
+
+    for (i = 0; i < count / 8; i++) {
+        if (bits.u.bitmap[i]) {
+            size = i + 1;
+        }
+    }
+    if (size == 0) {
+        return;
+    }
+
+    bits.select = VIRTIO_INPUT_CFG_EV_BITS;
+    bits.subsel = type;
+    bits.size   = size;
+    g_array_append_val(vi->config, bits);
+}
+
+static int unix_sock_new(char *path)
+{
+    int sock;
+    struct sockaddr_un un;
+    size_t len;
+
+    sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sock <= 0) {
+        perror("socket");
+        return -1;
+    }
+
+    un.sun_family = AF_UNIX;
+    snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+    len = sizeof(un.sun_family) + strlen(un.sun_path);
+
+    unlink(path);
+    if (bind(sock, (struct sockaddr *)&un, len) < 0) {
+        perror("bind");
+        goto fail;
+    }
+
+    if (listen(sock, 1) < 0) {
+        perror("listen");
+        goto fail;
+    }
+
+    return sock;
+
+fail:
+    close(sock);
+
+    return -1;
+}
+
+static char **opt_fname;
+static int opt_fdnum = 3;
+static char *opt_socket_path;
+static gboolean opt_nograb;
+static char *opt_pid_path;
+
+static GOptionEntry entries[] = {
+    { "no-grab", 'n', 0, G_OPTION_ARG_NONE, &opt_nograb,
+      "Don't grab device", NULL },
+    { "pid", 'p', 0, G_OPTION_ARG_FILENAME, &opt_pid_path,
+      "PID path", "PATH" },
+    { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum,
+      "Use inherited fd socket", "FDNUM" },
+    { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path,
+      "Use UNIX socket path", "PATH" },
+    { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_fname,
+      "EVDEV filename", "..." },
+    { NULL, }
+};
+
+int
+main(int argc, char *argv[])
+{
+    GMainLoop *loop = NULL;
+    VuInput vi = { 0, };
+    int rc, ver, fd;
+    virtio_input_config id;
+    struct input_id ids;
+    GError *error = NULL;
+    GOptionContext *context;
+    Error *err = NULL;
+
+    context = g_option_context_new("EVDEV - vhost-user-input sample");
+    g_option_context_add_main_entries(context, entries, NULL);
+    if (!g_option_context_parse(context, &argc, &argv, &error)) {
+        g_printerr("Option parsing failed: %s\n", error->message);
+        exit(EXIT_FAILURE);
+    }
+    if (!opt_fname || !opt_fname[0] || opt_fname[1]) {
+        g_printerr("Please specify a single EVDEV filename\n");
+        exit(EXIT_FAILURE);
+    }
+
+    if (opt_pid_path && !qemu_write_pidfile(opt_pid_path, &err)) {
+        g_printerr("%s\n", error_get_pretty(err));
+        error_free(err);
+        exit(EXIT_FAILURE);
+    }
+
+    vi.evdevfd = open(opt_fname[0], O_RDWR);
+    if (vi.evdevfd < 0) {
+        g_printerr("Failed to open evdev: %s\n", g_strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    rc = ioctl(vi.evdevfd, EVIOCGVERSION, &ver);
+    if (rc < 0) {
+        g_printerr("%s: is not an evdev device\n", argv[1]);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!opt_nograb) {
+        rc = ioctl(vi.evdevfd, EVIOCGRAB, 1);
+        if (rc < 0) {
+            g_printerr("Failed to grab device\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    vi.config = g_array_new(false, false, sizeof(virtio_input_config));
+    memset(&id, 0, sizeof(id));
+    ioctl(vi.evdevfd, EVIOCGNAME(sizeof(id.u.string) - 1), id.u.string);
+    id.select = VIRTIO_INPUT_CFG_ID_NAME;
+    id.size = strlen(id.u.string);
+    g_array_append_val(vi.config, id);
+
+    if (ioctl(vi.evdevfd, EVIOCGID, &ids) == 0) {
+        memset(&id, 0, sizeof(id));
+        id.select = VIRTIO_INPUT_CFG_ID_DEVIDS;
+        id.size = sizeof(struct virtio_input_devids);
+        id.u.ids.bustype = cpu_to_le16(ids.bustype);
+        id.u.ids.vendor  = cpu_to_le16(ids.vendor);
+        id.u.ids.product = cpu_to_le16(ids.product);
+        id.u.ids.version = cpu_to_le16(ids.version);
+        g_array_append_val(vi.config, id);
+    }
+
+    vi_bits_config(&vi, EV_KEY, KEY_CNT);
+    vi_bits_config(&vi, EV_REL, REL_CNT);
+    vi_bits_config(&vi, EV_ABS, ABS_CNT);
+    vi_bits_config(&vi, EV_MSC, MSC_CNT);
+    vi_bits_config(&vi, EV_SW,  SW_CNT);
+    g_debug("config length: %u", vi.config->len);
+
+    if (opt_socket_path) {
+        int lsock = unix_sock_new(opt_socket_path);
+        fd = accept(lsock, NULL, NULL);
+        close(lsock);
+    } else {
+        fd = opt_fdnum;
+    }
+    if (fd == -1) {
+        g_printerr("Invalid socket");
+        exit(EXIT_FAILURE);
+    }
+    vug_init(&vi.dev, fd, vi_panic, &vuiface);
+
+    loop = g_main_loop_new(NULL, FALSE);
+    g_main_loop_run(loop);
+    g_main_loop_unref(loop);
+
+    vug_deinit(&vi.dev);
+
+    return 0;
+}
diff --git a/MAINTAINERS b/MAINTAINERS
index dd8d07651f..ee13fe9ef1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1233,6 +1233,7 @@ M: Gerd Hoffmann <kraxel@redhat.com>
 S: Maintained
 F: hw/input/virtio-input*.c
 F: include/hw/virtio/virtio-input.h
+F: contrib/vhost-user-input/*
 
 virtio-serial
 M: Amit Shah <amit@kernel.org>
diff --git a/Makefile b/Makefile
index 2da686be33..0c61563d75 100644
--- a/Makefile
+++ b/Makefile
@@ -419,6 +419,7 @@ dummy := $(call unnest-vars,, \
                 libvhost-user-obj-y \
                 vhost-user-scsi-obj-y \
                 vhost-user-blk-obj-y \
+                vhost-user-input-obj-y \
                 qga-vss-dll-obj-y \
                 block-obj-y \
                 block-obj-m \
@@ -717,6 +718,8 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a
 	$(call LINK, $^)
 vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
 	$(call LINK, $^)
+vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqemuutil.a
+	$(call LINK, $^)
 
 module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
 	$(call quiet-command,$(PYTHON) $< $@ \
diff --git a/Makefile.objs b/Makefile.objs
index 7a9828da28..b0f48d667c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -193,6 +193,7 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
 vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
 vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
 vhost-user-blk-obj-y = contrib/vhost-user-blk/
+vhost-user-input-obj-y = contrib/vhost-user-input/
 
 ######################################################################
 trace-events-subdirs =
diff --git a/configure b/configure
index 2a7796ea80..211301f497 100755
--- a/configure
+++ b/configure
@@ -5634,6 +5634,9 @@ if test "$want_tools" = "yes" ; then
   if [ "$ivshmem" = "yes" ]; then
     tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools"
   fi
+  if [ "$linux" = "yes" ] ; then
+    tools="vhost-user-input\$(EXESUF) $tools"
+  fi
 fi
 if test "$softmmu" = yes ; then
   if test "$linux" = yes; then
diff --git a/contrib/vhost-user-input/Makefile.objs b/contrib/vhost-user-input/Makefile.objs
new file mode 100644
index 0000000000..b1fad90d51
--- /dev/null
+++ b/contrib/vhost-user-input/Makefile.objs
@@ -0,0 +1 @@
+vhost-user-input-obj-y = main.o
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 15/29] Add vhost-user-input-pci
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (13 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 14/29] contrib: add vhost-user-input Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 16/29] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
                   ` (16 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Add a new virtio-input device, which connects to a vhost-user
backend. Usage:

-object vhost-user-backend,id=vuid \
-device vhost-user-input-pci,vhost-user=vuid

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/virtio/virtio-pci.h           |  10 +++
 include/hw/virtio/virtio-input.h |  14 ++++
 hw/input/vhost-user-input.c      | 110 +++++++++++++++++++++++++++++++
 hw/virtio/virtio-pci.c           |  20 ++++++
 MAINTAINERS                      |   1 +
 hw/input/Makefile.objs           |   1 +
 6 files changed, 156 insertions(+)
 create mode 100644 hw/input/vhost-user-input.c

diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 813082b0d7..c7e28e1b9c 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -54,6 +54,7 @@ typedef struct VirtIORngPCI VirtIORngPCI;
 typedef struct VirtIOInputPCI VirtIOInputPCI;
 typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
 typedef struct VirtIOInputHostPCI VirtIOInputHostPCI;
+typedef struct VHostUserInputPCI VHostUserInputPCI;
 typedef struct VirtIOGPUPCI VirtIOGPUPCI;
 typedef struct VHostVSockPCI VHostVSockPCI;
 typedef struct VirtIOCryptoPCI VirtIOCryptoPCI;
@@ -376,6 +377,15 @@ struct VirtIOInputHostPCI {
 
 #endif
 
+#define TYPE_VHOST_USER_INPUT_PCI "vhost-user-input-pci"
+#define VHOST_USER_INPUT_PCI(obj)                                    \
+    OBJECT_CHECK(VHostUserInputPCI, (obj), TYPE_VHOST_USER_INPUT_PCI)
+
+struct VHostUserInputPCI {
+    VirtIOPCIProxy parent_obj;
+    VHostUserInput vhi;
+};
+
 /*
  * virtio-gpu-pci: This extends VirtioPCIProxy.
  */
diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-input.h
index 054c38836f..4fca03e796 100644
--- a/include/hw/virtio/virtio-input.h
+++ b/include/hw/virtio/virtio-input.h
@@ -2,6 +2,7 @@
 #define QEMU_VIRTIO_INPUT_H
 
 #include "ui/input.h"
+#include "sysemu/vhost-user-backend.h"
 
 /* ----------------------------------------------------------------- */
 /* virtio input protocol                                             */
@@ -42,11 +43,18 @@ typedef struct virtio_input_event virtio_input_event;
 #define VIRTIO_INPUT_HOST_GET_PARENT_CLASS(obj) \
         OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_INPUT_HOST)
 
+#define TYPE_VHOST_USER_INPUT   "vhost-user-input"
+#define VHOST_USER_INPUT(obj)                              \
+    OBJECT_CHECK(VHostUserInput, (obj), TYPE_VHOST_USER_INPUT)
+#define VHOST_USER_INPUT_GET_PARENT_CLASS(obj)             \
+    OBJECT_GET_PARENT_CLASS(obj, TYPE_VHOST_USER_INPUT)
+
 typedef struct VirtIOInput VirtIOInput;
 typedef struct VirtIOInputClass VirtIOInputClass;
 typedef struct VirtIOInputConfig VirtIOInputConfig;
 typedef struct VirtIOInputHID VirtIOInputHID;
 typedef struct VirtIOInputHost VirtIOInputHost;
+typedef struct VHostUserInput VHostUserInput;
 
 struct VirtIOInputConfig {
     virtio_input_config               config;
@@ -98,6 +106,12 @@ struct VirtIOInputHost {
     int                               fd;
 };
 
+struct VHostUserInput {
+    VirtIOInput                       parent_obj;
+
+    VhostUserBackend                  *vhost;
+};
+
 void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event);
 void virtio_input_init_config(VirtIOInput *vinput,
                               virtio_input_config *config);
diff --git a/hw/input/vhost-user-input.c b/hw/input/vhost-user-input.c
new file mode 100644
index 0000000000..ef1b23a8b2
--- /dev/null
+++ b/hw/input/vhost-user-input.c
@@ -0,0 +1,110 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+
+#include "hw/qdev.h"
+#include "hw/virtio/virtio-input.h"
+
+static void vhost_input_realize(DeviceState *dev, Error **errp)
+{
+    VHostUserInput *vhi = VHOST_USER_INPUT(dev);
+    VirtIOInput *vinput = VIRTIO_INPUT(dev);
+    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+    virtio_input_config *config;
+    int i, ret;
+
+    if (!vhi->vhost) {
+        error_setg(errp, "'vhost-user' property is required");
+        return;
+    }
+
+    if (vhost_user_backend_dev_init(vhi->vhost, vdev, 2, errp) == -1) {
+        return;
+    }
+
+    ret = vhost_user_input_get_config(&vhi->vhost->dev, &config);
+    if (ret < 0) {
+        error_setg(errp, "failed to get input config");
+        return;
+    }
+    for (i = 0; i < ret; i++) {
+        virtio_input_add_config(vinput, &config[i]);
+    }
+    g_free(config);
+}
+
+static void vhost_input_change_active(VirtIOInput *vinput)
+{
+    VHostUserInput *vhi = VHOST_USER_INPUT(vinput);
+
+    if (vinput->active) {
+        vhost_user_backend_start(vhi->vhost);
+    } else {
+        vhost_user_backend_stop(vhi->vhost);
+    }
+}
+
+static const VMStateDescription vmstate_vhost_input = {
+    .name = "vhost-user-input",
+    .unmigratable = 1,
+};
+
+static void vhost_input_class_init(ObjectClass *klass, void *data)
+{
+    VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd           = &vmstate_vhost_input;
+    vic->realize       = vhost_input_realize;
+    vic->change_active = vhost_input_change_active;
+}
+
+static void vhost_input_is_busy(const Object *obj, const char *name,
+                                Object *val, Error **errp)
+{
+    VHostUserInput *vhi = VHOST_USER_INPUT(obj);
+
+    if (vhi->vhost) {
+        error_setg(errp, "can't use already busy vhost-user");
+    } else {
+        qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
+    }
+}
+
+static void vhost_input_init(Object *obj)
+{
+    VHostUserInput *vhi = VHOST_USER_INPUT(obj);
+    VirtIOInput *vinput = VIRTIO_INPUT(obj);
+    struct virtio_input_config vhost_input_config[] = { { /* empty list */ } };
+
+    virtio_input_init_config(vinput, vhost_input_config);
+
+    object_property_add_link(obj, "vhost-user", TYPE_VHOST_USER_BACKEND,
+                             (Object **)&vhi->vhost,
+                             vhost_input_is_busy,
+                             OBJ_PROP_LINK_STRONG,
+                             &error_abort);
+}
+
+static const TypeInfo vhost_input_info = {
+    .name          = TYPE_VHOST_USER_INPUT,
+    .parent        = TYPE_VIRTIO_INPUT,
+    .instance_size = sizeof(VHostUserInput),
+    .instance_init = vhost_input_init,
+    .class_init    = vhost_input_class_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void vhost_input_register_types(void)
+{
+    type_register_static(&vhost_input_info);
+}
+
+type_init(vhost_input_register_types)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 3a01fe90f0..6bb0575257 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -2645,6 +2645,25 @@ static const TypeInfo virtio_host_pci_info = {
 };
 #endif
 
+static void vhost_input_pci_initfn(Object *obj)
+{
+    VHostUserInputPCI *dev = VHOST_USER_INPUT_PCI(obj);
+
+    virtio_instance_init_common(obj, &dev->vhi, sizeof(dev->vhi),
+                                TYPE_VHOST_USER_INPUT);
+
+    object_property_add_alias(obj, "vhost-user",
+                              OBJECT(&dev->vhi), "vhost-user",
+                              &error_abort);
+}
+
+static const TypeInfo vhost_input_pci_info = {
+    .name          = TYPE_VHOST_USER_INPUT_PCI,
+    .parent        = TYPE_VIRTIO_INPUT_PCI,
+    .instance_size = sizeof(VHostUserInputPCI),
+    .instance_init = vhost_input_pci_initfn,
+};
+
 /* virtio-pci-bus */
 
 static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -2701,6 +2720,7 @@ static void virtio_pci_register_types(void)
 #ifdef CONFIG_LINUX
     type_register_static(&virtio_host_pci_info);
 #endif
+    type_register_static(&vhost_input_pci_info);
     type_register_static(&virtio_pci_bus_info);
     type_register_static(&virtio_pci_info);
 #ifdef CONFIG_VIRTFS
diff --git a/MAINTAINERS b/MAINTAINERS
index ee13fe9ef1..50ace65b3b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1231,6 +1231,7 @@ L: qemu-s390x@nongnu.org
 virtio-input
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Maintained
+F: hw/input/vhost-user-input.c
 F: hw/input/virtio-input*.c
 F: include/hw/virtio/virtio-input.h
 F: contrib/vhost-user-input/*
diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs
index c8b00f71ec..6adc308cdd 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -11,6 +11,7 @@ common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input.o
 common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-hid.o
 ifeq ($(CONFIG_LINUX),y)
 common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-host.o
+common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO_INPUT)) += vhost-user-input.o
 endif
 
 obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 16/29] vhost-user: add vhost_user_gpu_set_socket()
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (14 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 15/29] Add vhost-user-input-pci Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 17/29] vhost-user: add vhost_user_gpu_get_num_capsets() Marc-André Lureau
                   ` (15 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Add a new vhost-user message to give a unix socket to a vhost-user
backend for GPU display updates.

Back when I started that work, I added a new GPU channel because the
vhost-user protocol wasn't bidirectional. Since then, there is a
vhost-user-slave channel for the slave to send requests to the master.
We could extend it with GPU messages. However, the GPU protocol is
quite orthogonal to vhost-user, thus I chose to have a new dedicated
channel.

See vhost-user-gpu.rst for the protocol details.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/libvhost-user/libvhost-user.h |   1 +
 include/hw/virtio/vhost-backend.h     |   1 +
 hw/virtio/vhost-user.c                |  11 ++
 MAINTAINERS                           |   6 +
 docs/interop/vhost-user-gpu.rst       | 236 ++++++++++++++++++++++++++
 docs/interop/vhost-user.txt           |   9 +
 6 files changed, 264 insertions(+)
 create mode 100644 docs/interop/vhost-user-gpu.rst

diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index a4afbc3a46..42e227cce6 100644
--- a/contrib/libvhost-user/libvhost-user.h
+++ b/contrib/libvhost-user/libvhost-user.h
@@ -92,6 +92,7 @@ typedef enum VhostUserRequest {
     VHOST_USER_POSTCOPY_LISTEN  = 29,
     VHOST_USER_POSTCOPY_END     = 30,
     VHOST_USER_INPUT_GET_CONFIG = 31,
+    VHOST_USER_GPU_SET_SOCKET   = 32,
     VHOST_USER_MAX
 } VhostUserRequest;
 
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index 1fca321d8a..daf9180c33 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -163,5 +163,6 @@ int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev,
 
 int vhost_user_input_get_config(struct vhost_dev *dev,
                                 struct virtio_input_config **config);
+int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd);
 
 #endif /* VHOST_BACKEND_H */
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 0c6914fab7..8c1a1742b0 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -90,6 +90,7 @@ typedef enum VhostUserRequest {
     VHOST_USER_POSTCOPY_LISTEN  = 29,
     VHOST_USER_POSTCOPY_END     = 30,
     VHOST_USER_INPUT_GET_CONFIG = 31,
+    VHOST_USER_GPU_SET_SOCKET   = 32,
     VHOST_USER_MAX
 } VhostUserRequest;
 
@@ -397,6 +398,16 @@ err:
     return -1;
 }
 
+int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd)
+{
+    VhostUserMsg msg = {
+        .hdr.request = VHOST_USER_GPU_SET_SOCKET,
+        .hdr.flags = VHOST_USER_VERSION,
+    };
+
+    return vhost_user_write(dev, &msg, &fd, 1);
+}
+
 static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
                                    struct vhost_log *log)
 {
diff --git a/MAINTAINERS b/MAINTAINERS
index 50ace65b3b..5a83bf56a3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1371,6 +1371,12 @@ F: hw/display/virtio-gpu*
 F: hw/display/virtio-vga.c
 F: include/hw/virtio/virtio-gpu.h
 
+vhost-user-gpu
+M: Marc-André Lureau <marcandre.lureau@redhat.com>
+M: Gerd Hoffmann <kraxel@redhat.com>
+S: Maintained
+F: docs/interop/vhost-user-gpu.rst
+
 Cirrus VGA
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Odd Fixes
diff --git a/docs/interop/vhost-user-gpu.rst b/docs/interop/vhost-user-gpu.rst
new file mode 100644
index 0000000000..309d07fd6d
--- /dev/null
+++ b/docs/interop/vhost-user-gpu.rst
@@ -0,0 +1,236 @@
+=======================
+Vhost-user-gpu Protocol
+=======================
+
+This work is licensed under the terms of the GNU GPL, version 2 or later.
+See the COPYING file in the top-level directory.
+
+Overview
+========
+
+The vhost-user-gpu protocol is aiming at sharing the rendering result
+of a virtio-gpu, done from a vhost-user slave process to a vhost-user
+master process (such as QEMU). It bears a resemblance to a display
+server protocol, if you consider QEMU as the display server and the
+slave as the client, but in a very limited way. Typically, it will
+work by setting a scanout/display configuration, before sending flush
+events for the display updates. It will also update the cursor shape
+and position.
+
+The protocol is sent over a UNIX domain stream socket, since it uses
+socket ancillary data to share opened file descriptors (DMABUF fds or
+shared memory).
+
+Requests are sent by the slave, and the optional replies by the master.
+
+Wire format
+===========
+
+Unless specified differently, numbers are in the machine native byte
+order.
+
+A vhost-user-gpu request consists of 2 header fields and a payload:
+
+::
+
+  ------------------------------------
+  | u32:request | u32:size | payload |
+  ------------------------------------
+
+- request: 32-bit type of the request
+- size: 32-bit size of the payload
+
+A reply consists only of a payload, whose content depends on on the request.
+
+
+Payload types
+-------------
+
+VhostUserGpuCursorPos
+^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+   ----------------------------------
+   | u32:scanout-id | u32:x | u32:y |
+   ----------------------------------
+
+- scanout-id: the scanout where the cursor is located
+- x/y: the cursor postion
+
+VhostUserGpuCursorUpdate
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+   -----------------------------------------------------------------------------
+   | VhostUserGpuCursorPos:pos | u32:hot_x | u32:hot_y | [u32; 64 * 64] cursor |
+   -----------------------------------------------------------------------------
+
+- pos: the cursor location
+- hot_x/hot_y: the cursor hot location
+- cursor: RGBA cursor data
+
+VhostUserGpuScanout
+^^^^^^^^^^^^^^^^^^^
+
+::
+
+   ----------------------------------
+   | u32:scanout-id | u32:w | u32:h |
+   ----------------------------------
+
+- scanout-id: the scanout configuration to set
+- w/h: the scanout width/height size
+
+
+VhostUserGpuUpdate
+^^^^^^^^^^^^^^^^^^
+
+::
+
+   ---------------------------------------------------------
+   | u32:scanout-id | u32:x | u32:y | u32:w | u32:h | data |
+   ---------------------------------------------------------
+
+- scanout-id: the scanout content to update
+- x/y/w/h: region of the update
+- data: RGBA data (size is computed based on the region size, and request type)
+
+VhostUserGpuDMABUFScanout
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+   ---------------------------------------------------
+   | u32:scanout-id | u32:x | u32:y | u32:w | u32:h | ...
+   ----------------------------------------------------------
+    u32:fdw | u32:fwh | u32:stride | u32:flags | i32:fourcc |
+   ----------------------------------------------------------
+
+- scanout-id: the scanout configuration to set
+- x/y: the location of the scanout within the DMABUF
+- w/h: the scanout width/height size
+- fdw/fdh/stride/flags/fourcc: the DMABUF width/height/stride/flags/drm-fourcc
+
+
+In QEMU the vhost-user-gpu message is implemented with the following struct:
+
+::
+
+  typedef struct VhostUserGpuMsg {
+      uint32_t request; /* VhostUserGpuRequest */
+      uint32_t size; /* the following payload size */
+      union {
+          VhostUserGpuCursorPos cursor_pos;
+          VhostUserGpuCursorUpdate cursor_update;
+          VhostUserGpuScanout scanout;
+          VhostUserGpuUpdate update;
+          VhostUserGpuDMABUFScanout dmabuf_scanout;
+          uint64_t u64;
+      } payload;
+  } QEMU_PACKED VhostUserGpuMsg;
+
+Protocol features
+=================
+
+None yet.
+
+As the protocol may need to evolve, new messages and communication
+changes are negotiated thanks to preliminary
+VHOST_USER_GPU_GET_PROTOCOL_FEATURES and
+VHOST_USER_GPU_SET_PROTOCOL_FEATURES requests.
+
+Message types
+=============
+
+- VHOST_USER_GPU_GET_PROTOCOL_FEATURES
+
+  Id:1
+  Request payload: N/A
+  Reply payload: uint64_t
+
+  Get the supported protocol features bitmask.
+
+- VHOST_USER_GPU_SET_PROTOCOL_FEATURES
+
+  Id:2
+  Request payload: uint64_t
+  Reply payload: N/A
+
+  Enable protocol features using a bitmask.
+
+- VHOST_USER_GPU_GET_DISPLAY_INFO
+
+  Id:3
+  Request payload: N/A
+  Reply payload: struct virtio_gpu_resp_display_info (numbers in LE,
+                 according to the virtio protocol)
+
+  Get the preferred display configuration.
+
+- VHOST_USER_GPU_CURSOR_POS
+
+  Id: 4
+  Request payload: struct VhostUserGpuCursorPos
+  Reply payload: N/A
+
+  Set/show the cursor position.
+
+- VHOST_USER_GPU_CURSOR_POS_HIDE
+
+  Id:5
+  Request payload: struct VhostUserGpuCursorPos
+  Reply payload: N/A
+
+  Set/hide the cursor.
+
+- VHOST_USER_GPU_CURSOR_UPDATE
+
+  Id:6
+  Request payload: struct VhostUserGpuCursorUpdate
+  Reply payload: N/A
+
+  Update the cursor shape and location.
+
+- VHOST_USER_GPU_SCANOUT
+
+  Id:7
+  Request payload: struct VhostUserGpuScanout
+  Reply payload: N/A
+
+  Set the scanout resolution. To disable a scanout, the dimensions
+  width/height are set to 0.
+
+- VHOST_USER_GPU_UPDATE
+
+  Id:8
+  Request payload: struct VhostUserGpuUpdate
+  Reply payload: N/A
+
+  Update the scanout content. The data payload contains the graphical bits.
+  The display should be flushed and presented.
+
+- VHOST_USER_GPU_DMABUF_SCANOUT
+
+  Id:9
+  Request payload: struct VhostUserGpuDMABUFScanout
+  Reply payload: N/A
+
+  Set the scanout resolution/configuration, and share a DMABUF file
+  descriptor for the scanout content, which is passed as ancillary
+  data. To disable a scanout, the dimensions width/height are set
+  to 0, there is no file descriptor passed.
+
+- VHOST_USER_GPU_DMABUF_UPDATE
+
+  Id:10
+  Request payload: struct VhostUserGpuUpdate
+  Reply payload: u32
+
+  The display should be flushed and presented according to updated
+  region from VhostUserGpuUpdate.
+
+  Note: there is no data payload, since the scanout is shared thanks
+  to DMABUF, that must have been set previously with
+  VHOST_USER_GPU_DMABUF_SCANOUT.
diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt
index cba1ddde16..c4c063a8c0 100644
--- a/docs/interop/vhost-user.txt
+++ b/docs/interop/vhost-user.txt
@@ -768,6 +768,15 @@ Master message types
 
       Ask vhost user input backend the list of virtio_input_config.
 
+ * VHOST_USER_GPU_SET_SOCKET
+      Id: 32
+      Master payload: N/A
+      Slave payload: N/A
+
+      Sets the GPU protocol socket file descriptor, which is passed as
+      ancillary data. The GPU protocol is used to inform the master of
+      rendering state and updates. See vhost-user-gpu.rst for details.
+
 Slave message types
 -------------------
 
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 17/29] vhost-user: add vhost_user_gpu_get_num_capsets()
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (15 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 16/29] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 18/29] virtio: add virtio-gpu bswap helpers header Marc-André Lureau
                   ` (14 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

See vhost-user.txt protocol documentation for details.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/libvhost-user/libvhost-user.h |  1 +
 include/hw/virtio/vhost-backend.h     |  1 +
 hw/virtio/vhost-user.c                | 15 +++++++++++++++
 docs/interop/vhost-user.txt           |  8 ++++++++
 4 files changed, 25 insertions(+)

diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index 42e227cce6..9f30e05bd1 100644
--- a/contrib/libvhost-user/libvhost-user.h
+++ b/contrib/libvhost-user/libvhost-user.h
@@ -93,6 +93,7 @@ typedef enum VhostUserRequest {
     VHOST_USER_POSTCOPY_END     = 30,
     VHOST_USER_INPUT_GET_CONFIG = 31,
     VHOST_USER_GPU_SET_SOCKET   = 32,
+    VHOST_USER_GPU_GET_NUM_CAPSETS = 33,
     VHOST_USER_MAX
 } VhostUserRequest;
 
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index daf9180c33..730e24c33b 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -164,5 +164,6 @@ int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev,
 int vhost_user_input_get_config(struct vhost_dev *dev,
                                 struct virtio_input_config **config);
 int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd);
+int vhost_user_gpu_get_num_capsets(struct vhost_dev *dev, uint32_t *num);
 
 #endif /* VHOST_BACKEND_H */
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 8c1a1742b0..bfec6528f2 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -91,6 +91,7 @@ typedef enum VhostUserRequest {
     VHOST_USER_POSTCOPY_END     = 30,
     VHOST_USER_INPUT_GET_CONFIG = 31,
     VHOST_USER_GPU_SET_SOCKET   = 32,
+    VHOST_USER_GPU_GET_NUM_CAPSETS = 33,
     VHOST_USER_MAX
 } VhostUserRequest;
 
@@ -922,6 +923,20 @@ static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64)
     return 0;
 }
 
+int vhost_user_gpu_get_num_capsets(struct vhost_dev *dev, uint32_t *num)
+{
+    uint64_t u64;
+
+    if (vhost_user_get_u64(dev, VHOST_USER_GPU_GET_NUM_CAPSETS, &u64) < 0) {
+        return -1;
+    }
+    if (u64 > INT32_MAX) {
+        return -1;
+    }
+    *num = u64;
+    return 0;
+}
+
 static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features)
 {
     return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features);
diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt
index c4c063a8c0..ba5e37d714 100644
--- a/docs/interop/vhost-user.txt
+++ b/docs/interop/vhost-user.txt
@@ -777,6 +777,14 @@ Master message types
       ancillary data. The GPU protocol is used to inform the master of
       rendering state and updates. See vhost-user-gpu.rst for details.
 
+ * VHOST_USER_GPU_GET_NUM_CAPSETS
+      Id: 33
+      Master payload: N/A
+      Slave payload: u64
+
+      Get the virtio-gpu 'num_capsets' config value. For non-virgl
+      rendering, the value can be 0.
+
 Slave message types
 -------------------
 
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 18/29] virtio: add virtio-gpu bswap helpers header
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (16 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 17/29] vhost-user: add vhost_user_gpu_get_num_capsets() Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 19/29] util: promote qemu_egl_rendernode_open() to libqemuutil Marc-André Lureau
                   ` (13 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

The helper functions are useful to build the vhost-user-gpu backend.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu-bswap.h | 61 ++++++++++++++++++++++++++++
 hw/display/virtio-gpu.c              | 43 +-------------------
 2 files changed, 62 insertions(+), 42 deletions(-)
 create mode 100644 include/hw/virtio/virtio-gpu-bswap.h

diff --git a/include/hw/virtio/virtio-gpu-bswap.h b/include/hw/virtio/virtio-gpu-bswap.h
new file mode 100644
index 0000000000..38d12160f6
--- /dev/null
+++ b/include/hw/virtio/virtio-gpu-bswap.h
@@ -0,0 +1,61 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ *     Dave Airlie <airlied@redhat.com>
+ *     Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_VIRTIO_GPU_BSWAP_H
+#define HW_VIRTIO_GPU_BSWAP_H
+
+#include "qemu/bswap.h"
+
+static inline void
+virtio_gpu_ctrl_hdr_bswap(struct virtio_gpu_ctrl_hdr *hdr)
+{
+    le32_to_cpus(&hdr->type);
+    le32_to_cpus(&hdr->flags);
+    le64_to_cpus(&hdr->fence_id);
+    le32_to_cpus(&hdr->ctx_id);
+    le32_to_cpus(&hdr->padding);
+}
+
+static inline void
+virtio_gpu_bswap_32(void *ptr, size_t size)
+{
+#ifdef HOST_WORDS_BIGENDIAN
+
+    size_t i;
+    struct virtio_gpu_ctrl_hdr *hdr = (struct virtio_gpu_ctrl_hdr *) ptr;
+
+    virtio_gpu_ctrl_hdr_bswap(hdr);
+
+    i = sizeof(struct virtio_gpu_ctrl_hdr);
+    while (i < size) {
+        le32_to_cpus((uint32_t *)(ptr + i));
+        i = i + sizeof(uint32_t);
+    }
+
+#endif
+}
+
+static inline void
+virtio_gpu_t2d_bswap(struct virtio_gpu_transfer_to_host_2d *t2d)
+{
+    virtio_gpu_ctrl_hdr_bswap(&t2d->hdr);
+    le32_to_cpus(&t2d->r.x);
+    le32_to_cpus(&t2d->r.y);
+    le32_to_cpus(&t2d->r.width);
+    le32_to_cpus(&t2d->r.height);
+    le64_to_cpus(&t2d->offset);
+    le32_to_cpus(&t2d->resource_id);
+    le32_to_cpus(&t2d->padding);
+}
+
+#endif
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index ec366f4c35..28602c1a35 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -19,6 +19,7 @@
 #include "trace.h"
 #include "hw/virtio/virtio.h"
 #include "hw/virtio/virtio-gpu.h"
+#include "hw/virtio/virtio-gpu-bswap.h"
 #include "hw/virtio/virtio-bus.h"
 #include "migration/blocker.h"
 #include "qemu/log.h"
@@ -31,48 +32,6 @@ virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
 
 static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res);
 
-static void
-virtio_gpu_ctrl_hdr_bswap(struct virtio_gpu_ctrl_hdr *hdr)
-{
-    le32_to_cpus(&hdr->type);
-    le32_to_cpus(&hdr->flags);
-    le64_to_cpus(&hdr->fence_id);
-    le32_to_cpus(&hdr->ctx_id);
-    le32_to_cpus(&hdr->padding);
-}
-
-static void virtio_gpu_bswap_32(void *ptr,
-                                size_t size)
-{
-#ifdef HOST_WORDS_BIGENDIAN
-
-    size_t i;
-    struct virtio_gpu_ctrl_hdr *hdr = (struct virtio_gpu_ctrl_hdr *) ptr;
-
-    virtio_gpu_ctrl_hdr_bswap(hdr);
-
-    i = sizeof(struct virtio_gpu_ctrl_hdr);
-    while (i < size) {
-        le32_to_cpus((uint32_t *)(ptr + i));
-        i = i + sizeof(uint32_t);
-    }
-
-#endif
-}
-
-static void
-virtio_gpu_t2d_bswap(struct virtio_gpu_transfer_to_host_2d *t2d)
-{
-    virtio_gpu_ctrl_hdr_bswap(&t2d->hdr);
-    le32_to_cpus(&t2d->r.x);
-    le32_to_cpus(&t2d->r.y);
-    le32_to_cpus(&t2d->r.width);
-    le32_to_cpus(&t2d->r.height);
-    le64_to_cpus(&t2d->offset);
-    le32_to_cpus(&t2d->resource_id);
-    le32_to_cpus(&t2d->padding);
-}
-
 #ifdef CONFIG_VIRGL
 #include <virglrenderer.h>
 #define VIRGL(_g, _virgl, _simple, ...)                     \
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 19/29] util: promote qemu_egl_rendernode_open() to libqemuutil
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (17 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 18/29] virtio: add virtio-gpu bswap helpers header Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile() Marc-André Lureau
                   ` (12 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

vhost-user-gpu will share the same code to open a DRM node.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qemu/drm.h |  6 +++++
 ui/egl-helpers.c   | 51 ++---------------------------------
 util/drm.c         | 66 ++++++++++++++++++++++++++++++++++++++++++++++
 MAINTAINERS        |  1 +
 util/Makefile.objs |  1 +
 5 files changed, 76 insertions(+), 49 deletions(-)
 create mode 100644 include/qemu/drm.h
 create mode 100644 util/drm.c

diff --git a/include/qemu/drm.h b/include/qemu/drm.h
new file mode 100644
index 0000000000..4c3e622f5c
--- /dev/null
+++ b/include/qemu/drm.h
@@ -0,0 +1,6 @@
+#ifndef QEMU_DRM_H_
+#define QEMU_DRM_H_
+
+int qemu_drm_rendernode_open(const char *rendernode);
+
+#endif
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 71b6a97bd1..4f475142fc 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -15,9 +15,7 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 #include "qemu/osdep.h"
-#include <glob.h>
-#include <dirent.h>
-
+#include "qemu/drm.h"
 #include "qemu/error-report.h"
 #include "ui/console.h"
 #include "ui/egl-helpers.h"
@@ -147,57 +145,12 @@ int qemu_egl_rn_fd;
 struct gbm_device *qemu_egl_rn_gbm_dev;
 EGLContext qemu_egl_rn_ctx;
 
-static int qemu_egl_rendernode_open(const char *rendernode)
-{
-    DIR *dir;
-    struct dirent *e;
-    int r, fd;
-    char *p;
-
-    if (rendernode) {
-        return open(rendernode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
-    }
-
-    dir = opendir("/dev/dri");
-    if (!dir) {
-        return -1;
-    }
-
-    fd = -1;
-    while ((e = readdir(dir))) {
-        if (e->d_type != DT_CHR) {
-            continue;
-        }
-
-        if (strncmp(e->d_name, "renderD", 7)) {
-            continue;
-        }
-
-        p = g_strdup_printf("/dev/dri/%s", e->d_name);
-
-        r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
-        if (r < 0) {
-            g_free(p);
-            continue;
-        }
-        fd = r;
-        g_free(p);
-        break;
-    }
-
-    closedir(dir);
-    if (fd < 0) {
-        return -1;
-    }
-    return fd;
-}
-
 int egl_rendernode_init(const char *rendernode, DisplayGLMode mode)
 {
     qemu_egl_rn_fd = -1;
     int rc;
 
-    qemu_egl_rn_fd = qemu_egl_rendernode_open(rendernode);
+    qemu_egl_rn_fd = qemu_drm_rendernode_open(rendernode);
     if (qemu_egl_rn_fd == -1) {
         error_report("egl: no drm render node available");
         goto err;
diff --git a/util/drm.c b/util/drm.c
new file mode 100644
index 0000000000..a23ff24538
--- /dev/null
+++ b/util/drm.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015-2016 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "qemu/drm.h"
+
+#include <glob.h>
+#include <dirent.h>
+
+int qemu_drm_rendernode_open(const char *rendernode)
+{
+    DIR *dir;
+    struct dirent *e;
+    int r, fd;
+    char *p;
+
+    if (rendernode) {
+        return open(rendernode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
+    }
+
+    dir = opendir("/dev/dri");
+    if (!dir) {
+        return -1;
+    }
+
+    fd = -1;
+    while ((e = readdir(dir))) {
+        if (e->d_type != DT_CHR) {
+            continue;
+        }
+
+        if (strncmp(e->d_name, "renderD", 7)) {
+            continue;
+        }
+
+        p = g_strdup_printf("/dev/dri/%s", e->d_name);
+
+        r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
+        if (r < 0) {
+            g_free(p);
+            continue;
+        }
+        fd = r;
+        g_free(p);
+        break;
+    }
+
+    closedir(dir);
+    if (fd < 0) {
+        return -1;
+    }
+    return fd;
+}
diff --git a/MAINTAINERS b/MAINTAINERS
index 5a83bf56a3..01e2b47c34 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1565,6 +1565,7 @@ S: Odd Fixes
 F: ui/
 F: include/ui/
 F: qapi/ui.json
+F: util/drm.c
 
 Cocoa graphics
 M: Peter Maydell <peter.maydell@linaro.org>
diff --git a/util/Makefile.objs b/util/Makefile.objs
index e1c3fed4dc..1810f970ef 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -49,3 +49,4 @@ util-obj-y += stats64.o
 util-obj-y += systemd.o
 util-obj-y += iova-tree.o
 util-obj-$(CONFIG_LINUX) += vfio-helpers.o
+util-obj-$(CONFIG_LINUX) += drm.o
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile()
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (18 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 19/29] util: promote qemu_egl_rendernode_open() to libqemuutil Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-08-28 15:52   ` Daniel P. Berrangé
  2018-08-31 10:42   ` Daniel P. Berrangé
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking Marc-André Lureau
                   ` (11 subsequent siblings)
  31 siblings, 2 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

There are variants of qemu_create_pidfile() in qemu-pr-helper and
qemu-ga. Let's have a common implementation in libqemuutil.

The code is based from pr-helper write_pidfile(), but allows the
caller to deal with error reporting and behaviour.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qemu/osdep.h  |  3 ++-
 os-posix.c            | 24 -------------------
 os-win32.c            | 25 --------------------
 qga/main.c            | 54 ++++++++-----------------------------------
 scsi/qemu-pr-helper.c | 40 ++++----------------------------
 util/oslib-posix.c    | 33 ++++++++++++++++++++++++++
 util/oslib-win32.c    | 27 ++++++++++++++++++++++
 vl.c                  |  4 ++--
 8 files changed, 79 insertions(+), 131 deletions(-)

diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index a91068df0e..47fa570bd4 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -448,7 +448,8 @@ bool qemu_has_ofd_lock(void);
 #define FMT_pid "%d"
 #endif
 
-int qemu_create_pidfile(const char *filename);
+bool qemu_write_pidfile(const char *pidfile, Error **errp);
+
 int qemu_get_thread_id(void);
 
 #ifndef CONFIG_IOVEC
diff --git a/os-posix.c b/os-posix.c
index 9ce6f74513..0e9403b4ff 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -352,30 +352,6 @@ void os_set_line_buffering(void)
     setvbuf(stdout, NULL, _IOLBF, 0);
 }
 
-int qemu_create_pidfile(const char *filename)
-{
-    char buffer[128];
-    int len;
-    int fd;
-
-    fd = qemu_open(filename, O_RDWR | O_CREAT, 0600);
-    if (fd == -1) {
-        return -1;
-    }
-    if (lockf(fd, F_TLOCK, 0) == -1) {
-        close(fd);
-        return -1;
-    }
-    len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", getpid());
-    if (write(fd, buffer, len) != len) {
-        close(fd);
-        return -1;
-    }
-
-    /* keep pidfile open & locked forever */
-    return 0;
-}
-
 bool is_daemonized(void)
 {
     return daemonize;
diff --git a/os-win32.c b/os-win32.c
index 0674f94b57..0e0d7f50f3 100644
--- a/os-win32.c
+++ b/os-win32.c
@@ -97,28 +97,3 @@ int os_parse_cmd_args(int index, const char *optarg)
 {
     return -1;
 }
-
-int qemu_create_pidfile(const char *filename)
-{
-    char buffer[128];
-    int len;
-    HANDLE file;
-    OVERLAPPED overlap;
-    BOOL ret;
-    memset(&overlap, 0, sizeof(overlap));
-
-    file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
-                      OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-
-    if (file == INVALID_HANDLE_VALUE) {
-        return -1;
-    }
-    len = snprintf(buffer, sizeof(buffer), "%d\n", getpid());
-    ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len,
-                    NULL, &overlap);
-    CloseHandle(file);
-    if (ret == 0) {
-        return -1;
-    }
-    return 0;
-}
diff --git a/qga/main.c b/qga/main.c
index 537cc0e162..cc50692098 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -341,46 +341,6 @@ static FILE *ga_open_logfile(const char *logfile)
     return f;
 }
 
-#ifndef _WIN32
-static bool ga_open_pidfile(const char *pidfile)
-{
-    int pidfd;
-    char pidstr[32];
-
-    pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
-    if (pidfd == -1 || lockf(pidfd, F_TLOCK, 0)) {
-        g_critical("Cannot lock pid file, %s", strerror(errno));
-        if (pidfd != -1) {
-            close(pidfd);
-        }
-        return false;
-    }
-
-    if (ftruncate(pidfd, 0)) {
-        g_critical("Failed to truncate pid file");
-        goto fail;
-    }
-    snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
-    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
-        g_critical("Failed to write pid file");
-        goto fail;
-    }
-
-    /* keep pidfile open & locked forever */
-    return true;
-
-fail:
-    unlink(pidfile);
-    close(pidfd);
-    return false;
-}
-#else /* _WIN32 */
-static bool ga_open_pidfile(const char *pidfile)
-{
-    return true;
-}
-#endif
-
 static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
 {
     return strcmp(str1, str2);
@@ -480,8 +440,11 @@ void ga_unset_frozen(GAState *s)
     ga_enable_logging(s);
     g_warning("logging re-enabled due to filesystem unfreeze");
     if (s->deferred_options.pid_filepath) {
-        if (!ga_open_pidfile(s->deferred_options.pid_filepath)) {
-            g_warning("failed to create/open pid file");
+        Error *err = NULL;
+
+        if (!qemu_write_pidfile(s->deferred_options.pid_filepath, &err)) {
+            g_warning("%s", error_get_pretty(err));
+            error_free(err);
         }
         s->deferred_options.pid_filepath = NULL;
     }
@@ -516,8 +479,11 @@ static void become_daemon(const char *pidfile)
     }
 
     if (pidfile) {
-        if (!ga_open_pidfile(pidfile)) {
-            g_critical("failed to create pidfile");
+        Error *err = NULL;
+
+        if (!qemu_write_pidfile(pidfile, &err)) {
+            g_critical("%s", error_get_pretty(err));
+            error_free(err);
             exit(EXIT_FAILURE);
         }
     }
diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c
index 1528a712a0..cf6d360652 100644
--- a/scsi/qemu-pr-helper.c
+++ b/scsi/qemu-pr-helper.c
@@ -117,39 +117,6 @@ QEMU_COPYRIGHT "\n"
     , name);
 }
 
-static void write_pidfile(void)
-{
-    int pidfd;
-    char pidstr[32];
-
-    pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
-    if (pidfd == -1) {
-        error_report("Cannot open pid file, %s", strerror(errno));
-        exit(EXIT_FAILURE);
-    }
-
-    if (lockf(pidfd, F_TLOCK, 0)) {
-        error_report("Cannot lock pid file, %s", strerror(errno));
-        goto fail;
-    }
-    if (ftruncate(pidfd, 0)) {
-        error_report("Failed to truncate pid file");
-        goto fail;
-    }
-
-    snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
-    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
-        error_report("Failed to write pid file");
-        goto fail;
-    }
-    return;
-
-fail:
-    unlink(pidfile);
-    close(pidfd);
-    exit(EXIT_FAILURE);
-}
-
 /* SG_IO support */
 
 typedef struct PRHelperSGIOData {
@@ -1076,8 +1043,11 @@ int main(int argc, char **argv)
         }
     }
 
-    if (daemonize || pidfile_specified)
-        write_pidfile();
+    if ((daemonize || pidfile_specified) &&
+        !qemu_write_pidfile(pidfile, &local_err)) {
+        error_report_err(local_err);
+        exit(EXIT_FAILURE);
+    }
 
 #ifdef CONFIG_LIBCAP
     if (drop_privileges() < 0) {
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 13b6f8d776..da1d4a3201 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -88,6 +88,39 @@ int qemu_daemon(int nochdir, int noclose)
     return daemon(nochdir, noclose);
 }
 
+bool qemu_write_pidfile(const char *pidfile, Error **errp)
+{
+    int pidfd;
+    char pidstr[32];
+
+    pidfd = qemu_open(pidfile, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+    if (pidfd == -1) {
+        error_setg_errno(errp, errno, "Cannot open pid file");
+        return false;
+    }
+
+    if (lockf(pidfd, F_TLOCK, 0)) {
+        error_setg_errno(errp, errno, "Cannot lock pid file");
+        goto fail;
+    }
+    if (ftruncate(pidfd, 0)) {
+        error_setg_errno(errp, errno, "Failed to truncate pid file");
+        goto fail;
+    }
+
+    snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
+    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
+        error_setg(errp, "Failed to write pid file");
+        goto fail;
+    }
+    return true;
+
+fail:
+    unlink(pidfile);
+    close(pidfd);
+    return false;
+}
+
 void *qemu_oom_check(void *ptr)
 {
     if (ptr == NULL) {
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index bb5ad28bd3..66d05c88e6 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -767,3 +767,30 @@ ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags,
     }
     return ret;
 }
+
+bool qemu_write_pidfile(const char *filename, Error **errp)
+{
+    char buffer[128];
+    int len;
+    HANDLE file;
+    OVERLAPPED overlap;
+    BOOL ret;
+    memset(&overlap, 0, sizeof(overlap));
+
+    file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+                      OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (file == INVALID_HANDLE_VALUE) {
+        error_setg(errp, "Failed to create PID file");
+        return false;
+    }
+    len = snprintf(buffer, sizeof(buffer), "%d\n", getpid());
+    ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len,
+                    NULL, &overlap);
+    CloseHandle(file);
+    if (ret == 0) {
+        error_setg(errp, "Failed to write PID file");
+        return false;
+    }
+    return true;
+}
diff --git a/vl.c b/vl.c
index 5272b4939f..8a9d3457ec 100644
--- a/vl.c
+++ b/vl.c
@@ -3993,8 +3993,8 @@ int main(int argc, char **argv, char **envp)
     os_daemonize();
     rcu_disable_atfork();
 
-    if (pid_file && qemu_create_pidfile(pid_file) != 0) {
-        error_report("could not acquire pid file: %s", strerror(errno));
+    if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
+        error_reportf_err(err, "cannot create PID file: ");
         exit(1);
     }
 
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (19 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile() Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-08-28 15:59   ` Daniel P. Berrangé
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 22/29] contrib: add vhost-user-gpu Marc-André Lureau
                   ` (10 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

According to Daniel Berrange, fcntl() locks have better portable
semantics than lockf().

Use an exclusive lock on the first byte with fcntl().

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 util/oslib-posix.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index da1d4a3201..26b11490b9 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -92,6 +92,11 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp)
 {
     int pidfd;
     char pidstr[32];
+    struct flock lock = {
+        .l_type = F_WRLCK,
+        .l_whence = SEEK_SET,
+        .l_len = 1,
+    };
 
     pidfd = qemu_open(pidfile, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
     if (pidfd == -1) {
@@ -99,10 +104,11 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp)
         return false;
     }
 
-    if (lockf(pidfd, F_TLOCK, 0)) {
+    if (fcntl(pidfd, F_SETLK, &lock)) {
         error_setg_errno(errp, errno, "Cannot lock pid file");
         goto fail;
     }
+
     if (ftruncate(pidfd, 0)) {
         error_setg_errno(errp, errno, "Failed to truncate pid file");
         goto fail;
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 22/29] contrib: add vhost-user-gpu
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (20 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 23/29] virtio-gpu: remove unused qdev Marc-André Lureau
                   ` (9 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Add a vhost-user gpu backend example, based on virtio-gpu/3d device. It
is to be associated with a vhost-user-backend object, ex:

-object vhost-user-backend,id=vug,cmd="vhost-user-gpu"

TODO:
- add/check multi-head support
- crash & resume handling
- accelerated rendering/display to avoid the many round trips

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 contrib/vhost-user-gpu/drm.h         |   63 ++
 contrib/vhost-user-gpu/virgl.h       |   25 +
 contrib/vhost-user-gpu/vugpu.h       |  168 ++++
 contrib/vhost-user-gpu/drm.c         |  190 +++++
 contrib/vhost-user-gpu/main.c        | 1164 ++++++++++++++++++++++++++
 contrib/vhost-user-gpu/virgl.c       |  579 +++++++++++++
 MAINTAINERS                          |    2 +
 Makefile                             |    3 +
 Makefile.objs                        |    1 +
 configure                            |   32 +
 contrib/vhost-user-gpu/Makefile.objs |   10 +
 11 files changed, 2237 insertions(+)
 create mode 100644 contrib/vhost-user-gpu/drm.h
 create mode 100644 contrib/vhost-user-gpu/virgl.h
 create mode 100644 contrib/vhost-user-gpu/vugpu.h
 create mode 100644 contrib/vhost-user-gpu/drm.c
 create mode 100644 contrib/vhost-user-gpu/main.c
 create mode 100644 contrib/vhost-user-gpu/virgl.c
 create mode 100644 contrib/vhost-user-gpu/Makefile.objs

diff --git a/contrib/vhost-user-gpu/drm.h b/contrib/vhost-user-gpu/drm.h
new file mode 100644
index 0000000000..ee95816cc9
--- /dev/null
+++ b/contrib/vhost-user-gpu/drm.h
@@ -0,0 +1,63 @@
+/*
+ * Virtio vhost-user GPU Device
+ *
+ * DRM helpers
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef VHOST_USER_GPU_DRM_H
+#define VHOST_USER_GPU_DRM_H
+
+#include "qemu/osdep.h"
+
+#ifdef CONFIG_LIBDRM_INTEL
+#include <i915_drm.h>
+#include <intel_bufmgr.h>
+#endif
+
+struct drm_buffer;
+
+struct drm_device {
+    bool inited;
+    int fd;
+    char *name;
+#ifdef CONFIG_LIBDRM_INTEL
+    drm_intel_bufmgr *bufmgr;
+#endif
+
+    bool (*alloc_bo)(struct drm_buffer *buf);
+    void (*free_bo)(struct drm_buffer *buf);
+    bool (*export_bo_to_prime)(struct drm_buffer *buf, int *fd);
+    bool (*map_bo)(struct drm_buffer *buf);
+    void (*unmap_bo)(struct drm_buffer *buf);
+    void (*device_destroy)(struct drm_device *dev);
+};
+
+struct drm_buffer {
+    struct drm_device *dev;
+
+#ifdef CONFIG_LIBDRM_INTEL
+    drm_intel_bo *intel_bo;
+#endif /* HAVE_LIBDRM_INTEL */
+
+    uint32_t gem_handle;
+    int dmabuf_fd;
+    uint8_t *mmap;
+
+    int width;
+    int height;
+    int bpp;
+    unsigned long stride;
+    int format;
+};
+
+bool drm_device_init(struct drm_device *dev, int fd);
+void drm_device_destroy(struct drm_device *dev);
+
+bool drm_buffer_create(struct drm_buffer *buffer, struct drm_device *dev,
+                       int width, int height);
+bool drm_buffer_get_dmabuf_fd(struct drm_buffer *buffer, int *fd);
+void drm_buffer_destroy(struct drm_buffer *buffer);
+
+#endif
diff --git a/contrib/vhost-user-gpu/virgl.h b/contrib/vhost-user-gpu/virgl.h
new file mode 100644
index 0000000000..f952bc9d4f
--- /dev/null
+++ b/contrib/vhost-user-gpu/virgl.h
@@ -0,0 +1,25 @@
+/*
+ * Virtio vhost-user GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2018
+ *
+ * Authors:
+ *     Dave Airlie <airlied@redhat.com>
+ *     Gerd Hoffmann <kraxel@redhat.com>
+ *     Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef VUGPU_VIRGL_H_
+#define VUGPU_VIRGL_H_
+
+#include "vugpu.h"
+
+bool vg_virgl_init(VuGpu *g);
+uint32_t vg_virgl_get_num_capsets(void);
+void vg_virgl_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd);
+void vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id,
+                                 gpointer data);
+
+#endif
diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
new file mode 100644
index 0000000000..37e2bc9fcd
--- /dev/null
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -0,0 +1,168 @@
+/*
+ * Virtio vhost-user GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2018
+ *
+ * Authors:
+ *     Dave Airlie <airlied@redhat.com>
+ *     Gerd Hoffmann <kraxel@redhat.com>
+ *     Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef VUGPU_H_
+#define VUGPU_H_
+
+#include "qemu/osdep.h"
+
+#include "contrib/libvhost-user/libvhost-user-glib.h"
+#include "standard-headers/linux/virtio_gpu.h"
+
+#include "qemu/queue.h"
+#include "qemu/iov.h"
+#include "qemu/bswap.h"
+#include "drm.h"
+
+typedef enum VhostUserGpuRequest {
+    VHOST_USER_GPU_NONE = 0,
+    VHOST_USER_GPU_GET_PROTOCOL_FEATURES,
+    VHOST_USER_GPU_SET_PROTOCOL_FEATURES,
+    VHOST_USER_GPU_GET_DISPLAY_INFO,
+    VHOST_USER_GPU_CURSOR_POS,
+    VHOST_USER_GPU_CURSOR_POS_HIDE,
+    VHOST_USER_GPU_CURSOR_UPDATE,
+    VHOST_USER_GPU_SCANOUT,
+    VHOST_USER_GPU_UPDATE,
+    VHOST_USER_GPU_DMABUF_SCANOUT,
+    VHOST_USER_GPU_DMABUF_UPDATE,
+} VhostUserGpuRequest;
+
+typedef struct VhostUserGpuDisplayInfoReply {
+    struct virtio_gpu_resp_display_info info;
+} VhostUserGpuDisplayInfoReply;
+
+typedef struct VhostUserGpuCursorPos {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+} QEMU_PACKED VhostUserGpuCursorPos;
+
+typedef struct VhostUserGpuCursorUpdate {
+    VhostUserGpuCursorPos pos;
+    uint32_t hot_x;
+    uint32_t hot_y;
+    uint32_t data[64 * 64];
+} QEMU_PACKED VhostUserGpuCursorUpdate;
+
+typedef struct VhostUserGpuScanout {
+    uint32_t scanout_id;
+    uint32_t width;
+    uint32_t height;
+} QEMU_PACKED VhostUserGpuScanout;
+
+typedef struct VhostUserGpuUpdate {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+    uint32_t width;
+    uint32_t height;
+    uint8_t data[];
+} QEMU_PACKED VhostUserGpuUpdate;
+
+typedef struct VhostUserGpuDMABUFScanout {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+    uint32_t width;
+    uint32_t height;
+    uint32_t fd_width;
+    uint32_t fd_height;
+    uint32_t fd_stride;
+    uint32_t fd_flags;
+    int fd_drm_fourcc;
+} QEMU_PACKED VhostUserGpuDMABUFScanout;
+
+typedef struct VhostUserGpuMsg {
+    uint32_t request; /* VhostUserGpuRequest */
+    uint32_t size; /* the following payload size */
+    union {
+        VhostUserGpuCursorPos cursor_pos;
+        VhostUserGpuCursorUpdate cursor_update;
+        VhostUserGpuScanout scanout;
+        VhostUserGpuUpdate update;
+        VhostUserGpuDMABUFScanout dmabuf_scanout;
+        uint64_t u64;
+    } payload;
+} QEMU_PACKED VhostUserGpuMsg;
+
+static VhostUserGpuMsg m __attribute__ ((unused));
+#define VHOST_USER_GPU_HDR_SIZE (sizeof(m.request) + sizeof(m.size))
+
+struct virtio_gpu_scanout {
+    uint32_t width, height;
+    int x, y;
+    int invalidate;
+    uint32_t resource_id;
+};
+
+typedef struct VuGpu {
+    VugDev dev;
+    struct drm_device drm_dev;
+    int sock_fd;
+    int drm_rnode_fd;
+    GSource *renderer_source;
+
+    bool virgl;
+    bool virgl_inited;
+    uint32_t inflight;
+
+    struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS];
+    QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist;
+    QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
+} VuGpu;
+
+struct virtio_gpu_ctrl_command {
+    VuVirtqElement elem;
+    VuVirtq *vq;
+    struct virtio_gpu_ctrl_hdr cmd_hdr;
+    uint32_t error;
+    bool finished;
+    QTAILQ_ENTRY(virtio_gpu_ctrl_command) next;
+};
+
+#define VUGPU_FILL_CMD(out) do {                                \
+        size_t s;                                               \
+        s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0,  \
+                       &out, sizeof(out));                      \
+        if (s != sizeof(out)) {                                 \
+            g_critical("%s: command size incorrect %zu vs %zu", \
+                       __func__, s, sizeof(out));               \
+            return;                                             \
+        }                                                       \
+    } while (0)
+
+
+void    vg_ctrl_response(VuGpu *g,
+                         struct virtio_gpu_ctrl_command *cmd,
+                         struct virtio_gpu_ctrl_hdr *resp,
+                         size_t resp_len);
+
+void    vg_ctrl_response_nodata(VuGpu *g,
+                                struct virtio_gpu_ctrl_command *cmd,
+                                enum virtio_gpu_ctrl_type type);
+
+int     vg_create_mapping_iov(VuGpu *g,
+                              struct virtio_gpu_resource_attach_backing *ab,
+                              struct virtio_gpu_ctrl_command *cmd,
+                              struct iovec **iov);
+
+void    vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd);
+
+void    vg_wait_ok(VuGpu *g);
+
+void    vg_send_msg(VuGpu *g, const VhostUserGpuMsg *msg, int fd);
+
+int     vg_sock_fd_read(int sock, void *buf, ssize_t buflen);
+
+#endif
diff --git a/contrib/vhost-user-gpu/drm.c b/contrib/vhost-user-gpu/drm.c
new file mode 100644
index 0000000000..c3691c35ba
--- /dev/null
+++ b/contrib/vhost-user-gpu/drm.c
@@ -0,0 +1,190 @@
+/*
+ * Virtio vhost-user GPU Device
+ *
+ * DRM helpers
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <xf86drm.h> /* not much to do with xf86 */
+#include <drm_fourcc.h>
+
+#include "drm.h"
+
+#ifdef CONFIG_LIBDRM_INTEL
+static bool
+intel_alloc_bo(struct drm_buffer *buf)
+{
+    uint32_t tiling = I915_TILING_NONE;
+
+    buf->intel_bo = drm_intel_bo_alloc_tiled(buf->dev->bufmgr, "vhost-user-gpu",
+                                             buf->width, buf->height,
+                                             (buf->bpp / 8), &tiling,
+                                             &buf->stride, 0);
+
+    if (!buf->intel_bo) {
+        return false;
+    }
+
+    if (tiling != I915_TILING_NONE) {
+        drm_intel_bo_unreference(buf->intel_bo);
+        return false;
+    }
+
+    return true;
+}
+
+static void
+intel_free_bo(struct drm_buffer *buf)
+{
+    drm_intel_bo_unreference(buf->intel_bo);
+}
+
+static bool
+intel_map_bo(struct drm_buffer *buf)
+{
+    if (drm_intel_gem_bo_map_gtt(buf->intel_bo) != 0) {
+        return false;
+    }
+
+    buf->mmap = buf->intel_bo->virtual;
+
+    return true;
+}
+
+static bool
+intel_export_bo_to_prime(struct drm_buffer *buffer, int *fd)
+{
+    if (drm_intel_bo_gem_export_to_prime(buffer->intel_bo, fd) < 0) {
+        return false;
+    }
+
+    return true;
+}
+
+static void
+intel_unmap_bo(struct drm_buffer *buf)
+{
+    drm_intel_gem_bo_unmap_gtt(buf->intel_bo);
+}
+
+static void
+intel_device_destroy(struct drm_device *dev)
+{
+    drm_intel_bufmgr_destroy(dev->bufmgr);
+}
+
+#endif /* CONFIG_LIBDRM_INTEL */
+
+void
+drm_device_destroy(struct drm_device *dev)
+{
+    if (!dev->inited) {
+        return;
+    }
+
+    dev->device_destroy(dev);
+}
+
+bool
+drm_device_init(struct drm_device *dev, int fd)
+{
+    drmVersionPtr version = drmGetVersion(fd);
+
+    dev->fd = fd;
+    dev->name = strdup(version->name);
+    drmFreeVersion(version);
+
+    if (0) {
+        /* nothing */
+    }
+#ifdef CONFIG_LIBDRM_INTEL
+    else if (!strcmp(dev->name, "i915")) {
+        dev->bufmgr = drm_intel_bufmgr_gem_init(fd, 32);
+        if (!dev->bufmgr) {
+            return false;
+        }
+        dev->alloc_bo = intel_alloc_bo;
+        dev->free_bo = intel_free_bo;
+        dev->export_bo_to_prime = intel_export_bo_to_prime;
+        dev->map_bo = intel_map_bo;
+        dev->unmap_bo = intel_unmap_bo;
+        dev->device_destroy = intel_device_destroy;
+    }
+#endif
+    else {
+        g_warning("Error: drm device %s unsupported.", dev->name);
+        return false;
+    }
+
+    dev->inited = true;
+    return true;
+}
+
+static bool
+drm_buffer_map(struct drm_buffer *buf)
+{
+    struct drm_device *dev = buf->dev;
+
+    return dev->map_bo(buf);
+}
+
+static void
+drm_buffer_unmap(struct drm_buffer *buf)
+{
+    struct drm_device *dev = buf->dev;
+
+    dev->unmap_bo(buf);
+}
+
+bool
+drm_buffer_get_dmabuf_fd(struct drm_buffer *buffer, int *fd)
+{
+    if (!buffer->dev->export_bo_to_prime(buffer, fd)) {
+        g_warning("gem_export_to_prime failed");
+        return false;
+    }
+
+    if (*fd < 0) {
+        g_warning("error: dmabuf_fd < 0");
+        return false;
+    }
+
+    return true;
+}
+
+bool
+drm_buffer_create(struct drm_buffer *buffer, struct drm_device *dev,
+                  int width, int height)
+{
+    buffer->dev = dev;
+    buffer->width = width;
+    buffer->height = height;
+    buffer->bpp = 32;
+    buffer->format = DRM_FORMAT_XRGB8888;
+    if (!dev->alloc_bo(buffer)) {
+        g_warning("alloc_bo failed");
+        return false;
+    }
+
+    if (!drm_buffer_map(buffer)) {
+        g_warning("map_bo failed");
+        goto err;
+    }
+
+    return true;
+
+err:
+    dev->free_bo(buffer);
+    return false;
+}
+
+void
+drm_buffer_destroy(struct drm_buffer *buffer)
+{
+    struct drm_device *drm_dev = buffer->dev;
+
+    drm_buffer_unmap(buffer);
+    drm_dev->free_bo(buffer);
+}
diff --git a/contrib/vhost-user-gpu/main.c b/contrib/vhost-user-gpu/main.c
new file mode 100644
index 0000000000..911fa0af6d
--- /dev/null
+++ b/contrib/vhost-user-gpu/main.c
@@ -0,0 +1,1164 @@
+/*
+ * Virtio vhost-user GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2018
+ *
+ * Authors:
+ *     Dave Airlie <airlied@redhat.com>
+ *     Gerd Hoffmann <kraxel@redhat.com>
+ *     Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/drm.h"
+#include "qapi/error.h"
+
+#include <pixman.h>
+
+#include "vugpu.h"
+#include "hw/virtio/virtio-gpu-bswap.h"
+#include "virgl.h"
+
+struct virtio_gpu_simple_resource {
+    uint32_t resource_id;
+    uint32_t width;
+    uint32_t height;
+    uint32_t format;
+    struct iovec *iov;
+    unsigned int iov_cnt;
+    uint32_t scanout_bitmask;
+    pixman_image_t *image;
+    struct drm_buffer drm_buffer;
+    QTAILQ_ENTRY(virtio_gpu_simple_resource) next;
+};
+
+static int opt_fdnum = 3;
+static char *opt_socket_path;
+static char *opt_render_node;
+static char *opt_pid_path;
+static gboolean opt_virgl;
+
+static const char *
+vg_cmd_to_string(int cmd)
+{
+#define CMD(cmd) [cmd] = #cmd
+    static const char *vg_cmd_str[] = {
+        CMD(VIRTIO_GPU_UNDEFINED),
+
+        /* 2d commands */
+        CMD(VIRTIO_GPU_CMD_GET_DISPLAY_INFO),
+        CMD(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D),
+        CMD(VIRTIO_GPU_CMD_RESOURCE_UNREF),
+        CMD(VIRTIO_GPU_CMD_SET_SCANOUT),
+        CMD(VIRTIO_GPU_CMD_RESOURCE_FLUSH),
+        CMD(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D),
+        CMD(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING),
+        CMD(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING),
+        CMD(VIRTIO_GPU_CMD_GET_CAPSET_INFO),
+        CMD(VIRTIO_GPU_CMD_GET_CAPSET),
+
+        /* 3d commands */
+        CMD(VIRTIO_GPU_CMD_CTX_CREATE),
+        CMD(VIRTIO_GPU_CMD_CTX_DESTROY),
+        CMD(VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE),
+        CMD(VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE),
+        CMD(VIRTIO_GPU_CMD_RESOURCE_CREATE_3D),
+        CMD(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D),
+        CMD(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D),
+        CMD(VIRTIO_GPU_CMD_SUBMIT_3D),
+
+        /* cursor commands */
+        CMD(VIRTIO_GPU_CMD_UPDATE_CURSOR),
+        CMD(VIRTIO_GPU_CMD_MOVE_CURSOR),
+    };
+#undef REQ
+
+    if (cmd >= 0 && cmd < G_N_ELEMENTS(vg_cmd_str)) {
+        return vg_cmd_str[cmd];
+    } else {
+        return "unknown";
+    }
+}
+
+int
+vg_sock_fd_read(int sock, void *buf, ssize_t buflen)
+{
+    int ret;
+
+    do {
+        ret = read(sock, buf, buflen);
+    } while (ret < 0 && (errno == EINTR || errno == EAGAIN));
+
+    g_warn_if_fail(ret == buflen);
+    return ret;
+}
+
+static void
+vg_sock_fd_close(VuGpu *g)
+{
+    if (g->sock_fd >= 0) {
+        close(g->sock_fd);
+        g->sock_fd = -1;
+    }
+}
+
+
+void
+vg_wait_ok(VuGpu *g)
+{
+    uint32_t ok;
+
+    if (vg_sock_fd_read(g->sock_fd, &ok, sizeof(ok)) < 0) {
+        vg_sock_fd_close(g);
+    }
+}
+
+static int
+vg_sock_fd_write(int sock, const void *buf, ssize_t buflen, int fd)
+{
+    ssize_t ret;
+    struct msghdr msg;
+    struct iovec iov;
+    union {
+        struct cmsghdr cmsghdr;
+        char control[CMSG_SPACE(sizeof(int))];
+    } cmsgu;
+    struct cmsghdr *cmsg;
+
+    iov.iov_base = (void *)buf;
+    iov.iov_len = buflen;
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    if (fd != -1) {
+        msg.msg_control = cmsgu.control;
+        msg.msg_controllen = sizeof(cmsgu.control);
+
+        cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+
+        *((int *)CMSG_DATA(cmsg)) = fd;
+    } else {
+        msg.msg_control = NULL;
+        msg.msg_controllen = 0;
+    }
+
+    do {
+        ret = sendmsg(sock, &msg, 0);
+    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+    g_warn_if_fail(ret == buflen);
+    return ret;
+}
+
+void
+vg_send_msg(VuGpu *vg, const VhostUserGpuMsg *msg, int fd)
+{
+    if (vg_sock_fd_write(vg->sock_fd, msg,
+                         VHOST_USER_GPU_HDR_SIZE + msg->size, fd) < 0) {
+        vg_sock_fd_close(vg);
+    }
+}
+
+static struct virtio_gpu_simple_resource *
+virtio_gpu_find_resource(VuGpu *g, uint32_t resource_id)
+{
+    struct virtio_gpu_simple_resource *res;
+
+    QTAILQ_FOREACH(res, &g->reslist, next) {
+        if (res->resource_id == resource_id) {
+            return res;
+        }
+    }
+    return NULL;
+}
+
+void
+vg_ctrl_response(VuGpu *g,
+                 struct virtio_gpu_ctrl_command *cmd,
+                 struct virtio_gpu_ctrl_hdr *resp,
+                 size_t resp_len)
+{
+    size_t s;
+
+    if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE) {
+        resp->flags |= VIRTIO_GPU_FLAG_FENCE;
+        resp->fence_id = cmd->cmd_hdr.fence_id;
+        resp->ctx_id = cmd->cmd_hdr.ctx_id;
+    }
+    virtio_gpu_ctrl_hdr_bswap(resp);
+    s = iov_from_buf(cmd->elem.in_sg, cmd->elem.in_num, 0, resp, resp_len);
+    if (s != resp_len) {
+        g_critical("%s: response size incorrect %zu vs %zu",
+                   __func__, s, resp_len);
+    }
+    vu_queue_push(&g->dev.parent, cmd->vq, &cmd->elem, s);
+    vu_queue_notify(&g->dev.parent, cmd->vq);
+    cmd->finished = true;
+}
+
+void
+vg_ctrl_response_nodata(VuGpu *g,
+                        struct virtio_gpu_ctrl_command *cmd,
+                        enum virtio_gpu_ctrl_type type)
+{
+    struct virtio_gpu_ctrl_hdr resp = {
+        .type = type,
+    };
+
+    vg_ctrl_response(g, cmd, &resp, sizeof(resp));
+}
+
+void
+vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_resp_display_info dpy_info = { 0 ,};
+    VhostUserGpuMsg msg = {
+        .request = VHOST_USER_GPU_GET_DISPLAY_INFO,
+        .size = 0,
+    };
+
+    vg_send_msg(vg, &msg, -1);
+    if (vg_sock_fd_read(vg->sock_fd, &dpy_info, sizeof(dpy_info)) < 0) {
+        vg_sock_fd_close(vg);
+        return;
+    }
+
+    vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info));
+}
+
+static pixman_format_code_t
+get_pixman_format(uint32_t virtio_gpu_format)
+{
+    switch (virtio_gpu_format) {
+#ifdef HOST_WORDS_BIGENDIAN
+    case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
+        return PIXMAN_b8g8r8x8;
+    case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
+        return PIXMAN_b8g8r8a8;
+    case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
+        return PIXMAN_x8r8g8b8;
+    case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
+        return PIXMAN_a8r8g8b8;
+    case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
+        return PIXMAN_r8g8b8x8;
+    case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
+        return PIXMAN_r8g8b8a8;
+    case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
+        return PIXMAN_x8b8g8r8;
+    case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
+        return PIXMAN_a8b8g8r8;
+#else
+    case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
+        return PIXMAN_x8r8g8b8;
+    case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
+        return PIXMAN_a8r8g8b8;
+    case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
+        return PIXMAN_b8g8r8x8;
+    case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
+        return PIXMAN_b8g8r8a8;
+    case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
+        return PIXMAN_x8b8g8r8;
+    case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
+        return PIXMAN_a8b8g8r8;
+    case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
+        return PIXMAN_r8g8b8x8;
+    case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
+        return PIXMAN_r8g8b8a8;
+#endif
+    default:
+        return 0;
+    }
+}
+
+static void
+vg_resource_create_2d(VuGpu *g,
+                      struct virtio_gpu_ctrl_command *cmd)
+{
+    pixman_format_code_t pformat;
+    struct virtio_gpu_simple_resource *res;
+    struct virtio_gpu_resource_create_2d c2d;
+
+    VUGPU_FILL_CMD(c2d);
+    virtio_gpu_bswap_32(&c2d, sizeof(c2d));
+
+    if (c2d.resource_id == 0) {
+        g_critical("%s: resource id 0 is not allowed", __func__);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+        return;
+    }
+
+    res = virtio_gpu_find_resource(g, c2d.resource_id);
+    if (res) {
+        g_critical("%s: resource already exists %d", __func__, c2d.resource_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+        return;
+    }
+
+    res = g_new0(struct virtio_gpu_simple_resource, 1);
+    res->width = c2d.width;
+    res->height = c2d.height;
+    res->format = c2d.format;
+    res->resource_id = c2d.resource_id;
+
+    pformat = get_pixman_format(c2d.format);
+    if (!pformat) {
+        g_critical("%s: host couldn't handle guest format %d",
+                   __func__, c2d.format);
+        g_free(res);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        return;
+    }
+    drm_buffer_create(&res->drm_buffer, &g->drm_dev, c2d.width, c2d.height);
+    res->image = pixman_image_create_bits(pformat,
+                                          c2d.width,
+                                          c2d.height,
+                                          (uint32_t *)res->drm_buffer.mmap,
+                                          res->drm_buffer.stride);
+    if (!res->image) {
+        g_critical("%s: resource creation failed %d %d %d",
+                   __func__, c2d.resource_id, c2d.width, c2d.height);
+        g_free(res);
+        cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
+        return;
+    }
+
+    QTAILQ_INSERT_HEAD(&g->reslist, res, next);
+}
+
+static void
+vg_disable_scanout(VuGpu *g, int scanout_id)
+{
+    struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id];
+    struct virtio_gpu_simple_resource *res;
+
+    if (scanout->resource_id == 0) {
+        return;
+    }
+
+    res = virtio_gpu_find_resource(g, scanout->resource_id);
+    if (res) {
+        res->scanout_bitmask &= ~(1 << scanout_id);
+    }
+
+    scanout->width = 0;
+    scanout->height = 0;
+
+    {
+        VhostUserGpuMsg msg = {
+            .request = VHOST_USER_GPU_SCANOUT,
+            .size = sizeof(VhostUserGpuScanout),
+            .payload.scanout.scanout_id = scanout_id,
+        };
+        vg_send_msg(g, &msg, -1);
+    }
+}
+
+static void
+vg_resource_destroy(VuGpu *g,
+                    struct virtio_gpu_simple_resource *res)
+{
+    int i;
+
+    if (res->scanout_bitmask) {
+        for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
+            if (res->scanout_bitmask & (1 << i)) {
+                vg_disable_scanout(g, i);
+            }
+        }
+    }
+
+    drm_buffer_destroy(&res->drm_buffer);
+    pixman_image_unref(res->image);
+    QTAILQ_REMOVE(&g->reslist, res, next);
+    g_free(res);
+}
+
+static void
+vg_resource_unref(VuGpu *g,
+                  struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_simple_resource *res;
+    struct virtio_gpu_resource_unref unref;
+
+    VUGPU_FILL_CMD(unref);
+    virtio_gpu_bswap_32(&unref, sizeof(unref));
+
+    res = virtio_gpu_find_resource(g, unref.resource_id);
+    if (!res) {
+        g_critical("%s: illegal resource specified %d",
+                   __func__, unref.resource_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+        return;
+    }
+    vg_resource_destroy(g, res);
+}
+
+int
+vg_create_mapping_iov(VuGpu *g,
+                      struct virtio_gpu_resource_attach_backing *ab,
+                      struct virtio_gpu_ctrl_command *cmd,
+                      struct iovec **iov)
+{
+    struct virtio_gpu_mem_entry *ents;
+    size_t esize, s;
+    int i;
+
+    if (ab->nr_entries > 16384) {
+        g_critical("%s: nr_entries is too big (%d > 16384)",
+                   __func__, ab->nr_entries);
+        return -1;
+    }
+
+    esize = sizeof(*ents) * ab->nr_entries;
+    ents = g_malloc(esize);
+    s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
+                   sizeof(*ab), ents, esize);
+    if (s != esize) {
+        g_critical("%s: command data size incorrect %zu vs %zu",
+                   __func__, s, esize);
+        g_free(ents);
+        return -1;
+    }
+
+    *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries);
+    for (i = 0; i < ab->nr_entries; i++) {
+        uint64_t len = ents[i].length;
+        (*iov)[i].iov_len = ents[i].length;
+        (*iov)[i].iov_base = vu_gpa_to_va(&g->dev.parent, &len, ents[i].addr);
+        if (!(*iov)[i].iov_base || len != ents[i].length) {
+            g_critical("%s: resource %d element %d",
+                       __func__, ab->resource_id, i);
+            g_free(*iov);
+            g_free(ents);
+            *iov = NULL;
+            return -1;
+        }
+    }
+    g_free(ents);
+    return 0;
+}
+
+static void
+vg_resource_attach_backing(VuGpu *g,
+                           struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_simple_resource *res;
+    struct virtio_gpu_resource_attach_backing ab;
+    int ret;
+
+    VUGPU_FILL_CMD(ab);
+    virtio_gpu_bswap_32(&ab, sizeof(ab));
+
+    res = virtio_gpu_find_resource(g, ab.resource_id);
+    if (!res) {
+        g_critical("%s: illegal resource specified %d",
+                   __func__, ab.resource_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+        return;
+    }
+
+    ret = vg_create_mapping_iov(g, &ab, cmd, &res->iov);
+    if (ret != 0) {
+        cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+        return;
+    }
+
+    res->iov_cnt = ab.nr_entries;
+}
+
+static void
+vg_resource_detach_backing(VuGpu *g,
+                           struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_simple_resource *res;
+    struct virtio_gpu_resource_detach_backing detach;
+
+    VUGPU_FILL_CMD(detach);
+    virtio_gpu_bswap_32(&detach, sizeof(detach));
+
+    res = virtio_gpu_find_resource(g, detach.resource_id);
+    if (!res || !res->iov) {
+        g_critical("%s: illegal resource specified %d",
+                   __func__, detach.resource_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+        return;
+    }
+
+    g_free(res->iov);
+    res->iov = NULL;
+    res->iov_cnt = 0;
+}
+
+static void
+vg_transfer_to_host_2d(VuGpu *g,
+                       struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_simple_resource *res;
+    int h;
+    uint32_t src_offset, dst_offset, stride;
+    int bpp;
+    pixman_format_code_t format;
+    struct virtio_gpu_transfer_to_host_2d t2d;
+
+    VUGPU_FILL_CMD(t2d);
+    virtio_gpu_t2d_bswap(&t2d);
+
+    res = virtio_gpu_find_resource(g, t2d.resource_id);
+    if (!res || !res->iov) {
+        g_critical("%s: illegal resource specified %d",
+                   __func__, t2d.resource_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+        return;
+    }
+
+    if (t2d.r.x > res->width ||
+        t2d.r.y > res->height ||
+        t2d.r.width > res->width ||
+        t2d.r.height > res->height ||
+        t2d.r.x + t2d.r.width > res->width ||
+        t2d.r.y + t2d.r.height > res->height) {
+        g_critical("%s: transfer bounds outside resource"
+                   " bounds for resource %d: %d %d %d %d vs %d %d",
+                   __func__, t2d.resource_id, t2d.r.x, t2d.r.y,
+                   t2d.r.width, t2d.r.height, res->width, res->height);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        return;
+    }
+
+    format = pixman_image_get_format(res->image);
+    bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+    stride = pixman_image_get_stride(res->image);
+
+    if (t2d.offset || t2d.r.x || t2d.r.y ||
+        t2d.r.width != pixman_image_get_width(res->image)) {
+        void *img_data = pixman_image_get_data(res->image);
+        for (h = 0; h < t2d.r.height; h++) {
+            src_offset = t2d.offset + stride * h;
+            dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);
+
+            iov_to_buf(res->iov, res->iov_cnt, src_offset,
+                       img_data
+                       + dst_offset, t2d.r.width * bpp);
+        }
+    } else {
+        iov_to_buf(res->iov, res->iov_cnt, 0,
+                   pixman_image_get_data(res->image),
+                   pixman_image_get_stride(res->image)
+                   * pixman_image_get_height(res->image));
+    }
+}
+
+static void
+vg_set_scanout(VuGpu *g,
+               struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_simple_resource *res, *ores;
+    struct virtio_gpu_scanout *scanout;
+    struct virtio_gpu_set_scanout ss;
+
+    VUGPU_FILL_CMD(ss);
+    virtio_gpu_bswap_32(&ss, sizeof(ss));
+
+    if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUTS) {
+        g_critical("%s: illegal scanout id specified %d",
+                   __func__, ss.scanout_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+        return;
+    }
+
+    if (ss.resource_id == 0) {
+        vg_disable_scanout(g, ss.scanout_id);
+        return;
+    }
+
+    /* create a surface for this scanout */
+    res = virtio_gpu_find_resource(g, ss.resource_id);
+    if (!res) {
+        g_critical("%s: illegal resource specified %d",
+                      __func__, ss.resource_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+        return;
+    }
+
+    if (ss.r.x > res->width ||
+        ss.r.y > res->height ||
+        ss.r.width > res->width ||
+        ss.r.height > res->height ||
+        ss.r.x + ss.r.width > res->width ||
+        ss.r.y + ss.r.height > res->height) {
+        g_critical("%s: illegal scanout %d bounds for"
+                   " resource %d, (%d,%d)+%d,%d vs %d %d",
+                   __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y,
+                   ss.r.width, ss.r.height, res->width, res->height);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        return;
+    }
+
+    scanout = &g->scanout[ss.scanout_id];
+
+    ores = virtio_gpu_find_resource(g, scanout->resource_id);
+    if (ores) {
+        ores->scanout_bitmask &= ~(1 << ss.scanout_id);
+    }
+
+    res->scanout_bitmask |= (1 << ss.scanout_id);
+    scanout->resource_id = ss.resource_id;
+    scanout->x = ss.r.x;
+    scanout->y = ss.r.y;
+    scanout->width = ss.r.width;
+    scanout->height = ss.r.height;
+
+    if (res->drm_buffer.mmap) {
+        struct drm_buffer *buffer = &res->drm_buffer;
+        VhostUserGpuMsg msg = {
+            .request = VHOST_USER_GPU_DMABUF_SCANOUT,
+            .size = sizeof(VhostUserGpuDMABUFScanout),
+            .payload.dmabuf_scanout.scanout_id = ss.scanout_id,
+            .payload.dmabuf_scanout.x =  ss.r.x,
+            .payload.dmabuf_scanout.y =  ss.r.y,
+            .payload.dmabuf_scanout.width = ss.r.width,
+            .payload.dmabuf_scanout.height = ss.r.height,
+            .payload.dmabuf_scanout.fd_width = buffer->width,
+            .payload.dmabuf_scanout.fd_height = buffer->height,
+            .payload.dmabuf_scanout.fd_stride = buffer->stride,
+            .payload.dmabuf_scanout.fd_drm_fourcc = buffer->format
+        };
+        int fd;
+
+        if (drm_buffer_get_dmabuf_fd(buffer, &fd)) {
+            vg_send_msg(g, &msg, fd);
+            close(fd);
+        }
+    } else {
+        VhostUserGpuMsg msg = {
+            .request = VHOST_USER_GPU_SCANOUT,
+            .size = sizeof(VhostUserGpuScanout),
+            .payload.scanout.scanout_id = ss.scanout_id,
+            .payload.scanout.width = scanout->width,
+            .payload.scanout.height = scanout->height
+        };
+        vg_send_msg(g, &msg, -1);
+        /* TODO: VHOST_USER_GPU_UPDATE with associated resource ? */
+    }
+}
+
+static void
+vg_resource_flush(VuGpu *g,
+                  struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_simple_resource *res;
+    struct virtio_gpu_resource_flush rf;
+    pixman_region16_t flush_region;
+    int i;
+
+    VUGPU_FILL_CMD(rf);
+    virtio_gpu_bswap_32(&rf, sizeof(rf));
+
+    res = virtio_gpu_find_resource(g, rf.resource_id);
+    if (!res) {
+        g_critical("%s: illegal resource specified %d\n",
+                   __func__, rf.resource_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+        return;
+    }
+
+    if (rf.r.x > res->width ||
+        rf.r.y > res->height ||
+        rf.r.width > res->width ||
+        rf.r.height > res->height ||
+        rf.r.x + rf.r.width > res->width ||
+        rf.r.y + rf.r.height > res->height) {
+        g_critical("%s: flush bounds outside resource"
+                   " bounds for resource %d: %d %d %d %d vs %d %d\n",
+                   __func__, rf.resource_id, rf.r.x, rf.r.y,
+                   rf.r.width, rf.r.height, res->width, res->height);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        return;
+    }
+
+    pixman_region_init_rect(&flush_region,
+                            rf.r.x, rf.r.y, rf.r.width, rf.r.height);
+    for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
+        struct virtio_gpu_scanout *scanout;
+        pixman_region16_t region, finalregion;
+        pixman_box16_t *extents;
+
+        if (!(res->scanout_bitmask & (1 << i))) {
+            continue;
+        }
+        scanout = &g->scanout[i];
+
+        pixman_region_init(&finalregion);
+        pixman_region_init_rect(&region, scanout->x, scanout->y,
+                                scanout->width, scanout->height);
+
+        pixman_region_intersect(&finalregion, &flush_region, &region);
+
+        extents = pixman_region_extents(&finalregion);
+        size_t width = extents->x2 - extents->x1;
+        size_t height = extents->y2 - extents->y1;
+
+        if (res->drm_buffer.mmap) {
+            VhostUserGpuMsg vmsg = {
+                .request = VHOST_USER_GPU_DMABUF_UPDATE,
+                .size = sizeof(VhostUserGpuUpdate),
+                .payload.update.scanout_id = i,
+                .payload.update.x = extents->x1,
+                .payload.update.y = extents->y1,
+                .payload.update.width = width,
+                .payload.update.height = height
+            };
+            vg_send_msg(g, &vmsg, -1);
+            vg_wait_ok(g);
+        } else {
+            size_t bpp =
+                PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8;
+            size_t size = width * height * bpp;
+
+            VhostUserGpuMsg *msg = g_malloc(VHOST_USER_GPU_HDR_SIZE +
+                                        sizeof(VhostUserGpuUpdate) + size);
+            msg->request = VHOST_USER_GPU_UPDATE;
+            msg->size = sizeof(VhostUserGpuUpdate) + size;
+            msg->payload.update.scanout_id = i;
+            msg->payload.update.x = extents->x1;
+            msg->payload.update.y = extents->y1;
+            msg->payload.update.width = width;
+            msg->payload.update.height = height;
+            pixman_image_t *i =
+                pixman_image_create_bits(pixman_image_get_format(res->image),
+                                         msg->payload.update.width,
+                                         msg->payload.update.height,
+                                         (uint32_t *)msg->payload.update.data,
+                                         width * bpp);
+            pixman_image_composite(PIXMAN_OP_SRC,
+                                   res->image, NULL, i,
+                                   extents->x1, extents->y1,
+                                   0, 0, 0, 0,
+                                   width, height);
+            pixman_image_unref(i);
+            vg_send_msg(g, msg, -1);
+            g_free(msg);
+        }
+        pixman_region_fini(&region);
+        pixman_region_fini(&finalregion);
+    }
+    pixman_region_fini(&flush_region);
+}
+
+static void
+vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd)
+{
+    switch (cmd->cmd_hdr.type) {
+    case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
+        vg_get_display_info(vg, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
+        vg_resource_create_2d(vg, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_UNREF:
+        vg_resource_unref(vg, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+        vg_resource_flush(vg, cmd);
+        break;
+    case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+        vg_transfer_to_host_2d(vg, cmd);
+        break;
+    case VIRTIO_GPU_CMD_SET_SCANOUT:
+        vg_set_scanout(vg, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
+        vg_resource_attach_backing(vg, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
+        vg_resource_detach_backing(vg, cmd);
+        break;
+    default:
+        g_warning("TODO handle ctrl %x\n", cmd->cmd_hdr.type);
+        cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+        break;
+    }
+    if (!cmd->finished) {
+        vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error :
+                                VIRTIO_GPU_RESP_OK_NODATA);
+    }
+}
+
+static void
+vg_handle_ctrl(VuDev *dev, int qidx)
+{
+    VuGpu *vg = container_of(dev, VuGpu, dev.parent);
+    VuVirtq *vq = vu_get_queue(dev, qidx);
+    struct virtio_gpu_ctrl_command *cmd = NULL;
+    size_t len;
+
+    for (;;) {
+        cmd = vu_queue_pop(dev, vq, sizeof(struct virtio_gpu_ctrl_command));
+        if (!cmd) {
+            break;
+        }
+        cmd->vq = vq;
+        cmd->error = 0;
+        cmd->finished = false;
+
+        len = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
+                         0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr));
+        if (len != sizeof(cmd->cmd_hdr)) {
+            g_warning("%s: command size incorrect %zu vs %zu\n",
+                      __func__, len, sizeof(cmd->cmd_hdr));
+        }
+
+        virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr);
+        g_debug("%d %s\n", cmd->cmd_hdr.type,
+                vg_cmd_to_string(cmd->cmd_hdr.type));
+
+        if (vg->virgl) {
+            vg_virgl_process_cmd(vg, cmd);
+        } else {
+            vg_process_cmd(vg, cmd);
+        }
+
+        if (!cmd->finished) {
+            QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next);
+            vg->inflight++;
+        } else {
+            g_free(cmd);
+        }
+    }
+}
+
+static void
+update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data)
+{
+    struct virtio_gpu_simple_resource *res;
+
+    res = virtio_gpu_find_resource(g, resource_id);
+    g_return_if_fail(res != NULL);
+    g_return_if_fail(pixman_image_get_width(res->image) == 64);
+    g_return_if_fail(pixman_image_get_height(res->image) == 64);
+    g_return_if_fail(
+        PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) == 32);
+
+    memcpy(data, pixman_image_get_data(res->image), 64 * 64 * sizeof(uint32_t));
+}
+
+static void
+vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
+{
+    bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR;
+
+    g_debug("%s move:%d\n", G_STRFUNC, move);
+
+    if (move) {
+        VhostUserGpuMsg msg = {
+            .request = cursor->resource_id ?
+                VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE,
+            .size = sizeof(VhostUserGpuCursorPos),
+            .payload.cursor_pos = {
+                .scanout_id = cursor->pos.scanout_id,
+                .x = cursor->pos.x,
+                .y = cursor->pos.y,
+            }
+        };
+        vg_send_msg(g, &msg, -1);
+    } else {
+        VhostUserGpuMsg msg = {
+            .request = VHOST_USER_GPU_CURSOR_UPDATE,
+            .size = sizeof(VhostUserGpuCursorUpdate),
+            .payload.cursor_update = {
+                .pos = {
+                    .scanout_id = cursor->pos.scanout_id,
+                    .x = cursor->pos.x,
+                    .y = cursor->pos.y,
+                },
+                .hot_x = cursor->hot_x,
+                .hot_y = cursor->hot_y,
+            }
+        };
+        if (g->virgl) {
+            vg_virgl_update_cursor_data(g, cursor->resource_id,
+                                        msg.payload.cursor_update.data);
+        } else {
+            update_cursor_data_simple(g, cursor->resource_id,
+                                      msg.payload.cursor_update.data);
+        }
+        vg_send_msg(g, &msg, -1);
+    }
+}
+
+static void
+vg_handle_cursor(VuDev *dev, int qidx)
+{
+    VuGpu *g = container_of(dev, VuGpu, dev.parent);
+    VuVirtq *vq = vu_get_queue(dev, qidx);
+    VuVirtqElement *elem;
+    size_t len;
+    struct virtio_gpu_update_cursor cursor;
+
+    for (;;) {
+        elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
+        if (!elem) {
+            break;
+        }
+        g_debug("cursor out:%d in:%d\n", elem->out_num, elem->in_num);
+
+        len = iov_to_buf(elem->out_sg, elem->out_num,
+                         0, &cursor, sizeof(cursor));
+        if (len != sizeof(cursor)) {
+            g_warning("%s: cursor size incorrect %zu vs %zu\n",
+                      __func__, len, sizeof(cursor));
+        } else {
+            virtio_gpu_bswap_32(&cursor, sizeof(cursor));
+            vg_process_cursor_cmd(g, &cursor);
+        }
+        vu_queue_push(dev, vq, elem, 0);
+        vu_queue_notify(dev, vq);
+        g_free(elem);
+    }
+}
+
+static void
+vg_panic(VuDev *dev, const char *msg)
+{
+    g_critical("%s\n", msg);
+    exit(1);
+}
+
+static void
+vg_queue_set_started(VuDev *dev, int qidx, bool started)
+{
+    VuVirtq *vq = vu_get_queue(dev, qidx);
+
+    g_debug("queue started %d:%d\n", qidx, started);
+
+    switch (qidx) {
+    case 0:
+        vu_set_queue_handler(dev, vq, started ? vg_handle_ctrl : NULL);
+        break;
+    case 1:
+        vu_set_queue_handler(dev, vq, started ? vg_handle_cursor : NULL);
+        break;
+    default:
+        break;
+    }
+}
+
+static void
+set_protocol_features(VuGpu *g)
+{
+    uint64_t u64;
+    VhostUserGpuMsg msg = {
+        .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES
+    };
+
+    vg_send_msg(g, &msg, -1);
+    if (vg_sock_fd_read(g->sock_fd, &u64, sizeof(u64)) < 0) {
+        vg_sock_fd_close(g);
+        return;
+    }
+    msg = (VhostUserGpuMsg) {
+        .request = VHOST_USER_GPU_SET_PROTOCOL_FEATURES,
+        .size = sizeof(uint64_t),
+        .payload.u64 = 0
+    };
+    vg_send_msg(g, &msg, -1);
+}
+
+static int
+vg_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply)
+{
+    VuGpu *g = container_of(dev, VuGpu, dev.parent);
+
+    switch (msg->request) {
+    case VHOST_USER_GPU_SET_SOCKET: {
+        g_return_val_if_fail(msg->fd_num == 1, 1);
+        g_return_val_if_fail(g->sock_fd == -1, 1);
+        g->sock_fd = msg->fds[0];
+        set_protocol_features(g);
+        return 1;
+    }
+    case VHOST_USER_GPU_GET_NUM_CAPSETS:
+        g_return_val_if_fail(msg->fd_num == 0, 1);
+        msg->payload.u64 = vg_virgl_get_num_capsets();
+        msg->size = sizeof(msg->payload.u64);
+        *do_reply = true;
+        return 1;
+    default:
+        return 0;
+    }
+
+    return 0;
+}
+
+static uint64_t
+vg_get_features(VuDev *dev)
+{
+    uint64_t features = 0;
+
+    if (opt_virgl) {
+        features |= 1 << VIRTIO_GPU_F_VIRGL;
+    }
+
+    return features;
+}
+
+static void
+vg_set_features(VuDev *dev, uint64_t features)
+{
+    VuGpu *g = container_of(dev, VuGpu, dev.parent);
+    bool virgl = features & (1 << VIRTIO_GPU_F_VIRGL);
+
+    if (virgl && !g->virgl_inited) {
+        if (!vg_virgl_init(g)) {
+            vg_panic(dev, "Failed to initialize virgl");
+        }
+        g->virgl_inited = true;
+    }
+
+    g->virgl = virgl;
+}
+
+static const VuDevIface vuiface = {
+    .set_features = vg_set_features,
+    .get_features = vg_get_features,
+    .queue_set_started = vg_queue_set_started,
+    .process_msg = vg_process_msg,
+};
+
+static void
+vg_destroy(VuGpu *g)
+{
+    struct virtio_gpu_simple_resource *res, *tmp;
+
+    vug_deinit(&g->dev);
+
+    vg_sock_fd_close(g);
+
+    QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
+        vg_resource_destroy(g, res);
+    }
+
+    drm_device_destroy(&g->drm_dev);
+}
+
+static int unix_sock_new(char *path)
+{
+    int sock;
+    struct sockaddr_un un;
+    size_t len;
+
+    sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sock <= 0) {
+        perror("socket");
+        return -1;
+    }
+
+    un.sun_family = AF_UNIX;
+    snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+    len = sizeof(un.sun_family) + strlen(un.sun_path);
+
+    unlink(path);
+    if (bind(sock, (struct sockaddr *)&un, len) < 0) {
+        perror("bind");
+        goto fail;
+    }
+
+    if (listen(sock, 1) < 0) {
+        perror("listen");
+        goto fail;
+    }
+
+    return sock;
+
+fail:
+    close(sock);
+
+    return -1;
+}
+
+static GOptionEntry entries[] = {
+    { "pid", 'p', 0, G_OPTION_ARG_FILENAME, &opt_pid_path,
+      "PID path", "PATH" },
+    { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum,
+      "Use inherited fd socket", "FDNUM" },
+    { "socket-path", 's', 0, G_OPTION_ARG_INT, &opt_socket_path,
+      "Use UNIX socket path", "PATH" },
+    { "render-node", 'r', 0, G_OPTION_ARG_FILENAME, &opt_render_node,
+      "Specify DRM render node", "PATH" },
+    { "virgl", 'v', 0, G_OPTION_ARG_NONE, &opt_virgl,
+      "Turn virgl rendering on", NULL },
+    { NULL, }
+};
+
+int
+main(int argc, char *argv[])
+{
+    GOptionContext *context;
+    GError *error = NULL;
+    Error *err = NULL;
+    GMainLoop *loop = NULL;
+    int fd;
+    VuGpu g = { .sock_fd = -1, .drm_rnode_fd = -1 };
+
+    QTAILQ_INIT(&g.reslist);
+    QTAILQ_INIT(&g.fenceq);
+
+    context = g_option_context_new("QEMU vhost-user-gpu");
+    g_option_context_add_main_entries(context, entries, NULL);
+    if (!g_option_context_parse(context, &argc, &argv, &error)) {
+        g_printerr("Option parsing failed: %s\n", error->message);
+        exit(EXIT_FAILURE);
+    }
+    g_option_context_free(context);
+
+    if (opt_pid_path && !qemu_write_pidfile(opt_pid_path, &err)) {
+        g_printerr("%s\n", error_get_pretty(err));
+        error_free(err);
+        exit(EXIT_FAILURE);
+    }
+
+    g.drm_rnode_fd = qemu_drm_rendernode_open(opt_render_node);
+    if (opt_render_node && g.drm_rnode_fd == -1) {
+        g_printerr("Failed to open DRM rendernode.\n");
+        exit(EXIT_FAILURE);
+    }
+
+    if (g.drm_rnode_fd >= 0) {
+        if (!drm_device_init(&g.drm_dev, g.drm_rnode_fd)) {
+            g_warning("Failed to init DRM device, using fallback path");
+        }
+    }
+
+    if (opt_socket_path) {
+        int lsock = unix_sock_new(opt_socket_path);
+        fd = accept(lsock, NULL, NULL);
+        close(lsock);
+    } else {
+        fd = opt_fdnum;
+    }
+    if (fd == -1) {
+        g_printerr("Invalid socket");
+        exit(EXIT_FAILURE);
+    }
+
+    vug_init(&g.dev, fd, vg_panic, &vuiface);
+
+    loop = g_main_loop_new(NULL, FALSE);
+    g_main_loop_run(loop);
+    g_main_loop_unref(loop);
+
+    vg_destroy(&g);
+    if (g.drm_rnode_fd >= 0) {
+        close(g.drm_rnode_fd);
+    }
+
+    return 0;
+}
diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c
new file mode 100644
index 0000000000..43413e29df
--- /dev/null
+++ b/contrib/vhost-user-gpu/virgl.c
@@ -0,0 +1,579 @@
+/*
+ * Virtio vhost-user GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2018
+ *
+ * Authors:
+ *     Dave Airlie <airlied@redhat.com>
+ *     Gerd Hoffmann <kraxel@redhat.com>
+ *     Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <virglrenderer.h>
+#include "virgl.h"
+
+void
+vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id,
+                            gpointer data)
+{
+    uint32_t width, height;
+    uint32_t *cursor;
+
+    cursor = virgl_renderer_get_cursor_data(resource_id, &width, &height);
+    g_return_if_fail(cursor != NULL);
+    g_return_if_fail(width == 64);
+    g_return_if_fail(height == 64);
+
+    memcpy(data, cursor, 64 * 64 * sizeof(uint32_t));
+    free(cursor);
+}
+
+static void
+virgl_cmd_context_create(VuGpu *g,
+                         struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_ctx_create cc;
+
+    VUGPU_FILL_CMD(cc);
+
+    virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen,
+                                  cc.debug_name);
+}
+
+static void
+virgl_cmd_context_destroy(VuGpu *g,
+                          struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_ctx_destroy cd;
+
+    VUGPU_FILL_CMD(cd);
+
+    virgl_renderer_context_destroy(cd.hdr.ctx_id);
+}
+
+static void
+virgl_cmd_create_resource_2d(VuGpu *g,
+                             struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_resource_create_2d c2d;
+    struct virgl_renderer_resource_create_args args;
+
+    VUGPU_FILL_CMD(c2d);
+
+    args.handle = c2d.resource_id;
+    args.target = 2;
+    args.format = c2d.format;
+    args.bind = (1 << 1);
+    args.width = c2d.width;
+    args.height = c2d.height;
+    args.depth = 1;
+    args.array_size = 1;
+    args.last_level = 0;
+    args.nr_samples = 0;
+    args.flags = VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
+    virgl_renderer_resource_create(&args, NULL, 0);
+}
+
+static void
+virgl_cmd_create_resource_3d(VuGpu *g,
+                             struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_resource_create_3d c3d;
+    struct virgl_renderer_resource_create_args args;
+
+    VUGPU_FILL_CMD(c3d);
+
+    args.handle = c3d.resource_id;
+    args.target = c3d.target;
+    args.format = c3d.format;
+    args.bind = c3d.bind;
+    args.width = c3d.width;
+    args.height = c3d.height;
+    args.depth = c3d.depth;
+    args.array_size = c3d.array_size;
+    args.last_level = c3d.last_level;
+    args.nr_samples = c3d.nr_samples;
+    args.flags = c3d.flags;
+    virgl_renderer_resource_create(&args, NULL, 0);
+}
+
+static void
+virgl_cmd_resource_unref(VuGpu *g,
+                         struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_resource_unref unref;
+
+    VUGPU_FILL_CMD(unref);
+
+    virgl_renderer_resource_unref(unref.resource_id);
+}
+
+/* Not yet(?) defined in standard-headers, remove when possible */
+#ifndef VIRTIO_GPU_CAPSET_VIRGL2
+#define VIRTIO_GPU_CAPSET_VIRGL2 2
+#endif
+
+static void
+virgl_cmd_get_capset_info(VuGpu *g,
+                          struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_get_capset_info info;
+    struct virtio_gpu_resp_capset_info resp;
+
+    VUGPU_FILL_CMD(info);
+
+    if (info.capset_index == 0) {
+        resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL;
+        virgl_renderer_get_cap_set(resp.capset_id,
+                                   &resp.capset_max_version,
+                                   &resp.capset_max_size);
+    } else if (info.capset_index == 1) {
+        resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL2;
+        virgl_renderer_get_cap_set(resp.capset_id,
+                                   &resp.capset_max_version,
+                                   &resp.capset_max_size);
+    } else {
+        resp.capset_max_version = 0;
+        resp.capset_max_size = 0;
+    }
+    resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO;
+    vg_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
+}
+
+uint32_t
+vg_virgl_get_num_capsets(void)
+{
+    uint32_t capset2_max_ver, capset2_max_size;
+    virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2,
+                               &capset2_max_ver,
+                               &capset2_max_size);
+
+    return capset2_max_ver ? 2 : 1;
+}
+
+static void
+virgl_cmd_get_capset(VuGpu *g,
+                     struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_get_capset gc;
+    struct virtio_gpu_resp_capset *resp;
+    uint32_t max_ver, max_size;
+
+    VUGPU_FILL_CMD(gc);
+
+    virgl_renderer_get_cap_set(gc.capset_id, &max_ver,
+                               &max_size);
+    resp = g_malloc0(sizeof(*resp) + max_size);
+
+    resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET;
+    virgl_renderer_fill_caps(gc.capset_id,
+                             gc.capset_version,
+                             (void *)resp->capset_data);
+    vg_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size);
+    g_free(resp);
+}
+
+static void
+virgl_cmd_submit_3d(VuGpu *g,
+                    struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_cmd_submit cs;
+    void *buf;
+    size_t s;
+
+    VUGPU_FILL_CMD(cs);
+
+    buf = g_malloc(cs.size);
+    s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
+                   sizeof(cs), buf, cs.size);
+    if (s != cs.size) {
+        g_critical("%s: size mismatch (%zd/%d)", __func__, s, cs.size);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4);
+
+out:
+    g_free(buf);
+}
+
+static void
+virgl_cmd_transfer_to_host_2d(VuGpu *g,
+                              struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_transfer_to_host_2d t2d;
+    struct virtio_gpu_box box;
+
+    VUGPU_FILL_CMD(t2d);
+
+    box.x = t2d.r.x;
+    box.y = t2d.r.y;
+    box.z = 0;
+    box.w = t2d.r.width;
+    box.h = t2d.r.height;
+    box.d = 1;
+
+    virgl_renderer_transfer_write_iov(t2d.resource_id,
+                                      0,
+                                      0,
+                                      0,
+                                      0,
+                                      (struct virgl_box *)&box,
+                                      t2d.offset, NULL, 0);
+}
+
+static void
+virgl_cmd_transfer_to_host_3d(VuGpu *g,
+                              struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_transfer_host_3d t3d;
+
+    VUGPU_FILL_CMD(t3d);
+
+    virgl_renderer_transfer_write_iov(t3d.resource_id,
+                                      t3d.hdr.ctx_id,
+                                      t3d.level,
+                                      t3d.stride,
+                                      t3d.layer_stride,
+                                      (struct virgl_box *)&t3d.box,
+                                      t3d.offset, NULL, 0);
+}
+
+static void
+virgl_cmd_transfer_from_host_3d(VuGpu *g,
+                                struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_transfer_host_3d tf3d;
+
+    VUGPU_FILL_CMD(tf3d);
+
+    virgl_renderer_transfer_read_iov(tf3d.resource_id,
+                                     tf3d.hdr.ctx_id,
+                                     tf3d.level,
+                                     tf3d.stride,
+                                     tf3d.layer_stride,
+                                     (struct virgl_box *)&tf3d.box,
+                                     tf3d.offset, NULL, 0);
+}
+
+static void
+virgl_resource_attach_backing(VuGpu *g,
+                              struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_resource_attach_backing att_rb;
+    struct iovec *res_iovs;
+    int ret;
+
+    VUGPU_FILL_CMD(att_rb);
+
+    ret = vg_create_mapping_iov(g, &att_rb, cmd, &res_iovs);
+    if (ret != 0) {
+        cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+        return;
+    }
+
+    virgl_renderer_resource_attach_iov(att_rb.resource_id,
+                                       res_iovs, att_rb.nr_entries);
+}
+
+static void
+virgl_resource_detach_backing(VuGpu *g,
+                              struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_resource_detach_backing detach_rb;
+    struct iovec *res_iovs = NULL;
+    int num_iovs = 0;
+
+    VUGPU_FILL_CMD(detach_rb);
+
+    virgl_renderer_resource_detach_iov(detach_rb.resource_id,
+                                       &res_iovs,
+                                       &num_iovs);
+    if (res_iovs == NULL || num_iovs == 0) {
+        return;
+    }
+    g_free(res_iovs);
+}
+
+static void
+virgl_cmd_set_scanout(VuGpu *g,
+                      struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_set_scanout ss;
+    struct virgl_renderer_resource_info info;
+    int ret;
+
+    VUGPU_FILL_CMD(ss);
+
+    if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUTS) {
+        g_critical("%s: illegal scanout id specified %d",
+                   __func__, ss.scanout_id);
+        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+        return;
+    }
+
+    memset(&info, 0, sizeof(info));
+
+    if (ss.resource_id && ss.r.width && ss.r.height) {
+        ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
+        if (ret == -1) {
+            g_critical("%s: illegal resource specified %d\n",
+                       __func__, ss.resource_id);
+            cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+            return;
+        }
+
+        int fd = -1;
+        if (virgl_renderer_get_fd_for_texture(info.tex_id, &fd) < 0) {
+            g_critical("%s: failed to get fd for texture\n", __func__);
+            cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+            return;
+        }
+        assert(fd >= 0);
+        VhostUserGpuMsg msg = {
+            .request = VHOST_USER_GPU_DMABUF_SCANOUT,
+            .size = sizeof(VhostUserGpuDMABUFScanout),
+            .payload.dmabuf_scanout.scanout_id = ss.scanout_id,
+            .payload.dmabuf_scanout.x =  ss.r.x,
+            .payload.dmabuf_scanout.y =  ss.r.y,
+            .payload.dmabuf_scanout.width = ss.r.width,
+            .payload.dmabuf_scanout.height = ss.r.height,
+            .payload.dmabuf_scanout.fd_width = info.width,
+            .payload.dmabuf_scanout.fd_height = info.height,
+            .payload.dmabuf_scanout.fd_stride = info.stride,
+            .payload.dmabuf_scanout.fd_flags = info.flags,
+            .payload.dmabuf_scanout.fd_drm_fourcc = info.drm_fourcc
+        };
+        vg_send_msg(g, &msg, fd);
+        close(fd);
+    } else {
+        VhostUserGpuMsg msg = {
+            .request = VHOST_USER_GPU_DMABUF_SCANOUT,
+            .size = sizeof(VhostUserGpuDMABUFScanout),
+            .payload.dmabuf_scanout.scanout_id = ss.scanout_id,
+        };
+        g_debug("disable scanout");
+        vg_send_msg(g, &msg, -1);
+    }
+    g->scanout[ss.scanout_id].resource_id = ss.resource_id;
+}
+
+static void
+virgl_cmd_resource_flush(VuGpu *g,
+                         struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_resource_flush rf;
+    int i;
+
+    VUGPU_FILL_CMD(rf);
+
+    if (!rf.resource_id) {
+        g_debug("bad resource id for flush..?");
+        return;
+    }
+    for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
+        if (g->scanout[i].resource_id != rf.resource_id) {
+            continue;
+        }
+        VhostUserGpuMsg msg = {
+            .request = VHOST_USER_GPU_DMABUF_UPDATE,
+            .size = sizeof(VhostUserGpuUpdate),
+            .payload.update.scanout_id = i,
+            .payload.update.x = rf.r.x,
+            .payload.update.y = rf.r.y,
+            .payload.update.width = rf.r.width,
+            .payload.update.height = rf.r.height
+        };
+        vg_send_msg(g, &msg, -1);
+        vg_wait_ok(g);
+    }
+}
+
+static void
+virgl_cmd_ctx_attach_resource(VuGpu *g,
+                              struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_ctx_resource att_res;
+
+    VUGPU_FILL_CMD(att_res);
+
+    virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resource_id);
+}
+
+static void
+virgl_cmd_ctx_detach_resource(VuGpu *g,
+                              struct virtio_gpu_ctrl_command *cmd)
+{
+    struct virtio_gpu_ctx_resource det_res;
+
+    VUGPU_FILL_CMD(det_res);
+
+    virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resource_id);
+}
+
+void vg_virgl_process_cmd(VuGpu *g, struct virtio_gpu_ctrl_command *cmd)
+{
+    virgl_renderer_force_ctx_0();
+    switch (cmd->cmd_hdr.type) {
+    case VIRTIO_GPU_CMD_CTX_CREATE:
+        virgl_cmd_context_create(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_CTX_DESTROY:
+        virgl_cmd_context_destroy(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
+        virgl_cmd_create_resource_2d(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D:
+        virgl_cmd_create_resource_3d(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_SUBMIT_3D:
+        virgl_cmd_submit_3d(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+        virgl_cmd_transfer_to_host_2d(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D:
+        virgl_cmd_transfer_to_host_3d(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D:
+        virgl_cmd_transfer_from_host_3d(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
+        virgl_resource_attach_backing(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
+        virgl_resource_detach_backing(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_SET_SCANOUT:
+        virgl_cmd_set_scanout(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+        virgl_cmd_resource_flush(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_RESOURCE_UNREF:
+        virgl_cmd_resource_unref(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE:
+        /* TODO add security */
+        virgl_cmd_ctx_attach_resource(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE:
+        /* TODO add security */
+        virgl_cmd_ctx_detach_resource(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_GET_CAPSET_INFO:
+        virgl_cmd_get_capset_info(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_GET_CAPSET:
+        virgl_cmd_get_capset(g, cmd);
+        break;
+    case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
+        vg_get_display_info(g, cmd);
+        break;
+    default:
+        g_debug("TODO handle ctrl %x\n", cmd->cmd_hdr.type);
+        cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+        break;
+    }
+
+    if (cmd->finished) {
+        return;
+    }
+
+    if (cmd->error) {
+        g_warning("%s: ctrl 0x%x, error 0x%x\n", __func__,
+                  cmd->cmd_hdr.type, cmd->error);
+        vg_ctrl_response_nodata(g, cmd, cmd->error);
+        return;
+    }
+
+    if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) {
+        vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+        return;
+    }
+
+    g_debug("Creating fence id:%" PRId64 " type:%d",
+            cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
+    virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
+}
+
+static void
+virgl_write_fence(void *opaque, uint32_t fence)
+{
+    VuGpu *g = opaque;
+    struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+    QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
+        /*
+         * the guest can end up emitting fences out of order
+         * so we should check all fenced cmds not just the first one.
+         */
+        if (cmd->cmd_hdr.fence_id > fence) {
+            continue;
+        }
+        g_debug("FENCE %" PRIu64, cmd->cmd_hdr.fence_id);
+        vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+        QTAILQ_REMOVE(&g->fenceq, cmd, next);
+        g_free(cmd);
+        g->inflight--;
+    }
+}
+
+#if defined(VIRGL_RENDERER_CALLBACKS_VERSION) && \
+    VIRGL_RENDERER_CALLBACKS_VERSION >= 2
+static int
+virgl_get_drm_fd(void *opaque)
+{
+    VuGpu *g = opaque;
+
+    return g->drm_rnode_fd;
+}
+#endif
+
+static struct virgl_renderer_callbacks virgl_cbs = {
+#if defined(VIRGL_RENDERER_CALLBACKS_VERSION) &&    \
+    VIRGL_RENDERER_CALLBACKS_VERSION >= 2
+    .get_drm_fd  = virgl_get_drm_fd,
+    .version     = 2,
+#else
+    .version     = 1,
+#endif
+    .write_fence = virgl_write_fence,
+};
+
+static void
+vg_virgl_poll(VuDev *dev, int condition, void *data)
+{
+    virgl_renderer_poll();
+}
+
+bool
+vg_virgl_init(VuGpu *g)
+{
+    int ret;
+
+    if (g->drm_rnode_fd && virgl_cbs.version == 1) {
+        g_warning("virgl will use the default rendernode");
+    }
+
+    ret = virgl_renderer_init(g,
+                              VIRGL_RENDERER_USE_EGL |
+                              VIRGL_RENDERER_THREAD_SYNC,
+                              &virgl_cbs);
+    if (ret != 0) {
+        return false;
+    }
+
+    ret = virgl_renderer_get_poll_fd();
+    if (ret != -1) {
+        g->renderer_source =
+            vug_source_new(&g->dev, ret, G_IO_IN, vg_virgl_poll, g);
+    }
+
+    return true;
+}
diff --git a/MAINTAINERS b/MAINTAINERS
index 01e2b47c34..d340928bcd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1376,6 +1376,8 @@ M: Marc-André Lureau <marcandre.lureau@redhat.com>
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Maintained
 F: docs/interop/vhost-user-gpu.rst
+F: contrib/vhost-user-gpu
+F: hw/display/vhost-user-*
 
 Cirrus VGA
 M: Gerd Hoffmann <kraxel@redhat.com>
diff --git a/Makefile b/Makefile
index 0c61563d75..32a93d37c0 100644
--- a/Makefile
+++ b/Makefile
@@ -420,6 +420,7 @@ dummy := $(call unnest-vars,, \
                 vhost-user-scsi-obj-y \
                 vhost-user-blk-obj-y \
                 vhost-user-input-obj-y \
+                vhost-user-gpu-obj-y \
                 qga-vss-dll-obj-y \
                 block-obj-y \
                 block-obj-m \
@@ -720,6 +721,8 @@ vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
 	$(call LINK, $^)
 vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqemuutil.a
 	$(call LINK, $^)
+vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a libqemustub.a
+	$(call LINK, $^)
 
 module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
 	$(call quiet-command,$(PYTHON) $< $@ \
diff --git a/Makefile.objs b/Makefile.objs
index b0f48d667c..a8789a165e 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -194,6 +194,7 @@ vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
 vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
 vhost-user-blk-obj-y = contrib/vhost-user-blk/
 vhost-user-input-obj-y = contrib/vhost-user-input/
+vhost-user-gpu-obj-y = contrib/vhost-user-gpu/
 
 ######################################################################
 trace-events-subdirs =
diff --git a/configure b/configure
index 211301f497..5727c433a5 100755
--- a/configure
+++ b/configure
@@ -3882,6 +3882,20 @@ libs_softmmu="$libs_softmmu $fdt_libs"
 ##########################################
 # opengl probe (for sdl2, gtk, milkymist-tmu2)
 
+libdrm="no"
+if $pkg_config libdrm; then
+    libdrm_cflags="$($pkg_config --cflags libdrm)"
+    libdrm_libs="$($pkg_config --libs libdrm)"
+    libdrm="yes"
+fi
+
+libdrm_intel="no"
+if $pkg_config libdrm_intel; then
+    libdrm_intel_cflags="$($pkg_config --cflags libdrm_intel)"
+    libdrm_intel_libs="$($pkg_config --libs libdrm_intel)"
+    libdrm_intel="yes"
+fi
+
 if test "$opengl" != "no" ; then
   opengl_pkgs="epoxy gbm"
   if $pkg_config $opengl_pkgs; then
@@ -5637,6 +5651,9 @@ if test "$want_tools" = "yes" ; then
   if [ "$linux" = "yes" ] ; then
     tools="vhost-user-input\$(EXESUF) $tools"
   fi
+  if [ "$linux" = "yes" -a "$virglrenderer" = "yes" -a "$libdrm" = "yes" ] ; then
+    tools="vhost-user-gpu\$(EXESUF) $tools"
+  fi
 fi
 if test "$softmmu" = yes ; then
   if test "$linux" = yes; then
@@ -6507,6 +6524,18 @@ if test "$opengl" = "yes" ; then
   fi
 fi
 
+if test "$libdrm" = "yes" ; then
+    echo "CONFIG_LIBDRM=y" >> $config_host_mak
+    echo "LIBDRM_LIBS=$libdrm_libs" >> $config_host_mak
+    echo "LIBDRM_CFLAGS=$libdrm_cflags" >> $config_host_mak
+fi
+
+if test "$libdrm_intel" = "yes" ; then
+    echo "CONFIG_LIBDRM_INTEL=y" >> $config_host_mak
+    echo "LIBDRM_INTEL_LIBS=$libdrm_intel_libs" >> $config_host_mak
+    echo "LIBDRM_INTEL_CFLAGS=$libdrm_intel_cflags" >> $config_host_mak
+fi
+
 if test "$malloc_trim" = "yes" ; then
   echo "CONFIG_MALLOC_TRIM=y" >> $config_host_mak
 fi
@@ -7331,6 +7360,9 @@ echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak
 
 done # for target in $targets
 
+echo "PIXMAN_CFLAGS=$pixman_cflags" >> $config_host_mak
+echo "PIXMAN_LIBS=$pixman_libs" >> $config_host_mak
+
 if test -n "$enabled_cross_compilers"; then
     echo
     echo "NOTE: cross-compilers enabled: $enabled_cross_compilers"
diff --git a/contrib/vhost-user-gpu/Makefile.objs b/contrib/vhost-user-gpu/Makefile.objs
new file mode 100644
index 0000000000..4da8c09cbb
--- /dev/null
+++ b/contrib/vhost-user-gpu/Makefile.objs
@@ -0,0 +1,10 @@
+vhost-user-gpu-obj-y = main.o virgl.o drm.o
+
+main.o-cflags := $(PIXMAN_CFLAGS)
+main.o-libs := $(PIXMAN_LIBS)
+
+virgl.o-cflags := $(VIRGL_CFLAGS)
+virgl.o-libs := $(VIRGL_LIBS)
+
+drm.o-cflags := $(LIBDRM_CFLAGS) $(LIBDRM_INTEL_CFLAGS)
+drm.o-libs := $(LIBDRM_LIBS) $(LIBDRM_INTEL_LIBS)
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 23/29] virtio-gpu: remove unused qdev
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (21 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 22/29] contrib: add vhost-user-gpu Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 24/29] virtio-gpu: remove unused config_size Marc-André Lureau
                   ` (8 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu.h | 1 -
 hw/display/virtio-gpu.c        | 1 -
 2 files changed, 2 deletions(-)

diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 9780f755ef..c54c903a65 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -97,7 +97,6 @@ typedef struct VirtIOGPU {
     int enable;
 
     int config_size;
-    DeviceState *qdev;
 
     QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 28602c1a35..30027f3384 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1219,7 +1219,6 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
     QTAILQ_INIT(&g->fenceq);
 
     g->enabled_output_bitmask = 1;
-    g->qdev = qdev;
 
     for (i = 0; i < g->conf.max_outputs; i++) {
         g->scanout[i].con =
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 24/29] virtio-gpu: remove unused config_size
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (22 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 23/29] virtio-gpu: remove unused qdev Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 25/29] virtio-gpu: block both 2d and 3d rendering Marc-André Lureau
                   ` (7 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu.h | 2 --
 hw/display/virtio-gpu.c        | 3 +--
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index c54c903a65..4c68bc4559 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -96,8 +96,6 @@ typedef struct VirtIOGPU {
 
     int enable;
 
-    int config_size;
-
     QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 30027f3384..7cfb25879a 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1189,10 +1189,9 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
         }
     }
 
-    g->config_size = sizeof(struct virtio_gpu_config);
     g->virtio_config.num_scanouts = cpu_to_le32(g->conf.max_outputs);
     virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
-                g->config_size);
+                sizeof(struct virtio_gpu_config));
 
     g->req_state[0].width = g->conf.xres;
     g->req_state[0].height = g->conf.yres;
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 25/29] virtio-gpu: block both 2d and 3d rendering
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (23 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 24/29] virtio-gpu: remove unused config_size Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 26/29] virtio-gpu: remove useless 'waiting' field Marc-André Lureau
                   ` (6 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Now that 2d commands are translated to 3d rendering, qemu must stop
sending 3d updates (from 2d) to Spice as well.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu.h |  1 -
 hw/display/virtio-gpu-3d.c     | 21 ---------------------
 hw/display/virtio-gpu.c        | 25 ++++++++++++++++++++++---
 3 files changed, 22 insertions(+), 25 deletions(-)

diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 4c68bc4559..763e1291f1 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -166,7 +166,6 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
                                   struct virtio_gpu_ctrl_command *cmd);
 void virtio_gpu_virgl_fence_poll(VirtIOGPU *g);
 void virtio_gpu_virgl_reset(VirtIOGPU *g);
-void virtio_gpu_gl_block(void *opaque, bool block);
 int virtio_gpu_virgl_init(VirtIOGPU *g);
 int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g);
 #endif
diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
index 3558f38fe8..6fee0e8582 100644
--- a/hw/display/virtio-gpu-3d.c
+++ b/hw/display/virtio-gpu-3d.c
@@ -404,11 +404,6 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
 {
     VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
 
-    cmd->waiting = g->renderer_blocked;
-    if (cmd->waiting) {
-        return;
-    }
-
     virgl_renderer_force_ctx_0();
     switch (cmd->cmd_hdr.type) {
     case VIRTIO_GPU_CMD_CTX_CREATE:
@@ -604,22 +599,6 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g)
     }
 }
 
-void virtio_gpu_gl_block(void *opaque, bool block)
-{
-    VirtIOGPU *g = opaque;
-
-    if (block) {
-        g->renderer_blocked++;
-    } else {
-        g->renderer_blocked--;
-    }
-    assert(g->renderer_blocked >= 0);
-
-    if (g->renderer_blocked == 0) {
-        virtio_gpu_process_cmdq(g);
-    }
-}
-
 int virtio_gpu_virgl_init(VirtIOGPU *g)
 {
     int ret;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 7cfb25879a..26a2592c4a 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -840,12 +840,15 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
     while (!QTAILQ_EMPTY(&g->cmdq)) {
         cmd = QTAILQ_FIRST(&g->cmdq);
 
-        /* process command */
-        VIRGL(g, virtio_gpu_virgl_process_cmd, virtio_gpu_simple_process_cmd,
-              g, cmd);
+        cmd->waiting = g->renderer_blocked;
         if (cmd->waiting) {
             break;
         }
+
+        /* process command */
+        VIRGL(g, virtio_gpu_virgl_process_cmd, virtio_gpu_simple_process_cmd,
+              g, cmd);
+
         QTAILQ_REMOVE(&g->cmdq, cmd, next);
         if (virtio_gpu_stats_enabled(g->conf)) {
             g->stats.requests++;
@@ -981,6 +984,22 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
     return 0;
 }
 
+static void virtio_gpu_gl_block(void *opaque, bool block)
+{
+    VirtIOGPU *g = opaque;
+
+    if (block) {
+        g->renderer_blocked++;
+    } else {
+        g->renderer_blocked--;
+    }
+    assert(g->renderer_blocked >= 0);
+
+    if (g->renderer_blocked == 0) {
+        virtio_gpu_process_cmdq(g);
+    }
+}
+
 const GraphicHwOps virtio_gpu_ops = {
     .invalidate = virtio_gpu_invalidate_display,
     .gfx_update = virtio_gpu_update_display,
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 26/29] virtio-gpu: remove useless 'waiting' field
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (24 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 25/29] virtio-gpu: block both 2d and 3d rendering Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 27/29] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base Marc-André Lureau
                   ` (5 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Let's check renderer_blocked instead directly.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu.h | 1 -
 hw/display/virtio-gpu.c        | 4 +---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 763e1291f1..6b07efc4f7 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -81,7 +81,6 @@ struct virtio_gpu_ctrl_command {
     VirtQueue *vq;
     struct virtio_gpu_ctrl_hdr cmd_hdr;
     uint32_t error;
-    bool waiting;
     bool finished;
     QTAILQ_ENTRY(virtio_gpu_ctrl_command) next;
 };
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 26a2592c4a..b83982b0a3 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -840,8 +840,7 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
     while (!QTAILQ_EMPTY(&g->cmdq)) {
         cmd = QTAILQ_FIRST(&g->cmdq);
 
-        cmd->waiting = g->renderer_blocked;
-        if (cmd->waiting) {
+        if (g->renderer_blocked) {
             break;
         }
 
@@ -890,7 +889,6 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
         cmd->vq = vq;
         cmd->error = 0;
         cmd->finished = false;
-        cmd->waiting = false;
         QTAILQ_INSERT_TAIL(&g->cmdq, cmd, next);
         cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command));
     }
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 27/29] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (25 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 26/29] virtio-gpu: remove useless 'waiting' field Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 28/29] virtio-gpu: split virtio-gpu-pci & virtio-vga Marc-André Lureau
                   ` (4 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Add a base class that is common to virtio-gpu and vhost-user-gpu
devices.

The VirtIOGPUBase base class provides common functionalities necessary
for both virtio-gpu and vhost-user-gpu:
- common configuration (max-outputs, initial resolution, flags)
- virtio device initialization, including queue setup
- device pre-conditions checks (iommu)
- migration blocker
- virtio device callbacks
- hooking up to qemu display subsystem
- a few common helper functions to reset the device, retrieve display
informations
- a class callback to unblock the rendering (for GL updates)

What is left to the virtio-gpu subdevice to take care of, in short,
are all the virtio queues handling, command processing and migration.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu.h |  72 +++++--
 hw/display/virtio-gpu-3d.c     |  49 ++---
 hw/display/virtio-gpu-base.c   | 292 +++++++++++++++++++++++++++++
 hw/display/virtio-gpu-pci.c    |   2 +-
 hw/display/virtio-gpu.c        | 332 ++++++---------------------------
 hw/display/virtio-vga.c        |  15 +-
 hw/display/Makefile.objs       |   2 +-
 7 files changed, 447 insertions(+), 317 deletions(-)
 create mode 100644 hw/display/virtio-gpu-base.c

diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 6b07efc4f7..2edf47e9ab 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -22,6 +22,14 @@
 
 #include "standard-headers/linux/virtio_gpu.h"
 
+#define TYPE_VIRTIO_GPU_BASE "virtio-gpu-base"
+#define VIRTIO_GPU_BASE(obj)                                                \
+    OBJECT_CHECK(VirtIOGPUBase, (obj), TYPE_VIRTIO_GPU_BASE)
+#define VIRTIO_GPU_BASE_GET_CLASS(obj)                                      \
+    OBJECT_GET_CLASS(VirtIOGPUBaseClass, obj, TYPE_VIRTIO_GPU_BASE)
+#define VIRTIO_GPU_BASE_CLASS(klass)                                        \
+    OBJECT_CLASS_CHECK(VirtIOGPUBaseClass, klass, TYPE_VIRTIO_GPU_BASE)
+
 #define TYPE_VIRTIO_GPU "virtio-gpu-device"
 #define VIRTIO_GPU(obj)                                        \
         OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU)
@@ -58,7 +66,7 @@ struct virtio_gpu_requested_state {
     int x, y;
 };
 
-enum virtio_gpu_conf_flags {
+enum virtio_gpu_base_conf_flags {
     VIRTIO_GPU_FLAG_VIRGL_ENABLED = 1,
     VIRTIO_GPU_FLAG_STATS_ENABLED,
 };
@@ -68,8 +76,7 @@ enum virtio_gpu_conf_flags {
 #define virtio_gpu_stats_enabled(_cfg) \
     (_cfg.flags & (1 << VIRTIO_GPU_FLAG_STATS_ENABLED))
 
-struct virtio_gpu_conf {
-    uint64_t max_hostmem;
+struct virtio_gpu_base_conf {
     uint32_t max_outputs;
     uint32_t flags;
     uint32_t xres;
@@ -85,31 +92,53 @@ struct virtio_gpu_ctrl_command {
     QTAILQ_ENTRY(virtio_gpu_ctrl_command) next;
 };
 
-typedef struct VirtIOGPU {
+typedef struct VirtIOGPUBase {
     VirtIODevice parent_obj;
 
-    QEMUBH *ctrl_bh;
-    QEMUBH *cursor_bh;
+    Error *migration_blocker;
+
+    struct virtio_gpu_base_conf conf;
+    struct virtio_gpu_config virtio_config;
+
+    bool use_virgl_renderer;
+    int renderer_blocked;
+    int enable;
+
+    struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS];
+
+    int enabled_output_bitmask;
+    struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS];
+} VirtIOGPUBase;
+
+typedef struct VirtIOGPUBaseClass {
+    VirtioDeviceClass parent;
+
+    void (*gl_unblock)(VirtIOGPUBase *g);
+} VirtIOGPUBaseClass;
+
+#define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf)                       \
+    DEFINE_PROP_UINT32("max_outputs", _state, _conf.max_outputs, 1),    \
+    DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1024),               \
+    DEFINE_PROP_UINT32("yres", _state, _conf.yres, 768)
+
+typedef struct VirtIOGPU {
+    VirtIOGPUBase parent_obj;
+
+    uint64_t conf_max_hostmem;
+
     VirtQueue *ctrl_vq;
     VirtQueue *cursor_vq;
 
-    int enable;
+    QEMUBH *ctrl_bh;
+    QEMUBH *cursor_bh;
 
     QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq;
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
 
-    struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS];
-    struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS];
-
-    struct virtio_gpu_conf conf;
     uint64_t hostmem;
-    int enabled_output_bitmask;
-    struct virtio_gpu_config virtio_config;
 
-    bool use_virgl_renderer;
     bool renderer_inited;
-    int renderer_blocked;
     QEMUTimer *fence_poll;
     QEMUTimer *print_stats;
 
@@ -120,8 +149,6 @@ typedef struct VirtIOGPU {
         uint32_t req_3d;
         uint32_t bytes_3d;
     } stats;
-
-    Error *migration_blocker;
 } VirtIOGPU;
 
 extern const GraphicHwOps virtio_gpu_ops;
@@ -144,6 +171,16 @@ extern const GraphicHwOps virtio_gpu_ops;
         }                                                               \
     } while (0)
 
+/* virtio-gpu-base.c */
+bool virtio_gpu_base_device_realize(DeviceState *qdev,
+                                    int num_capsets,
+                                    VirtIOHandleOutput ctrl_cb,
+                                    VirtIOHandleOutput cursor_cb,
+                                    Error **errp);
+void virtio_gpu_base_reset(VirtIOGPUBase *g);
+void virtio_gpu_base_fill_display_info(VirtIOGPUBase *g,
+                        struct virtio_gpu_resp_display_info *dpy_info);
+
 /* virtio-gpu.c */
 void virtio_gpu_ctrl_response(VirtIOGPU *g,
                               struct virtio_gpu_ctrl_command *cmd,
@@ -167,4 +204,5 @@ void virtio_gpu_virgl_fence_poll(VirtIOGPU *g);
 void virtio_gpu_virgl_reset(VirtIOGPU *g);
 int virtio_gpu_virgl_init(VirtIOGPU *g);
 int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g);
+
 #endif
diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
index 6fee0e8582..173c9d21e5 100644
--- a/hw/display/virtio-gpu-3d.c
+++ b/hw/display/virtio-gpu-3d.c
@@ -118,11 +118,11 @@ static void virgl_cmd_context_destroy(VirtIOGPU *g,
 static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y,
                                 int width, int height)
 {
-    if (!g->scanout[idx].con) {
+    if (!g->parent_obj.scanout[idx].con) {
         return;
     }
 
-    dpy_gl_update(g->scanout[idx].con, x, y, width, height);
+    dpy_gl_update(g->parent_obj.scanout[idx].con, x, y, width, height);
 }
 
 static void virgl_cmd_resource_flush(VirtIOGPU *g,
@@ -135,8 +135,8 @@ static void virgl_cmd_resource_flush(VirtIOGPU *g,
     trace_virtio_gpu_cmd_res_flush(rf.resource_id,
                                    rf.r.width, rf.r.height, rf.r.x, rf.r.y);
 
-    for (i = 0; i < g->conf.max_outputs; i++) {
-        if (g->scanout[i].resource_id != rf.resource_id) {
+    for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
+        if (g->parent_obj.scanout[i].resource_id != rf.resource_id) {
             continue;
         }
         virtio_gpu_rect_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.height);
@@ -154,13 +154,13 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
     trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
                                      ss.r.width, ss.r.height, ss.r.x, ss.r.y);
 
-    if (ss.scanout_id >= g->conf.max_outputs) {
+    if (ss.scanout_id >= g->parent_obj.conf.max_outputs) {
         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
                       __func__, ss.scanout_id);
         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
         return;
     }
-    g->enable = 1;
+    g->parent_obj.enable = 1;
 
     memset(&info, 0, sizeof(info));
 
@@ -173,20 +173,22 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
             cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
             return;
         }
-        qemu_console_resize(g->scanout[ss.scanout_id].con,
+        qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con,
                             ss.r.width, ss.r.height);
         virgl_renderer_force_ctx_0();
-        dpy_gl_scanout_texture(g->scanout[ss.scanout_id].con, info.tex_id,
-                               info.flags & 1 /* FIXME: Y_0_TOP */,
-                               info.width, info.height,
-                               ss.r.x, ss.r.y, ss.r.width, ss.r.height);
+        dpy_gl_scanout_texture(
+            g->parent_obj.scanout[ss.scanout_id].con, info.tex_id,
+            info.flags & 1 /* FIXME: Y_0_TOP */,
+            info.width, info.height,
+            ss.r.x, ss.r.y, ss.r.width, ss.r.height);
     } else {
         if (ss.scanout_id != 0) {
-            dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
+            dpy_gfx_replace_surface(
+                g->parent_obj.scanout[ss.scanout_id].con, NULL);
         }
-        dpy_gl_scanout_disable(g->scanout[ss.scanout_id].con);
+        dpy_gl_scanout_disable(g->parent_obj.scanout[ss.scanout_id].con);
     }
-    g->scanout[ss.scanout_id].resource_id = ss.resource_id;
+    g->parent_obj.scanout[ss.scanout_id].resource_id = ss.resource_id;
 }
 
 static void virgl_cmd_submit_3d(VirtIOGPU *g,
@@ -209,7 +211,7 @@ static void virgl_cmd_submit_3d(VirtIOGPU *g,
         goto out;
     }
 
-    if (virtio_gpu_stats_enabled(g->conf)) {
+    if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
         g->stats.req_3d++;
         g->stats.bytes_3d += cs.size;
     }
@@ -504,7 +506,7 @@ static void virgl_write_fence(void *opaque, uint32_t fence)
         QTAILQ_REMOVE(&g->fenceq, cmd, next);
         g_free(cmd);
         g->inflight--;
-        if (virtio_gpu_stats_enabled(g->conf)) {
+        if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
             fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
         }
     }
@@ -521,7 +523,7 @@ virgl_create_context(void *opaque, int scanout_idx,
     qparams.major_ver = params->major_ver;
     qparams.minor_ver = params->minor_ver;
 
-    ctx = dpy_gl_ctx_create(g->scanout[scanout_idx].con, &qparams);
+    ctx = dpy_gl_ctx_create(g->parent_obj.scanout[scanout_idx].con, &qparams);
     return (virgl_renderer_gl_context)ctx;
 }
 
@@ -530,7 +532,7 @@ static void virgl_destroy_context(void *opaque, virgl_renderer_gl_context ctx)
     VirtIOGPU *g = opaque;
     QEMUGLContext qctx = (QEMUGLContext)ctx;
 
-    dpy_gl_ctx_destroy(g->scanout[0].con, qctx);
+    dpy_gl_ctx_destroy(g->parent_obj.scanout[0].con, qctx);
 }
 
 static int virgl_make_context_current(void *opaque, int scanout_idx,
@@ -539,7 +541,8 @@ static int virgl_make_context_current(void *opaque, int scanout_idx,
     VirtIOGPU *g = opaque;
     QEMUGLContext qctx = (QEMUGLContext)ctx;
 
-    return dpy_gl_ctx_make_current(g->scanout[scanout_idx].con, qctx);
+    return dpy_gl_ctx_make_current(g->parent_obj.scanout[scanout_idx].con,
+                                   qctx);
 }
 
 static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = {
@@ -591,11 +594,11 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g)
     int i;
 
     /* virgl_renderer_reset() ??? */
-    for (i = 0; i < g->conf.max_outputs; i++) {
+    for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
         if (i != 0) {
-            dpy_gfx_replace_surface(g->scanout[i].con, NULL);
+            dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL);
         }
-        dpy_gl_scanout_disable(g->scanout[i].con);
+        dpy_gl_scanout_disable(g->parent_obj.scanout[i].con);
     }
 }
 
@@ -611,7 +614,7 @@ int virtio_gpu_virgl_init(VirtIOGPU *g)
     g->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL,
                                  virtio_gpu_fence_poll, g);
 
-    if (virtio_gpu_stats_enabled(g->conf)) {
+    if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
         g->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL,
                                       virtio_gpu_print_stats, g);
         timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c
new file mode 100644
index 0000000000..2619e94304
--- /dev/null
+++ b/hw/display/virtio-gpu-base.c
@@ -0,0 +1,292 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ *     Dave Airlie <airlied@redhat.com>
+ *     Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/virtio/virtio-gpu.h"
+#include "migration/blocker.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+
+void
+virtio_gpu_base_reset(VirtIOGPUBase *g)
+{
+    int i;
+
+    g->enable = 0;
+    g->use_virgl_renderer = false;
+
+    for (i = 0; i < g->conf.max_outputs; i++) {
+        g->scanout[i].resource_id = 0;
+        g->scanout[i].width = 0;
+        g->scanout[i].height = 0;
+        g->scanout[i].x = 0;
+        g->scanout[i].y = 0;
+        g->scanout[i].ds = NULL;
+    }
+}
+
+void
+virtio_gpu_base_fill_display_info(VirtIOGPUBase *g,
+                                  struct virtio_gpu_resp_display_info *dpy_info)
+{
+    int i;
+
+    for (i = 0; i < g->conf.max_outputs; i++) {
+        if (g->enabled_output_bitmask & (1 << i)) {
+            dpy_info->pmodes[i].enabled = 1;
+            dpy_info->pmodes[i].r.width = cpu_to_le32(g->req_state[i].width);
+            dpy_info->pmodes[i].r.height = cpu_to_le32(g->req_state[i].height);
+        }
+    }
+}
+
+static void virtio_gpu_invalidate_display(void *opaque)
+{
+}
+
+static void virtio_gpu_update_display(void *opaque)
+{
+}
+
+static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
+{
+}
+
+static void virtio_gpu_notify_event(VirtIOGPUBase *g, uint32_t event_type)
+{
+    g->virtio_config.events_read |= event_type;
+    virtio_notify_config(&g->parent_obj);
+}
+
+static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
+{
+    VirtIOGPUBase *g = opaque;
+
+    if (idx >= g->conf.max_outputs) {
+        return -1;
+    }
+
+    g->req_state[idx].x = info->xoff;
+    g->req_state[idx].y = info->yoff;
+    g->req_state[idx].width = info->width;
+    g->req_state[idx].height = info->height;
+
+    if (info->width && info->height) {
+        g->enabled_output_bitmask |= (1 << idx);
+    } else {
+        g->enabled_output_bitmask &= ~(1 << idx);
+    }
+
+    /* send event to guest */
+    virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY);
+    return 0;
+}
+
+static void
+virtio_gpu_gl_block(void *opaque, bool block)
+{
+    VirtIOGPUBase *g = opaque;
+    VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_GET_CLASS(g);
+
+    if (block) {
+        g->renderer_blocked++;
+    } else {
+        g->renderer_blocked--;
+    }
+    assert(g->renderer_blocked >= 0);
+    if (g->renderer_blocked == 0) {
+        vgc->gl_unblock(g);
+    }
+}
+
+const GraphicHwOps virtio_gpu_ops = {
+    .invalidate = virtio_gpu_invalidate_display,
+    .gfx_update = virtio_gpu_update_display,
+    .text_update = virtio_gpu_text_update,
+    .ui_info = virtio_gpu_ui_info,
+    .gl_block = virtio_gpu_gl_block,
+};
+
+bool
+virtio_gpu_base_device_realize(DeviceState *qdev,
+                               int num_capsets,
+                               VirtIOHandleOutput ctrl_cb,
+                               VirtIOHandleOutput cursor_cb,
+                               Error **errp)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev);
+    Error *local_err = NULL;
+    int i;
+
+    if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) {
+        error_setg(errp, "virtio-gpu does not support vIOMMU yet");
+        return false;
+    }
+
+    if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) {
+        error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS);
+        return false;
+    }
+
+    g->use_virgl_renderer = false;
+    if (virtio_gpu_virgl_enabled(g->conf)) {
+        error_setg(&g->migration_blocker, "virgl is not yet migratable");
+        migrate_add_blocker(g->migration_blocker, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+            error_free(g->migration_blocker);
+            return false;
+        }
+    }
+
+    g->virtio_config.num_scanouts = cpu_to_le32(g->conf.max_outputs);
+    virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
+                sizeof(struct virtio_gpu_config));
+
+    if (virtio_gpu_virgl_enabled(g->conf)) {
+        /* use larger control queue in 3d mode */
+        virtio_add_queue(vdev, 256, ctrl_cb);
+        virtio_add_queue(vdev, 16, cursor_cb);
+        g->virtio_config.num_capsets = num_capsets;
+    } else {
+        virtio_add_queue(vdev, 64, ctrl_cb);
+        virtio_add_queue(vdev, 16, cursor_cb);
+    }
+
+    g->enabled_output_bitmask = 1;
+
+    g->req_state[0].width = g->conf.xres;
+    g->req_state[0].height = g->conf.yres;
+
+    for (i = 0; i < g->conf.max_outputs; i++) {
+        g->scanout[i].con =
+            graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
+        if (i > 0) {
+            dpy_gfx_replace_surface(g->scanout[i].con, NULL);
+        }
+    }
+
+    return true;
+}
+
+static void
+virtio_gpu_base_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(vdev);
+    memcpy(config, &g->virtio_config, sizeof(g->virtio_config));
+}
+
+static void
+virtio_gpu_base_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(vdev);
+    struct virtio_gpu_config vgconfig;
+
+    memcpy(&vgconfig, config, sizeof(g->virtio_config));
+
+    if (vgconfig.events_clear) {
+        g->virtio_config.events_read &= ~vgconfig.events_clear;
+    }
+}
+
+static uint64_t
+virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t features,
+                             Error **errp)
+{
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(vdev);
+
+    if (virtio_gpu_virgl_enabled(g->conf)) {
+        features |= (1 << VIRTIO_GPU_F_VIRGL);
+    }
+    return features;
+}
+
+static void
+virtio_gpu_base_set_features(VirtIODevice *vdev, uint64_t features)
+{
+    static const uint32_t virgl = (1 << VIRTIO_GPU_F_VIRGL);
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(vdev);
+
+    g->use_virgl_renderer = ((features & virgl) == virgl);
+    trace_virtio_gpu_features(g->use_virgl_renderer);
+}
+
+static void
+virtio_gpu_base_device_unrealize(DeviceState *qdev, Error **errp)
+{
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev);
+
+    if (g->migration_blocker) {
+        migrate_del_blocker(g->migration_blocker);
+        error_free(g->migration_blocker);
+    }
+}
+
+static void
+virtio_gpu_base_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+    vdc->unrealize = virtio_gpu_base_device_unrealize;
+    vdc->get_features = virtio_gpu_base_get_features;
+    vdc->set_features = virtio_gpu_base_set_features;
+    vdc->get_config = virtio_gpu_base_get_config;
+    vdc->set_config = virtio_gpu_base_set_config;
+
+    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+    dc->hotpluggable = false;
+}
+
+static const TypeInfo virtio_gpu_base_info = {
+    .name = TYPE_VIRTIO_GPU_BASE,
+    .parent = TYPE_VIRTIO_DEVICE,
+    .instance_size = sizeof(VirtIOGPUBase),
+    .class_size = sizeof(VirtIOGPUBaseClass),
+    .class_init = virtio_gpu_base_class_init,
+    .abstract = true
+};
+
+static void
+virtio_register_types(void)
+{
+    type_register_static(&virtio_gpu_base_info);
+}
+
+type_init(virtio_register_types)
+
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr)                != 24);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor)           != 56);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref)          != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d)      != 40);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout)             != 48);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush)          != 48);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d)     != 56);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry)               != 16);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info)       != 408);
+
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d)        != 72);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d)      != 72);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create)              != 96);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy)             != 24);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource)            != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit)              != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info)         != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info)        != 40);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset)              != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset)             != 24);
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
index cece4aa495..741badd909 100644
--- a/hw/display/virtio-gpu-pci.c
+++ b/hw/display/virtio-gpu-pci.c
@@ -27,7 +27,7 @@ static Property virtio_gpu_pci_properties[] = {
 static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
 {
     VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev);
-    VirtIOGPU *g = &vgpu->vdev;
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vgpu->vdev);
     DeviceState *vdev = DEVICE(&vgpu->vdev);
     int i;
     Error *local_error = NULL;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index b83982b0a3..8609cb6eeb 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -21,9 +21,9 @@
 #include "hw/virtio/virtio-gpu.h"
 #include "hw/virtio/virtio-gpu-bswap.h"
 #include "hw/virtio/virtio-bus.h"
-#include "migration/blocker.h"
 #include "qemu/log.h"
 #include "qapi/error.h"
+#include "qemu/error-report.h"
 
 #define VIRTIO_GPU_VM_VERSION 1
 
@@ -36,7 +36,7 @@ static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res);
 #include <virglrenderer.h>
 #define VIRGL(_g, _virgl, _simple, ...)                     \
     do {                                                    \
-        if (_g->use_virgl_renderer) {                       \
+        if (_g->parent_obj.use_virgl_renderer) {            \
             _virgl(__VA_ARGS__);                            \
         } else {                                            \
             _simple(__VA_ARGS__);                           \
@@ -104,10 +104,10 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
     struct virtio_gpu_scanout *s;
     bool move = cursor->hdr.type == VIRTIO_GPU_CMD_MOVE_CURSOR;
 
-    if (cursor->pos.scanout_id >= g->conf.max_outputs) {
+    if (cursor->pos.scanout_id >= g->parent_obj.conf.max_outputs) {
         return;
     }
-    s = &g->scanout[cursor->pos.scanout_id];
+    s = &g->parent_obj.scanout[cursor->pos.scanout_id];
 
     trace_virtio_gpu_update_cursor(cursor->pos.scanout_id,
                                    cursor->pos.x,
@@ -138,50 +138,6 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
                   cursor->resource_id ? 1 : 0);
 }
 
-static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config)
-{
-    VirtIOGPU *g = VIRTIO_GPU(vdev);
-    memcpy(config, &g->virtio_config, sizeof(g->virtio_config));
-}
-
-static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
-{
-    VirtIOGPU *g = VIRTIO_GPU(vdev);
-    struct virtio_gpu_config vgconfig;
-
-    memcpy(&vgconfig, config, sizeof(g->virtio_config));
-
-    if (vgconfig.events_clear) {
-        g->virtio_config.events_read &= ~vgconfig.events_clear;
-    }
-}
-
-static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features,
-                                        Error **errp)
-{
-    VirtIOGPU *g = VIRTIO_GPU(vdev);
-
-    if (virtio_gpu_virgl_enabled(g->conf)) {
-        features |= (1 << VIRTIO_GPU_F_VIRGL);
-    }
-    return features;
-}
-
-static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features)
-{
-    static const uint32_t virgl = (1 << VIRTIO_GPU_F_VIRGL);
-    VirtIOGPU *g = VIRTIO_GPU(vdev);
-
-    g->use_virgl_renderer = ((features & virgl) == virgl);
-    trace_virtio_gpu_features(g->use_virgl_renderer);
-}
-
-static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type)
-{
-    g->virtio_config.events_read |= event_type;
-    virtio_notify_config(&g->parent_obj);
-}
-
 static struct virtio_gpu_simple_resource *
 virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id)
 {
@@ -230,21 +186,6 @@ void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g,
     virtio_gpu_ctrl_response(g, cmd, &resp, sizeof(resp));
 }
 
-static void
-virtio_gpu_fill_display_info(VirtIOGPU *g,
-                             struct virtio_gpu_resp_display_info *dpy_info)
-{
-    int i;
-
-    for (i = 0; i < g->conf.max_outputs; i++) {
-        if (g->enabled_output_bitmask & (1 << i)) {
-            dpy_info->pmodes[i].enabled = 1;
-            dpy_info->pmodes[i].r.width = cpu_to_le32(g->req_state[i].width);
-            dpy_info->pmodes[i].r.height = cpu_to_le32(g->req_state[i].height);
-        }
-    }
-}
-
 void virtio_gpu_get_display_info(VirtIOGPU *g,
                                  struct virtio_gpu_ctrl_command *cmd)
 {
@@ -253,7 +194,7 @@ void virtio_gpu_get_display_info(VirtIOGPU *g,
     trace_virtio_gpu_cmd_get_display_info();
     memset(&display_info, 0, sizeof(display_info));
     display_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO;
-    virtio_gpu_fill_display_info(g, &display_info);
+    virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_info);
     virtio_gpu_ctrl_response(g, cmd, &display_info.hdr,
                              sizeof(display_info));
 }
@@ -339,7 +280,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
     }
 
     res->hostmem = calc_image_hostmem(pformat, c2d.width, c2d.height);
-    if (res->hostmem + g->hostmem < g->conf.max_hostmem) {
+    if (res->hostmem + g->hostmem < g->conf_max_hostmem) {
         res->image = pixman_image_create_bits(pformat,
                                               c2d.width,
                                               c2d.height,
@@ -361,7 +302,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
 
 static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id)
 {
-    struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id];
+    struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id];
     struct virtio_gpu_simple_resource *res;
     DisplaySurface *ds = NULL;
 
@@ -393,7 +334,7 @@ static void virtio_gpu_resource_destroy(VirtIOGPU *g,
     int i;
 
     if (res->scanout_bitmask) {
-        for (i = 0; i < g->conf.max_outputs; i++) {
+        for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
             if (res->scanout_bitmask & (1 << i)) {
                 virtio_gpu_disable_scanout(g, i);
             }
@@ -523,7 +464,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
 
     pixman_region_init_rect(&flush_region,
                             rf.r.x, rf.r.y, rf.r.width, rf.r.height);
-    for (i = 0; i < g->conf.max_outputs; i++) {
+    for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
         struct virtio_gpu_scanout *scanout;
         pixman_region16_t region, finalregion;
         pixman_box16_t *extents;
@@ -531,7 +472,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
         if (!(res->scanout_bitmask & (1 << i))) {
             continue;
         }
-        scanout = &g->scanout[i];
+        scanout = &g->parent_obj.scanout[i];
 
         pixman_region_init(&finalregion);
         pixman_region_init_rect(&region, scanout->x, scanout->y,
@@ -541,7 +482,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
         pixman_region_translate(&finalregion, -scanout->x, -scanout->y);
         extents = pixman_region_extents(&finalregion);
         /* work out the area we need to update for each console */
-        dpy_gfx_update(g->scanout[i].con,
+        dpy_gfx_update(g->parent_obj.scanout[i].con,
                        extents->x1, extents->y1,
                        extents->x2 - extents->x1,
                        extents->y2 - extents->y1);
@@ -572,14 +513,14 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
     trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
                                      ss.r.width, ss.r.height, ss.r.x, ss.r.y);
 
-    if (ss.scanout_id >= g->conf.max_outputs) {
+    if (ss.scanout_id >= g->parent_obj.conf.max_outputs) {
         qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
                       __func__, ss.scanout_id);
         cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
         return;
     }
 
-    g->enable = 1;
+    g->parent_obj.enable = 1;
     if (ss.resource_id == 0) {
         virtio_gpu_disable_scanout(g, ss.scanout_id);
         return;
@@ -608,7 +549,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
         return;
     }
 
-    scanout = &g->scanout[ss.scanout_id];
+    scanout = &g->parent_obj.scanout[ss.scanout_id];
 
     format = pixman_image_get_format(res->image);
     bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8);
@@ -631,7 +572,8 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
             return;
         }
         pixman_image_unref(rect);
-        dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds);
+        dpy_gfx_replace_surface(g->parent_obj.scanout[ss.scanout_id].con,
+                                scanout->ds);
     }
 
     ores = virtio_gpu_find_resource(g, scanout->resource_id);
@@ -840,7 +782,7 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
     while (!QTAILQ_EMPTY(&g->cmdq)) {
         cmd = QTAILQ_FIRST(&g->cmdq);
 
-        if (g->renderer_blocked) {
+        if (g->parent_obj.renderer_blocked) {
             break;
         }
 
@@ -849,14 +791,14 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
               g, cmd);
 
         QTAILQ_REMOVE(&g->cmdq, cmd, next);
-        if (virtio_gpu_stats_enabled(g->conf)) {
+        if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
             g->stats.requests++;
         }
 
         if (!cmd->finished) {
             QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next);
             g->inflight++;
-            if (virtio_gpu_stats_enabled(g->conf)) {
+            if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
                 if (g->stats.max_inflight < g->inflight) {
                     g->stats.max_inflight = g->inflight;
                 }
@@ -868,6 +810,11 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
     }
 }
 
+static void virtio_gpu_gl_unblock(VirtIOGPUBase *g)
+{
+    virtio_gpu_process_cmdq(VIRTIO_GPU(g));
+}
+
 static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
     VirtIOGPU *g = VIRTIO_GPU(vdev);
@@ -878,7 +825,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
     }
 
 #ifdef CONFIG_VIRGL
-    if (!g->renderer_inited && g->use_virgl_renderer) {
+    if (!g->renderer_inited && g->parent_obj.use_virgl_renderer) {
         virtio_gpu_virgl_init(g);
         g->renderer_inited = true;
     }
@@ -896,7 +843,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
     virtio_gpu_process_cmdq(g);
 
 #ifdef CONFIG_VIRGL
-    if (g->use_virgl_renderer) {
+    if (g->parent_obj.use_virgl_renderer) {
         virtio_gpu_virgl_fence_poll(g);
     }
 #endif
@@ -905,7 +852,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 static void virtio_gpu_ctrl_bh(void *opaque)
 {
     VirtIOGPU *g = opaque;
-    virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq);
+    virtio_gpu_handle_ctrl(&g->parent_obj.parent_obj, g->ctrl_vq);
 }
 
 static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
@@ -943,71 +890,9 @@ static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
 static void virtio_gpu_cursor_bh(void *opaque)
 {
     VirtIOGPU *g = opaque;
-    virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq);
-}
-
-static void virtio_gpu_invalidate_display(void *opaque)
-{
-}
-
-static void virtio_gpu_update_display(void *opaque)
-{
-}
-
-static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
-{
-}
-
-static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
-{
-    VirtIOGPU *g = opaque;
-
-    if (idx >= g->conf.max_outputs) {
-        return -1;
-    }
-
-    g->req_state[idx].x = info->xoff;
-    g->req_state[idx].y = info->yoff;
-    g->req_state[idx].width = info->width;
-    g->req_state[idx].height = info->height;
-
-    if (info->width && info->height) {
-        g->enabled_output_bitmask |= (1 << idx);
-    } else {
-        g->enabled_output_bitmask &= ~(1 << idx);
-    }
-
-    /* send event to guest */
-    virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY);
-    return 0;
+    virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq);
 }
 
-static void virtio_gpu_gl_block(void *opaque, bool block)
-{
-    VirtIOGPU *g = opaque;
-
-    if (block) {
-        g->renderer_blocked++;
-    } else {
-        g->renderer_blocked--;
-    }
-    assert(g->renderer_blocked >= 0);
-
-    if (g->renderer_blocked == 0) {
-        virtio_gpu_process_cmdq(g);
-    }
-}
-
-const GraphicHwOps virtio_gpu_ops = {
-    .invalidate = virtio_gpu_invalidate_display,
-    .gfx_update = virtio_gpu_update_display,
-    .text_update = virtio_gpu_text_update,
-    .ui_info = virtio_gpu_ui_info,
-#ifdef CONFIG_VIRGL
-    .gl_block = virtio_gpu_gl_block,
-#endif
-};
-
 static const VMStateDescription vmstate_virtio_gpu_scanout = {
     .name = "virtio-gpu-one-scanout",
     .version_id = 1,
@@ -1030,10 +915,11 @@ static const VMStateDescription vmstate_virtio_gpu_scanouts = {
     .name = "virtio-gpu-scanouts",
     .version_id = 1,
     .fields = (VMStateField[]) {
-        VMSTATE_INT32(enable, struct VirtIOGPU),
-        VMSTATE_UINT32_EQUAL(conf.max_outputs, struct VirtIOGPU, NULL),
-        VMSTATE_STRUCT_VARRAY_UINT32(scanout, struct VirtIOGPU,
-                                     conf.max_outputs, 1,
+        VMSTATE_INT32(parent_obj.enable, struct VirtIOGPU),
+        VMSTATE_UINT32_EQUAL(parent_obj.conf.max_outputs,
+                             struct VirtIOGPU, NULL),
+        VMSTATE_STRUCT_VARRAY_UINT32(parent_obj.scanout, struct VirtIOGPU,
+                                     parent_obj.conf.max_outputs, 1,
                                      vmstate_virtio_gpu_scanout,
                                      struct virtio_gpu_scanout),
         VMSTATE_END_OF_LIST()
@@ -1143,8 +1029,8 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
 
     /* load & apply scanout state */
     vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1);
-    for (i = 0; i < g->conf.max_outputs; i++) {
-        scanout = &g->scanout[i];
+    for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
+        scanout = &g->parent_obj.scanout[i];
         if (!scanout->resource_id) {
             continue;
         }
@@ -1173,117 +1059,54 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
     VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
     VirtIOGPU *g = VIRTIO_GPU(qdev);
     bool have_virgl;
-    Error *local_err = NULL;
-    int i;
+    int num_capsets = 0;
 
-    if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) {
-        error_setg(errp, "virtio-gpu does not support vIOMMU yet");
-        return;
-    }
-
-    if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) {
-        error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS);
-        return;
-    }
-
-    g->use_virgl_renderer = false;
 #if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN)
     have_virgl = false;
 #else
     have_virgl = display_opengl;
 #endif
     if (!have_virgl) {
-        g->conf.flags &= ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED);
-    }
-
-    if (virtio_gpu_virgl_enabled(g->conf)) {
-        error_setg(&g->migration_blocker, "virgl is not yet migratable");
-        migrate_add_blocker(g->migration_blocker, &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-            error_free(g->migration_blocker);
-            return;
-        }
+        g->parent_obj.conf.flags &= ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED);
     }
 
-    g->virtio_config.num_scanouts = cpu_to_le32(g->conf.max_outputs);
-    virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
-                sizeof(struct virtio_gpu_config));
-
-    g->req_state[0].width = g->conf.xres;
-    g->req_state[0].height = g->conf.yres;
-
-    if (virtio_gpu_virgl_enabled(g->conf)) {
-        /* use larger control queue in 3d mode */
-        g->ctrl_vq   = virtio_add_queue(vdev, 256, virtio_gpu_handle_ctrl_cb);
-        g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
-
 #if defined(CONFIG_VIRGL)
-        g->virtio_config.num_capsets = virtio_gpu_virgl_get_num_capsets(g);
-#else
-        g->virtio_config.num_capsets = 0;
+    num_capsets = virtio_gpu_virgl_get_num_capsets(g);
 #endif
-    } else {
-        g->ctrl_vq   = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
-        g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+
+    if (!virtio_gpu_base_device_realize(qdev,
+                                        num_capsets,
+                                        virtio_gpu_handle_ctrl_cb,
+                                        virtio_gpu_handle_cursor_cb,
+                                        errp)) {
+        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);
     g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
     QTAILQ_INIT(&g->reslist);
     QTAILQ_INIT(&g->cmdq);
     QTAILQ_INIT(&g->fenceq);
-
-    g->enabled_output_bitmask = 1;
-
-    for (i = 0; i < g->conf.max_outputs; i++) {
-        g->scanout[i].con =
-            graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
-        if (i > 0) {
-            dpy_gfx_replace_surface(g->scanout[i].con, NULL);
-        }
-    }
-}
-
-static void virtio_gpu_device_unrealize(DeviceState *qdev, Error **errp)
-{
-    VirtIOGPU *g = VIRTIO_GPU(qdev);
-    if (g->migration_blocker) {
-        migrate_del_blocker(g->migration_blocker);
-        error_free(g->migration_blocker);
-    }
-}
-
-static void virtio_gpu_instance_init(Object *obj)
-{
 }
 
 static void virtio_gpu_reset(VirtIODevice *vdev)
 {
     VirtIOGPU *g = VIRTIO_GPU(vdev);
     struct virtio_gpu_simple_resource *res, *tmp;
-    int i;
 
-    g->enable = 0;
+#ifdef CONFIG_VIRGL
+    if (g->parent_obj.use_virgl_renderer) {
+        virtio_gpu_virgl_reset(g);
+    }
+#endif
 
     QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
         virtio_gpu_resource_destroy(g, res);
     }
-    for (i = 0; i < g->conf.max_outputs; i++) {
-        g->scanout[i].resource_id = 0;
-        g->scanout[i].width = 0;
-        g->scanout[i].height = 0;
-        g->scanout[i].x = 0;
-        g->scanout[i].y = 0;
-        g->scanout[i].ds = NULL;
-    }
 
-#ifdef CONFIG_VIRGL
-    if (g->use_virgl_renderer) {
-        virtio_gpu_virgl_reset(g);
-        g->use_virgl_renderer = 0;
-    }
-#endif
+    virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev));
 }
 
 /*
@@ -1314,16 +1137,15 @@ static const VMStateDescription vmstate_virtio_gpu = {
 };
 
 static Property virtio_gpu_properties[] = {
-    DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1),
-    DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf.max_hostmem, 256 * MiB),
+    VIRTIO_GPU_BASE_PROPERTIES(VirtIOGPU, parent_obj.conf),
+    DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf_max_hostmem,
+                     256 * MiB),
 #ifdef CONFIG_VIRGL
-    DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags,
+    DEFINE_PROP_BIT("virgl", VirtIOGPU, parent_obj.conf.flags,
                     VIRTIO_GPU_FLAG_VIRGL_ENABLED, true),
-    DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags,
+    DEFINE_PROP_BIT("stats", VirtIOGPU, parent_obj.conf.flags,
                     VIRTIO_GPU_FLAG_STATS_ENABLED, false),
 #endif
-    DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024),
-    DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -1331,27 +1153,20 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+    VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_CLASS(klass);
 
+    vgc->gl_unblock = virtio_gpu_gl_unblock;
     vdc->realize = virtio_gpu_device_realize;
-    vdc->unrealize = virtio_gpu_device_unrealize;
-    vdc->get_config = virtio_gpu_get_config;
-    vdc->set_config = virtio_gpu_set_config;
-    vdc->get_features = virtio_gpu_get_features;
-    vdc->set_features = virtio_gpu_set_features;
-
     vdc->reset = virtio_gpu_reset;
 
-    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
-    dc->props = virtio_gpu_properties;
     dc->vmsd = &vmstate_virtio_gpu;
-    dc->hotpluggable = false;
+    dc->props = virtio_gpu_properties;
 }
 
 static const TypeInfo virtio_gpu_info = {
     .name = TYPE_VIRTIO_GPU,
-    .parent = TYPE_VIRTIO_DEVICE,
+    .parent = TYPE_VIRTIO_GPU_BASE,
     .instance_size = sizeof(VirtIOGPU),
-    .instance_init = virtio_gpu_instance_init,
     .class_init = virtio_gpu_class_init,
 };
 
@@ -1361,26 +1176,3 @@ static void virtio_register_types(void)
 }
 
 type_init(virtio_register_types)
-
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr)                != 24);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor)           != 56);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref)          != 32);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d)      != 40);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout)             != 48);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush)          != 48);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d)     != 56);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry)               != 16);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info)       != 408);
-
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d)        != 72);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d)      != 72);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create)              != 96);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy)             != 24);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource)            != 32);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit)              != 32);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info)         != 32);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info)        != 40);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset)              != 32);
-QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset)             != 24);
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index 8d3d9e14a7..00939b7e0c 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -22,8 +22,9 @@ typedef struct VirtIOVGA {
 static void virtio_vga_invalidate_display(void *opaque)
 {
     VirtIOVGA *vvga = opaque;
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
 
-    if (vvga->vdev.enable) {
+    if (g->enable) {
         virtio_gpu_ops.invalidate(&vvga->vdev);
     } else {
         vvga->vga.hw_ops->invalidate(&vvga->vga);
@@ -33,8 +34,9 @@ static void virtio_vga_invalidate_display(void *opaque)
 static void virtio_vga_update_display(void *opaque)
 {
     VirtIOVGA *vvga = opaque;
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
 
-    if (vvga->vdev.enable) {
+    if (g->enable) {
         virtio_gpu_ops.gfx_update(&vvga->vdev);
     } else {
         vvga->vga.hw_ops->gfx_update(&vvga->vga);
@@ -44,8 +46,9 @@ static void virtio_vga_update_display(void *opaque)
 static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
 {
     VirtIOVGA *vvga = opaque;
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
 
-    if (vvga->vdev.enable) {
+    if (g->enable) {
         if (virtio_gpu_ops.text_update) {
             virtio_gpu_ops.text_update(&vvga->vdev, chardata);
         }
@@ -98,7 +101,7 @@ static const VMStateDescription vmstate_virtio_vga = {
 static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
 {
     VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
-    VirtIOGPU *g = &vvga->vdev;
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
     VGACommonState *vga = &vvga->vga;
     Error *err = NULL;
     uint32_t offset;
@@ -168,7 +171,9 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
 static void virtio_vga_reset(DeviceState *dev)
 {
     VirtIOVGA *vvga = VIRTIO_VGA(dev);
-    vvga->vdev.enable = 0;
+    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
+
+    g->enable = 0;
 
     vga_dirty_log_start(&vvga->vga);
 }
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index fb8408c6d0..5e60015b77 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -39,7 +39,7 @@ obj-$(CONFIG_VGA) += vga.o
 
 common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
 
-obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o virtio-gpu-3d.o
+obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d.o
 obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) += virtio-gpu-pci.o
 obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
 virtio-gpu.o-cflags := $(VIRGL_CFLAGS)
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 28/29] virtio-gpu: split virtio-gpu-pci & virtio-vga
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (26 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 27/29] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 29/29] hw/display: add vhost-user-vga & gpu-pci Marc-André Lureau
                   ` (3 subsequent siblings)
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Add base classes that are common to vhost-user-gpu-pci and
vhost-user-vga.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/display/virtio-vga.h     |  22 +++++++
 hw/virtio/virtio-pci.h      |  16 ++---
 hw/display/virtio-gpu-pci.c |  39 +++++++++---
 hw/display/virtio-vga.c     | 122 +++++++++++++++++++-----------------
 MAINTAINERS                 |   2 +-
 5 files changed, 126 insertions(+), 75 deletions(-)
 create mode 100644 hw/display/virtio-vga.h

diff --git a/hw/display/virtio-vga.h b/hw/display/virtio-vga.h
new file mode 100644
index 0000000000..212624449c
--- /dev/null
+++ b/hw/display/virtio-vga.h
@@ -0,0 +1,22 @@
+#ifndef VIRTIO_VGA_H_
+#define VIRTIO_VGA_H_
+
+#include "hw/virtio/virtio-pci.h"
+#include "vga_int.h"
+
+/*
+ * virtio-vga-base: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_VGA_BASE "virtio-vga-base"
+#define VIRTIO_VGA_BASE(obj)                                \
+    OBJECT_CHECK(VirtIOVGABase, (obj), TYPE_VIRTIO_VGA_BASE)
+
+typedef struct VirtIOVGABase {
+    VirtIOPCIProxy parent_obj;
+
+    VirtIOGPUBase *vgpu;
+    VGACommonState vga;
+    MemoryRegion   vga_mrs[3];
+} VirtIOVGABase;
+
+#endif /* VIRTIO_VGA_H_ */
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index c7e28e1b9c..42240f47b3 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -386,17 +386,19 @@ struct VHostUserInputPCI {
     VHostUserInput vhi;
 };
 
+
 /*
- * virtio-gpu-pci: This extends VirtioPCIProxy.
+ * virtio-gpu-pci-base: This extends VirtioPCIProxy.
  */
-#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
-#define VIRTIO_GPU_PCI(obj) \
-        OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
+#define TYPE_VIRTIO_GPU_PCI_BASE "virtio-gpu-pci-base"
+#define VIRTIO_GPU_PCI_BASE(obj)                                 \
+    OBJECT_CHECK(VirtIOGPUPCIBase, (obj), TYPE_VIRTIO_GPU_PCI_BASE)
 
-struct VirtIOGPUPCI {
+typedef struct VirtIOGPUPCIBase {
     VirtIOPCIProxy parent_obj;
-    VirtIOGPU vdev;
-};
+
+    VirtIOGPUBase *vgpu;
+} VirtIOGPUPCIBase;
 
 #ifdef CONFIG_VHOST_VSOCK
 /*
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
index 741badd909..5db6ad890d 100644
--- a/hw/display/virtio-gpu-pci.c
+++ b/hw/display/virtio-gpu-pci.c
@@ -19,16 +19,16 @@
 #include "hw/virtio/virtio-pci.h"
 #include "hw/virtio/virtio-gpu.h"
 
-static Property virtio_gpu_pci_properties[] = {
+static Property virtio_gpu_pci_base_properties[] = {
     DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+static void virtio_gpu_pci_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
 {
-    VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev);
-    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vgpu->vdev);
-    DeviceState *vdev = DEVICE(&vgpu->vdev);
+    VirtIOGPUPCIBase *vgpu = VIRTIO_GPU_PCI_BASE(vpci_dev);
+    VirtIOGPUBase *g = vgpu->vgpu;
+    DeviceState *vdev = DEVICE(g);
     int i;
     Error *local_error = NULL;
 
@@ -48,37 +48,56 @@ static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
     }
 }
 
-static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data)
+static void virtio_gpu_pci_base_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
     PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
 
     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
-    dc->props = virtio_gpu_pci_properties;
+    dc->props = virtio_gpu_pci_base_properties;
     dc->hotpluggable = false;
-    k->realize = virtio_gpu_pci_realize;
+    k->realize = virtio_gpu_pci_base_realize;
     pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER;
 }
 
+static const TypeInfo virtio_gpu_pci_base_info = {
+    .name = TYPE_VIRTIO_GPU_PCI_BASE,
+    .parent = TYPE_VIRTIO_PCI,
+    .instance_size = sizeof(VirtIOGPUPCIBase),
+    .class_init = virtio_gpu_pci_base_class_init,
+    .abstract = true
+};
+
+#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
+#define VIRTIO_GPU_PCI(obj)                                 \
+    OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
+
+struct VirtIOGPUPCI {
+    VirtIOGPUPCIBase parent_obj;
+    VirtIOGPU vdev;
+};
+
 static void virtio_gpu_initfn(Object *obj)
 {
     VirtIOGPUPCI *dev = VIRTIO_GPU_PCI(obj);
 
     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
                                 TYPE_VIRTIO_GPU);
+    VIRTIO_GPU_PCI_BASE(obj)->vgpu = VIRTIO_GPU_BASE(&dev->vdev);
 }
 
 static const TypeInfo virtio_gpu_pci_info = {
     .name = TYPE_VIRTIO_GPU_PCI,
-    .parent = TYPE_VIRTIO_PCI,
+    .parent = TYPE_VIRTIO_GPU_PCI_BASE,
     .instance_size = sizeof(VirtIOGPUPCI),
     .instance_init = virtio_gpu_initfn,
-    .class_init = virtio_gpu_pci_class_init,
 };
 
 static void virtio_gpu_pci_register_types(void)
 {
+    type_register_static(&virtio_gpu_pci_base_info);
     type_register_static(&virtio_gpu_pci_info);
 }
+
 type_init(virtio_gpu_pci_register_types)
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index 00939b7e0c..57b7b65423 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -1,56 +1,41 @@
 #include "qemu/osdep.h"
 #include "hw/hw.h"
 #include "hw/pci/pci.h"
-#include "vga_int.h"
-#include "hw/virtio/virtio-pci.h"
 #include "qapi/error.h"
+#include "virtio-vga.h"
 
-/*
- * virtio-vga: This extends VirtioPCIProxy.
- */
-#define TYPE_VIRTIO_VGA "virtio-vga"
-#define VIRTIO_VGA(obj) \
-        OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
-
-typedef struct VirtIOVGA {
-    VirtIOPCIProxy parent_obj;
-    VirtIOGPU      vdev;
-    VGACommonState vga;
-    MemoryRegion   vga_mrs[3];
-} VirtIOVGA;
-
-static void virtio_vga_invalidate_display(void *opaque)
+static void virtio_vga_base_invalidate_display(void *opaque)
 {
-    VirtIOVGA *vvga = opaque;
-    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
+    VirtIOVGABase *vvga = opaque;
+    VirtIOGPUBase *g = vvga->vgpu;
 
     if (g->enable) {
-        virtio_gpu_ops.invalidate(&vvga->vdev);
+        virtio_gpu_ops.invalidate(g);
     } else {
         vvga->vga.hw_ops->invalidate(&vvga->vga);
     }
 }
 
-static void virtio_vga_update_display(void *opaque)
+static void virtio_vga_base_update_display(void *opaque)
 {
-    VirtIOVGA *vvga = opaque;
-    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
+    VirtIOVGABase *vvga = opaque;
+    VirtIOGPUBase *g = vvga->vgpu;
 
     if (g->enable) {
-        virtio_gpu_ops.gfx_update(&vvga->vdev);
+        virtio_gpu_ops.gfx_update(g);
     } else {
         vvga->vga.hw_ops->gfx_update(&vvga->vga);
     }
 }
 
-static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
+static void virtio_vga_base_text_update(void *opaque, console_ch_t *chardata)
 {
-    VirtIOVGA *vvga = opaque;
-    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
+    VirtIOVGABase *vvga = opaque;
+    VirtIOGPUBase *g = vvga->vgpu;
 
     if (g->enable) {
         if (virtio_gpu_ops.text_update) {
-            virtio_gpu_ops.text_update(&vvga->vdev, chardata);
+            virtio_gpu_ops.text_update(g, chardata);
         }
     } else {
         if (vvga->vga.hw_ops->text_update) {
@@ -59,49 +44,52 @@ static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
     }
 }
 
-static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
+static int virtio_vga_base_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
 {
-    VirtIOVGA *vvga = opaque;
+    VirtIOVGABase *vvga = opaque;
+    VirtIOGPUBase *g = vvga->vgpu;
 
     if (virtio_gpu_ops.ui_info) {
-        return virtio_gpu_ops.ui_info(&vvga->vdev, idx, info);
+        return virtio_gpu_ops.ui_info(g, idx, info);
     }
     return -1;
 }
 
-static void virtio_vga_gl_block(void *opaque, bool block)
+static void virtio_vga_base_gl_block(void *opaque, bool block)
 {
-    VirtIOVGA *vvga = opaque;
+    VirtIOVGABase *vvga = opaque;
+    VirtIOGPUBase *g = vvga->vgpu;
 
     if (virtio_gpu_ops.gl_block) {
-        virtio_gpu_ops.gl_block(&vvga->vdev, block);
+        virtio_gpu_ops.gl_block(g, block);
     }
 }
 
-static const GraphicHwOps virtio_vga_ops = {
-    .invalidate = virtio_vga_invalidate_display,
-    .gfx_update = virtio_vga_update_display,
-    .text_update = virtio_vga_text_update,
-    .ui_info = virtio_vga_ui_info,
-    .gl_block = virtio_vga_gl_block,
+static const GraphicHwOps virtio_vga_base_ops = {
+    .invalidate = virtio_vga_base_invalidate_display,
+    .gfx_update = virtio_vga_base_update_display,
+    .text_update = virtio_vga_base_text_update,
+    .ui_info = virtio_vga_base_ui_info,
+    .gl_block = virtio_vga_base_gl_block,
 };
 
-static const VMStateDescription vmstate_virtio_vga = {
+static const VMStateDescription vmstate_virtio_vga_base = {
     .name = "virtio-vga",
     .version_id = 2,
     .minimum_version_id = 2,
     .fields = (VMStateField[]) {
         /* no pci stuff here, saving the virtio device will handle that */
-        VMSTATE_STRUCT(vga, VirtIOVGA, 0, vmstate_vga_common, VGACommonState),
+        VMSTATE_STRUCT(vga, VirtIOVGABase, 0,
+                       vmstate_vga_common, VGACommonState),
         VMSTATE_END_OF_LIST()
     }
 };
 
 /* VGA device wrapper around PCI device around virtio GPU */
-static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
 {
-    VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
-    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
+    VirtIOVGABase *vvga = VIRTIO_VGA_BASE(vpci_dev);
+    VirtIOGPUBase *g = vvga->vgpu;
     VGACommonState *vga = &vvga->vga;
     Error *err = NULL;
     uint32_t offset;
@@ -159,7 +147,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
                                  vvga->vga_mrs, true);
 
     vga->con = g->scanout[0].con;
-    graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga);
+    graphic_console_set_hwops(vga->con, &virtio_vga_base_ops, vvga);
 
     for (i = 0; i < g->conf.max_outputs; i++) {
         object_property_set_link(OBJECT(g->scanout[i].con),
@@ -168,56 +156,76 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
     }
 }
 
-static void virtio_vga_reset(DeviceState *dev)
+static void virtio_vga_base_reset(DeviceState *dev)
 {
-    VirtIOVGA *vvga = VIRTIO_VGA(dev);
-    VirtIOGPUBase *g = VIRTIO_GPU_BASE(&vvga->vdev);
+    VirtIOVGABase *vvga = VIRTIO_VGA_BASE(dev);
+    VirtIOGPUBase *g = vvga->vgpu;
 
     g->enable = 0;
 
     vga_dirty_log_start(&vvga->vga);
 }
 
-static Property virtio_vga_properties[] = {
+static Property virtio_vga_base_properties[] = {
     DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-static void virtio_vga_class_init(ObjectClass *klass, void *data)
+static void virtio_vga_base_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
     PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
 
     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
-    dc->props = virtio_vga_properties;
-    dc->reset = virtio_vga_reset;
-    dc->vmsd = &vmstate_virtio_vga;
+    dc->props = virtio_vga_base_properties;
+    dc->reset = virtio_vga_base_reset;
+    dc->vmsd = &vmstate_virtio_vga_base;
     dc->hotpluggable = false;
 
-    k->realize = virtio_vga_realize;
+    k->realize = virtio_vga_base_realize;
     pcidev_k->romfile = "vgabios-virtio.bin";
     pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA;
 }
 
+static TypeInfo virtio_vga_base_info = {
+    .name          = TYPE_VIRTIO_VGA_BASE,
+    .parent        = TYPE_VIRTIO_PCI,
+    .instance_size = sizeof(struct VirtIOVGABase),
+    .class_init    = virtio_vga_base_class_init,
+    .abstract      = true,
+};
+
+#define TYPE_VIRTIO_VGA "virtio-vga"
+
+#define VIRTIO_VGA(obj)                             \
+    OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
+
+typedef struct VirtIOVGA {
+    VirtIOVGABase parent_obj;
+
+    VirtIOGPU     vdev;
+} VirtIOVGA;
+
 static void virtio_vga_inst_initfn(Object *obj)
 {
     VirtIOVGA *dev = VIRTIO_VGA(obj);
 
     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
                                 TYPE_VIRTIO_GPU);
+    VIRTIO_VGA_BASE(dev)->vgpu = VIRTIO_GPU_BASE(&dev->vdev);
 }
 
 static TypeInfo virtio_vga_info = {
     .name          = TYPE_VIRTIO_VGA,
-    .parent        = TYPE_VIRTIO_PCI,
+    .parent        = TYPE_VIRTIO_VGA_BASE,
     .instance_size = sizeof(struct VirtIOVGA),
     .instance_init = virtio_vga_inst_initfn,
-    .class_init    = virtio_vga_class_init,
 };
 
 static void virtio_vga_register_types(void)
 {
+    type_register_static(&virtio_vga_base_info);
     type_register_static(&virtio_vga_info);
 }
 
diff --git a/MAINTAINERS b/MAINTAINERS
index d340928bcd..7b6a18edd4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1368,7 +1368,7 @@ virtio-gpu
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Maintained
 F: hw/display/virtio-gpu*
-F: hw/display/virtio-vga.c
+F: hw/display/virtio-vga.*
 F: include/hw/virtio/virtio-gpu.h
 
 vhost-user-gpu
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* [Qemu-devel] [PATCH v4 29/29] hw/display: add vhost-user-vga & gpu-pci
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (27 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 28/29] virtio-gpu: split virtio-gpu-pci & virtio-vga Marc-André Lureau
@ 2018-07-13 13:09 ` Marc-André Lureau
  2018-08-29  9:13   ` Daniel P. Berrangé
  2018-08-14 23:26 ` [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (2 subsequent siblings)
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-07-13 13:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: airlied, kraxel, Marc-André Lureau

Add new virtio-gpu devices with a "vhost-user" property. Tthe
associated vhost-user backend is used to handle the virtio rings and
provide rendering results thanks to the vhost-user-gpu protocol.

Example usage:
-object vhost-user-backend,id=vug,cmd="./vhost-user-gpu"
-device vhost-user-vga,vhost-user=vug

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/virtio/virtio-pci.h          |   1 +
 include/hw/virtio/virtio-gpu.h  |  13 +
 hw/display/vhost-user-gpu-pci.c |  51 ++++
 hw/display/vhost-user-gpu.c     | 524 ++++++++++++++++++++++++++++++++
 hw/display/vhost-user-vga.c     |  52 ++++
 vl.c                            |   1 +
 hw/display/Makefile.objs        |   3 +
 7 files changed, 645 insertions(+)
 create mode 100644 hw/display/vhost-user-gpu-pci.c
 create mode 100644 hw/display/vhost-user-gpu.c
 create mode 100644 hw/display/vhost-user-vga.c

diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 42240f47b3..ec8eeb2a93 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -56,6 +56,7 @@ typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
 typedef struct VirtIOInputHostPCI VirtIOInputHostPCI;
 typedef struct VHostUserInputPCI VHostUserInputPCI;
 typedef struct VirtIOGPUPCI VirtIOGPUPCI;
+typedef struct VhostUserGPUPCI VhostUserGPUPCI;
 typedef struct VHostVSockPCI VHostVSockPCI;
 typedef struct VirtIOCryptoPCI VirtIOCryptoPCI;
 
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 2edf47e9ab..83e9ee2eaa 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -19,6 +19,7 @@
 #include "ui/console.h"
 #include "hw/virtio/virtio.h"
 #include "qemu/log.h"
+#include "sysemu/vhost-user-backend.h"
 
 #include "standard-headers/linux/virtio_gpu.h"
 
@@ -34,6 +35,8 @@
 #define VIRTIO_GPU(obj)                                        \
         OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU)
 
+#define TYPE_VHOST_USER_GPU "vhost-user-gpu"
+
 #define VIRTIO_ID_GPU 16
 
 struct virtio_gpu_simple_resource {
@@ -151,6 +154,16 @@ typedef struct VirtIOGPU {
     } stats;
 } VirtIOGPU;
 
+typedef struct VhostUserGPU {
+    VirtIOGPUBase parent_obj;
+
+    VhostUserBackend *vhost;
+    int vhost_gpu_fd; /* closed by the chardev */
+    CharBackend vhost_chr;
+    QemuDmaBuf dmabuf[VIRTIO_GPU_MAX_SCANOUTS];
+    bool backend_blocked;
+} VhostUserGPU;
+
 extern const GraphicHwOps virtio_gpu_ops;
 
 /* to share between PCI and VGA */
diff --git a/hw/display/vhost-user-gpu-pci.c b/hw/display/vhost-user-gpu-pci.c
new file mode 100644
index 0000000000..2eef4982a2
--- /dev/null
+++ b/hw/display/vhost-user-gpu-pci.c
@@ -0,0 +1,51 @@
+/*
+ * vhost-user GPU PCI device
+ *
+ * Copyright Red Hat, Inc. 2018
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/virtio/virtio-pci.h"
+
+#define TYPE_VHOST_USER_GPU_PCI "vhost-user-gpu-pci"
+#define VHOST_USER_GPU_PCI(obj)                                     \
+    OBJECT_CHECK(VhostUserGPUPCI, (obj), TYPE_VHOST_USER_GPU_PCI)
+
+struct VhostUserGPUPCI {
+    VirtIOGPUPCIBase parent_obj;
+
+    VhostUserGPU vdev;
+};
+
+static void vhost_user_gpu_pci_initfn(Object *obj)
+{
+    VhostUserGPUPCI *dev = VHOST_USER_GPU_PCI(obj);
+
+    virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+                                TYPE_VHOST_USER_GPU);
+
+    VIRTIO_GPU_PCI_BASE(obj)->vgpu = VIRTIO_GPU_BASE(&dev->vdev);
+
+    object_property_add_alias(obj, "vhost-user",
+                              OBJECT(&dev->vdev), "vhost-user",
+                              &error_abort);
+}
+
+static const TypeInfo vhost_user_gpu_pci_info = {
+    .name = TYPE_VHOST_USER_GPU_PCI,
+    .parent = TYPE_VIRTIO_GPU_PCI_BASE,
+    .instance_size = sizeof(VhostUserGPUPCI),
+    .instance_init = vhost_user_gpu_pci_initfn,
+};
+
+static void vhost_user_gpu_pci_register_types(void)
+{
+    type_register_static(&vhost_user_gpu_pci_info);
+}
+
+type_init(vhost_user_gpu_pci_register_types)
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
new file mode 100644
index 0000000000..3427c151e7
--- /dev/null
+++ b/hw/display/vhost-user-gpu.c
@@ -0,0 +1,524 @@
+/*
+ * vhost-user GPU Device
+ *
+ * Copyright Red Hat, Inc. 2018
+ *
+ * Authors:
+ *     Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/virtio/virtio-gpu.h"
+#include "chardev/char-fe.h"
+#include "qapi/error.h"
+#include "migration/blocker.h"
+
+#define VHOST_USER_GPU(obj)                                    \
+    OBJECT_CHECK(VhostUserGPU, (obj), TYPE_VHOST_USER_GPU)
+
+typedef enum VhostUserGpuRequest {
+    VHOST_USER_GPU_NONE = 0,
+    VHOST_USER_GPU_GET_PROTOCOL_FEATURES,
+    VHOST_USER_GPU_SET_PROTOCOL_FEATURES,
+    VHOST_USER_GPU_GET_DISPLAY_INFO,
+    VHOST_USER_GPU_CURSOR_POS,
+    VHOST_USER_GPU_CURSOR_POS_HIDE,
+    VHOST_USER_GPU_CURSOR_UPDATE,
+    VHOST_USER_GPU_SCANOUT,
+    VHOST_USER_GPU_UPDATE,
+    VHOST_USER_GPU_DMABUF_SCANOUT,
+    VHOST_USER_GPU_DMABUF_UPDATE,
+} VhostUserGpuRequest;
+
+typedef struct VhostUserGpuDisplayInfoReply {
+    struct virtio_gpu_resp_display_info info;
+} VhostUserGpuDisplayInfoReply;
+
+typedef struct VhostUserGpuCursorPos {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+} QEMU_PACKED VhostUserGpuCursorPos;
+
+typedef struct VhostUserGpuCursorUpdate {
+    VhostUserGpuCursorPos pos;
+    uint32_t hot_x;
+    uint32_t hot_y;
+    uint32_t data[64 * 64];
+} QEMU_PACKED VhostUserGpuCursorUpdate;
+
+typedef struct VhostUserGpuScanout {
+    uint32_t scanout_id;
+    uint32_t width;
+    uint32_t height;
+} QEMU_PACKED VhostUserGpuScanout;
+
+typedef struct VhostUserGpuUpdate {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+    uint32_t width;
+    uint32_t height;
+    uint8_t data[];
+} QEMU_PACKED VhostUserGpuUpdate;
+
+typedef struct VhostUserGpuDMABUFScanout {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+    uint32_t width;
+    uint32_t height;
+    uint32_t fd_width;
+    uint32_t fd_height;
+    uint32_t fd_stride;
+    uint32_t fd_flags;
+    int fd_drm_fourcc;
+} QEMU_PACKED VhostUserGpuDMABUFScanout;
+
+typedef struct VhostUserGpuMsg {
+    uint32_t request; /* VhostUserGpuRequest */
+    uint32_t size; /* the following payload size */
+    union {
+        VhostUserGpuCursorPos cursor_pos;
+        VhostUserGpuCursorUpdate cursor_update;
+        VhostUserGpuScanout scanout;
+        VhostUserGpuUpdate update;
+        VhostUserGpuDMABUFScanout dmabuf_scanout;
+        uint64_t u64;
+    } payload;
+} QEMU_PACKED VhostUserGpuMsg;
+
+static VhostUserGpuMsg m __attribute__ ((unused));
+#define VHOST_USER_GPU_HDR_SIZE (sizeof(m.request) + sizeof(m.size))
+
+static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked);
+
+static void
+vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg)
+{
+    VhostUserGpuCursorPos *pos = &msg->payload.cursor_pos;
+    struct virtio_gpu_scanout *s;
+
+    if (pos->scanout_id >= g->parent_obj.conf.max_outputs) {
+        return;
+    }
+    s = &g->parent_obj.scanout[pos->scanout_id];
+
+    if (msg->request == VHOST_USER_GPU_CURSOR_UPDATE) {
+        VhostUserGpuCursorUpdate *up = &msg->payload.cursor_update;
+        if (!s->current_cursor) {
+            s->current_cursor = cursor_alloc(64, 64);
+        }
+
+        s->current_cursor->hot_x = up->hot_x;
+        s->current_cursor->hot_y = up->hot_y;
+
+        memcpy(s->current_cursor->data, up->data,
+               64 * 64 * sizeof(uint32_t));
+
+        dpy_cursor_define(s->con, s->current_cursor);
+    }
+
+    dpy_mouse_set(s->con, pos->x, pos->y,
+                  msg->request != VHOST_USER_GPU_CURSOR_POS_HIDE);
+}
+
+static void
+vhost_user_gpu_unblock(VhostUserGPU *g)
+{
+    uint32_t ok;
+
+    qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)&ok, sizeof(ok));
+
+}
+static void
+vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
+{
+    QemuConsole *con = NULL;
+    struct virtio_gpu_scanout *s;
+
+    switch (msg->request) {
+    case VHOST_USER_GPU_GET_PROTOCOL_FEATURES: {
+        uint64_t u64 = 0;
+        qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)&u64, sizeof(u64));
+        break;
+    }
+    case VHOST_USER_GPU_SET_PROTOCOL_FEATURES: {
+        break;
+    }
+    case VHOST_USER_GPU_GET_DISPLAY_INFO: {
+        struct virtio_gpu_resp_display_info display_info = { 0, };
+        display_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO;
+        virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_info);
+        qemu_chr_fe_write(&g->vhost_chr,
+                          (uint8_t *)&display_info, sizeof(display_info));
+        break;
+    }
+    case VHOST_USER_GPU_SCANOUT: {
+        VhostUserGpuScanout *m = &msg->payload.scanout;
+
+        if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
+            return;
+        }
+
+        g->parent_obj.enable = 1;
+        s = &g->parent_obj.scanout[m->scanout_id];
+        con = s->con;
+
+        if (m->scanout_id == 0 && m->width == 0) {
+            s->ds = qemu_create_message_surface(640, 480,
+                                                "Guest disabled display.");
+        } else {
+            s->ds = qemu_create_displaysurface(m->width, m->height);
+        }
+        if (!s->ds) {
+            return;
+        }
+
+        dpy_gfx_replace_surface(con, s->ds);
+        break;
+    }
+    case VHOST_USER_GPU_DMABUF_SCANOUT: {
+        VhostUserGpuDMABUFScanout *m = &msg->payload.dmabuf_scanout;
+        int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr);
+        QemuDmaBuf *dmabuf;
+
+        if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
+            if (fd >= 0) {
+                close(fd);
+            }
+            break;
+        }
+
+        g->parent_obj.enable = 1;
+        con = g->parent_obj.scanout[m->scanout_id].con;
+        dmabuf = &g->dmabuf[m->scanout_id];
+        if (dmabuf->fd >= 0) {
+            close(dmabuf->fd);
+            dmabuf->fd = -1;
+        }
+        dpy_gl_release_dmabuf(con, dmabuf);
+        if (fd == -1) {
+            dpy_gl_scanout_disable(con);
+            break;
+        }
+        *dmabuf = (QemuDmaBuf) {
+            .fd = fd,
+            .width = m->fd_width,
+            .height = m->fd_height,
+            .stride = m->fd_stride,
+            .fourcc = m->fd_drm_fourcc,
+            .y0_top = m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
+        };
+        dpy_gl_scanout_dmabuf(con, dmabuf);
+        break;
+    }
+    case VHOST_USER_GPU_DMABUF_UPDATE: {
+        VhostUserGpuUpdate *m = &msg->payload.update;
+
+        if (m->scanout_id >= g->parent_obj.conf.max_outputs ||
+            !g->parent_obj.scanout[m->scanout_id].con) {
+            error_report("invalid scanout update: %d", m->scanout_id);
+            vhost_user_gpu_unblock(g);
+            break;
+        }
+
+        con = g->parent_obj.scanout[m->scanout_id].con;
+        dpy_gl_update(con, m->x, m->y, m->width, m->height);
+        g->backend_blocked = true;
+        break;
+    }
+    case VHOST_USER_GPU_UPDATE: {
+        VhostUserGpuUpdate *m = &msg->payload.update;
+
+        if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
+            break;
+        }
+        s = &g->parent_obj.scanout[m->scanout_id];
+        con = s->con;
+        pixman_image_t *image =
+            pixman_image_create_bits(PIXMAN_x8r8g8b8,
+                                     m->width,
+                                     m->height,
+                                     (uint32_t *)m->data,
+                                     m->width * 4);
+
+        pixman_image_composite(PIXMAN_OP_SRC,
+                               image, NULL, s->ds->image,
+                               0, 0, 0, 0, m->x, m->y, m->width, m->height);
+
+        pixman_image_unref(image);
+        dpy_gfx_update(con, m->x, m->y, m->width, m->height);
+        break;
+    }
+    default:
+        g_warning("unhandled message %d %d", msg->request, msg->size);
+    }
+
+    if (con && qemu_console_is_gl_blocked(con)) {
+        vhost_user_gpu_update_blocked(g, true);
+    }
+}
+
+static void
+vhost_user_gpu_chr_read(void *opaque)
+{
+    VhostUserGPU *g = opaque;
+    VhostUserGpuMsg *msg = NULL;
+    VhostUserGpuRequest request;
+    uint32_t size;
+    int r;
+
+    r = qemu_chr_fe_read_all(&g->vhost_chr,
+                             (uint8_t *)&request, sizeof(uint32_t));
+    if (r != sizeof(uint32_t)) {
+        error_report("failed to read msg header: %d, %d", r, errno);
+        goto end;
+    }
+
+    r = qemu_chr_fe_read_all(&g->vhost_chr,
+                             (uint8_t *)&size, sizeof(uint32_t));
+    if (r != sizeof(uint32_t)) {
+        error_report("failed to read msg size");
+        goto end;
+    }
+
+    msg = g_malloc(VHOST_USER_GPU_HDR_SIZE + size);
+    g_return_if_fail(msg != NULL);
+
+    r = qemu_chr_fe_read_all(&g->vhost_chr,
+                             (uint8_t *)&msg->payload, size);
+    if (r != size) {
+        error_report("failed to read msg payload %d != %d", r, size);
+        goto end;
+    }
+
+    msg->request = request;
+    msg->size = size;
+
+    if (request == VHOST_USER_GPU_CURSOR_UPDATE ||
+        request == VHOST_USER_GPU_CURSOR_POS ||
+        request == VHOST_USER_GPU_CURSOR_POS_HIDE) {
+        vhost_user_gpu_handle_cursor(g, msg);
+    } else {
+        vhost_user_gpu_handle_display(g, msg);
+    }
+
+end:
+    g_free(msg);
+}
+
+static void
+vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked)
+{
+    qemu_set_fd_handler(g->vhost_gpu_fd,
+                        blocked ? NULL : vhost_user_gpu_chr_read, NULL, g);
+}
+
+static void
+vhost_user_gpu_gl_unblock(VirtIOGPUBase *b)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(b);
+
+    if (g->backend_blocked) {
+        vhost_user_gpu_unblock(VHOST_USER_GPU(g));
+        g->backend_blocked = false;
+    }
+
+    vhost_user_gpu_update_blocked(VHOST_USER_GPU(g), false);
+}
+
+static bool
+vhost_user_gpu_do_set_socket(VhostUserGPU *g, Error **errp)
+{
+    Chardev *chr;
+    int sv[2];
+
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+        error_setg_errno(errp, errno, "socketpair() failed");
+        return false;
+    }
+
+    chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET));
+    if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) {
+        error_setg(errp, "Failed to make socket chardev");
+        goto err;
+    }
+    if (!qemu_chr_fe_init(&g->vhost_chr, chr, errp)) {
+        goto err;
+    }
+    if (vhost_user_gpu_set_socket(&g->vhost->dev, sv[1]) < 0) {
+        error_setg(errp, "Failed to set vhost-user-gpu socket");
+        qemu_chr_fe_deinit(&g->vhost_chr, false);
+        goto err;
+    }
+
+    g->vhost_gpu_fd = sv[0];
+    vhost_user_gpu_update_blocked(g, false);
+    close(sv[1]);
+    return true;
+
+err:
+    close(sv[0]);
+    close(sv[1]);
+    if (chr) {
+        object_unref(OBJECT(chr));
+    }
+    return false;
+}
+
+static void
+vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(vdev);
+    Error *err = NULL;
+
+    if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) {
+        if (!vhost_user_gpu_do_set_socket(g, &err)) {
+            error_report_err(err);
+            return;
+        }
+        vhost_user_backend_start(g->vhost);
+    } else {
+        /* unblock any wait and stop processing */
+        if (g->vhost_gpu_fd != -1) {
+            vhost_user_gpu_update_blocked(g, true);
+            qemu_chr_fe_deinit(&g->vhost_chr, true);
+            g->vhost_gpu_fd = -1;
+        }
+        vhost_user_backend_stop(g->vhost);
+    }
+}
+
+static bool
+vhost_user_gpu_guest_notifier_pending(VirtIODevice *vdev, int idx)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(vdev);
+
+    return vhost_virtqueue_pending(&g->vhost->dev, idx);
+}
+
+static void
+vhost_user_gpu_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(vdev);
+
+    vhost_virtqueue_mask(&g->vhost->dev, vdev, idx, mask);
+}
+
+static void
+vhost_user_gpu_is_busy(const Object *obj, const char *name,
+                       Object *val, Error **errp)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(obj);
+
+    if (g->vhost) {
+        error_setg(errp, "can't use already busy vhost-user");
+    } else {
+        qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
+    }
+}
+
+static void
+vhost_user_gpu_instance_init(Object *obj)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(obj);
+
+    object_property_add_link(obj, "vhost-user", TYPE_VHOST_USER_BACKEND,
+                             (Object **)&g->vhost,
+                             vhost_user_gpu_is_busy,
+                             OBJ_PROP_LINK_STRONG,
+                             &error_abort);
+}
+
+static void
+vhost_user_gpu_reset(VirtIODevice *vdev)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(vdev);
+
+    virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev));
+
+    vhost_user_backend_stop(g->vhost);
+}
+
+static void
+vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(qdev);
+    VirtIODevice *vdev = VIRTIO_DEVICE(g);
+    uint32_t num_capsets;
+
+    if (!g->vhost) {
+        error_setg(errp, "'vhost-user' property is required");
+        return;
+    }
+
+    if (vhost_user_backend_dev_init(g->vhost, vdev, 2, errp) < 0) {
+        return;
+    }
+
+    if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_VIRGL)) {
+        g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED;
+    }
+
+    if (vhost_user_gpu_get_num_capsets(&g->vhost->dev, &num_capsets) < 0) {
+        error_setg(errp, "Failed to get num-capsets");
+        return;
+    }
+
+    if (!virtio_gpu_base_device_realize(qdev, num_capsets, NULL, NULL, errp)) {
+        return;
+    }
+
+    if (!g->parent_obj.migration_blocker) {
+        error_setg(&g->parent_obj.migration_blocker,
+                   "vhost-user-gpu is not yet migratable");
+        if (migrate_add_blocker(g->parent_obj.migration_blocker, errp) < 0) {
+            return;
+        }
+    }
+
+    g->vhost_gpu_fd = -1;
+}
+
+static Property vhost_user_gpu_properties[] = {
+    VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void
+vhost_user_gpu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+    VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_CLASS(klass);
+
+    vgc->gl_unblock = vhost_user_gpu_gl_unblock;
+
+    vdc->realize = vhost_user_gpu_device_realize;
+    vdc->reset = vhost_user_gpu_reset;
+    vdc->set_status   = vhost_user_gpu_set_status;
+    vdc->guest_notifier_mask = vhost_user_gpu_guest_notifier_mask;
+    vdc->guest_notifier_pending = vhost_user_gpu_guest_notifier_pending;
+    vdc->reset = vhost_user_gpu_reset;
+
+    dc->props = vhost_user_gpu_properties;
+}
+
+static const TypeInfo vhost_user_gpu_info = {
+    .name = TYPE_VHOST_USER_GPU,
+    .parent = TYPE_VIRTIO_GPU_BASE,
+    .instance_size = sizeof(VhostUserGPU),
+    .instance_init = vhost_user_gpu_instance_init,
+    .class_init = vhost_user_gpu_class_init,
+};
+
+static void vhost_user_gpu_register_types(void)
+{
+    type_register_static(&vhost_user_gpu_info);
+}
+
+type_init(vhost_user_gpu_register_types)
diff --git a/hw/display/vhost-user-vga.c b/hw/display/vhost-user-vga.c
new file mode 100644
index 0000000000..0dec0c3d71
--- /dev/null
+++ b/hw/display/vhost-user-vga.c
@@ -0,0 +1,52 @@
+/*
+ * vhost-user VGA device
+ *
+ * Copyright Red Hat, Inc. 2018
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "virtio-vga.h"
+
+#define TYPE_VHOST_USER_VGA "vhost-user-vga"
+
+#define VHOST_USER_VGA(obj)                                \
+    OBJECT_CHECK(VhostUserVGA, (obj), TYPE_VHOST_USER_VGA)
+
+typedef struct VhostUserVGA {
+    VirtIOVGABase parent_obj;
+
+    VhostUserGPU vdev;
+} VhostUserVGA;
+
+static void vhost_user_vga_inst_initfn(Object *obj)
+{
+    VhostUserVGA *dev = VHOST_USER_VGA(obj);
+
+    virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+                                TYPE_VHOST_USER_GPU);
+
+    VIRTIO_VGA_BASE(dev)->vgpu = VIRTIO_GPU_BASE(&dev->vdev);
+
+    object_property_add_alias(obj, "vhost-user",
+                              OBJECT(&dev->vdev), "vhost-user",
+                              &error_abort);
+}
+
+static TypeInfo vhost_user_vga_info = {
+    .name          = TYPE_VHOST_USER_VGA,
+    .parent        = TYPE_VIRTIO_VGA_BASE,
+    .instance_size = sizeof(struct VhostUserVGA),
+    .instance_init = vhost_user_vga_inst_initfn,
+};
+
+static void vhost_user_vga_register_types(void)
+{
+    type_register_static(&vhost_user_vga_info);
+}
+
+type_init(vhost_user_vga_register_types)
diff --git a/vl.c b/vl.c
index 8a9d3457ec..9dee1bf20a 100644
--- a/vl.c
+++ b/vl.c
@@ -237,6 +237,7 @@ static struct {
     { .driver = "vmware-svga",          .flag = &default_vga       },
     { .driver = "qxl-vga",              .flag = &default_vga       },
     { .driver = "virtio-vga",           .flag = &default_vga       },
+    { .driver = "vhost-user-vga",       .flag = &default_vga       },
 };
 
 static QemuOptsList qemu_rtc_opts = {
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 5e60015b77..65ce98f767 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -40,8 +40,11 @@ obj-$(CONFIG_VGA) += vga.o
 common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
 
 obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d.o
+obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VHOST_USER)) += vhost-user-gpu.o
 obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) += virtio-gpu-pci.o
+obj-$(call land,$(CONFIG_VHOST_USER),$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI))) += vhost-user-gpu-pci.o
 obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
+obj-$(call land,$(CONFIG_VIRTIO_VGA),$(CONFIG_VHOST_USER)) += vhost-user-vga.o
 virtio-gpu.o-cflags := $(VIRGL_CFLAGS)
 virtio-gpu.o-libs += $(VIRGL_LIBS)
 virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS)
-- 
2.18.0.129.ge3331758f1

^ permalink raw reply related	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (28 preceding siblings ...)
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 29/29] hw/display: add vhost-user-vga & gpu-pci Marc-André Lureau
@ 2018-08-14 23:26 ` Marc-André Lureau
  2018-08-21  7:51   ` Gerd Hoffmann
  2018-08-28 10:49 ` Marc-André Lureau
  2018-08-29  9:50 ` Daniel P. Berrangé
  31 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-08-14 23:26 UTC (permalink / raw)
  To: QEMU; +Cc: Gerd Hoffmann

On Fri, Jul 13, 2018 at 3:12 PM Marc-André Lureau
<marcandre.lureau@redhat.com> wrote:
>
> Hi,
>
> vhost-user allows to drive a virtio device in a seperate
> process. After vhost-user-net, we have seen
> vhost-user-{scsi,blk,crypto} added more recently.
>
> This series, initially proposed 2 years ago
> (https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01905.html)
> contributes with vhost-user-input and vhost-user-gpu.
>
> Additionally, to factor out common code and ease the usage, a
> vhost-user-backend is introduced as an intermediary object between the
> backend and the qemu device.
>
> You may start a vhost-user-gpu with virgl rendering in a separate
> process like this:
>
> $ ./vhost-user-gpu --virgl -s vgpu.sock &
> $ qemu...
>   -chardev socket,id=chr,path=vgpu.sock
>   -object vhost-user-backend,id=vug,chardev=chr
>   -device vhost-user-vga,vhost-user=vug
>
> You may also specify the backend command and the arguments as part of
> vhost-user-backend qemu arguments. For example, to start a
> vhost-user-input backend on input device /dev/input/event19:
>
> -object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event19"
> -device virtio-input-host-pci,vhost-user=vuid
>
> The vhost-user-gpu backend requires virgl from git.
>
> The libvirt support is on-going work:
> https://github.com/elmarco/libvirt/commits/vhost-user-gpu
>
> The GPU benchmarks are encouraging, giving up to x5 performance on
> Unigine Heaven 4.0.
>
> Feedback welcome,

ping

fwiw, there is a related series for libvirt support: "[libvirt] [RFC
PATCH 00/17] Add vhost-user-gpu support"

thanks

>
> v4:
>  - move qemu_write_pidfile() in util, improve it a bit
>  - add --pid and --fd arguments to vhost-user to help with libvirt support
>  - various bug fixes for synchronization, and tearing down
>
> v3: deal with most comments from rfcv2 and various improvements
>  - "vhost-user-backend: allow to specify binary to execute" as seperate
>   patch, not for inclusion since Daniel as concerned about parsing
>   shell strings with glib
>  - use dmabuf to share 2d rendering result (with intel gem only atm)
>  - document the vhost-user-gpu protocol
>  - make vhost-user-gpu-pci and vhost-user-vga seperate devices (instead
>    of adding vhost-user support to existing devices)
>  - allow to specify virgl rendering, and rendernode
>  - let's promote out of RFC status :)
>
> RFCv2: (addressing some of Gerd comments digged in the archives)
>  - rebased, clean ups, various small fixes, update commit messages
>  - teach the vhost-user-backend to take a chardev
>  - add vhost-user-input-pci, instead of adding vhost code to virtio-input-host-pci
>
> Marc-André Lureau (29):
>   chardev: avoid crash if no associated address
>   chardev: remove qemu_chr_fe_read_all() counter
>   chardev: unref if underlying chardev has no parent
>   dmabuf: add y0_top, pass it to spice
>   vhost-user: simplify vhost_user_init/vhost_user_cleanup
>   libvhost-user: exit by default on VHOST_USER_NONE
>   vhost-user: wrap some read/write with retry handling
>   Add vhost-user-backend
>   qio: add qio_channel_command_new_spawn_with_pre_exec()
>   HACK: vhost-user-backend: allow to specify binary to execute
>   vhost-user: split vhost_user_read()
>   vhost-user: add vhost_user_input_get_config()
>   libvhost-user: export vug_source_new()
>   contrib: add vhost-user-input
>   Add vhost-user-input-pci
>   vhost-user: add vhost_user_gpu_set_socket()
>   vhost-user: add vhost_user_gpu_get_num_capsets()
>   virtio: add virtio-gpu bswap helpers header
>   util: promote qemu_egl_rendernode_open() to libqemuutil
>   util: add qemu_write_pidfile()
>   util: use fcntl() for qemu_write_pidfile() locking
>   contrib: add vhost-user-gpu
>   virtio-gpu: remove unused qdev
>   virtio-gpu: remove unused config_size
>   virtio-gpu: block both 2d and 3d rendering
>   virtio-gpu: remove useless 'waiting' field
>   virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
>   virtio-gpu: split virtio-gpu-pci & virtio-vga
>   hw/display: add vhost-user-vga & gpu-pci
>
>  contrib/libvhost-user/libvhost-user-glib.h |    3 +
>  contrib/libvhost-user/libvhost-user.h      |    3 +
>  contrib/vhost-user-gpu/drm.h               |   63 ++
>  contrib/vhost-user-gpu/virgl.h             |   25 +
>  contrib/vhost-user-gpu/vugpu.h             |  168 +++
>  hw/display/virtio-vga.h                    |   22 +
>  hw/virtio/virtio-pci.h                     |   27 +-
>  include/hw/virtio/vhost-backend.h          |    6 +
>  include/hw/virtio/vhost-user-blk.h         |    2 +-
>  include/hw/virtio/vhost-user-scsi.h        |    2 +-
>  include/hw/virtio/vhost-user.h             |    2 +-
>  include/hw/virtio/virtio-gpu-bswap.h       |   61 +
>  include/hw/virtio/virtio-gpu.h             |   90 +-
>  include/hw/virtio/virtio-input.h           |   14 +
>  include/io/channel-command.h               |   18 +
>  include/qemu/drm.h                         |    6 +
>  include/qemu/osdep.h                       |    3 +-
>  include/sysemu/vhost-user-backend.h        |   60 +
>  include/ui/console.h                       |    1 +
>  backends/cryptodev-vhost-user.c            |   18 +-
>  backends/vhost-user.c                      |  320 ++++++
>  chardev/char-fe.c                          |   13 +-
>  chardev/char-socket.c                      |    8 +-
>  contrib/libvhost-user/libvhost-user-glib.c |   15 +-
>  contrib/libvhost-user/libvhost-user.c      |    3 +-
>  contrib/vhost-user-gpu/drm.c               |  190 ++++
>  contrib/vhost-user-gpu/main.c              | 1164 ++++++++++++++++++++
>  contrib/vhost-user-gpu/virgl.c             |  579 ++++++++++
>  contrib/vhost-user-input/main.c            |  379 +++++++
>  hw/block/vhost-user-blk.c                  |   22 +-
>  hw/display/vhost-user-gpu-pci.c            |   51 +
>  hw/display/vhost-user-gpu.c                |  524 +++++++++
>  hw/display/vhost-user-vga.c                |   52 +
>  hw/display/virtio-gpu-3d.c                 |   70 +-
>  hw/display/virtio-gpu-base.c               |  292 +++++
>  hw/display/virtio-gpu-pci.c                |   39 +-
>  hw/display/virtio-gpu.c                    |  368 ++-----
>  hw/display/virtio-vga.c                    |  127 ++-
>  hw/input/vhost-user-input.c                |  110 ++
>  hw/scsi/vhost-user-scsi.c                  |   20 +-
>  hw/virtio/vhost-stub.c                     |    4 +-
>  hw/virtio/vhost-user.c                     |  143 ++-
>  hw/virtio/virtio-pci.c                     |   20 +
>  io/channel-command.c                       |   33 +-
>  net/vhost-user.c                           |   13 +-
>  os-posix.c                                 |   24 -
>  os-win32.c                                 |   25 -
>  qga/main.c                                 |   54 +-
>  scsi/qemu-pr-helper.c                      |   40 +-
>  ui/egl-helpers.c                           |   51 +-
>  ui/spice-display.c                         |    3 +-
>  util/drm.c                                 |   66 ++
>  util/oslib-posix.c                         |   39 +
>  util/oslib-win32.c                         |   27 +
>  vl.c                                       |    8 +-
>  MAINTAINERS                                |   15 +-
>  Makefile                                   |    6 +
>  Makefile.objs                              |    2 +
>  backends/Makefile.objs                     |    3 +-
>  configure                                  |   35 +
>  contrib/vhost-user-gpu/Makefile.objs       |   10 +
>  contrib/vhost-user-input/Makefile.objs     |    1 +
>  docs/interop/vhost-user-gpu.rst            |  236 ++++
>  docs/interop/vhost-user.txt                |   24 +
>  hw/display/Makefile.objs                   |    5 +-
>  hw/input/Makefile.objs                     |    1 +
>  qemu-options.hx                            |   24 +
>  util/Makefile.objs                         |    1 +
>  68 files changed, 5130 insertions(+), 723 deletions(-)
>  create mode 100644 contrib/vhost-user-gpu/drm.h
>  create mode 100644 contrib/vhost-user-gpu/virgl.h
>  create mode 100644 contrib/vhost-user-gpu/vugpu.h
>  create mode 100644 hw/display/virtio-vga.h
>  create mode 100644 include/hw/virtio/virtio-gpu-bswap.h
>  create mode 100644 include/qemu/drm.h
>  create mode 100644 include/sysemu/vhost-user-backend.h
>  create mode 100644 backends/vhost-user.c
>  create mode 100644 contrib/vhost-user-gpu/drm.c
>  create mode 100644 contrib/vhost-user-gpu/main.c
>  create mode 100644 contrib/vhost-user-gpu/virgl.c
>  create mode 100644 contrib/vhost-user-input/main.c
>  create mode 100644 hw/display/vhost-user-gpu-pci.c
>  create mode 100644 hw/display/vhost-user-gpu.c
>  create mode 100644 hw/display/vhost-user-vga.c
>  create mode 100644 hw/display/virtio-gpu-base.c
>  create mode 100644 hw/input/vhost-user-input.c
>  create mode 100644 util/drm.c
>  create mode 100644 contrib/vhost-user-gpu/Makefile.objs
>  create mode 100644 contrib/vhost-user-input/Makefile.objs
>  create mode 100644 docs/interop/vhost-user-gpu.rst
>
> --
> 2.18.0.129.ge3331758f1
>
>


-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 04/29] dmabuf: add y0_top, pass it to spice
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 04/29] dmabuf: add y0_top, pass it to spice Marc-André Lureau
@ 2018-08-21  6:25   ` Gerd Hoffmann
  0 siblings, 0 replies; 58+ messages in thread
From: Gerd Hoffmann @ 2018-08-21  6:25 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied

On Fri, Jul 13, 2018 at 03:08:51PM +0200, Marc-André Lureau wrote:
> Some scanouts during boot are top-down without it.
> 
> y0_top is set from VHOST_USER_GPU_DMABUF_SCANOUT code path in the last
> patch of this series.
> 
> In current QEMU code base, only vfio/display uses dmabuf API. But the
> VFIO query interface doesn't provide or need that detail so far.

Cherry-picked into ui queue.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-08-14 23:26 ` [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
@ 2018-08-21  7:51   ` Gerd Hoffmann
  2018-08-21 10:10     ` Marc-André Lureau
  0 siblings, 1 reply; 58+ messages in thread
From: Gerd Hoffmann @ 2018-08-21  7:51 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU

  Hi,

> > Feedback welcome,
> 
> ping

Cherry-picked two patches for the ui pull request.

What is the plan for the command line handling, specifically the patch
tagged with HACK?

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-08-21  7:51   ` Gerd Hoffmann
@ 2018-08-21 10:10     ` Marc-André Lureau
  2018-08-21 10:13       ` Daniel P. Berrangé
  0 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-08-21 10:10 UTC (permalink / raw)
  To: Gerd Hoffmann, Daniel P. Berrange; +Cc: QEMU

Hi

On Tue, Aug 21, 2018 at 9:51 AM Gerd Hoffmann <kraxel@redhat.com> wrote:
>
>   Hi,
>
> > > Feedback welcome,
> >
> > ping
>
> Cherry-picked two patches for the ui pull request.

thanks

>
> What is the plan for the command line handling, specifically the patch
> tagged with HACK?

The RFC libvirt series doesn't use it.

However, it's very convenient for qemu command line users. Daniel
doesn't want g_shell_parse_argv(), but he would like the -object json:
command line form instead. I don't know what is the status about
supporting that syntax.

You may skip patch 9 & 10 for review & merge. But for easy testing, I
recommend to use them atm :)

thanks



--
Marc-André Lureau

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-08-21 10:10     ` Marc-André Lureau
@ 2018-08-21 10:13       ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-21 10:13 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Gerd Hoffmann, QEMU

On Tue, Aug 21, 2018 at 12:10:46PM +0200, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 21, 2018 at 9:51 AM Gerd Hoffmann <kraxel@redhat.com> wrote:
> >
> >   Hi,
> >
> > > > Feedback welcome,
> > >
> > > ping
> >
> > Cherry-picked two patches for the ui pull request.
> 
> thanks
> 
> >
> > What is the plan for the command line handling, specifically the patch
> > tagged with HACK?
> 
> The RFC libvirt series doesn't use it.
> 
> However, it's very convenient for qemu command line users. Daniel
> doesn't want g_shell_parse_argv(), but he would like the -object json:
> command line form instead. I don't know what is the status about
> supporting that syntax.

IIRC the last suggestion I saw was to not allow custom ARGV at all ?
ie define a standard contract that all scripts must follow, as we
have done with the ifup scripts.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (29 preceding siblings ...)
  2018-08-14 23:26 ` [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
@ 2018-08-28 10:49 ` Marc-André Lureau
  2018-08-29  9:50 ` Daniel P. Berrangé
  31 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-08-28 10:49 UTC (permalink / raw)
  To: QEMU; +Cc: David Airlie, Gerd Hoffmann

Hi

On Fri, Jul 13, 2018 at 3:12 PM Marc-André Lureau
<marcandre.lureau@redhat.com> wrote:
>
> Hi,
>
> vhost-user allows to drive a virtio device in a seperate
> process. After vhost-user-net, we have seen
> vhost-user-{scsi,blk,crypto} added more recently.
>
> This series, initially proposed 2 years ago
> (https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01905.html)
> contributes with vhost-user-input and vhost-user-gpu.
>
> Additionally, to factor out common code and ease the usage, a
> vhost-user-backend is introduced as an intermediary object between the
> backend and the qemu device.
>
> You may start a vhost-user-gpu with virgl rendering in a separate
> process like this:
>
> $ ./vhost-user-gpu --virgl -s vgpu.sock &
> $ qemu...
>   -chardev socket,id=chr,path=vgpu.sock
>   -object vhost-user-backend,id=vug,chardev=chr
>   -device vhost-user-vga,vhost-user=vug
>
> You may also specify the backend command and the arguments as part of
> vhost-user-backend qemu arguments. For example, to start a
> vhost-user-input backend on input device /dev/input/event19:
>
> -object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event19"
> -device virtio-input-host-pci,vhost-user=vuid
>
> The vhost-user-gpu backend requires virgl from git.
>
> The libvirt support is on-going work:
> https://github.com/elmarco/libvirt/commits/vhost-user-gpu
>
> The GPU benchmarks are encouraging, giving up to x5 performance on
> Unigine Heaven 4.0.
>

I rebased the series: (on top of virtio-vga: fix reset)

https://github.com/elmarco/qemu/ vhost-user-gpu

> Feedback welcome,
>
> v4:
>  - move qemu_write_pidfile() in util, improve it a bit
>  - add --pid and --fd arguments to vhost-user to help with libvirt support
>  - various bug fixes for synchronization, and tearing down
>
> v3: deal with most comments from rfcv2 and various improvements
>  - "vhost-user-backend: allow to specify binary to execute" as seperate
>   patch, not for inclusion since Daniel as concerned about parsing
>   shell strings with glib
>  - use dmabuf to share 2d rendering result (with intel gem only atm)
>  - document the vhost-user-gpu protocol
>  - make vhost-user-gpu-pci and vhost-user-vga seperate devices (instead
>    of adding vhost-user support to existing devices)
>  - allow to specify virgl rendering, and rendernode
>  - let's promote out of RFC status :)
>
> RFCv2: (addressing some of Gerd comments digged in the archives)
>  - rebased, clean ups, various small fixes, update commit messages
>  - teach the vhost-user-backend to take a chardev
>  - add vhost-user-input-pci, instead of adding vhost code to virtio-input-host-pci
>
> Marc-André Lureau (29):
>   chardev: avoid crash if no associated address
>   chardev: remove qemu_chr_fe_read_all() counter
>   chardev: unref if underlying chardev has no parent
>   dmabuf: add y0_top, pass it to spice
>   vhost-user: simplify vhost_user_init/vhost_user_cleanup
>   libvhost-user: exit by default on VHOST_USER_NONE
>   vhost-user: wrap some read/write with retry handling
>   Add vhost-user-backend
>   qio: add qio_channel_command_new_spawn_with_pre_exec()
>   HACK: vhost-user-backend: allow to specify binary to execute
>   vhost-user: split vhost_user_read()
>   vhost-user: add vhost_user_input_get_config()
>   libvhost-user: export vug_source_new()
>   contrib: add vhost-user-input
>   Add vhost-user-input-pci
>   vhost-user: add vhost_user_gpu_set_socket()
>   vhost-user: add vhost_user_gpu_get_num_capsets()
>   virtio: add virtio-gpu bswap helpers header
>   util: promote qemu_egl_rendernode_open() to libqemuutil
>   util: add qemu_write_pidfile()
>   util: use fcntl() for qemu_write_pidfile() locking
>   contrib: add vhost-user-gpu
>   virtio-gpu: remove unused qdev
>   virtio-gpu: remove unused config_size
>   virtio-gpu: block both 2d and 3d rendering
>   virtio-gpu: remove useless 'waiting' field
>   virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
>   virtio-gpu: split virtio-gpu-pci & virtio-vga
>   hw/display: add vhost-user-vga & gpu-pci
>
>  contrib/libvhost-user/libvhost-user-glib.h |    3 +
>  contrib/libvhost-user/libvhost-user.h      |    3 +
>  contrib/vhost-user-gpu/drm.h               |   63 ++
>  contrib/vhost-user-gpu/virgl.h             |   25 +
>  contrib/vhost-user-gpu/vugpu.h             |  168 +++
>  hw/display/virtio-vga.h                    |   22 +
>  hw/virtio/virtio-pci.h                     |   27 +-
>  include/hw/virtio/vhost-backend.h          |    6 +
>  include/hw/virtio/vhost-user-blk.h         |    2 +-
>  include/hw/virtio/vhost-user-scsi.h        |    2 +-
>  include/hw/virtio/vhost-user.h             |    2 +-
>  include/hw/virtio/virtio-gpu-bswap.h       |   61 +
>  include/hw/virtio/virtio-gpu.h             |   90 +-
>  include/hw/virtio/virtio-input.h           |   14 +
>  include/io/channel-command.h               |   18 +
>  include/qemu/drm.h                         |    6 +
>  include/qemu/osdep.h                       |    3 +-
>  include/sysemu/vhost-user-backend.h        |   60 +
>  include/ui/console.h                       |    1 +
>  backends/cryptodev-vhost-user.c            |   18 +-
>  backends/vhost-user.c                      |  320 ++++++
>  chardev/char-fe.c                          |   13 +-
>  chardev/char-socket.c                      |    8 +-
>  contrib/libvhost-user/libvhost-user-glib.c |   15 +-
>  contrib/libvhost-user/libvhost-user.c      |    3 +-
>  contrib/vhost-user-gpu/drm.c               |  190 ++++
>  contrib/vhost-user-gpu/main.c              | 1164 ++++++++++++++++++++
>  contrib/vhost-user-gpu/virgl.c             |  579 ++++++++++
>  contrib/vhost-user-input/main.c            |  379 +++++++
>  hw/block/vhost-user-blk.c                  |   22 +-
>  hw/display/vhost-user-gpu-pci.c            |   51 +
>  hw/display/vhost-user-gpu.c                |  524 +++++++++
>  hw/display/vhost-user-vga.c                |   52 +
>  hw/display/virtio-gpu-3d.c                 |   70 +-
>  hw/display/virtio-gpu-base.c               |  292 +++++
>  hw/display/virtio-gpu-pci.c                |   39 +-
>  hw/display/virtio-gpu.c                    |  368 ++-----
>  hw/display/virtio-vga.c                    |  127 ++-
>  hw/input/vhost-user-input.c                |  110 ++
>  hw/scsi/vhost-user-scsi.c                  |   20 +-
>  hw/virtio/vhost-stub.c                     |    4 +-
>  hw/virtio/vhost-user.c                     |  143 ++-
>  hw/virtio/virtio-pci.c                     |   20 +
>  io/channel-command.c                       |   33 +-
>  net/vhost-user.c                           |   13 +-
>  os-posix.c                                 |   24 -
>  os-win32.c                                 |   25 -
>  qga/main.c                                 |   54 +-
>  scsi/qemu-pr-helper.c                      |   40 +-
>  ui/egl-helpers.c                           |   51 +-
>  ui/spice-display.c                         |    3 +-
>  util/drm.c                                 |   66 ++
>  util/oslib-posix.c                         |   39 +
>  util/oslib-win32.c                         |   27 +
>  vl.c                                       |    8 +-
>  MAINTAINERS                                |   15 +-
>  Makefile                                   |    6 +
>  Makefile.objs                              |    2 +
>  backends/Makefile.objs                     |    3 +-
>  configure                                  |   35 +
>  contrib/vhost-user-gpu/Makefile.objs       |   10 +
>  contrib/vhost-user-input/Makefile.objs     |    1 +
>  docs/interop/vhost-user-gpu.rst            |  236 ++++
>  docs/interop/vhost-user.txt                |   24 +
>  hw/display/Makefile.objs                   |    5 +-
>  hw/input/Makefile.objs                     |    1 +
>  qemu-options.hx                            |   24 +
>  util/Makefile.objs                         |    1 +
>  68 files changed, 5130 insertions(+), 723 deletions(-)
>  create mode 100644 contrib/vhost-user-gpu/drm.h
>  create mode 100644 contrib/vhost-user-gpu/virgl.h
>  create mode 100644 contrib/vhost-user-gpu/vugpu.h
>  create mode 100644 hw/display/virtio-vga.h
>  create mode 100644 include/hw/virtio/virtio-gpu-bswap.h
>  create mode 100644 include/qemu/drm.h
>  create mode 100644 include/sysemu/vhost-user-backend.h
>  create mode 100644 backends/vhost-user.c
>  create mode 100644 contrib/vhost-user-gpu/drm.c
>  create mode 100644 contrib/vhost-user-gpu/main.c
>  create mode 100644 contrib/vhost-user-gpu/virgl.c
>  create mode 100644 contrib/vhost-user-input/main.c
>  create mode 100644 hw/display/vhost-user-gpu-pci.c
>  create mode 100644 hw/display/vhost-user-gpu.c
>  create mode 100644 hw/display/vhost-user-vga.c
>  create mode 100644 hw/display/virtio-gpu-base.c
>  create mode 100644 hw/input/vhost-user-input.c
>  create mode 100644 util/drm.c
>  create mode 100644 contrib/vhost-user-gpu/Makefile.objs
>  create mode 100644 contrib/vhost-user-input/Makefile.objs
>  create mode 100644 docs/interop/vhost-user-gpu.rst
>
> --
> 2.18.0.129.ge3331758f1
>
>


-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 06/29] libvhost-user: exit by default on VHOST_USER_NONE
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 06/29] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
@ 2018-08-28 13:12   ` Jens Freimann
  0 siblings, 0 replies; 58+ messages in thread
From: Jens Freimann @ 2018-08-28 13:12 UTC (permalink / raw)
  To: qemu-devel, marcandre.lureau

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=unknown-8bit; format=flowed, Size: 644 bytes --]

> Since commit 2566378d6d13bf4d28c7770bdbda5f7682594bbe, libvhost-user
> no longer panics on disconnect (rc == 0), and instead silently ignores
> an invalid VHOST_USER_NONE message.
> 
> Without extra work from the API user, this will simply busy-loop on
> HUP events. The obvious thing to do is to exit(0) instead, while
> additional or different work can be done by overriding
> iface->process_msg().
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  contrib/libvhost-user/libvhost-user.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)

Reviewed-by: Jens Freimann <jfreimann@redhat.com> 

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 02/29] chardev: remove qemu_chr_fe_read_all() counter
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 02/29] chardev: remove qemu_chr_fe_read_all() counter Marc-André Lureau
@ 2018-08-28 15:05   ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-28 15:05 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:08:49PM +0200, Marc-André Lureau wrote:
> There is no obvious reason to have a loop counter. This limits from
> reading several megabytes large buffers in one go, since socket
> read/write usually have a limit.

The counter was there since this method's introduction in
7b0bfdf52d694c9a3a96505aa42ce3f8d63acd35, and no obvious
indication of its purpose in that commit.

I can imagine maybe it was a misguided attempt to prevent
QEMU blocking forever if no data was pending, but it does
not in fact achieve that.

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  chardev/char-fe.c | 6 +-----
>  1 file changed, 1 insertion(+), 5 deletions(-)


Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 03/29] chardev: unref if underlying chardev has no parent
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 03/29] chardev: unref if underlying chardev has no parent Marc-André Lureau
@ 2018-08-28 15:06   ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-28 15:06 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:08:50PM +0200, Marc-André Lureau wrote:
> It's possible to write code creating a chardev backend that is not
> registered. When it is not user-created, it makes sense to keep it
> hidden. Let the associated frontend destroy it also in this case.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  chardev/char-fe.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 09/29] qio: add qio_channel_command_new_spawn_with_pre_exec()
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 09/29] qio: add qio_channel_command_new_spawn_with_pre_exec() Marc-André Lureau
@ 2018-08-28 15:09   ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-28 15:09 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:08:56PM +0200, Marc-André Lureau wrote:
> Add a new function to let caller do some tuning thanks to a callback
> before exec().
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/io/channel-command.h | 18 ++++++++++++++++++
>  io/channel-command.c         | 33 ++++++++++++++++++++++++++-------
>  2 files changed, 44 insertions(+), 7 deletions(-)
> 
> diff --git a/include/io/channel-command.h b/include/io/channel-command.h
> index 336d47fa5c..96c833daab 100644
> --- a/include/io/channel-command.h
> +++ b/include/io/channel-command.h
> @@ -71,6 +71,24 @@ qio_channel_command_new_pid(int writefd,
>                              int readfd,
>                              pid_t pid);
>  
> +/**
> + * qio_channel_command_new_spawn_with_pre_exec:
> + * @argv: the NULL terminated list of command arguments
> + * @flags: the I/O mode, one of O_RDONLY, O_WRONLY, O_RDWR
> + * @errp: pointer to a NULL-initialized error object

Missing the new args

> + *
> + * Create a channel for performing I/O with the
> + * command to be spawned with arguments @argv.
> + *
> + * Returns: the command channel object, or NULL on error
> + */
> +QIOChannelCommand *
> +qio_channel_command_new_spawn_with_pre_exec(const char *const argv[],
> +                                            int flags,
> +                                            void (*pre_exec_cb)(void *),
> +                                            void *data,
> +                                            Error **errp);

I have a slight preference for using a typedef for the callback signature,
and providing API docs explaining the contract.

eg the callback should call _exit() if something happens that it can't
handle

> +
>  /**
>   * qio_channel_command_new_spawn:
>   * @argv: the NULL terminated list of command arguments
> diff --git a/io/channel-command.c b/io/channel-command.c
> index 3e7eb17eff..05903ff194 100644
> --- a/io/channel-command.c
> +++ b/io/channel-command.c
> @@ -46,9 +46,12 @@ qio_channel_command_new_pid(int writefd,
>  
>  #ifndef WIN32
>  QIOChannelCommand *
> -qio_channel_command_new_spawn(const char *const argv[],
> -                              int flags,
> -                              Error **errp)
> +qio_channel_command_new_spawn_with_pre_exec(const char *const argv[],
> +                                            int flags,
> +                                            void (*pre_exec_cb)(void *),
> +                                            void *data,
> +                                            Error **errp)
> +
>  {
>      pid_t pid = -1;
>      int stdinfd[2] = { -1, -1 };
> @@ -104,6 +107,10 @@ qio_channel_command_new_spawn(const char *const argv[],
>              close(devnull);
>          }
>  
> +        if (pre_exec_cb) {
> +            pre_exec_cb(data);
> +        }
> +
>          execv(argv[0], (char * const *)argv);
>          _exit(1);
>      }
> @@ -139,12 +146,13 @@ qio_channel_command_new_spawn(const char *const argv[],
>      }
>      return NULL;
>  }
> -
>  #else /* WIN32 */
>  QIOChannelCommand *
> -qio_channel_command_new_spawn(const char *const argv[],
> -                              int flags,
> -                              Error **errp)
> +qio_channel_command_new_spawn_with_pre_exec(const char *const argv[],
> +                                            int flags,
> +                                            void (*pre_exec_cb)(void *),
> +                                            void *data,
> +                                            Error **errp)
>  {
>      error_setg_errno(errp, ENOSYS,
>                       "Command spawn not supported on this platform");
> @@ -152,6 +160,17 @@ qio_channel_command_new_spawn(const char *const argv[],
>  }
>  #endif /* WIN32 */
>  
> +
> +QIOChannelCommand *
> +qio_channel_command_new_spawn(const char *const argv[],
> +                              int flags,
> +                              Error **errp)
> +{
> +    return qio_channel_command_new_spawn_with_pre_exec(argv, flags,
> +                                                       NULL, NULL, errp);
> +}
> +
> +
>  #ifndef WIN32
>  static int qio_channel_command_abort(QIOChannelCommand *ioc,
>                                       Error **errp)
> -- 
> 2.18.0.129.ge3331758f1
> 
> 

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 10/29] HACK: vhost-user-backend: allow to specify binary to execute
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 10/29] HACK: vhost-user-backend: allow to specify binary to execute Marc-André Lureau
@ 2018-08-28 15:44   ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-28 15:44 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:08:57PM +0200, Marc-André Lureau wrote:
> An executable with its arguments may be given as 'cmd' property, ex:
> -object vhost-user-backend,id=vui,cmd="./vhost-user-input
> /dev/input..". The executable is then spawn and, by convention, the
> vhost-user socket is passed as fd=3. It may be considered a security
> breach to allow creating processes that may execute arbitrary
> executables, so this may be restricted to some known executables (via
> signature etc) or directory.

NB, Libvirt runs all QEMU instances with a seccomp policy that forbids
any use of execve(), so libvirt won't use 'cmd' at all.

> To make the patch more acceptable, the command argument would have to
> be passed via an array (probably via -object json: syntax), instead of
> using g_shell_parse_argv().

I still think we should not allow args to be specified at all. Declare
a stable API contract for this script where QEMU defines what argv and
env will be provided, in the same way we do for TAP ifup scripts. Then
on the CLI we'd only need a binary path.

> diff --git a/backends/vhost-user.c b/backends/vhost-user.c
> index bf39c0751d..32d3ec0e8b 100644
> --- a/backends/vhost-user.c
> +++ b/backends/vhost-user.c
> @@ -136,31 +136,105 @@ vhost_user_backend_stop(VhostUserBackend *b)
>      b->started = false;
>  }
>  
> +static void
> +pre_exec_cb(void *data)
> +{
> +    int *sv = data;
> +    int maxfd = sysconf(_SC_OPEN_MAX);
> +    int fd;
> +
> +    dup2(sv[1], 3);

error checking...

> +    for (fd = 4; fd < maxfd; fd++) {
> +        close(fd);

This shouldn't fail, but to be robust we should check
errors anyway.

> +    }
> +}
> +

> diff --git a/qemu-options.hx b/qemu-options.hx
> index 413b97d5e9..9243a5f8ab 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -4292,16 +4292,20 @@ secondary:
>  If you want to know the detail of above command line, you can read
>  the colo-compare git log.
>  
> -@item -object vhost-user-backend,id=id=@var{id},chardev=@var{chardevid}
> +@item -object vhost-user-backend,id=id=@var{id}[,chardev=@var{chardevid},cmd=@var{cmd}]
>  
>  Create a vhost-user-backend object that holds a connection to a
>  vhost-user backend and can be referenced from virtio/vhost-user
>  devices that support it.
>  
>  The @var{id} parameter is a unique ID that will be used to reference
> -this vhost-user backend from the @option{vhost-user} device. The
> -@var{chardev} parameter is the unique ID of a character device backend
> -that provides the connection to the vhost-user slave process. (Since 3.0)
> +this vhost-user backend from the @option{vhost-user} device.
> +
> +You must specify either @var{chardev} or @var{cmd}. The @var{chardev}
> +parameter is the unique ID of a character device backend that provides
> +the connection to the vhost-user slave process.  The @var{cmd}
> +parameter will simplify handling of the backend, by running the given
> +command and establishing the connection. (Since 3.1)

Should note that 'cmd' is not usable with seccomp policy that blocks
execve.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 11/29] vhost-user: split vhost_user_read()
  2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 11/29] vhost-user: split vhost_user_read() Marc-André Lureau
@ 2018-08-28 15:46   ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-28 15:46 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:08:58PM +0200, Marc-André Lureau wrote:
> Split vhost_user_read(), so only header can be read with
> vhost_user_read_header().
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  hw/virtio/vhost-user.c | 27 +++++++++++++++++++--------
>  1 file changed, 19 insertions(+), 8 deletions(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile()
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile() Marc-André Lureau
@ 2018-08-28 15:52   ` Daniel P. Berrangé
  2018-08-28 16:04     ` Marc-André Lureau
  2018-08-31 10:42   ` Daniel P. Berrangé
  1 sibling, 1 reply; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-28 15:52 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:09:07PM +0200, Marc-André Lureau wrote:
> There are variants of qemu_create_pidfile() in qemu-pr-helper and
> qemu-ga. Let's have a common implementation in libqemuutil.
> 
> The code is based from pr-helper write_pidfile(), but allows the
> caller to deal with error reporting and behaviour.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/qemu/osdep.h  |  3 ++-
>  os-posix.c            | 24 -------------------
>  os-win32.c            | 25 --------------------
>  qga/main.c            | 54 ++++++++-----------------------------------
>  scsi/qemu-pr-helper.c | 40 ++++----------------------------
>  util/oslib-posix.c    | 33 ++++++++++++++++++++++++++
>  util/oslib-win32.c    | 27 ++++++++++++++++++++++
>  vl.c                  |  4 ++--
>  8 files changed, 79 insertions(+), 131 deletions(-)



> diff --git a/util/oslib-posix.c b/util/oslib-posix.c
> index 13b6f8d776..da1d4a3201 100644
> --- a/util/oslib-posix.c
> +++ b/util/oslib-posix.c
> @@ -88,6 +88,39 @@ int qemu_daemon(int nochdir, int noclose)
>      return daemon(nochdir, noclose);
>  }
>  
> +bool qemu_write_pidfile(const char *pidfile, Error **errp)
> +{
> +    int pidfd;
> +    char pidstr[32];
> +
> +    pidfd = qemu_open(pidfile, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
> +    if (pidfd == -1) {
> +        error_setg_errno(errp, errno, "Cannot open pid file");
> +        return false;
> +    }
> +
> +    if (lockf(pidfd, F_TLOCK, 0)) {
> +        error_setg_errno(errp, errno, "Cannot lock pid file");
> +        goto fail;
> +    }
> +    if (ftruncate(pidfd, 0)) {
> +        error_setg_errno(errp, errno, "Failed to truncate pid file");
> +        goto fail;
> +    }
> +
> +    snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
> +    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
> +        error_setg(errp, "Failed to write pid file");
> +        goto fail;
> +    }
> +    return true;
> +
> +fail:
> +    unlink(pidfile);

Danger,  Will Robinson !

We can get to this fail: label if we were unable to lockf() the
pidfile. ie someone else owns the pidfile, and we've now unlinked
the pidfile they own.


> +    close(pidfd);
> +    return false;
> +}
> +
>  void *qemu_oom_check(void *ptr)
>  {
>      if (ptr == NULL) {


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking Marc-André Lureau
@ 2018-08-28 15:59   ` Daniel P. Berrangé
  2018-08-28 16:07     ` Marc-André Lureau
  2018-08-28 23:41     ` Marc-André Lureau
  0 siblings, 2 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-28 15:59 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:09:08PM +0200, Marc-André Lureau wrote:
> According to Daniel Berrange, fcntl() locks have better portable
> semantics than lockf().

Specifically I was referring to this from 'man lockf':

   On Linux, lockf() is just an interface  on  top  of  fcntl(2)  locking.
   Many other systems implement lockf() in this way, but note that POSIX.1
   leaves the relationship between lockf() and fcntl(2) locks unspecified.
   A  portable  application  should  probably  avoid mixing calls to these
   interfaces.

IOW, if its just a shim around fcntl() on many systems, it is clearer
if we just use fcntl() directly, as we then know how fcntl() locks will
behave if they're on a network filesystem like NFS.

> Use an exclusive lock on the first byte with fcntl().
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  util/oslib-posix.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/util/oslib-posix.c b/util/oslib-posix.c
> index da1d4a3201..26b11490b9 100644
> --- a/util/oslib-posix.c
> +++ b/util/oslib-posix.c
> @@ -92,6 +92,11 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp)
>  {
>      int pidfd;
>      char pidstr[32];
> +    struct flock lock = {
> +        .l_type = F_WRLCK,
> +        .l_whence = SEEK_SET,
> +        .l_len = 1,
> +    };

For the same semantics as lockf we should use  len == 0 (ie infinity)


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile()
  2018-08-28 15:52   ` Daniel P. Berrangé
@ 2018-08-28 16:04     ` Marc-André Lureau
  0 siblings, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-08-28 16:04 UTC (permalink / raw)
  To: Daniel P. Berrange, Paolo Bonzini; +Cc: David Airlie, QEMU, Gerd Hoffmann

Hi
On Tue, Aug 28, 2018 at 5:53 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Fri, Jul 13, 2018 at 03:09:07PM +0200, Marc-André Lureau wrote:
> > There are variants of qemu_create_pidfile() in qemu-pr-helper and
> > qemu-ga. Let's have a common implementation in libqemuutil.
> >
> > The code is based from pr-helper write_pidfile(), but allows the
> > caller to deal with error reporting and behaviour.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  include/qemu/osdep.h  |  3 ++-
> >  os-posix.c            | 24 -------------------
> >  os-win32.c            | 25 --------------------
> >  qga/main.c            | 54 ++++++++-----------------------------------
> >  scsi/qemu-pr-helper.c | 40 ++++----------------------------
> >  util/oslib-posix.c    | 33 ++++++++++++++++++++++++++
> >  util/oslib-win32.c    | 27 ++++++++++++++++++++++
> >  vl.c                  |  4 ++--
> >  8 files changed, 79 insertions(+), 131 deletions(-)
>
>
>
> > diff --git a/util/oslib-posix.c b/util/oslib-posix.c
> > index 13b6f8d776..da1d4a3201 100644
> > --- a/util/oslib-posix.c
> > +++ b/util/oslib-posix.c
> > @@ -88,6 +88,39 @@ int qemu_daemon(int nochdir, int noclose)
> >      return daemon(nochdir, noclose);
> >  }
> >
> > +bool qemu_write_pidfile(const char *pidfile, Error **errp)
> > +{
> > +    int pidfd;
> > +    char pidstr[32];
> > +
> > +    pidfd = qemu_open(pidfile, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
> > +    if (pidfd == -1) {
> > +        error_setg_errno(errp, errno, "Cannot open pid file");
> > +        return false;
> > +    }
> > +
> > +    if (lockf(pidfd, F_TLOCK, 0)) {
> > +        error_setg_errno(errp, errno, "Cannot lock pid file");
> > +        goto fail;
> > +    }
> > +    if (ftruncate(pidfd, 0)) {
> > +        error_setg_errno(errp, errno, "Failed to truncate pid file");
> > +        goto fail;
> > +    }
> > +
> > +    snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
> > +    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
> > +        error_setg(errp, "Failed to write pid file");
> > +        goto fail;
> > +    }
> > +    return true;
> > +
> > +fail:
> > +    unlink(pidfile);
>
> Danger,  Will Robinson !
>
> We can get to this fail: label if we were unable to lockf() the
> pidfile. ie someone else owns the pidfile, and we've now unlinked
> the pidfile they own.

The code was based on qemu-pr-helper, so the problem exists there.

So we better follow ga_open_pidfile() version here, and close the fd,
return an error. ok

>
>
> > +    close(pidfd);
> > +    return false;
> > +}
> > +
> >  void *qemu_oom_check(void *ptr)
> >  {
> >      if (ptr == NULL) {
>
>
> Regards,
> Daniel
> --
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
>


-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking
  2018-08-28 15:59   ` Daniel P. Berrangé
@ 2018-08-28 16:07     ` Marc-André Lureau
  2018-08-28 23:41     ` Marc-André Lureau
  1 sibling, 0 replies; 58+ messages in thread
From: Marc-André Lureau @ 2018-08-28 16:07 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: David Airlie, QEMU, Gerd Hoffmann

Hi

On Tue, Aug 28, 2018 at 6:01 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Fri, Jul 13, 2018 at 03:09:08PM +0200, Marc-André Lureau wrote:
> > According to Daniel Berrange, fcntl() locks have better portable
> > semantics than lockf().
>
> Specifically I was referring to this from 'man lockf':
>
>    On Linux, lockf() is just an interface  on  top  of  fcntl(2)  locking.
>    Many other systems implement lockf() in this way, but note that POSIX.1
>    leaves the relationship between lockf() and fcntl(2) locks unspecified.
>    A  portable  application  should  probably  avoid mixing calls to these
>    interfaces.
>
> IOW, if its just a shim around fcntl() on many systems, it is clearer
> if we just use fcntl() directly, as we then know how fcntl() locks will
> behave if they're on a network filesystem like NFS.
>
> > Use an exclusive lock on the first byte with fcntl().
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  util/oslib-posix.c | 8 +++++++-
> >  1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/util/oslib-posix.c b/util/oslib-posix.c
> > index da1d4a3201..26b11490b9 100644
> > --- a/util/oslib-posix.c
> > +++ b/util/oslib-posix.c
> > @@ -92,6 +92,11 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp)
> >  {
> >      int pidfd;
> >      char pidstr[32];
> > +    struct flock lock = {
> > +        .l_type = F_WRLCK,
> > +        .l_whence = SEEK_SET,
> > +        .l_len = 1,
> > +    };
>
> For the same semantics as lockf we should use  len == 0 (ie infinity)
>

ok

>
> Regards,
> Daniel
> --
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
>


-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking
  2018-08-28 15:59   ` Daniel P. Berrangé
  2018-08-28 16:07     ` Marc-André Lureau
@ 2018-08-28 23:41     ` Marc-André Lureau
  2018-08-29  8:12       ` Daniel P. Berrangé
  1 sibling, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-08-28 23:41 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: David Airlie, QEMU, Gerd Hoffmann

Hi

On Tue, Aug 28, 2018 at 6:01 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Fri, Jul 13, 2018 at 03:09:08PM +0200, Marc-André Lureau wrote:
> > According to Daniel Berrange, fcntl() locks have better portable
> > semantics than lockf().
>
> Specifically I was referring to this from 'man lockf':
>
>    On Linux, lockf() is just an interface  on  top  of  fcntl(2)  locking.
>    Many other systems implement lockf() in this way, but note that POSIX.1
>    leaves the relationship between lockf() and fcntl(2) locks unspecified.
>    A  portable  application  should  probably  avoid mixing calls to these
>    interfaces.
>
> IOW, if its just a shim around fcntl() on many systems, it is clearer
> if we just use fcntl() directly, as we then know how fcntl() locks will
> behave if they're on a network filesystem like NFS.
>
> > Use an exclusive lock on the first byte with fcntl().
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  util/oslib-posix.c | 8 +++++++-
> >  1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/util/oslib-posix.c b/util/oslib-posix.c
> > index da1d4a3201..26b11490b9 100644
> > --- a/util/oslib-posix.c
> > +++ b/util/oslib-posix.c
> > @@ -92,6 +92,11 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp)
> >  {
> >      int pidfd;
> >      char pidstr[32];
> > +    struct flock lock = {
> > +        .l_type = F_WRLCK,
> > +        .l_whence = SEEK_SET,
> > +        .l_len = 1,
> > +    };
>
> For the same semantics as lockf we should use  len == 0 (ie infinity)

I went to look at how libvirt implements it. Interestingly, it uses
start=0, len=1, and you added it in:

https://libvirt.org/git/?p=libvirt.git;a=blobdiff;f=src/util/virpidfile.c;h=38cc7e28c619defa81d55a47f596eeb6f9832cfd;hp=c0b12867db09d138f0382d76db098c78722be7c4;hb=e1da464d88cd39794809ec0d82dcdfbede744ad6;hpb=b7e5ca48f8c753a85ae77be3430fef83629d2178

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking
  2018-08-28 23:41     ` Marc-André Lureau
@ 2018-08-29  8:12       ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-29  8:12 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: David Airlie, QEMU, Gerd Hoffmann

On Wed, Aug 29, 2018 at 01:41:14AM +0200, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 28, 2018 at 6:01 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
> >
> > On Fri, Jul 13, 2018 at 03:09:08PM +0200, Marc-André Lureau wrote:
> > > According to Daniel Berrange, fcntl() locks have better portable
> > > semantics than lockf().
> >
> > Specifically I was referring to this from 'man lockf':
> >
> >    On Linux, lockf() is just an interface  on  top  of  fcntl(2)  locking.
> >    Many other systems implement lockf() in this way, but note that POSIX.1
> >    leaves the relationship between lockf() and fcntl(2) locks unspecified.
> >    A  portable  application  should  probably  avoid mixing calls to these
> >    interfaces.
> >
> > IOW, if its just a shim around fcntl() on many systems, it is clearer
> > if we just use fcntl() directly, as we then know how fcntl() locks will
> > behave if they're on a network filesystem like NFS.
> >
> > > Use an exclusive lock on the first byte with fcntl().
> > >
> > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > > ---
> > >  util/oslib-posix.c | 8 +++++++-
> > >  1 file changed, 7 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/util/oslib-posix.c b/util/oslib-posix.c
> > > index da1d4a3201..26b11490b9 100644
> > > --- a/util/oslib-posix.c
> > > +++ b/util/oslib-posix.c
> > > @@ -92,6 +92,11 @@ bool qemu_write_pidfile(const char *pidfile, Error **errp)
> > >  {
> > >      int pidfd;
> > >      char pidstr[32];
> > > +    struct flock lock = {
> > > +        .l_type = F_WRLCK,
> > > +        .l_whence = SEEK_SET,
> > > +        .l_len = 1,
> > > +    };
> >
> > For the same semantics as lockf we should use  len == 0 (ie infinity)
> 
> I went to look at how libvirt implements it. Interestingly, it uses
> start=0, len=1, and you added it in:

libvirt never used lockf though. Both are valid, I was just saying that
to be identical to what lockf did, it would use len == 0.


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 29/29] hw/display: add vhost-user-vga & gpu-pci
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 29/29] hw/display: add vhost-user-vga & gpu-pci Marc-André Lureau
@ 2018-08-29  9:13   ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-29  9:13 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:09:16PM +0200, Marc-André Lureau wrote:
> Add new virtio-gpu devices with a "vhost-user" property. Tthe
> associated vhost-user backend is used to handle the virtio rings and
> provide rendering results thanks to the vhost-user-gpu protocol.
> 
> Example usage:
> -object vhost-user-backend,id=vug,cmd="./vhost-user-gpu"
> -device vhost-user-vga,vhost-user=vug

This is a pretty different approach from what QEMU has done for modelling
in the past. We've always had separation between the frontend device and
the backend provider, with a single frontend device impl being plugged
into an arbitrary backend type. This though is introducing a frontend
device that actually has the name of a backend which is rather bizarre
and quite misleading as it on first sight looks like a new device type.

If we must introduce new device types, at very least the confusion could
be reduced by calling it "virtio-vga-vhost" so it is clear its the same
device type as virtio-vga, likewise 'virtio-gpu-pci-vhost'

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
                   ` (30 preceding siblings ...)
  2018-08-28 10:49 ` Marc-André Lureau
@ 2018-08-29  9:50 ` Daniel P. Berrangé
  2018-08-29 10:22   ` Dr. David Alan Gilbert
  2018-08-29 11:34   ` Marc-André Lureau
  31 siblings, 2 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-29  9:50 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:08:47PM +0200, Marc-André Lureau wrote:
> Hi,
> 
> vhost-user allows to drive a virtio device in a seperate
> process. After vhost-user-net, we have seen
> vhost-user-{scsi,blk,crypto} added more recently.
> 
> This series, initially proposed 2 years ago
> (https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01905.html)
> contributes with vhost-user-input and vhost-user-gpu.
> 
> Additionally, to factor out common code and ease the usage, a
> vhost-user-backend is introduced as an intermediary object between the
> backend and the qemu device.
> 
> You may start a vhost-user-gpu with virgl rendering in a separate
> process like this:
> 
> $ ./vhost-user-gpu --virgl -s vgpu.sock &
> $ qemu...
>   -chardev socket,id=chr,path=vgpu.sock
>   -object vhost-user-backend,id=vug,chardev=chr
>   -device vhost-user-vga,vhost-user=vug
> 
> You may also specify the backend command and the arguments as part of
> vhost-user-backend qemu arguments. For example, to start a
> vhost-user-input backend on input device /dev/input/event19:
> 
> -object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event19"
> -device virtio-input-host-pci,vhost-user=vuid
> 
> The vhost-user-gpu backend requires virgl from git.
> 
> The libvirt support is on-going work:
> https://github.com/elmarco/libvirt/commits/vhost-user-gpu
> 
> The GPU benchmarks are encouraging, giving up to x5 performance on
> Unigine Heaven 4.0.

What is the main driving motivation behind this featureset ? Is it aimed
at providing performance, or security, or allowing arbitrary out of tree
backends, or all three ?

Although we've got a number of vhost-user backends I'm pretty concerned
about the direction this is taking QEMU overall.

Managing QEMU is non-trivial for a number of reasons. We've done a lot of
work to provide standardized modelling of CLI args, guest ABI stability
via association with machine types, migration data stream stability,
QEMU feature capabilities detection and so on.

The move to outsource backends to external binaries is discarding some
or all of these items, rewinding alot of progress we've made in the
managability of QEMU. Each external binary has to now reinvent the
things that are already solved in QEMU, and you can be sure each impl
will reinvent the concepts differently.

I can't help feeling that we are shooting ourselves in the foot here
long term.

We've always rejected the idea of having loadable modules in QEMU, but
as a result we've end up with outsourcing the backend entirely via the
vhost-user framework, so the end result is even more opaque than if we
had loadable modules, and is unable to take advantage of our standardized
modelling frameworks & capabilities :-(

If we just look at performance & security, and ignore 3rd party impls
for a minute, I really don't like that to gain perf + security  we have
to change from:

 $ qemu...
   -device virtio-vga
 
To

 $ ./vhost-user-gpu --virgl -s vgpu.sock &
 $ qemu...
   -chardev socket,id=chr,path=vgpu.sock
   -object vhost-user-backend,id=vug,chardev=chr
   -device vhost-user-vga,vhost-user=vug

Once we have the ability to run the backend via an external process,
to gain performance & security benefits, assuming feature parity is
achieved, I question why anyone would want to continue with the old
in-process approach ? IOW the goal should be that the original args 

 $ qemu...
   -device virtio-vga

should "do the right thing" to launch the external process when you
have upgraded to a new enough QEMU, so that everyone immediately
benefits from the more secure & performant architecture. 

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-08-29  9:50 ` Daniel P. Berrangé
@ 2018-08-29 10:22   ` Dr. David Alan Gilbert
  2018-08-29 10:37     ` Daniel P. Berrangé
  2018-08-29 11:34   ` Marc-André Lureau
  1 sibling, 1 reply; 58+ messages in thread
From: Dr. David Alan Gilbert @ 2018-08-29 10:22 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Marc-André Lureau, airlied, qemu-devel, kraxel

* Daniel P. Berrangé (berrange@redhat.com) wrote:
> On Fri, Jul 13, 2018 at 03:08:47PM +0200, Marc-André Lureau wrote:
> > Hi,
> > 
> > vhost-user allows to drive a virtio device in a seperate
> > process. After vhost-user-net, we have seen
> > vhost-user-{scsi,blk,crypto} added more recently.
> > 
> > This series, initially proposed 2 years ago
> > (https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01905.html)
> > contributes with vhost-user-input and vhost-user-gpu.
> > 
> > Additionally, to factor out common code and ease the usage, a
> > vhost-user-backend is introduced as an intermediary object between the
> > backend and the qemu device.
> > 
> > You may start a vhost-user-gpu with virgl rendering in a separate
> > process like this:
> > 
> > $ ./vhost-user-gpu --virgl -s vgpu.sock &
> > $ qemu...
> >   -chardev socket,id=chr,path=vgpu.sock
> >   -object vhost-user-backend,id=vug,chardev=chr
> >   -device vhost-user-vga,vhost-user=vug
> > 
> > You may also specify the backend command and the arguments as part of
> > vhost-user-backend qemu arguments. For example, to start a
> > vhost-user-input backend on input device /dev/input/event19:
> > 
> > -object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event19"
> > -device virtio-input-host-pci,vhost-user=vuid
> > 
> > The vhost-user-gpu backend requires virgl from git.
> > 
> > The libvirt support is on-going work:
> > https://github.com/elmarco/libvirt/commits/vhost-user-gpu
> > 
> > The GPU benchmarks are encouraging, giving up to x5 performance on
> > Unigine Heaven 4.0.
> 
> What is the main driving motivation behind this featureset ? Is it aimed
> at providing performance, or security, or allowing arbitrary out of tree
> backends, or all three ?
> 
> Although we've got a number of vhost-user backends I'm pretty concerned
> about the direction this is taking QEMU overall.
> 
> Managing QEMU is non-trivial for a number of reasons. We've done a lot of
> work to provide standardized modelling of CLI args, guest ABI stability
> via association with machine types, migration data stream stability,
> QEMU feature capabilities detection and so on.
> 
> The move to outsource backends to external binaries is discarding some
> or all of these items, rewinding alot of progress we've made in the
> managability of QEMU. Each external binary has to now reinvent the
> things that are already solved in QEMU, and you can be sure each impl
> will reinvent the concepts differently.
> 
> I can't help feeling that we are shooting ourselves in the foot here
> long term.
> 
> We've always rejected the idea of having loadable modules in QEMU, but
> as a result we've end up with outsourcing the backend entirely via the
> vhost-user framework, so the end result is even more opaque than if we
> had loadable modules, and is unable to take advantage of our standardized
> modelling frameworks & capabilities :-(
> 
> If we just look at performance & security, and ignore 3rd party impls
> for a minute, I really don't like that to gain perf + security  we have
> to change from:
> 
>  $ qemu...
>    -device virtio-vga
>  
> To
> 
>  $ ./vhost-user-gpu --virgl -s vgpu.sock &
>  $ qemu...
>    -chardev socket,id=chr,path=vgpu.sock
>    -object vhost-user-backend,id=vug,chardev=chr
>    -device vhost-user-vga,vhost-user=vug
> 
> Once we have the ability to run the backend via an external process,
> to gain performance & security benefits, assuming feature parity is
> achieved, I question why anyone would want to continue with the old
> in-process approach ? IOW the goal should be that the original args 
> 
>  $ qemu...
>    -device virtio-vga
> 
> should "do the right thing" to launch the external process when you
> have upgraded to a new enough QEMU, so that everyone immediately
> benefits from the more secure & performant architecture. 

But which external process should it run, under what privilieges
and with sockets placed where?
While it's true it would be good to have a nice simple way, often
the qemu process might not have the privs to run that external process
or know where to put the sockets; that's exactly the type of thing
we're happy to leave to libvirt to wire up.

Dave

> Regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-08-29 10:22   ` Dr. David Alan Gilbert
@ 2018-08-29 10:37     ` Daniel P. Berrangé
  0 siblings, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-29 10:37 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Marc-André Lureau, airlied, qemu-devel, kraxel

On Wed, Aug 29, 2018 at 11:22:01AM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrangé (berrange@redhat.com) wrote:
> > On Fri, Jul 13, 2018 at 03:08:47PM +0200, Marc-André Lureau wrote:
> > > Hi,
> > > 
> > > vhost-user allows to drive a virtio device in a seperate
> > > process. After vhost-user-net, we have seen
> > > vhost-user-{scsi,blk,crypto} added more recently.
> > > 
> > > This series, initially proposed 2 years ago
> > > (https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01905.html)
> > > contributes with vhost-user-input and vhost-user-gpu.
> > > 
> > > Additionally, to factor out common code and ease the usage, a
> > > vhost-user-backend is introduced as an intermediary object between the
> > > backend and the qemu device.
> > > 
> > > You may start a vhost-user-gpu with virgl rendering in a separate
> > > process like this:
> > > 
> > > $ ./vhost-user-gpu --virgl -s vgpu.sock &
> > > $ qemu...
> > >   -chardev socket,id=chr,path=vgpu.sock
> > >   -object vhost-user-backend,id=vug,chardev=chr
> > >   -device vhost-user-vga,vhost-user=vug
> > > 
> > > You may also specify the backend command and the arguments as part of
> > > vhost-user-backend qemu arguments. For example, to start a
> > > vhost-user-input backend on input device /dev/input/event19:
> > > 
> > > -object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event19"
> > > -device virtio-input-host-pci,vhost-user=vuid
> > > 
> > > The vhost-user-gpu backend requires virgl from git.
> > > 
> > > The libvirt support is on-going work:
> > > https://github.com/elmarco/libvirt/commits/vhost-user-gpu
> > > 
> > > The GPU benchmarks are encouraging, giving up to x5 performance on
> > > Unigine Heaven 4.0.
> > 
> > What is the main driving motivation behind this featureset ? Is it aimed
> > at providing performance, or security, or allowing arbitrary out of tree
> > backends, or all three ?
> > 
> > Although we've got a number of vhost-user backends I'm pretty concerned
> > about the direction this is taking QEMU overall.
> > 
> > Managing QEMU is non-trivial for a number of reasons. We've done a lot of
> > work to provide standardized modelling of CLI args, guest ABI stability
> > via association with machine types, migration data stream stability,
> > QEMU feature capabilities detection and so on.
> > 
> > The move to outsource backends to external binaries is discarding some
> > or all of these items, rewinding alot of progress we've made in the
> > managability of QEMU. Each external binary has to now reinvent the
> > things that are already solved in QEMU, and you can be sure each impl
> > will reinvent the concepts differently.
> > 
> > I can't help feeling that we are shooting ourselves in the foot here
> > long term.
> > 
> > We've always rejected the idea of having loadable modules in QEMU, but
> > as a result we've end up with outsourcing the backend entirely via the
> > vhost-user framework, so the end result is even more opaque than if we
> > had loadable modules, and is unable to take advantage of our standardized
> > modelling frameworks & capabilities :-(
> > 
> > If we just look at performance & security, and ignore 3rd party impls
> > for a minute, I really don't like that to gain perf + security  we have
> > to change from:
> > 
> >  $ qemu...
> >    -device virtio-vga
> >  
> > To
> > 
> >  $ ./vhost-user-gpu --virgl -s vgpu.sock &
> >  $ qemu...
> >    -chardev socket,id=chr,path=vgpu.sock
> >    -object vhost-user-backend,id=vug,chardev=chr
> >    -device vhost-user-vga,vhost-user=vug
> > 
> > Once we have the ability to run the backend via an external process,
> > to gain performance & security benefits, assuming feature parity is
> > achieved, I question why anyone would want to continue with the old
> > in-process approach ? IOW the goal should be that the original args 
> > 
> >  $ qemu...
> >    -device virtio-vga
> > 
> > should "do the right thing" to launch the external process when you
> > have upgraded to a new enough QEMU, so that everyone immediately
> > benefits from the more secure & performant architecture. 
> 
> But which external process should it run, under what privilieges
> and with sockets placed where?
> While it's true it would be good to have a nice simple way, often
> the qemu process might not have the privs to run that external process
> or know where to put the sockets; that's exactly the type of thing
> we're happy to leave to libvirt to wire up.

We can still do both - the simple case where QEMU just spawns a process,

  $ qemu...
    -device virtio-vga

while also allowing connections to a pre-spawned process to have extra
privs via:

  $ qemu...
    -device virtio-vga,vhost=chardevID

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-08-29  9:50 ` Daniel P. Berrangé
  2018-08-29 10:22   ` Dr. David Alan Gilbert
@ 2018-08-29 11:34   ` Marc-André Lureau
  2018-09-07 13:11     ` Marc-André Lureau
  1 sibling, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-08-29 11:34 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: qemu-devel, David Airlie, Hoffmann, Gerd

Hi

On Wed, Aug 29, 2018 at 11:50 AM, Daniel P. Berrangé
<berrange@redhat.com> wrote:
> On Fri, Jul 13, 2018 at 03:08:47PM +0200, Marc-André Lureau wrote:
>> Hi,
>>
>> vhost-user allows to drive a virtio device in a seperate
>> process. After vhost-user-net, we have seen
>> vhost-user-{scsi,blk,crypto} added more recently.
>>
>> This series, initially proposed 2 years ago
>> (https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01905.html)
>> contributes with vhost-user-input and vhost-user-gpu.
>>
>> Additionally, to factor out common code and ease the usage, a
>> vhost-user-backend is introduced as an intermediary object between the
>> backend and the qemu device.
>>
>> You may start a vhost-user-gpu with virgl rendering in a separate
>> process like this:
>>
>> $ ./vhost-user-gpu --virgl -s vgpu.sock &
>> $ qemu...
>>   -chardev socket,id=chr,path=vgpu.sock
>>   -object vhost-user-backend,id=vug,chardev=chr
>>   -device vhost-user-vga,vhost-user=vug
>>
>> You may also specify the backend command and the arguments as part of
>> vhost-user-backend qemu arguments. For example, to start a
>> vhost-user-input backend on input device /dev/input/event19:
>>
>> -object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event19"
>> -device virtio-input-host-pci,vhost-user=vuid
>>
>> The vhost-user-gpu backend requires virgl from git.
>>
>> The libvirt support is on-going work:
>> https://github.com/elmarco/libvirt/commits/vhost-user-gpu
>>
>> The GPU benchmarks are encouraging, giving up to x5 performance on
>> Unigine Heaven 4.0.
>
> What is the main driving motivation behind this featureset ? Is it aimed
> at providing performance, or security, or allowing arbitrary out of tree
> backends, or all three ?

Mainly security/stability & performance. Allowing arbitrary out of
tree backends is a bonus for me, I don't care much if we don't allow
it.

> Although we've got a number of vhost-user backends I'm pretty concerned
> about the direction this is taking QEMU overall.
>
> Managing QEMU is non-trivial for a number of reasons. We've done a lot of
> work to provide standardized modelling of CLI args, guest ABI stability
> via association with machine types, migration data stream stability,
> QEMU feature capabilities detection and so on.
>
> The move to outsource backends to external binaries is discarding some
> or all of these items, rewinding alot of progress we've made in the
> managability of QEMU. Each external binary has to now reinvent the
> things that are already solved in QEMU, and you can be sure each impl
> will reinvent the concepts differently.
>
> I can't help feeling that we are shooting ourselves in the foot here
> long term.

I have a bit of the same feeling. For ex, I was a bit reluctant having
the TPM emulator as an external process (new protocol, external
binaries, with various management work, "opaque" migration). But in
the end, the integration with libvirt makes thing quite easy for the
user. So I changed a bit my mind, and I think this is one task that
libvirt can be really good at.

>
> We've always rejected the idea of having loadable modules in QEMU, but
> as a result we've end up with outsourcing the backend entirely via the
> vhost-user framework, so the end result is even more opaque than if we
> had loadable modules, and is unable to take advantage of our standardized
> modelling frameworks & capabilities :-(

I agree, that's one of the reason I put together libvhostuser in the
first place. If qemu ships its own backend, there is no reason we
don't share modelling & code etc.

For ex, I hope more vhost-users will use the -object
vhost-user-backend to do basic connection and virtio setup. (in the
series, -gpu and -input, I didn't really investigate -crypto, -net,
-blk etc)

> If we just look at performance & security, and ignore 3rd party impls
> for a minute, I really don't like that to gain perf + security  we have
> to change from:
>
>  $ qemu...
>    -device virtio-vga
>
> To
>
>  $ ./vhost-user-gpu --virgl -s vgpu.sock &
>  $ qemu...
>    -chardev socket,id=chr,path=vgpu.sock
>    -object vhost-user-backend,id=vug,chardev=chr
>    -device vhost-user-vga,vhost-user=vug
>

That's a bit incovenient for qemu command line users. But who runs
qemu this way but some developers? (others have higher-level scripts /
tools or libvirt)

> Once we have the ability to run the backend via an external process,
> to gain performance & security benefits, assuming feature parity is
> achieved, I question why anyone would want to continue with the old
> in-process approach ? IOW the goal should be that the original args
>
>  $ qemu...
>    -device virtio-vga
>
> should "do the right thing" to launch the external process when you
> have upgraded to a new enough QEMU, so that everyone immediately
> benefits from the more secure & performant architecture.


That's not incompatible with having the lengthy version. But this
comes with some downside atm, since migration is not implemented for
ex (for 2d, virgl doesn't have migration).

And seccomp spawn rule disable forking.

And libvirt will probably want to manage the external process for
security/resource/tweaking reasons.

thanks for the feedback!

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile()
  2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile() Marc-André Lureau
  2018-08-28 15:52   ` Daniel P. Berrangé
@ 2018-08-31 10:42   ` Daniel P. Berrangé
  1 sibling, 0 replies; 58+ messages in thread
From: Daniel P. Berrangé @ 2018-08-31 10:42 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, airlied, kraxel

On Fri, Jul 13, 2018 at 03:09:07PM +0200, Marc-André Lureau wrote:
> There are variants of qemu_create_pidfile() in qemu-pr-helper and
> qemu-ga. Let's have a common implementation in libqemuutil.
> 
> The code is based from pr-helper write_pidfile(), but allows the
> caller to deal with error reporting and behaviour.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

> diff --git a/util/oslib-posix.c b/util/oslib-posix.c
> index 13b6f8d776..da1d4a3201 100644
> --- a/util/oslib-posix.c
> +++ b/util/oslib-posix.c
> @@ -88,6 +88,39 @@ int qemu_daemon(int nochdir, int noclose)
>      return daemon(nochdir, noclose);
>  }
>  
> +bool qemu_write_pidfile(const char *pidfile, Error **errp)
> +{
> +    int pidfd;
> +    char pidstr[32];
> +
> +    pidfd = qemu_open(pidfile, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
> +    if (pidfd == -1) {
> +        error_setg_errno(errp, errno, "Cannot open pid file");
> +        return false;
> +    }
> +
> +    if (lockf(pidfd, F_TLOCK, 0)) {
> +        error_setg_errno(errp, errno, "Cannot lock pid file");
> +        goto fail;
> +    }
> +    if (ftruncate(pidfd, 0)) {
> +        error_setg_errno(errp, errno, "Failed to truncate pid file");
> +        goto fail;
> +    }
> +
> +    snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
> +    if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
> +        error_setg(errp, "Failed to write pid file");
> +        goto fail;
> +    }
> +    return true;
> +
> +fail:
> +    unlink(pidfile);
> +    close(pidfd);
> +    return false;
> +}

Thinking about this again, I think it is not robust enough.

QEMU will leave the pidfile existing on disk when it exits which
initially made me think it avoids the deletion race. The app
managing QEMU, however, may well delete the pidfile after it has
seen QEMU exit, and even if the app locks the pidfile before
deleting it, there is still a race.

eg consider the following sequence

      QEMU 1        libvirtd        QEMU 2

1.    lock(pidfile)

2.    exit()

3.                 open(pidfile)

4.                 lock(pidfile)

5.                                  open(pidfile)

6.                 unlink(pidfile)

7.                 close(pidfile)

8.                                  lock(pidfile)


IOW, at step 8 the new QEMU has successfully acquired the lock, but the
pidfile no longer exists on disk because it was deleted after the original
QEMU exited.

While we could just say no external app should ever delete the pidfile,
I don't think that is satisfactory as people don't read docs, and admins
don't like stale pidfiles being left around on disk.

To make this robust, I think we might want to copy libvirt's approach to
pidfile acquisition which runs in a loop and checks that the file on
disk /after/ acquiring the lock matches the file that was locked. Then
we could in fact safely let QEMU delete its own pidfiles on clean exit.

    while (1) {
        struct stat a, b;
        if ((fd = open(path, O_WRONLY|O_CREAT, 0644)) < 0) {
            return -1;
        }

        if (fstat(fd, &b) < 0) {
            close(fd);
            return -1;
        }

        if (lockf(fd, F_TLOCK, 0) < 0) {
            close(fd);
            return -1;
        }

        /* Now make sure the pidfile we locked is the same
         * one that now exists on the filesystem
         */
        if (stat(path, &a) < 0) {
	    close(fd);
            /* Someone else must be racing with us, so try again */
            continue;
        }
	
        if (a.st_ino == b.st_ino)
            break;

        close(fd);
        /* Someone else must be racing with us, so try again */
    }


    if (ftruncate(fd, 0)) {
        close(fd);
	return -1;
    }

    snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
    if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
        close(fd);
	return -1;
    }

    return fd;


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-08-29 11:34   ` Marc-André Lureau
@ 2018-09-07 13:11     ` Marc-André Lureau
  2018-09-11  8:59       ` Gerd Hoffmann
  0 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-09-07 13:11 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: David Airlie, QEMU, Gerd Hoffmann

 Hi

On Wed, Aug 29, 2018 at 4:00 PM Marc-André Lureau
<marcandre.lureau@redhat.com> wrote:
>
> Hi
>
> On Wed, Aug 29, 2018 at 11:50 AM, Daniel P. Berrangé
> <berrange@redhat.com> wrote:
> > On Fri, Jul 13, 2018 at 03:08:47PM +0200, Marc-André Lureau wrote:
> >> Hi,
> >>
> >> vhost-user allows to drive a virtio device in a seperate
> >> process. After vhost-user-net, we have seen
> >> vhost-user-{scsi,blk,crypto} added more recently.
> >>
> >> This series, initially proposed 2 years ago
> >> (https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01905.html)
> >> contributes with vhost-user-input and vhost-user-gpu.
> >>
> >> Additionally, to factor out common code and ease the usage, a
> >> vhost-user-backend is introduced as an intermediary object between the
> >> backend and the qemu device.
> >>
> >> You may start a vhost-user-gpu with virgl rendering in a separate
> >> process like this:
> >>
> >> $ ./vhost-user-gpu --virgl -s vgpu.sock &
> >> $ qemu...
> >>   -chardev socket,id=chr,path=vgpu.sock
> >>   -object vhost-user-backend,id=vug,chardev=chr
> >>   -device vhost-user-vga,vhost-user=vug
> >>
> >> You may also specify the backend command and the arguments as part of
> >> vhost-user-backend qemu arguments. For example, to start a
> >> vhost-user-input backend on input device /dev/input/event19:
> >>
> >> -object vhost-user-backend,id=vuid,cmd="vhost-user-input /dev/input/event19"
> >> -device virtio-input-host-pci,vhost-user=vuid
> >>
> >> The vhost-user-gpu backend requires virgl from git.
> >>
> >> The libvirt support is on-going work:
> >> https://github.com/elmarco/libvirt/commits/vhost-user-gpu
> >>
> >> The GPU benchmarks are encouraging, giving up to x5 performance on
> >> Unigine Heaven 4.0.
> >
> > What is the main driving motivation behind this featureset ? Is it aimed
> > at providing performance, or security, or allowing arbitrary out of tree
> > backends, or all three ?
>
> Mainly security/stability & performance. Allowing arbitrary out of
> tree backends is a bonus for me, I don't care much if we don't allow
> it.
>
> > Although we've got a number of vhost-user backends I'm pretty concerned
> > about the direction this is taking QEMU overall.
> >
> > Managing QEMU is non-trivial for a number of reasons. We've done a lot of
> > work to provide standardized modelling of CLI args, guest ABI stability
> > via association with machine types, migration data stream stability,
> > QEMU feature capabilities detection and so on.
> >
> > The move to outsource backends to external binaries is discarding some
> > or all of these items, rewinding alot of progress we've made in the
> > managability of QEMU. Each external binary has to now reinvent the
> > things that are already solved in QEMU, and you can be sure each impl
> > will reinvent the concepts differently.
> >
> > I can't help feeling that we are shooting ourselves in the foot here
> > long term.
>
> I have a bit of the same feeling. For ex, I was a bit reluctant having
> the TPM emulator as an external process (new protocol, external
> binaries, with various management work, "opaque" migration). But in
> the end, the integration with libvirt makes thing quite easy for the
> user. So I changed a bit my mind, and I think this is one task that
> libvirt can be really good at.
>
> >
> > We've always rejected the idea of having loadable modules in QEMU, but
> > as a result we've end up with outsourcing the backend entirely via the
> > vhost-user framework, so the end result is even more opaque than if we
> > had loadable modules, and is unable to take advantage of our standardized
> > modelling frameworks & capabilities :-(
>
> I agree, that's one of the reason I put together libvhostuser in the
> first place. If qemu ships its own backend, there is no reason we
> don't share modelling & code etc.
>
> For ex, I hope more vhost-users will use the -object
> vhost-user-backend to do basic connection and virtio setup. (in the
> series, -gpu and -input, I didn't really investigate -crypto, -net,
> -blk etc)
>
> > If we just look at performance & security, and ignore 3rd party impls
> > for a minute, I really don't like that to gain perf + security  we have
> > to change from:
> >
> >  $ qemu...
> >    -device virtio-vga
> >
> > To
> >
> >  $ ./vhost-user-gpu --virgl -s vgpu.sock &
> >  $ qemu...
> >    -chardev socket,id=chr,path=vgpu.sock
> >    -object vhost-user-backend,id=vug,chardev=chr
> >    -device vhost-user-vga,vhost-user=vug
> >
>
> That's a bit incovenient for qemu command line users. But who runs
> qemu this way but some developers? (others have higher-level scripts /
> tools or libvirt)
>
> > Once we have the ability to run the backend via an external process,
> > to gain performance & security benefits, assuming feature parity is
> > achieved, I question why anyone would want to continue with the old
> > in-process approach ? IOW the goal should be that the original args
> >
> >  $ qemu...
> >    -device virtio-vga
> >
> > should "do the right thing" to launch the external process when you
> > have upgraded to a new enough QEMU, so that everyone immediately
> > benefits from the more secure & performant architecture.
>
>
> That's not incompatible with having the lengthy version. But this
> comes with some downside atm, since migration is not implemented for
> ex (for 2d, virgl doesn't have migration).
>
> And seccomp spawn rule disable forking.
>
> And libvirt will probably want to manage the external process for
> security/resource/tweaking reasons.

I would like to make progress on this, but I feel a bit stuck. As
explained earlier, libvirt will have to manage the external processes.
So what we would like to see, is a stable interface for the various
vhost-user backends.

I suggest the vhost-user binary (vhost-user-gpu or vhost-user-input
etc), the default binary be in the system PATH, so that libvirt & co
can find it. Host administrator can use update-alternative or such if
they are other implementations. (other selections mechanisms can be
added later)

I thinks the vhost-user backends could have the following common
options handling:
- take a "--fd=FDNUM" argument, that would indicate which fd the
socket has been passed on.
- OR take a "--socket-path=PATH"
- optionally? take a "--pid=PATH" argument, to write out the process PID

We probably need a way to list the capabilities of the backend.:
--dump-caps, could list known keywords, one per line? (grab, virgl,
opengles, etc...)

Other options may vary based on the backend type, for ex:
- vhost-user-input EVDEV-PATH (required)
- vhost-user-gpu --render-node=PATH (optional)

I am not sure how this should be documented.

Any help appreciated!
thanks

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-09-07 13:11     ` Marc-André Lureau
@ 2018-09-11  8:59       ` Gerd Hoffmann
  2018-09-11  9:16         ` Marc-André Lureau
  0 siblings, 1 reply; 58+ messages in thread
From: Gerd Hoffmann @ 2018-09-11  8:59 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Daniel P. Berrange, David Airlie, QEMU

> > >  $ ./vhost-user-gpu --virgl -s vgpu.sock &
> > >  $ qemu...
> > >    -chardev socket,id=chr,path=vgpu.sock
> > >    -object vhost-user-backend,id=vug,chardev=chr
> > >    -device vhost-user-vga,vhost-user=vug
> >
> > That's a bit incovenient for qemu command line users. But who runs
> > qemu this way but some developers? (others have higher-level scripts /
> > tools or libvirt)

In practice this will *allways* run over a unix socket, right?
So I'm wondering whenever it makes sense to take the chardev
indirection here ...

> I suggest the vhost-user binary (vhost-user-gpu or vhost-user-input
> etc), the default binary be in the system PATH, so that libvirt & co
> can find it. Host administrator can use update-alternative or such if
> they are other implementations. (other selections mechanisms can be
> added later)

Might make sense to place them in /usr/libexec instead.

> I thinks the vhost-user backends could have the following common
> options handling:
> - take a "--fd=FDNUM" argument, that would indicate which fd the
> socket has been passed on.

Which is what libvirt would use, right?

> - OR take a "--socket-path=PATH"

Convinient when not using libvirt, looks ok to me.

> - optionally? take a "--pid=PATH" argument, to write out the process PID

Do we need that?  When libvirt forks off the process it knows the PID
without pidfile, right?

> We probably need a way to list the capabilities of the backend.:
> --dump-caps, could list known keywords, one per line? (grab, virgl,
> opengles, etc...)

Hmm, good question.

I'd tend include more information here.  Such as a description of the
capability.  The command line switch to enable it.  Print as json, we
have support in both qemu and libvirt.  Something like

  [ 'name'   : 'vulkan',
    'help'   : 'enable virgl vulkan support',
    'switch' : '--enable-vulkan' ]

What would libvirt like to consume here?  Would it actually be able to
use whatever it finds there?  Or would libvirt look for specific known
features only, matching them with domain xml attributes?

> Other options may vary based on the backend type, for ex:
> - vhost-user-input EVDEV-PATH (required)

I'd go for "--evdev-path=/dev/input/..." syntax even for mandatory
arguments.

> - vhost-user-gpu --render-node=PATH (optional)
> 
> I am not sure how this should be documented.

docs/specs/ ?

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-09-11  8:59       ` Gerd Hoffmann
@ 2018-09-11  9:16         ` Marc-André Lureau
  2018-09-11 10:44           ` Gerd Hoffmann
  0 siblings, 1 reply; 58+ messages in thread
From: Marc-André Lureau @ 2018-09-11  9:16 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Daniel P. Berrange, David Airlie, QEMU

Hi

On Tue, Sep 11, 2018 at 12:59 PM Gerd Hoffmann <kraxel@redhat.com> wrote:
>
> > > >  $ ./vhost-user-gpu --virgl -s vgpu.sock &
> > > >  $ qemu...
> > > >    -chardev socket,id=chr,path=vgpu.sock
> > > >    -object vhost-user-backend,id=vug,chardev=chr
> > > >    -device vhost-user-vga,vhost-user=vug
> > >
> > > That's a bit incovenient for qemu command line users. But who runs
> > > qemu this way but some developers? (others have higher-level scripts /
> > > tools or libvirt)
>
> In practice this will *allways* run over a unix socket, right?
> So I'm wondering whenever it makes sense to take the chardev
> indirection here ...

The management layer can set things up and pass fd around, avoiding
socket paths etc.

>
> > I suggest the vhost-user binary (vhost-user-gpu or vhost-user-input
> > etc), the default binary be in the system PATH, so that libvirt & co
> > can find it. Host administrator can use update-alternative or such if
> > they are other implementations. (other selections mechanisms can be
> > added later)
>
> Might make sense to place them in /usr/libexec instead.

ok (slightly less convenient for command line users though)

>
> > I thinks the vhost-user backends could have the following common
> > options handling:
> > - take a "--fd=FDNUM" argument, that would indicate which fd the
> > socket has been passed on.
>
> Which is what libvirt would use, right?

yes (see the related libvirt series / branch)

>
> > - OR take a "--socket-path=PATH"
>
> Convinient when not using libvirt, looks ok to me.
>
> > - optionally? take a "--pid=PATH" argument, to write out the process PID
>
> Do we need that?  When libvirt forks off the process it knows the PID
> without pidfile, right?

Yes I am not sure it is strictly necessary (that's why I put
optionally). The helper could fork itself, for various reasons.

>
> > We probably need a way to list the capabilities of the backend.:
> > --dump-caps, could list known keywords, one per line? (grab, virgl,
> > opengles, etc...)
>
> Hmm, good question.
>
> I'd tend include more information here.  Such as a description of the
> capability.  The command line switch to enable it.  Print as json, we
> have support in both qemu and libvirt.  Something like
>
>   [ 'name'   : 'vulkan',
>     'help'   : 'enable virgl vulkan support',
>     'switch' : '--enable-vulkan' ]
>

I wouldn't go so far as describing the command line arguments here. Or
else, we will have to specify the argument type etc.
Instead, I would use a  list of well-known keywords, described in the
specification.

"vulkan" caps would mean that --enable-vulkan (without argument) is
accepted and has an expected behaviour (to handle vulkan commands /
rendering etc)

I don't see a problem putting the list in a json array, but I don't
see much benefit either doing it.

> What would libvirt like to consume here?  Would it actually be able to
> use whatever it finds there?  Or would libvirt look for specific known
> features only, matching them with domain xml attributes?

I am not sure I understand the qestion. libvirt would deal with the
binary capabilities like it deals with qemu capabilities.

It would use the best combination of options given a domain XML and
the capabilities.

If the domain XML requests a feature that the backend is not able to
support, it would provide a sensible error.

Unknown capabilities would be ignored.

>
> > Other options may vary based on the backend type, for ex:
> > - vhost-user-input EVDEV-PATH (required)
>
> I'd go for "--evdev-path=/dev/input/..." syntax even for mandatory
> arguments.

ok

>
> > - vhost-user-gpu --render-node=PATH (optional)
> >
> > I am not sure how this should be documented.
>
> docs/specs/ ?

ok, I suppose I can start some document and include libvir-list for discussion.


-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 58+ messages in thread

* Re: [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU
  2018-09-11  9:16         ` Marc-André Lureau
@ 2018-09-11 10:44           ` Gerd Hoffmann
  0 siblings, 0 replies; 58+ messages in thread
From: Gerd Hoffmann @ 2018-09-11 10:44 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Daniel P. Berrange, David Airlie, QEMU

On Tue, Sep 11, 2018 at 01:16:22PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Sep 11, 2018 at 12:59 PM Gerd Hoffmann <kraxel@redhat.com> wrote:
> >
> > > > >  $ ./vhost-user-gpu --virgl -s vgpu.sock &
> > > > >  $ qemu...
> > > > >    -chardev socket,id=chr,path=vgpu.sock
> > > > >    -object vhost-user-backend,id=vug,chardev=chr
> > > > >    -device vhost-user-vga,vhost-user=vug
> > > >
> > > > That's a bit incovenient for qemu command line users. But who runs
> > > > qemu this way but some developers? (others have higher-level scripts /
> > > > tools or libvirt)
> >
> > In practice this will *allways* run over a unix socket, right?
> > So I'm wondering whenever it makes sense to take the chardev
> > indirection here ...
> 
> The management layer can set things up and pass fd around, avoiding
> socket paths etc.

But that fd will be a socket too ...

> > > - optionally? take a "--pid=PATH" argument, to write out the process PID
> >
> > Do we need that?  When libvirt forks off the process it knows the PID
> > without pidfile, right?
> 
> Yes I am not sure it is strictly necessary (that's why I put
> optionally). The helper could fork itself, for various reasons.

Ok, good point.  But then I'd make this mandatory, and maybe name it
--pidfile.  Shouldn't a big burden as you probably have code for that in
the vhost-user helper lib

> Unknown capabilities would be ignored.

Ok, so when libvirt ignores unknown stuff anyway a list of keywords is
good enough I guess.  And for humans having --help is more useful.

I'd include the protocol into the names, for robustness reasons, i.e.
use "input-grab" or "gpu-vulkan".

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 58+ messages in thread

end of thread, other threads:[~2018-09-11 10:57 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-13 13:08 [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 01/29] chardev: avoid crash if no associated address Marc-André Lureau
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 02/29] chardev: remove qemu_chr_fe_read_all() counter Marc-André Lureau
2018-08-28 15:05   ` Daniel P. Berrangé
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 03/29] chardev: unref if underlying chardev has no parent Marc-André Lureau
2018-08-28 15:06   ` Daniel P. Berrangé
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 04/29] dmabuf: add y0_top, pass it to spice Marc-André Lureau
2018-08-21  6:25   ` Gerd Hoffmann
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 05/29] vhost-user: simplify vhost_user_init/vhost_user_cleanup Marc-André Lureau
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 06/29] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
2018-08-28 13:12   ` Jens Freimann
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 07/29] vhost-user: wrap some read/write with retry handling Marc-André Lureau
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 08/29] Add vhost-user-backend Marc-André Lureau
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 09/29] qio: add qio_channel_command_new_spawn_with_pre_exec() Marc-André Lureau
2018-08-28 15:09   ` Daniel P. Berrangé
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 10/29] HACK: vhost-user-backend: allow to specify binary to execute Marc-André Lureau
2018-08-28 15:44   ` Daniel P. Berrangé
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 11/29] vhost-user: split vhost_user_read() Marc-André Lureau
2018-08-28 15:46   ` Daniel P. Berrangé
2018-07-13 13:08 ` [Qemu-devel] [PATCH v4 12/29] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 13/29] libvhost-user: export vug_source_new() Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 14/29] contrib: add vhost-user-input Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 15/29] Add vhost-user-input-pci Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 16/29] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 17/29] vhost-user: add vhost_user_gpu_get_num_capsets() Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 18/29] virtio: add virtio-gpu bswap helpers header Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 19/29] util: promote qemu_egl_rendernode_open() to libqemuutil Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 20/29] util: add qemu_write_pidfile() Marc-André Lureau
2018-08-28 15:52   ` Daniel P. Berrangé
2018-08-28 16:04     ` Marc-André Lureau
2018-08-31 10:42   ` Daniel P. Berrangé
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 21/29] util: use fcntl() for qemu_write_pidfile() locking Marc-André Lureau
2018-08-28 15:59   ` Daniel P. Berrangé
2018-08-28 16:07     ` Marc-André Lureau
2018-08-28 23:41     ` Marc-André Lureau
2018-08-29  8:12       ` Daniel P. Berrangé
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 22/29] contrib: add vhost-user-gpu Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 23/29] virtio-gpu: remove unused qdev Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 24/29] virtio-gpu: remove unused config_size Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 25/29] virtio-gpu: block both 2d and 3d rendering Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 26/29] virtio-gpu: remove useless 'waiting' field Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 27/29] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 28/29] virtio-gpu: split virtio-gpu-pci & virtio-vga Marc-André Lureau
2018-07-13 13:09 ` [Qemu-devel] [PATCH v4 29/29] hw/display: add vhost-user-vga & gpu-pci Marc-André Lureau
2018-08-29  9:13   ` Daniel P. Berrangé
2018-08-14 23:26 ` [Qemu-devel] [PATCH v4 00/29] vhost-user for input & GPU Marc-André Lureau
2018-08-21  7:51   ` Gerd Hoffmann
2018-08-21 10:10     ` Marc-André Lureau
2018-08-21 10:13       ` Daniel P. Berrangé
2018-08-28 10:49 ` Marc-André Lureau
2018-08-29  9:50 ` Daniel P. Berrangé
2018-08-29 10:22   ` Dr. David Alan Gilbert
2018-08-29 10:37     ` Daniel P. Berrangé
2018-08-29 11:34   ` Marc-André Lureau
2018-09-07 13:11     ` Marc-André Lureau
2018-09-11  8:59       ` Gerd Hoffmann
2018-09-11  9:16         ` Marc-André Lureau
2018-09-11 10:44           ` Gerd Hoffmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.