All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU
@ 2018-06-18 16:17 Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 01/26] chardev: avoid crash if no associated address Marc-André Lureau
                   ` (26 more replies)
  0 siblings, 27 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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.

Feedback welcome,

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 (26):
  chardev: avoid crash if no associated address
  chardev: remove qemu_chr_fe_write_all() counter
  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
  qio: add qio_channel_command_new_spawn_with_pre_exec()
  Add vhost-user-backend
  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
  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             |  167 +++
 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       |   48 +
 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/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                          |    6 +-
 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               |  189 ++++
 contrib/vhost-user-gpu/main.c              | 1100 ++++++++++++++++++++
 contrib/vhost-user-gpu/virgl.c             |  579 +++++++++++
 contrib/vhost-user-input/main.c            |  364 +++++++
 hw/block/vhost-user-blk.c                  |   22 +-
 hw/display/vhost-user-gpu-pci.c            |   51 +
 hw/display/vhost-user-gpu.c                |  488 +++++++++
 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                    |  367 ++-----
 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                     |    2 +-
 hw/virtio/vhost-user.c                     |  143 ++-
 hw/virtio/virtio-pci.c                     |   20 +
 io/channel-command.c                       |   33 +-
 net/vhost-user.c                           |    8 +-
 ui/egl-helpers.c                           |   51 +-
 ui/spice-display.c                         |    3 +-
 util/drm.c                                 |   66 ++
 vl.c                                       |    4 +-
 MAINTAINERS                                |   12 +-
 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.txt            |  236 +++++
 docs/interop/vhost-user.txt                |   15 +
 hw/display/Makefile.objs                   |    5 +-
 hw/input/Makefile.objs                     |    1 +
 util/Makefile.objs                         |    1 +
 60 files changed, 4871 insertions(+), 585 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.txt

-- 
2.18.0.rc1

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

* [Qemu-devel] [PATCH v3 01/26] chardev: avoid crash if no associated address
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-19 12:01   ` Daniel P. Berrangé
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter Marc-André Lureau
                   ` (25 subsequent siblings)
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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>
---
 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 159e69c3b1..2a33079f8e 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -416,8 +416,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.rc1

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

* [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 01/26] chardev: avoid crash if no associated address Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-20 13:53   ` Paolo Bonzini
  2018-06-20 14:03   ` Peter Maydell
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 03/26] dmabuf: add y0_top, pass it to spice Marc-André Lureau
                   ` (24 subsequent siblings)
  26 siblings, 2 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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>
---
 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.rc1

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

* [Qemu-devel] [PATCH v3 03/26] dmabuf: add y0_top, pass it to spice
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 01/26] chardev: avoid crash if no associated address Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-19  6:21   ` Gerd Hoffmann
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup Marc-André Lureau
                   ` (23 subsequent siblings)
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, kraxel, Marc-André Lureau

Some scanouts during boot are top-down without this.

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.rc1

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

* [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (2 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 03/26] dmabuf: add y0_top, pass it to spice Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-21 12:33   ` Tiwei Bie
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 05/26] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
                   ` (22 subsequent siblings)
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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>
---
 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              |  2 +-
 hw/virtio/vhost-user.c              | 16 ++++++++++++----
 net/vhost-user.c                    |  8 ++++----
 9 files changed, 32 insertions(+), 60 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..323dfcc46a 100644
--- a/hw/virtio/vhost-stub.c
+++ b/hw/virtio/vhost-stub.c
@@ -7,7 +7,7 @@ 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;
 }
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..5e5b8f3fc9 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -291,14 +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();
+    user = g_new0(struct VhostUserState, 1);
     if (!user) {
         error_report("failed to init vhost_user");
         goto err;
@@ -312,11 +312,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.rc1

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

* [Qemu-devel] [PATCH v3 05/26] libvhost-user: exit by default on VHOST_USER_NONE
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (3 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 06/26] vhost-user: wrap some read/write with retry handling Marc-André Lureau
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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.rc1

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

* [Qemu-devel] [PATCH v3 06/26] vhost-user: wrap some read/write with retry handling
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (4 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 05/26] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 07/26] qio: add qio_channel_command_new_spawn_with_pre_exec() Marc-André Lureau
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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.rc1

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

* [Qemu-devel] [PATCH v3 07/26] qio: add qio_channel_command_new_spawn_with_pre_exec()
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (5 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 06/26] vhost-user: wrap some read/write with retry handling Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 08/26] Add vhost-user-backend Marc-André Lureau
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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.rc1

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

* [Qemu-devel] [PATCH v3 08/26] Add vhost-user-backend
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (6 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 07/26] qio: add qio_channel_command_new_spawn_with_pre_exec() Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-20 14:31   ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 09/26] HACK: vhost-user-backend: allow to specify binary to execute Marc-André Lureau
                   ` (18 subsequent siblings)
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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               | 257 ++++++++++++++++++++++++++++
 vl.c                                |   3 +-
 backends/Makefile.objs              |   3 +-
 4 files changed, 321 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..db97037306
--- /dev/null
+++ b/backends/vhost-user.c
@@ -0,0 +1,257 @@
+/*
+ * 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
+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;
+
+    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 6e34fb348d..1c21339ba7 100644
--- a/vl.c
+++ b/vl.c
@@ -2718,7 +2718,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/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
-- 
2.18.0.rc1

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

* [Qemu-devel] [PATCH v3 09/26] HACK: vhost-user-backend: allow to specify binary to execute
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (7 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 08/26] Add vhost-user-backend Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-19  6:19   ` Gerd Hoffmann
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 10/26] vhost-user: split vhost_user_read() Marc-André Lureau
                   ` (17 subsequent siblings)
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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 | 83 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 73 insertions(+), 10 deletions(-)

diff --git a/backends/vhost-user.c b/backends/vhost-user.c
index db97037306..32d3ec0e8b 100644
--- a/backends/vhost-user.c
+++ b/backends/vhost-user.c
@@ -154,26 +154,87 @@ 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)
@@ -202,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);
 }
 
@@ -210,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);
-- 
2.18.0.rc1

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

* [Qemu-devel] [PATCH v3 10/26] vhost-user: split vhost_user_read()
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (8 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 09/26] HACK: vhost-user-backend: allow to specify binary to execute Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-19  6:23   ` Gerd Hoffmann
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 11/26] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
                   ` (16 subsequent siblings)
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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.rc1

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

* [Qemu-devel] [PATCH v3 11/26] vhost-user: add vhost_user_input_get_config()
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (9 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 10/26] vhost-user: split vhost_user_read() Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 12/26] libvhost-user: export vug_source_new() Marc-André Lureau
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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>
---
 include/hw/virtio/vhost-backend.h |  4 +++
 hw/virtio/vhost-user.c            | 59 +++++++++++++++++++++++++++++++
 docs/interop/vhost-user.txt       |  7 ++++
 3 files changed, 70 insertions(+)

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..cbd2900f39 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,
     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 d51fd58242..2efa44d124 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: TBD
+      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.rc1

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

* [Qemu-devel] [PATCH v3 12/26] libvhost-user: export vug_source_new()
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (10 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 11/26] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 13/26] contrib: add vhost-user-input Marc-André Lureau
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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.rc1

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

* [Qemu-devel] [PATCH v3 13/26] contrib: add vhost-user-input
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (11 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 12/26] libvhost-user: export vug_source_new() Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 14/26] Add vhost-user-input-pci Marc-André Lureau
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, kraxel, Marc-André Lureau

Add a vhost-user input backend example, 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/libvhost-user/libvhost-user.h  |   1 +
 contrib/vhost-user-input/main.c        | 364 +++++++++++++++++++++++++
 Makefile                               |   3 +
 Makefile.objs                          |   1 +
 configure                              |   3 +
 contrib/vhost-user-input/Makefile.objs |   1 +
 6 files changed, 373 insertions(+)
 create mode 100644 contrib/vhost-user-input/main.c
 create mode 100644 contrib/vhost-user-input/Makefile.objs

diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index 4aa55b4d2d..40d7cbfc2a 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,
     VHOST_USER_MAX
 } VhostUserRequest;
 
diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/main.c
new file mode 100644
index 0000000000..fe12d1ce1f
--- /dev/null
+++ b/contrib/vhost-user-input/main.c
@@ -0,0 +1,364 @@
+/*
+ * 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 <glib.h>
+#include <linux/input.h>
+
+#include "qemu/osdep.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"
+
+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 char *opt_socket_path;
+static gboolean opt_nograb;
+
+static GOptionEntry entries[] = {
+    { "no-grab", 'n', 0, G_OPTION_ARG_NONE, &opt_nograb,
+      "Don't grab device", NULL },
+    { "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;
+
+    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(1);
+    }
+    if (!opt_fname || !opt_fname[0] || opt_fname[1]) {
+        g_printerr("Please specify a single EVDEV filename\n");
+        exit(1);
+    }
+
+    vi.evdevfd = open(opt_fname[0], O_RDWR);
+    if (vi.evdevfd < 0) {
+        g_printerr("Failed to open evdev: %s\n", g_strerror(errno));
+        exit(1);
+    }
+
+    rc = ioctl(vi.evdevfd, EVIOCGVERSION, &ver);
+    if (rc < 0) {
+        g_printerr("%s: is not an evdev device\n", argv[1]);
+        exit(1);
+    }
+
+    if (!opt_nograb) {
+        rc = ioctl(vi.evdevfd, EVIOCGRAB, 1);
+        if (rc < 0) {
+            g_printerr("Failed to grab device\n");
+            exit(1);
+        }
+    }
+
+    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 = 3;
+    }
+    if (fd == -1) {
+        g_printerr("Invalid socket");
+        exit(1);
+    }
+    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/Makefile b/Makefile
index e46f2b625a..1d4d9441b1 100644
--- a/Makefile
+++ b/Makefile
@@ -421,6 +421,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 \
@@ -719,6 +720,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 a8c4094c87..28aef55d06 100755
--- a/configure
+++ b/configure
@@ -5551,6 +5551,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.rc1

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

* [Qemu-devel] [PATCH v3 14/26] Add vhost-user-input-pci
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (12 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 13/26] contrib: add vhost-user-input Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-19  6:31   ` Gerd Hoffmann
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 15/26] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
                   ` (12 subsequent siblings)
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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-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 0fb5f38f9f..773624f095 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1206,6 +1206,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
 
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.rc1

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

* [Qemu-devel] [PATCH v3 15/26] vhost-user: add vhost_user_gpu_set_socket()
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (13 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 14/26] Add vhost-user-input-pci Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 16/26] vhost-user: add vhost_user_gpu_get_num_capsets() Marc-André Lureau
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, kraxel, Marc-André Lureau

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

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 and may not need the same kind of
stability guarantees, thus I chose to have a new dedicated channel.

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.txt       | 236 ++++++++++++++++++++++++++
 docs/interop/vhost-user.txt           |   8 +
 6 files changed, 263 insertions(+)
 create mode 100644 docs/interop/vhost-user-gpu.txt

diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index 40d7cbfc2a..9899bfda9e 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,
+    VHOST_USER_GPU_SET_SOCKET,
     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 cbd2900f39..37864bc06a 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,
+    VHOST_USER_GPU_SET_SOCKET,
     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 773624f095..e5dee15967 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1339,6 +1339,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.txt
+
 Cirrus VGA
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Odd Fixes
diff --git a/docs/interop/vhost-user-gpu.txt b/docs/interop/vhost-user-gpu.txt
new file mode 100644
index 0000000000..309d07fd6d
--- /dev/null
+++ b/docs/interop/vhost-user-gpu.txt
@@ -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 2efa44d124..40ba89675c 100644
--- a/docs/interop/vhost-user.txt
+++ b/docs/interop/vhost-user.txt
@@ -768,6 +768,14 @@ Master message types
 
       Ask vhost user input backend the list of virtio_input_config.
 
+ * VHOST_USER_GPU_SET_SOCKET
+      Id: TBD
+      Master 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.txt for details.
+
 Slave message types
 -------------------
 
-- 
2.18.0.rc1

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

* [Qemu-devel] [PATCH v3 16/26] vhost-user: add vhost_user_gpu_get_num_capsets()
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (14 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 15/26] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 17/26] virtio: add virtio-gpu bswap helpers header Marc-André Lureau
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, kraxel, Marc-André Lureau

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 +++++++++++++++
 3 files changed, 17 insertions(+)

diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index 9899bfda9e..b572071d61 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,
     VHOST_USER_GPU_SET_SOCKET,
+    VHOST_USER_GPU_GET_NUM_CAPSETS,
     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 37864bc06a..8702027269 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,
     VHOST_USER_GPU_SET_SOCKET,
+    VHOST_USER_GPU_GET_NUM_CAPSETS,
     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 > UINT32_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);
-- 
2.18.0.rc1

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

* [Qemu-devel] [PATCH v3 17/26] virtio: add virtio-gpu bswap helpers header
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (15 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 16/26] vhost-user: add vhost_user_gpu_get_num_capsets() Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 18/26] util: promote qemu_egl_rendernode_open() to libqemuutil Marc-André Lureau
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, kraxel, Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu-bswap.h | 48 ++++++++++++++++++++++++++++
 hw/display/virtio-gpu.c              | 43 +------------------------
 2 files changed, 49 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..bc9e895f9c
--- /dev/null
+++ b/include/hw/virtio/virtio-gpu-bswap.h
@@ -0,0 +1,48 @@
+#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 2dd3c3481a..6a55416be7 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -18,6 +18,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"
@@ -30,48 +31,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.rc1

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

* [Qemu-devel] [PATCH v3 18/26] util: promote qemu_egl_rendernode_open() to libqemuutil
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (16 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 17/26] virtio: add virtio-gpu bswap helpers header Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 19/26] contrib: add vhost-user-gpu Marc-André Lureau
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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 16dc3ded36..472a8aa5f0 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"
@@ -146,57 +144,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)
 {
     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 e5dee15967..42a5d46acd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1533,6 +1533,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.rc1

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

* [Qemu-devel] [PATCH v3 19/26] contrib: add vhost-user-gpu
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (17 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 18/26] util: promote qemu_egl_rendernode_open() to libqemuutil Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 20/26] virtio-gpu: remove unused qdev Marc-André Lureau
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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
- stop/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       |  167 ++++
 contrib/vhost-user-gpu/drm.c         |  189 +++++
 contrib/vhost-user-gpu/main.c        | 1100 ++++++++++++++++++++++++++
 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, 2171 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..e691fb6f27
--- /dev/null
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -0,0 +1,167 @@
+/*
+ * 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 "contrib/libvhost-user/libvhost-user-glib.h"
+#include "standard-headers/linux/virtio_gpu.h"
+
+#include "qemu/osdep.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);
+
+void    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..143cdea694
--- /dev/null
+++ b/contrib/vhost-user-gpu/drm.c
@@ -0,0 +1,189 @@
+/*
+ * 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);
+
+    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..0ec76900ee
--- /dev/null
+++ b/contrib/vhost-user-gpu/main.c
@@ -0,0 +1,1100 @@
+/*
+ * 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 <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 char *opt_socket_path;
+static char *opt_render_node;
+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";
+    }
+}
+
+void
+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);
+}
+
+void
+vg_wait_ok(VuGpu *g)
+{
+    uint32_t ok;
+
+    vg_sock_fd_read(g->sock_fd, &ok, sizeof(ok));
+}
+
+static void
+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);
+}
+
+void
+vg_send_msg(VuGpu *vg, const VhostUserGpuMsg *msg, int fd)
+{
+    vg_sock_fd_write(vg->sock_fd, msg, VHOST_USER_GPU_HDR_SIZE + msg->size, fd);
+}
+
+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);
+    vg_sock_fd_read(vg->sock_fd, &dpy_info, sizeof(dpy_info));
+
+    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_resource_destroy(VuGpu *g,
+                    struct virtio_gpu_simple_resource *res)
+{
+    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;
+    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) {
+        scanout = &g->scanout[ss.scanout_id];
+        if (scanout->resource_id) {
+            res = virtio_gpu_find_resource(g, scanout->resource_id);
+            if (res) {
+                res->scanout_bitmask &= ~(1 << ss.scanout_id);
+            }
+        }
+        if (ss.scanout_id == 0) {
+            g_critical("%s: illegal scanout id specified %d",
+                          __func__, ss.scanout_id);
+            cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+            return;
+        }
+        scanout->width = 0;
+        scanout->height = 0;
+        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];
+
+    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);
+    }
+}
+
+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);
+    vg_sock_fd_read(g->sock_fd, &u64, sizeof(u64));
+    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);
+
+    if (g->sock_fd != -1) {
+        close(g->sock_fd);
+        g->sock_fd = -1;
+    }
+
+    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[] = {
+    { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &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;
+    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(1);
+    }
+    g_option_context_free(context);
+
+    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(1);
+    }
+
+    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 = 3;
+    }
+    if (fd == -1) {
+        g_printerr("Invalid socket");
+        exit(1);
+    }
+
+    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 42a5d46acd..5dc312f7de 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1344,6 +1344,8 @@ M: Marc-André Lureau <marcandre.lureau@redhat.com>
 M: Gerd Hoffmann <kraxel@redhat.com>
 S: Maintained
 F: docs/interop/vhost-user-gpu.txt
+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 1d4d9441b1..5c42e8897e 100644
--- a/Makefile
+++ b/Makefile
@@ -422,6 +422,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 \
@@ -722,6 +723,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 28aef55d06..cab6edd35b 100755
--- a/configure
+++ b/configure
@@ -3815,6 +3815,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 libdrm gbm"
   if $pkg_config $opengl_pkgs; then
@@ -5554,6 +5568,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
@@ -6420,6 +6437,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
@@ -7169,6 +7198,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 [ "$fdt" = "git" ]; then
   echo "config-host.h: subdir-dtc" >> $config_host_mak
 fi
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.rc1

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

* [Qemu-devel] [PATCH v3 20/26] virtio-gpu: remove unused qdev
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (18 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 19/26] contrib: add vhost-user-gpu Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 21/26] virtio-gpu: remove unused config_size Marc-André Lureau
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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 d6ba61f2f1..6cb13d2711 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -102,7 +102,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 6a55416be7..7f7e18c2ee 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1192,7 +1192,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.rc1

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

* [Qemu-devel] [PATCH v3 21/26] virtio-gpu: remove unused config_size
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (19 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 20/26] virtio-gpu: remove unused qdev Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 22/26] virtio-gpu: block both 2d and 3d rendering Marc-André Lureau
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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 6cb13d2711..1cf8ec1aa8 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -101,8 +101,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 7f7e18c2ee..cd48a928ca 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1162,10 +1162,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.rc1

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

* [Qemu-devel] [PATCH v3 22/26] virtio-gpu: block both 2d and 3d rendering
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (20 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 21/26] virtio-gpu: remove unused config_size Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 23/26] virtio-gpu: remove useless 'waiting' field Marc-André Lureau
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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 1cf8ec1aa8..fb74fba4a9 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -171,7 +171,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 cd48a928ca..48b3e0ceaa 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -813,12 +813,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++;
@@ -954,6 +957,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.rc1

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

* [Qemu-devel] [PATCH v3 23/26] virtio-gpu: remove useless 'waiting' field
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (21 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 22/26] virtio-gpu: block both 2d and 3d rendering Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 24/26] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base Marc-André Lureau
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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 fb74fba4a9..5b19b0d9d4 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -86,7 +86,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 48b3e0ceaa..37055926d3 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -813,8 +813,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;
         }
 
@@ -863,7 +862,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.rc1

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

* [Qemu-devel] [PATCH v3 24/26] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (22 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 23/26] virtio-gpu: remove useless 'waiting' field Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-19  6:43   ` Gerd Hoffmann
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 25/26] virtio-gpu: split virtio-gpu-pci & virtio-vga Marc-André Lureau
                   ` (2 subsequent siblings)
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, kraxel, Marc-André Lureau

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

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        | 331 ++++++---------------------------
 hw/display/virtio-vga.c        |  15 +-
 hw/display/Makefile.objs       |   2 +-
 7 files changed, 446 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 5b19b0d9d4..4a17000ce4 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -27,6 +27,14 @@
 #define VIRTIO_GPU_CAPSET_VIRGL2 2
 #endif
 
+#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)
@@ -63,7 +71,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,
 };
@@ -73,8 +81,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;
@@ -90,31 +97,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;
 
@@ -125,8 +154,6 @@ typedef struct VirtIOGPU {
         uint32_t req_3d;
         uint32_t bytes_3d;
     } stats;
-
-    Error *migration_blocker;
 } VirtIOGPU;
 
 extern const GraphicHwOps virtio_gpu_ops;
@@ -149,6 +176,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,
@@ -172,4 +209,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 37055926d3..f4f67347e4 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -20,9 +20,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
 
@@ -35,7 +35,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__);                           \
@@ -103,10 +103,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,
@@ -137,50 +137,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)
 {
@@ -229,21 +185,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)
 {
@@ -252,7 +193,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));
 }
@@ -338,7 +279,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,
@@ -484,7 +425,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;
@@ -492,7 +433,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,
@@ -502,7 +443,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);
@@ -533,16 +474,16 @@ 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) {
-        scanout = &g->scanout[ss.scanout_id];
+        scanout = &g->parent_obj.scanout[ss.scanout_id];
         if (scanout->resource_id) {
             res = virtio_gpu_find_resource(g, scanout->resource_id);
             if (res) {
@@ -556,7 +497,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
             cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
             return;
         }
-        dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
+        dpy_gfx_replace_surface(g->parent_obj.scanout[ss.scanout_id].con, NULL);
         scanout->ds = NULL;
         scanout->width = 0;
         scanout->height = 0;
@@ -586,7 +527,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);
@@ -609,7 +550,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);
     }
 
     res->scanout_bitmask |= (1 << ss.scanout_id);
@@ -813,7 +755,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;
         }
 
@@ -822,14 +764,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;
                 }
@@ -841,6 +783,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);
@@ -851,7 +798,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;
     }
@@ -869,7 +816,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
@@ -878,7 +825,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)
@@ -916,71 +863,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,
@@ -1003,10 +888,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()
@@ -1116,8 +1002,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;
         }
@@ -1146,117 +1032,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));
 }
 
 /*
@@ -1287,17 +1110,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,
+    VIRTIO_GPU_BASE_PROPERTIES(VirtIOGPU, parent_obj.conf),
+    DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf_max_hostmem,
                      256 * 1024 * 1024),
 #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(),
 };
 
@@ -1305,27 +1126,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,
 };
 
@@ -1335,26 +1149,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 baa74ba82c..98388762f6 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 b5d97ab26d..d1d2711475 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -36,7 +36,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.rc1

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

* [Qemu-devel] [PATCH v3 25/26] virtio-gpu: split virtio-gpu-pci & virtio-vga
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (23 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 24/26] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-20 16:59   ` Marc-André Lureau
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 26/26] hw/display: add vhost-user-vga & gpu-pci Marc-André Lureau
  2018-06-18 18:50 ` [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU no-reply
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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 98388762f6..0c503460af 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 5dc312f7de..c958fff474 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1336,7 +1336,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.rc1

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

* [Qemu-devel] [PATCH v3 26/26] hw/display: add vhost-user-vga & gpu-pci
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (24 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 25/26] virtio-gpu: split virtio-gpu-pci & virtio-vga Marc-André Lureau
@ 2018-06-18 16:17 ` Marc-André Lureau
  2018-06-19  6:54   ` Gerd Hoffmann
  2018-06-18 18:50 ` [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU no-reply
  26 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-18 16:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: berrange, 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     | 488 ++++++++++++++++++++++++++++++++
 hw/display/vhost-user-vga.c     |  52 ++++
 vl.c                            |   1 +
 hw/display/Makefile.objs        |   3 +
 7 files changed, 609 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 4a17000ce4..c5648d426a 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"
 
@@ -39,6 +40,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 {
@@ -156,6 +159,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..4ba5382b01
--- /dev/null
+++ b/hw/display/vhost-user-gpu.c
@@ -0,0 +1,488 @@
+/*
+ * 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"
+
+#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;
+        }
+        s = &g->parent_obj.scanout[m->scanout_id];
+        con = s->con;
+
+        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;
+        }
+
+        con = g->parent_obj.scanout[m->scanout_id].con;
+        dmabuf = &g->dmabuf[m->scanout_id];
+        if (g->parent_obj.enable && 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 void
+vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val)
+{
+    VhostUserGPU *g = VHOST_USER_GPU(vdev);
+
+    if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) {
+        vhost_user_backend_start(g->vhost);
+    } else {
+        /* TODO: forcefully gl unblock ? */
+        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);
+    Chardev *chr;
+    int sv[2];
+    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 (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+        error_setg_errno(errp, errno, "socketpair() failed");
+        return;
+    }
+
+    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");
+        goto err;
+    }
+
+    g->vhost_gpu_fd = sv[0];
+    vhost_user_gpu_update_blocked(g, false);
+    close(sv[1]);
+    return;
+
+err:
+    close(sv[0]);
+    close(sv[1]);
+    if (chr) {
+        object_unref(OBJECT(chr));
+    }
+}
+
+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 1c21339ba7..3f620f56a7 100644
--- a/vl.c
+++ b/vl.c
@@ -235,6 +235,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 d1d2711475..a98328d692 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -37,8 +37,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.rc1

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

* Re: [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU
  2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
                   ` (25 preceding siblings ...)
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 26/26] hw/display: add vhost-user-vga & gpu-pci Marc-André Lureau
@ 2018-06-18 18:50 ` no-reply
  26 siblings, 0 replies; 52+ messages in thread
From: no-reply @ 2018-06-18 18:50 UTC (permalink / raw)
  To: marcandre.lureau; +Cc: famz, qemu-devel, kraxel

Hi,

This series seems to have some coding style problems. See output below for
more information:

Type: series
Message-id: 20180618161729.334-1-marcandre.lureau@redhat.com
Subject: [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU

=== TEST SCRIPT BEGIN ===
#!/bin/bash

BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0

git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram

commits="$(git log --format=%H --reverse $BASE..)"
for c in $commits; do
    echo "Checking PATCH $n/$total: $(git log -n 1 --format=%s $c)..."
    if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
        failed=1
        echo
    fi
    n=$((n+1))
done

exit $failed
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
Switched to a new branch 'test'
5b9f38a1ca hw/display: add vhost-user-vga & gpu-pci
326428ad0c virtio-gpu: split virtio-gpu-pci & virtio-vga
875a640573 virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
30f670795b virtio-gpu: remove useless 'waiting' field
8b62eaebc8 virtio-gpu: block both 2d and 3d rendering
099eb18894 virtio-gpu: remove unused config_size
c7ebfdac23 virtio-gpu: remove unused qdev
bcbc153a12 contrib: add vhost-user-gpu
287104e286 util: promote qemu_egl_rendernode_open() to libqemuutil
1734608225 virtio: add virtio-gpu bswap helpers header
fe8a420d35 vhost-user: add vhost_user_gpu_get_num_capsets()
106d9b6c82 vhost-user: add vhost_user_gpu_set_socket()
73ac986cce Add vhost-user-input-pci
7b3ced4f9e contrib: add vhost-user-input
6285d76ff7 libvhost-user: export vug_source_new()
901a25da6d vhost-user: add vhost_user_input_get_config()
7d0d590b7f vhost-user: split vhost_user_read()
a9bdc75110 HACK: vhost-user-backend: allow to specify binary to execute
c228612ab8 Add vhost-user-backend
0617a84bf3 qio: add qio_channel_command_new_spawn_with_pre_exec()
07bff83cac vhost-user: wrap some read/write with retry handling
a836d90351 libvhost-user: exit by default on VHOST_USER_NONE
fc54036235 vhost-user: simplify vhost_user_init/vhost_user_cleanup
9438a02db7 dmabuf: add y0_top, pass it to spice
9074228c84 chardev: remove qemu_chr_fe_write_all() counter
58f6db3fb0 chardev: avoid crash if no associated address

=== OUTPUT BEGIN ===
Checking PATCH 1/26: chardev: avoid crash if no associated address...
Checking PATCH 2/26: chardev: remove qemu_chr_fe_write_all() counter...
Checking PATCH 3/26: dmabuf: add y0_top, pass it to spice...
Checking PATCH 4/26: vhost-user: simplify vhost_user_init/vhost_user_cleanup...
Checking PATCH 5/26: libvhost-user: exit by default on VHOST_USER_NONE...
Checking PATCH 6/26: vhost-user: wrap some read/write with retry handling...
Checking PATCH 7/26: qio: add qio_channel_command_new_spawn_with_pre_exec()...
Checking PATCH 8/26: Add vhost-user-backend...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#35: 
new file mode 100644

total: 0 errors, 1 warnings, 335 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 9/26: HACK: vhost-user-backend: allow to specify binary to execute...
Checking PATCH 10/26: vhost-user: split vhost_user_read()...
Checking PATCH 11/26: vhost-user: add vhost_user_input_get_config()...
Checking PATCH 12/26: libvhost-user: export vug_source_new()...
Checking PATCH 13/26: contrib: add vhost-user-input...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#78: 
new file mode 100644

total: 0 errors, 1 warnings, 403 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 14/26: Add vhost-user-input-pci...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#42: 
new file mode 100644

total: 0 errors, 1 warnings, 215 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 15/26: vhost-user: add vhost_user_gpu_set_socket()...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#52: 
new file mode 100644

total: 0 errors, 1 warnings, 298 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 16/26: vhost-user: add vhost_user_gpu_get_num_capsets()...
Checking PATCH 17/26: virtio: add virtio-gpu bswap helpers header...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#74: 
new file mode 100644

total: 0 errors, 1 warnings, 103 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 18/26: util: promote qemu_egl_rendernode_open() to libqemuutil...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#27: 
new file mode 100644

total: 0 errors, 1 warnings, 151 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 19/26: contrib: add vhost-user-gpu...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#133: 
new file mode 100644

total: 0 errors, 1 warnings, 2219 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 20/26: virtio-gpu: remove unused qdev...
Checking PATCH 21/26: virtio-gpu: remove unused config_size...
Checking PATCH 22/26: virtio-gpu: block both 2d and 3d rendering...
Checking PATCH 23/26: virtio-gpu: remove useless 'waiting' field...
Checking PATCH 24/26: virtio-gpu: split virtio-gpu, introduce virtio-gpu-base...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#175: 
new file mode 100644

ERROR: Macros with complex values should be enclosed in parenthesis
#1196: FILE: include/hw/virtio/virtio-gpu.h:124:
+#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)

total: 1 errors, 1 warnings, 1143 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

Checking PATCH 25/26: virtio-gpu: split virtio-gpu-pci & virtio-vga...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#358: 
new file mode 100644

total: 0 errors, 1 warnings, 373 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
Checking PATCH 26/26: hw/display: add vhost-user-vga & gpu-pci...
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#37: 
new file mode 100644

total: 0 errors, 1 warnings, 647 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===

Test command exited with code: 1


---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to patchew-devel@redhat.com

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

* Re: [Qemu-devel] [PATCH v3 09/26] HACK: vhost-user-backend: allow to specify binary to execute
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 09/26] HACK: vhost-user-backend: allow to specify binary to execute Marc-André Lureau
@ 2018-06-19  6:19   ` Gerd Hoffmann
  2018-06-19  9:07     ` Daniel P. Berrangé
  0 siblings, 1 reply; 52+ messages in thread
From: Gerd Hoffmann @ 2018-06-19  6:19 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, berrange

On Mon, Jun 18, 2018 at 06:17:12PM +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.

Hmm, maybe let the device which uses vhost-user-backend handle this?

So you use "-device vhost-user-input-pci,device=/dev/input/$dev" and
vhost-user-input-pci translates that into ...
 
  argv = { "$dir/vhost-user-input", "-device", "/dev/input/$dev", NULL }

... for vhost-user-backend ?

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v3 03/26] dmabuf: add y0_top, pass it to spice
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 03/26] dmabuf: add y0_top, pass it to spice Marc-André Lureau
@ 2018-06-19  6:21   ` Gerd Hoffmann
  2018-06-19  9:05     ` Marc-André Lureau
  2018-06-19 10:21     ` Marc-André Lureau
  0 siblings, 2 replies; 52+ messages in thread
From: Gerd Hoffmann @ 2018-06-19  6:21 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, berrange

On Mon, Jun 18, 2018 at 06:17:06PM +0200, Marc-André Lureau wrote:
> Some scanouts during boot are top-down without this.
> 
> 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 {

Looks incomplete, who sets y0_top?

Also: send the bugfixes (patches 1-6 ?) separate, so they can be merged
already while the input + gpu details are hashed out?

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v3 10/26] vhost-user: split vhost_user_read()
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 10/26] vhost-user: split vhost_user_read() Marc-André Lureau
@ 2018-06-19  6:23   ` Gerd Hoffmann
  2018-06-19  9:01     ` Marc-André Lureau
  0 siblings, 1 reply; 52+ messages in thread
From: Gerd Hoffmann @ 2018-06-19  6:23 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, berrange

> @@ -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;

Hmm?  Looks like a pointless indirection ...

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

... if there is nothing to cleanup here.

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v3 14/26] Add vhost-user-input-pci
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 14/26] Add vhost-user-input-pci Marc-André Lureau
@ 2018-06-19  6:31   ` Gerd Hoffmann
  0 siblings, 0 replies; 52+ messages in thread
From: Gerd Hoffmann @ 2018-06-19  6:31 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, berrange

> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1206,6 +1206,7 @@ L: qemu-s390x@nongnu.org
>  virtio-input
>  M: Gerd Hoffmann <kraxel@redhat.com>

R: Marc-André Lureau <marcandre.lureau@redhat.com>

(or a separate vhost-input-user entry)

>  S: Maintained
> +F: hw/input/vhost-user-input.c

F: contrib/vhost-user-input/*

>  F: hw/input/virtio-input*.c
>  F: include/hw/virtio/virtio-input.h

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v3 24/26] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 24/26] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base Marc-André Lureau
@ 2018-06-19  6:43   ` Gerd Hoffmann
  2018-06-20 16:04     ` Marc-André Lureau
  0 siblings, 1 reply; 52+ messages in thread
From: Gerd Hoffmann @ 2018-06-19  6:43 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, berrange

On Mon, Jun 18, 2018 at 06:17:27PM +0200, Marc-André Lureau wrote:
> Add a base class that is common to virtio-gpu and vhost-user-gpu
> devices.

More verbose commit message please.  What functionality is common and
handled by the new (abstract?) base class?  (same for patch #25).

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v3 26/26] hw/display: add vhost-user-vga & gpu-pci
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 26/26] hw/display: add vhost-user-vga & gpu-pci Marc-André Lureau
@ 2018-06-19  6:54   ` Gerd Hoffmann
  2018-06-19  8:58     ` Marc-André Lureau
  0 siblings, 1 reply; 52+ messages in thread
From: Gerd Hoffmann @ 2018-06-19  6:54 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, berrange

  Hi,

> +    VHOST_USER_GPU_SCANOUT,
> +    VHOST_USER_GPU_UPDATE,
> +    VHOST_USER_GPU_DMABUF_SCANOUT,
> +    VHOST_USER_GPU_DMABUF_UPDATE,

Hmm, still two command versions.

I still think there should be no separate 2d code path.  Just
put the 2d display updates into a texture, or into a drm buffer,
then use VHOST_USER_GPU_DMABUF_* for both 2d and 3d updates (see
also ui/console-gl.c, which does basically the same).

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v3 26/26] hw/display: add vhost-user-vga & gpu-pci
  2018-06-19  6:54   ` Gerd Hoffmann
@ 2018-06-19  8:58     ` Marc-André Lureau
  0 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-19  8:58 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU

Hi

On Tue, Jun 19, 2018 at 8:54 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>   Hi,
>
>> +    VHOST_USER_GPU_SCANOUT,
>> +    VHOST_USER_GPU_UPDATE,
>> +    VHOST_USER_GPU_DMABUF_SCANOUT,
>> +    VHOST_USER_GPU_DMABUF_UPDATE,
>
> Hmm, still two command versions.
>
> I still think there should be no separate 2d code path.  Just
> put the 2d display updates into a texture, or into a drm buffer,
> then use VHOST_USER_GPU_DMABUF_* for both 2d and 3d updates (see
> also ui/console-gl.c, which does basically the same).

It will use DMABUF for both 2d and 3d, unless it's not capable of
doing it (currently only intel gpu). So the regular scanout/update are
fallback. They share a lot of similarities.


-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH v3 10/26] vhost-user: split vhost_user_read()
  2018-06-19  6:23   ` Gerd Hoffmann
@ 2018-06-19  9:01     ` Marc-André Lureau
  2018-06-19 11:20       ` Gerd Hoffmann
  0 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-19  9:01 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU

Hi

On Tue, Jun 19, 2018 at 8:23 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> @@ -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;
>
> Hmm?  Looks like a pointless indirection ...

Isn't it removed by the patch? :)

>
>>      }
>>
>>      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;
>
> ... if there is nothing to cleanup here.
>
> cheers,
>   Gerd
>
>



-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH v3 03/26] dmabuf: add y0_top, pass it to spice
  2018-06-19  6:21   ` Gerd Hoffmann
@ 2018-06-19  9:05     ` Marc-André Lureau
  2018-06-19 10:21     ` Marc-André Lureau
  1 sibling, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-19  9:05 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU

Hi

On Tue, Jun 19, 2018 at 8:21 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> On Mon, Jun 18, 2018 at 06:17:06PM +0200, Marc-André Lureau wrote:
>> Some scanouts during boot are top-down without this.
>>
>> 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 {
>
> Looks incomplete, who sets y0_top?
>
> Also: send the bugfixes (patches 1-6 ?) separate, so they can be merged
> already while the input + gpu details are hashed out?

That makes sending / managing seperate series more complicated. You
need to reference some other series for reviewers, patchew etc,

It's easier if the first / reviewed patches of the series are picked.
But that may involve separate maintainers, I'll ping some of them.

thanks

-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH v3 09/26] HACK: vhost-user-backend: allow to specify binary to execute
  2018-06-19  6:19   ` Gerd Hoffmann
@ 2018-06-19  9:07     ` Daniel P. Berrangé
  0 siblings, 0 replies; 52+ messages in thread
From: Daniel P. Berrangé @ 2018-06-19  9:07 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Marc-André Lureau, qemu-devel

On Tue, Jun 19, 2018 at 08:19:03AM +0200, Gerd Hoffmann wrote:
> On Mon, Jun 18, 2018 at 06:17:12PM +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.
> 
> Hmm, maybe let the device which uses vhost-user-backend handle this?
> 
> So you use "-device vhost-user-input-pci,device=/dev/input/$dev" and
> vhost-user-input-pci translates that into ...
>  
>   argv = { "$dir/vhost-user-input", "-device", "/dev/input/$dev", NULL }
> 
> ... for vhost-user-backend ?

Or just accept the binary name, but mandate a pre-determined set of
argv, in the same way we do for  TAP device 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] 52+ messages in thread

* Re: [Qemu-devel] [PATCH v3 03/26] dmabuf: add y0_top, pass it to spice
  2018-06-19  6:21   ` Gerd Hoffmann
  2018-06-19  9:05     ` Marc-André Lureau
@ 2018-06-19 10:21     ` Marc-André Lureau
  1 sibling, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-19 10:21 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU

Hi

On Tue, Jun 19, 2018 at 8:21 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> On Mon, Jun 18, 2018 at 06:17:06PM +0200, Marc-André Lureau wrote:
>> Some scanouts during boot are top-down without this.
>>
>> 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 {
>
> Looks incomplete, who sets y0_top?

It is set from VHOST_USER_GPU_DMABUF_SCANOUT code path in the last patch.

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




-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH v3 10/26] vhost-user: split vhost_user_read()
  2018-06-19  9:01     ` Marc-André Lureau
@ 2018-06-19 11:20       ` Gerd Hoffmann
  0 siblings, 0 replies; 52+ messages in thread
From: Gerd Hoffmann @ 2018-06-19 11:20 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU

On Tue, Jun 19, 2018 at 11:01:00AM +0200, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Jun 19, 2018 at 8:23 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> >> @@ -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;
> >
> > Hmm?  Looks like a pointless indirection ...
> 
> Isn't it removed by the patch? :)

Ah, right.  Sorry about the noise.

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v3 01/26] chardev: avoid crash if no associated address
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 01/26] chardev: avoid crash if no associated address Marc-André Lureau
@ 2018-06-19 12:01   ` Daniel P. Berrangé
  0 siblings, 0 replies; 52+ messages in thread
From: Daniel P. Berrangé @ 2018-06-19 12:01 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, kraxel

On Mon, Jun 18, 2018 at 06:17:04PM +0200, Marc-André Lureau wrote:
> 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>
> ---
>  chardev/char-socket.c | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 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] 52+ messages in thread

* Re: [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter Marc-André Lureau
@ 2018-06-20 13:53   ` Paolo Bonzini
  2018-06-20 14:03   ` Peter Maydell
  1 sibling, 0 replies; 52+ messages in thread
From: Paolo Bonzini @ 2018-06-20 13:53 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel; +Cc: kraxel

On 18/06/2018 18:17, 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.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@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) {
> 

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

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

* Re: [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter Marc-André Lureau
  2018-06-20 13:53   ` Paolo Bonzini
@ 2018-06-20 14:03   ` Peter Maydell
  2018-06-20 14:36     ` Marc-André Lureau
  1 sibling, 1 reply; 52+ messages in thread
From: Peter Maydell @ 2018-06-20 14:03 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU Developers, Gerd Hoffmann

On 18 June 2018 at 17:17, Marc-André Lureau <marcandre.lureau@redhat.com> 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.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@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;

Subject says "qemu_char_fe_write_all()" but patch is changing
qemu_chr_fe_read_all()...

thanks
-- PMM

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

* Re: [Qemu-devel] [PATCH v3 08/26] Add vhost-user-backend
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 08/26] Add vhost-user-backend Marc-André Lureau
@ 2018-06-20 14:31   ` Marc-André Lureau
  0 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-20 14:31 UTC (permalink / raw)
  To: QEMU; +Cc: Marc-André Lureau, Gerd Hoffmann

Hi

On Mon, Jun 18, 2018 at 6:17 PM, Marc-André Lureau
<marcandre.lureau@redhat.com> wrote:
> 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               | 257 ++++++++++++++++++++++++++++
>  vl.c                                |   3 +-
>  backends/Makefile.objs              |   3 +-
>  4 files changed, 321 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..db97037306
> --- /dev/null
> +++ b/backends/vhost-user.c
> @@ -0,0 +1,257 @@
> +/*
> + * 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
> +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);
> +    }
> +}

I moved that function to the next patch.

Added files to MAINTAINERS

Added help to qemu-options.hx

> +
> +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 6e34fb348d..1c21339ba7 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2718,7 +2718,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/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
> --
> 2.18.0.rc1
>
>



-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter
  2018-06-20 14:03   ` Peter Maydell
@ 2018-06-20 14:36     ` Marc-André Lureau
  0 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-20 14:36 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers, Gerd Hoffmann

On Wed, Jun 20, 2018 at 4:03 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 18 June 2018 at 17:17, Marc-André Lureau <marcandre.lureau@redhat.com> 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.
>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@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;
>
> Subject says "qemu_char_fe_write_all()" but patch is changing
> qemu_chr_fe_read_all()...
>

indeed, fixed.
thanks



-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH v3 24/26] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
  2018-06-19  6:43   ` Gerd Hoffmann
@ 2018-06-20 16:04     ` Marc-André Lureau
  2018-06-21  6:05       ` Gerd Hoffmann
  0 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-20 16:04 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel, P. Berrange, Daniel

Hi

On Tue, Jun 19, 2018 at 8:43 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> On Mon, Jun 18, 2018 at 06:17:27PM +0200, Marc-André Lureau wrote:
>> Add a base class that is common to virtio-gpu and vhost-user-gpu
>> devices.
>
> More verbose commit message please.  What functionality is common and
> handled by the new (abstract?) base class?  (same for patch #25).

Would that level of details be enough?

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.

thanks

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

* Re: [Qemu-devel] [PATCH v3 25/26] virtio-gpu: split virtio-gpu-pci & virtio-vga
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 25/26] virtio-gpu: split virtio-gpu-pci & virtio-vga Marc-André Lureau
@ 2018-06-20 16:59   ` Marc-André Lureau
  0 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-20 16:59 UTC (permalink / raw)
  To: qemu-devel, Hoffmann, Gerd

Hi

On Mon, Jun 18, 2018 at 6:17 PM, Marc-André Lureau
<marcandre.lureau@redhat.com> wrote:
> Add base classes that are common to vhost-user-gpu-pci and
> vhost-user-vga.
>

Here is a bit more details on the base class split:

Both virtio-gpu-pci and virtio-vga are inheriting from virtio-pci.
vhost-user-gpu devices will share very similar setup and integration
with qemu than virtio-gpu, except that the virtio backend differs
(VhostUserGPU vs VirtIOGPU), and vhost-user-gpu devices take an extra
"vhost-user" property (aliased to the backend property).

Extract virtio-gpu-pci-base and virtio-vga-base classes, they are
inherited by the respective virtio & vhost-user devices with the few
differences mentionned.

> 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 98388762f6..0c503460af 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 5dc312f7de..c958fff474 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1336,7 +1336,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.rc1
>

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

* Re: [Qemu-devel] [PATCH v3 24/26] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base
  2018-06-20 16:04     ` Marc-André Lureau
@ 2018-06-21  6:05       ` Gerd Hoffmann
  0 siblings, 0 replies; 52+ messages in thread
From: Gerd Hoffmann @ 2018-06-21  6:05 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, P. Berrange, Daniel

On Wed, Jun 20, 2018 at 06:04:25PM +0200, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Jun 19, 2018 at 8:43 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> > On Mon, Jun 18, 2018 at 06:17:27PM +0200, Marc-André Lureau wrote:
> >> Add a base class that is common to virtio-gpu and vhost-user-gpu
> >> devices.
> >
> > More verbose commit message please.  What functionality is common and
> > handled by the new (abstract?) base class?  (same for patch #25).
> 
> Would that level of details be enough?

> 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.

Looks good.

cheers,
  Gerd

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

* Re: [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup
  2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup Marc-André Lureau
@ 2018-06-21 12:33   ` Tiwei Bie
  2018-06-21 12:48     ` Marc-André Lureau
  0 siblings, 1 reply; 52+ messages in thread
From: Tiwei Bie @ 2018-06-21 12:33 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, kraxel

On Mon, Jun 18, 2018 at 06:17:07PM +0200, Marc-André Lureau wrote:
[...]
> diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c
> index 049089b5e2..323dfcc46a 100644
> --- a/hw/virtio/vhost-stub.c
> +++ b/hw/virtio/vhost-stub.c
> @@ -7,7 +7,7 @@ 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;

It would be better to return false.

Best regards,
Tiwei Bie

>  }
[...]

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

* Re: [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup
  2018-06-21 12:33   ` Tiwei Bie
@ 2018-06-21 12:48     ` Marc-André Lureau
  2018-06-21 13:27       ` Tiwei Bie
  0 siblings, 1 reply; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-21 12:48 UTC (permalink / raw)
  To: Tiwei Bie; +Cc: QEMU, Gerd Hoffmann

On Thu, Jun 21, 2018 at 2:33 PM, Tiwei Bie <tiwei.bie@intel.com> wrote:
> On Mon, Jun 18, 2018 at 06:17:07PM +0200, Marc-André Lureau wrote:
> [...]
>> diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c
>> index 049089b5e2..323dfcc46a 100644
>> --- a/hw/virtio/vhost-stub.c
>> +++ b/hw/virtio/vhost-stub.c
>> @@ -7,7 +7,7 @@ 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;
>
> It would be better to return false.
>

Good catch, fixed.

Except that, would you give a reviewed-by?

> Best regards,
> Tiwei Bie
>
>>  }
> [...]
>



-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup
  2018-06-21 12:48     ` Marc-André Lureau
@ 2018-06-21 13:27       ` Tiwei Bie
  2018-06-26 12:24         ` Marc-André Lureau
  0 siblings, 1 reply; 52+ messages in thread
From: Tiwei Bie @ 2018-06-21 13:27 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Gerd Hoffmann

On Thu, Jun 21, 2018 at 02:48:08PM +0200, Marc-André Lureau wrote:
> On Thu, Jun 21, 2018 at 2:33 PM, Tiwei Bie <tiwei.bie@intel.com> wrote:
> > On Mon, Jun 18, 2018 at 06:17:07PM +0200, Marc-André Lureau wrote:
> > [...]
> >> diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c
> >> index 049089b5e2..323dfcc46a 100644
> >> --- a/hw/virtio/vhost-stub.c
> >> +++ b/hw/virtio/vhost-stub.c
> >> @@ -7,7 +7,7 @@ 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;
> >
> > It would be better to return false.
> >
> 
> Good catch, fixed.
> 
> Except that, would you give a reviewed-by?

Sorry, I missed something previously. In below diff:

diff --git a/net/vhost-user.c b/net/vhost-user.c
index a39f9c9974..5e5b8f3fc9 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -291,14 +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();
+    user = g_new0(struct VhostUserState, 1);
     if (!user) {
         error_report("failed to init vhost_user");
         goto err;

It might be better to change the error message
to something like:

"failed to allocate vhost-user state".

Except that, it looks good to me and you can add
my reviewed-by.

Best regards,
Tiwei Bie

> 
> > Best regards,
> > Tiwei Bie
> >
> >>  }
> > [...]
> >
> 
> 
> 
> -- 
> Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup
  2018-06-21 13:27       ` Tiwei Bie
@ 2018-06-26 12:24         ` Marc-André Lureau
  0 siblings, 0 replies; 52+ messages in thread
From: Marc-André Lureau @ 2018-06-26 12:24 UTC (permalink / raw)
  To: Tiwei Bie; +Cc: QEMU, Gerd Hoffmann

Hi

On Thu, Jun 21, 2018 at 3:27 PM, Tiwei Bie <tiwei.bie@intel.com> wrote:
> On Thu, Jun 21, 2018 at 02:48:08PM +0200, Marc-André Lureau wrote:
>> On Thu, Jun 21, 2018 at 2:33 PM, Tiwei Bie <tiwei.bie@intel.com> wrote:
>> > On Mon, Jun 18, 2018 at 06:17:07PM +0200, Marc-André Lureau wrote:
>> > [...]
>> >> diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c
>> >> index 049089b5e2..323dfcc46a 100644
>> >> --- a/hw/virtio/vhost-stub.c
>> >> +++ b/hw/virtio/vhost-stub.c
>> >> @@ -7,7 +7,7 @@ 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;
>> >
>> > It would be better to return false.
>> >
>>
>> Good catch, fixed.
>>
>> Except that, would you give a reviewed-by?
>
> Sorry, I missed something previously. In below diff:
>
> diff --git a/net/vhost-user.c b/net/vhost-user.c
> index a39f9c9974..5e5b8f3fc9 100644
> --- a/net/vhost-user.c
> +++ b/net/vhost-user.c
> @@ -291,14 +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();
> +    user = g_new0(struct VhostUserState, 1);
>      if (!user) {
>          error_report("failed to init vhost_user");
>          goto err;
>
> It might be better to change the error message
> to something like:
>
> "failed to allocate vhost-user state".
>
> Except that, it looks good to me and you can add
> my reviewed-by.

Good point, g_new() aborts on failure, so we can simply remove the error report.

Thanks

>
> Best regards,
> Tiwei Bie
>
>>
>> > Best regards,
>> > Tiwei Bie
>> >
>> >>  }
>> > [...]
>> >
>>
>>
>>
>> --
>> Marc-André Lureau



-- 
Marc-André Lureau

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

end of thread, other threads:[~2018-06-26 12:24 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-18 16:17 [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 01/26] chardev: avoid crash if no associated address Marc-André Lureau
2018-06-19 12:01   ` Daniel P. Berrangé
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 02/26] chardev: remove qemu_chr_fe_write_all() counter Marc-André Lureau
2018-06-20 13:53   ` Paolo Bonzini
2018-06-20 14:03   ` Peter Maydell
2018-06-20 14:36     ` Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 03/26] dmabuf: add y0_top, pass it to spice Marc-André Lureau
2018-06-19  6:21   ` Gerd Hoffmann
2018-06-19  9:05     ` Marc-André Lureau
2018-06-19 10:21     ` Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 04/26] vhost-user: simplify vhost_user_init/vhost_user_cleanup Marc-André Lureau
2018-06-21 12:33   ` Tiwei Bie
2018-06-21 12:48     ` Marc-André Lureau
2018-06-21 13:27       ` Tiwei Bie
2018-06-26 12:24         ` Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 05/26] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 06/26] vhost-user: wrap some read/write with retry handling Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 07/26] qio: add qio_channel_command_new_spawn_with_pre_exec() Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 08/26] Add vhost-user-backend Marc-André Lureau
2018-06-20 14:31   ` Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 09/26] HACK: vhost-user-backend: allow to specify binary to execute Marc-André Lureau
2018-06-19  6:19   ` Gerd Hoffmann
2018-06-19  9:07     ` Daniel P. Berrangé
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 10/26] vhost-user: split vhost_user_read() Marc-André Lureau
2018-06-19  6:23   ` Gerd Hoffmann
2018-06-19  9:01     ` Marc-André Lureau
2018-06-19 11:20       ` Gerd Hoffmann
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 11/26] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 12/26] libvhost-user: export vug_source_new() Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 13/26] contrib: add vhost-user-input Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 14/26] Add vhost-user-input-pci Marc-André Lureau
2018-06-19  6:31   ` Gerd Hoffmann
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 15/26] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 16/26] vhost-user: add vhost_user_gpu_get_num_capsets() Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 17/26] virtio: add virtio-gpu bswap helpers header Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 18/26] util: promote qemu_egl_rendernode_open() to libqemuutil Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 19/26] contrib: add vhost-user-gpu Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 20/26] virtio-gpu: remove unused qdev Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 21/26] virtio-gpu: remove unused config_size Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 22/26] virtio-gpu: block both 2d and 3d rendering Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 23/26] virtio-gpu: remove useless 'waiting' field Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 24/26] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base Marc-André Lureau
2018-06-19  6:43   ` Gerd Hoffmann
2018-06-20 16:04     ` Marc-André Lureau
2018-06-21  6:05       ` Gerd Hoffmann
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 25/26] virtio-gpu: split virtio-gpu-pci & virtio-vga Marc-André Lureau
2018-06-20 16:59   ` Marc-André Lureau
2018-06-18 16:17 ` [Qemu-devel] [PATCH v3 26/26] hw/display: add vhost-user-vga & gpu-pci Marc-André Lureau
2018-06-19  6:54   ` Gerd Hoffmann
2018-06-19  8:58     ` Marc-André Lureau
2018-06-18 18:50 ` [Qemu-devel] [PATCH v3 00/26] vhost-user for input & GPU no-reply

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.