All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU
@ 2018-06-01 16:27 Marc-André Lureau
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address Marc-André Lureau
                   ` (12 more replies)
  0 siblings, 13 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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 -s vgpu.sock &
$ qemu...
  -chardev socket,id=chr,path=vgpu.sock
  -object vhost-user-backend,id=vug,chardev=chr
  -device virtio-vga,virgl=true,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

Feedback welcome,

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 (12):
  chardev: avoid crash if no associated address
  libvhost-user: exit by default on VHOST_USER_NONE
  vhost-user: wrap some read/write with retry handling
  Add vhost-user-backend
  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-input-pci
  vhost-user: add vhost_user_gpu_set_socket()
  Add virtio-gpu vhost-user backend
  contrib: add vhost-user-gpu

 contrib/libvhost-user/libvhost-user-glib.h |   3 +
 contrib/libvhost-user/libvhost-user.h      |   2 +
 contrib/vhost-user-gpu/virgl.h             |  24 +
 contrib/vhost-user-gpu/vugpu.h             | 152 ++++
 hw/virtio/virtio-pci.h                     |  10 +
 include/hw/virtio/vhost-backend.h          |   5 +
 include/hw/virtio/virtio-gpu.h             |   9 +
 include/hw/virtio/virtio-input.h           |  14 +
 include/sysemu/vhost-user-backend.h        |  58 ++
 include/ui/console.h                       |   1 +
 backends/vhost-user.c                      | 330 +++++++
 chardev/char-socket.c                      |   7 +-
 contrib/libvhost-user/libvhost-user-glib.c |  15 +-
 contrib/libvhost-user/libvhost-user.c      |   2 +
 contrib/vhost-user-gpu/main.c              | 953 +++++++++++++++++++++
 contrib/vhost-user-gpu/virgl.c             | 537 ++++++++++++
 contrib/vhost-user-input/main.c            | 358 ++++++++
 hw/display/vhost-gpu.c                     | 290 +++++++
 hw/display/virtio-gpu-3d.c                 |   8 +-
 hw/display/virtio-gpu-pci.c                |   5 +
 hw/display/virtio-gpu.c                    |  77 +-
 hw/display/virtio-vga.c                    |   5 +
 hw/input/vhost-user-input.c                | 124 +++
 hw/virtio/vhost-user.c                     | 112 ++-
 hw/virtio/virtio-pci.c                     |  20 +
 ui/spice-display.c                         |   3 +-
 vl.c                                       |   3 +-
 MAINTAINERS                                |   1 +
 Makefile                                   |   6 +
 Makefile.objs                              |   2 +
 backends/Makefile.objs                     |   4 +
 configure                                  |   5 +
 contrib/vhost-user-gpu/Makefile.objs       |   7 +
 contrib/vhost-user-input/Makefile.objs     |   1 +
 docs/interop/vhost-user.txt                |   6 +
 hw/display/Makefile.objs                   |   2 +-
 hw/input/Makefile.objs                     |   1 +
 37 files changed, 3137 insertions(+), 25 deletions(-)
 create mode 100644 contrib/vhost-user-gpu/virgl.h
 create mode 100644 contrib/vhost-user-gpu/vugpu.h
 create mode 100644 include/sysemu/vhost-user-backend.h
 create mode 100644 backends/vhost-user.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-gpu.c
 create mode 100644 hw/input/vhost-user-input.c
 create mode 100644 contrib/vhost-user-gpu/Makefile.objs
 create mode 100644 contrib/vhost-user-input/Makefile.objs


base-commit: c181ddaa176856b3cd2dfd12bbcf25fa9c884a97
-- 
2.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-08 14:52   ` Philippe Mathieu-Daudé
  2018-06-11  9:04   ` Daniel P. Berrangé
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 02/12] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
                   ` (11 subsequent siblings)
  12 siblings, 2 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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

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

diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 159e69c3b1..f1b7907798 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -416,8 +416,11 @@ 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);
+    chr->filename = NULL;
+    if (s->addr) {
+        chr->filename = SocketAddress_to_str("disconnected:", s->addr,
+                                             s->is_listen, s->is_telnet);
+    }
 }
 
 /* NB may be called even if tcp_chr_connect has not been
-- 
2.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 02/12] libvhost-user: exit by default on VHOST_USER_NONE
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-08 14:48   ` Philippe Mathieu-Daudé
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 03/12] vhost-user: wrap some read/write with retry handling Marc-André Lureau
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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 | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c
index 54e643d871..ebe30c7dab 100644
--- a/contrib/libvhost-user/libvhost-user.c
+++ b/contrib/libvhost-user/libvhost-user.c
@@ -1220,6 +1220,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
     case VHOST_USER_SET_CONFIG:
         return vu_set_config(dev, vmsg);
     case VHOST_USER_NONE:
+        /* if you need processing before exit, override iface->process_msg */
+        exit(0);
         break;
     case VHOST_USER_POSTCOPY_ADVISE:
         return vu_set_postcopy_advise(dev, vmsg);
-- 
2.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 03/12] vhost-user: wrap some read/write with retry handling
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address Marc-André Lureau
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 02/12] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-08 14:53   ` Philippe Mathieu-Daudé
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend Marc-André Lureau
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 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 ca554d4ff1..cc9298792d 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -869,7 +869,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;
@@ -898,7 +901,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;
@@ -941,7 +947,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.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (2 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 03/12] vhost-user: wrap some read/write with retry handling Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-04  9:36   ` Daniel P. Berrangé
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 05/12] vhost-user: split vhost_user_read() Marc-André Lureau
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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.

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

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/sysemu/vhost-user-backend.h |  58 +++++
 backends/vhost-user.c               | 330 ++++++++++++++++++++++++++++
 vl.c                                |   3 +-
 backends/Makefile.objs              |   4 +
 4 files changed, 394 insertions(+), 1 deletion(-)
 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..890db176fd
--- /dev/null
+++ b/include/sysemu/vhost-user-backend.h
@@ -0,0 +1,58 @@
+/*
+ * 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 "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;
+    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..ae40dee373
--- /dev/null
+++ b/backends/vhost-user.c
@@ -0,0 +1,330 @@
+/*
+ * 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;
+    }
+
+    b->vdev = vdev;
+    b->dev.nvqs = nvqs;
+    b->dev.vqs = g_new(struct vhost_virtqueue, nvqs);
+
+    ret = vhost_dev_init(&b->dev, &b->chr, 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 int
+vhost_user_backend_spawn_cmd(VhostUserBackend *b, int vhostfd, Error **errp)
+{
+    int devnull = open("/dev/null", O_RDWR);
+    pid_t pid;
+
+    assert(!b->child);
+
+    if (!b->cmd) {
+        error_setg_errno(errp, errno, "Missing cmd property");
+        return -1;
+    }
+    if (devnull < 0) {
+        error_setg_errno(errp, errno, "Unable to open /dev/null");
+        return -1;
+    }
+
+    pid = qemu_fork(errp);
+    if (pid < 0) {
+        close(devnull);
+        return -1;
+    }
+
+    if (pid == 0) { /* child */
+        int fd, maxfd = sysconf(_SC_OPEN_MAX);
+
+        dup2(devnull, STDIN_FILENO);
+        dup2(devnull, STDOUT_FILENO);
+        dup2(vhostfd, 3);
+
+        signal(SIGINT, SIG_IGN);
+
+        for (fd = 4; fd < maxfd; fd++) {
+            close(fd);
+        }
+
+        execlp("/bin/sh", "sh", "-c", b->cmd, NULL);
+        _exit(1);
+    }
+
+    b->child = QIO_CHANNEL(qio_channel_command_new_pid(devnull, devnull, pid));
+
+    return 0;
+}
+
+static void
+vhost_user_backend_complete(UserCreatable *uc, Error **errp)
+{
+    VhostUserBackend *b = VHOST_USER_BACKEND(uc);
+    Chardev *chr;
+
+    if (!!b->chr_name + !!b->cmd != 1) {
+        error_setg(errp, "You may specificy only one of 'chardev' or 'cmd'.");
+        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;
+        }
+    } else {
+        int sv[2];
+
+        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");
+            object_unref(OBJECT(chr));
+            return;
+        }
+
+        if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
+            return;
+        }
+
+        vhost_user_backend_spawn_cmd(b, sv[1], errp);
+
+        close(sv[1]);
+    }
+
+    b->completed = true;
+    /* could vhost_dev_init() happen here, so early vhost-user message
+     * can be exchanged */
+}
+
+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)
+{
+    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, "cmd", get_cmd, set_cmd, NULL);
+    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->cmd);
+    g_free(b->chr_name);
+
+    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 c4fe25560c..4ed094dd71 100644
--- a/vl.c
+++ b/vl.c
@@ -2833,7 +2833,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..bac27de11b 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -16,3 +16,7 @@ common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
 endif
 
 common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
+
+ifdef CONFIG_LINUX
+common-obj-$(CONFIG_VIRTIO) += vhost-user.o
+endif
-- 
2.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 05/12] vhost-user: split vhost_user_read()
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (3 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-08 14:57   ` Philippe Mathieu-Daudé
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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 cc9298792d..a87db01e55 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -196,7 +196,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->chr;
@@ -207,7 +207,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 */
@@ -215,7 +215,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->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 */
@@ -223,7 +237,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) {
@@ -233,14 +247,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.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config()
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (4 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 05/12] vhost-user: split vhost_user_read() Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-04  9:07   ` Dr. David Alan Gilbert
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 07/12] libvhost-user: export vug_source_new Marc-André Lureau
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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       |  6 ++++
 3 files changed, 69 insertions(+)

diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index 5dac61f9ea..6cc2edacc5 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,
@@ -156,4 +157,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 a87db01e55..19ed87d07c 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -84,6 +84,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;
 
@@ -324,6 +325,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->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 534caab18a..3df9927386 100644
--- a/docs/interop/vhost-user.txt
+++ b/docs/interop/vhost-user.txt
@@ -744,6 +744,12 @@ Master message types
       was previously sent.
       The value returned is an error indication; 0 is success.
 
+ * VHOST_USER_INPUT_GET_CONFIG
+
+      Slave payload: (struct virtio_input_config)*
+
+      Ask vhost user input backend the list of virtio_input_config.
+
 Slave message types
 -------------------
 
-- 
2.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 07/12] libvhost-user: export vug_source_new
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (5 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 08/12] contrib: add vhost-user-input Marc-André Lureau
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 08/12] contrib: add vhost-user-input
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (6 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 07/12] libvhost-user: export vug_source_new Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 09/12] Add vhost-input-pci Marc-André Lureau
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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        | 358 +++++++++++++++++++++++++
 Makefile                               |   3 +
 Makefile.objs                          |   1 +
 configure                              |   1 +
 contrib/vhost-user-input/Makefile.objs |   1 +
 6 files changed, 365 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 b27075ea3b..d6badc8653 100644
--- a/contrib/libvhost-user/libvhost-user.h
+++ b/contrib/libvhost-user/libvhost-user.h
@@ -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;
 
diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/main.c
new file mode 100644
index 0000000000..b2c2184446
--- /dev/null
+++ b/contrib/vhost-user-input/main.c
@@ -0,0 +1,358 @@
+#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", __func__);
+
+    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;
+}
+
+char **fname;
+char *socket_path;
+bool nograb;
+
+static GOptionEntry entries[] = {
+    { "no-grab", 'n', 0, G_OPTION_ARG_NONE, &nograb,
+      "Don't grab device", "" },
+    { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &socket_path,
+      "Use UNIX socket path", "" },
+    { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &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 (!fname || !fname[0] || fname[1]) {
+        g_printerr("Please specify a single EVDEV filename\n");
+        exit(1);
+    }
+
+    vi.evdevfd = open(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 (!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 (socket_path) {
+        int lsock = unix_sock_new(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 6d588d1f71..d1222da359 100644
--- a/Makefile
+++ b/Makefile
@@ -420,6 +420,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 c6c3554203..c20f8d35d7 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 a6a4616c3e..1d1caea9e7 100755
--- a/configure
+++ b/configure
@@ -5532,6 +5532,7 @@ if test "$want_tools" = "yes" ; then
   fi
   if [ "$ivshmem" = "yes" ]; then
     tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools"
+    tools="vhost-user-input\$(EXESUF) $tools"
   fi
 fi
 if test "$softmmu" = 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.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 09/12] Add vhost-input-pci
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (7 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 08/12] contrib: add vhost-user-input Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-04  8:58   ` Gerd Hoffmann
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 10/12] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Marc-André Lureau

Use a vhost-user input 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      | 124 +++++++++++++++++++++++++++++++
 hw/virtio/virtio-pci.c           |  20 +++++
 MAINTAINERS                      |   1 +
 hw/input/Makefile.objs           |   1 +
 6 files changed, 170 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..b293020d08
--- /dev/null
+++ b/hw/input/vhost-user-input.c
@@ -0,0 +1,124 @@
+/*
+ * 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 "qemu/sockets.h"
+
+#include "hw/qdev.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-input.h"
+
+#include <sys/ioctl.h>
+#include "standard-headers/linux/input.h"
+
+/* ----------------------------------------------------------------- */
+
+static struct virtio_input_config vhost_input_config[] = {
+    { /* empty list */ },
+};
+
+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 (!vhi->vhost) {
+        return;
+    }
+
+    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);
+
+    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_UNREF_ON_RELEASE,
+                             &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 5eb0c323ca..ecec2e713c 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -2646,6 +2646,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,
@@ -2702,6 +2721,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 41cd3736a9..67c0197570 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1202,6 +1202,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 77e53e6883..f787df3aa5 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -12,6 +12,7 @@ common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o
 ifeq ($(CONFIG_LINUX),y)
 common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o
 endif
+common-obj-$(CONFIG_VIRTIO) += vhost-user-input.o
 
 obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o
 obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o
-- 
2.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 10/12] vhost-user: add vhost_user_gpu_set_socket()
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (8 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 09/12] Add vhost-input-pci Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-04  9:28   ` Gerd Hoffmann
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend Marc-André Lureau
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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 recommend to have a new dedicated
channel.

FIXME: document the GPU protocol.

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

diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index d6badc8653..57a3a36100 100644
--- a/contrib/libvhost-user/libvhost-user.h
+++ b/contrib/libvhost-user/libvhost-user.h
@@ -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;
 
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index 6cc2edacc5..a60d278f05 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -159,5 +159,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 19ed87d07c..5cb76f436b 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -85,6 +85,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;
 
@@ -383,6 +384,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)
 {
-- 
2.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (9 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 10/12] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-04  9:37   ` Gerd Hoffmann
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 12/12] contrib: add vhost-user-gpu Marc-André Lureau
  2018-06-01 17:28 ` [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU no-reply
  12 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Marc-André Lureau

Add to virtio-gpu devices a "vhost-user" property. When set, the
associated vhost-user backend is used to handle the virtio rings.

For now, a socketpair is created for the backend to share the rendering
results with qemu via a simple VHOST_GPU protocol.

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

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/hw/virtio/virtio-gpu.h |   9 +
 include/ui/console.h           |   1 +
 hw/display/vhost-gpu.c         | 290 +++++++++++++++++++++++++++++++++
 hw/display/virtio-gpu-3d.c     |   8 +-
 hw/display/virtio-gpu-pci.c    |   5 +
 hw/display/virtio-gpu.c        |  77 ++++++++-
 hw/display/virtio-vga.c        |   5 +
 ui/spice-display.c             |   3 +-
 hw/display/Makefile.objs       |   2 +-
 9 files changed, 393 insertions(+), 7 deletions(-)
 create mode 100644 hw/display/vhost-gpu.c

diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 79bb3fb3dd..7cd514175a 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"
 #define TYPE_VIRTIO_GPU "virtio-gpu-device"
@@ -88,6 +89,9 @@ struct virtio_gpu_ctrl_command {
 typedef struct VirtIOGPU {
     VirtIODevice parent_obj;
 
+    VhostUserBackend *vhost;
+    CharBackend vhost_chr;
+
     QEMUBH *ctrl_bh;
     QEMUBH *cursor_bh;
     VirtQueue *ctrl_vq;
@@ -103,6 +107,7 @@ typedef struct VirtIOGPU {
     QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
 
     struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS];
+    QemuDmaBuf dmabuf[VIRTIO_GPU_MAX_SCANOUTS];
     struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS];
 
     struct virtio_gpu_conf conf;
@@ -171,4 +176,8 @@ 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);
+
+/* vhost-gpu.c */
+int vhost_gpu_init(VirtIOGPU *g, Error **errp);
+
 #endif
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/hw/display/vhost-gpu.c b/hw/display/vhost-gpu.c
new file mode 100644
index 0000000000..42d9143d3d
--- /dev/null
+++ b/hw/display/vhost-gpu.c
@@ -0,0 +1,290 @@
+/*
+ * Virtio vhost GPU Device
+ *
+ * Copyright Red Hat, Inc. 2016
+ *
+ * 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"
+
+typedef enum VhostGpuRequest {
+    VHOST_GPU_NONE = 0,
+    VHOST_GPU_CURSOR_POS,
+    VHOST_GPU_CURSOR_POS_HIDE,
+    VHOST_GPU_CURSOR_UPDATE,
+    VHOST_GPU_SCANOUT,
+    VHOST_GPU_UPDATE,
+    VHOST_GPU_GL_SCANOUT,
+    VHOST_GPU_GL_UPDATE,
+} VhostGpuRequest;
+
+typedef struct VhostGpuCursorPos {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+} QEMU_PACKED VhostGpuCursorPos;
+
+typedef struct VhostGpuCursorUpdate {
+    VhostGpuCursorPos pos;
+    uint32_t hot_x;
+    uint32_t hot_y;
+    uint32_t data[64 * 64];
+} QEMU_PACKED VhostGpuCursorUpdate;
+
+typedef struct VhostGpuScanout {
+    uint32_t scanout_id;
+    uint32_t width;
+    uint32_t height;
+} QEMU_PACKED VhostGpuScanout;
+
+typedef struct VhostGpuGlScanout {
+    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 VhostGpuGlScanout;
+
+typedef struct VhostGpuUpdate {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+    uint32_t width;
+    uint32_t height;
+    uint8_t data[];
+} QEMU_PACKED VhostGpuUpdate;
+
+typedef struct VhostGpuMsg {
+    VhostGpuRequest request;
+    uint32_t size; /* the following payload size */
+    union {
+        VhostGpuCursorPos cursor_pos;
+        VhostGpuCursorUpdate cursor_update;
+        VhostGpuScanout scanout;
+        VhostGpuUpdate update;
+        VhostGpuGlScanout gl_scanout;
+    } payload;
+} QEMU_PACKED VhostGpuMsg;
+
+static VhostGpuMsg m __attribute__ ((unused));
+#define VHOST_GPU_HDR_SIZE (sizeof(m.request) + sizeof(m.size))
+
+static void vhost_gpu_handle_cursor(VirtIOGPU *g, VhostGpuMsg *msg)
+{
+    VhostGpuCursorPos *pos = &msg->payload.cursor_pos;
+    struct virtio_gpu_scanout *s;
+
+    if (pos->scanout_id >= g->conf.max_outputs) {
+        return;
+    }
+    s = &g->scanout[pos->scanout_id];
+
+    if (msg->request == VHOST_GPU_CURSOR_UPDATE) {
+        VhostGpuCursorUpdate *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_GPU_CURSOR_POS_HIDE);
+}
+
+static void vhost_gpu_handle_display(VirtIOGPU *g, VhostGpuMsg *msg)
+{
+    struct virtio_gpu_scanout *s;
+
+    switch (msg->request) {
+    case VHOST_GPU_SCANOUT: {
+        VhostGpuScanout *m = &msg->payload.scanout;
+
+        if (m->scanout_id >= g->conf.max_outputs) {
+            return;
+        }
+        s = &g->scanout[m->scanout_id];
+
+        s->ds = qemu_create_displaysurface(m->width, m->height);
+        if (!s->ds) {
+            return;
+        }
+
+        dpy_gfx_replace_surface(s->con, s->ds);
+        break;
+    }
+    case VHOST_GPU_GL_SCANOUT: {
+        VhostGpuGlScanout *m = &msg->payload.gl_scanout;
+        int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr);
+        QemuDmaBuf *dmabuf;
+
+        if (m->scanout_id >= g->conf.max_outputs) {
+            if (fd >= 0) {
+                close(fd);
+            }
+            break;
+        }
+
+        dmabuf = &g->dmabuf[m->scanout_id];
+        if (g->enable && dmabuf->fd >= 0) {
+            close(dmabuf->fd);
+            dmabuf->fd = -1;
+        }
+        if (fd == -1) {
+            dpy_gl_release_dmabuf(g->scanout[m->scanout_id].con, dmabuf);
+            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,
+        };
+        g->enable = 1;
+        dpy_gl_scanout_dmabuf(g->scanout[m->scanout_id].con, dmabuf);
+        break;
+    }
+    case VHOST_GPU_GL_UPDATE: {
+        VhostGpuUpdate *m = &msg->payload.update;
+        QemuConsole *con = g->scanout[m->scanout_id].con;
+
+        if (m->scanout_id >= g->conf.max_outputs ||
+            !g->scanout[m->scanout_id].con) {
+            break;
+        }
+
+        graphic_hw_gl_block(con, true);
+        dpy_gl_update(con, m->x, m->y, m->width, m->height);
+        graphic_hw_gl_block(con, false);
+        break;
+    }
+    case VHOST_GPU_UPDATE: {
+        VhostGpuUpdate *m = &msg->payload.update;
+
+        if (m->scanout_id >= g->conf.max_outputs) {
+            break;
+        }
+        s = &g->scanout[m->scanout_id];
+
+        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(s->con, m->x, m->y, m->width, m->height);
+        break;
+    }
+    default:
+        g_warning("unhandled message %d %d", msg->request, msg->size);
+    }
+}
+
+static void vhost_gpu_chr_read(void *opaque)
+{
+    VirtIOGPU *g = opaque;
+    VhostGpuMsg *msg = NULL;
+    VhostGpuRequest 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_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_GPU_CURSOR_UPDATE ||
+        request == VHOST_GPU_CURSOR_POS ||
+        request == VHOST_GPU_CURSOR_POS_HIDE) {
+        vhost_gpu_handle_cursor(g, msg);
+    } else {
+        vhost_gpu_handle_display(g, msg);
+    }
+
+end:
+    g_free(msg);
+}
+
+int vhost_gpu_init(VirtIOGPU *g, Error **errp)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(g);
+    Chardev *chr;
+    int sv[2];
+
+    if (vhost_user_backend_dev_init(g->vhost, vdev, 2, errp) < 0) {
+        return -1;
+    }
+
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+        error_setg_errno(errp, errno, "socketpair() failed");
+        return -1;
+    }
+
+    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));
+        return -1;
+    }
+    if (!qemu_chr_fe_init(&g->vhost_chr, chr, errp)) {
+        object_unref(OBJECT(chr));
+        return -1;
+    }
+
+    qemu_set_fd_handler(sv[0], vhost_gpu_chr_read, NULL, g);
+
+    vhost_user_gpu_set_socket(&g->vhost->dev, sv[1]);
+
+    close(sv[1]);
+
+    return 0;
+}
diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
index 3558f38fe8..90984ea1bb 100644
--- a/hw/display/virtio-gpu-3d.c
+++ b/hw/display/virtio-gpu-3d.c
@@ -614,9 +614,13 @@ void virtio_gpu_gl_block(void *opaque, bool block)
         g->renderer_blocked--;
     }
     assert(g->renderer_blocked >= 0);
-
     if (g->renderer_blocked == 0) {
-        virtio_gpu_process_cmdq(g);
+        if (g->vhost && g->enable) {
+            uint32_t ok; /*unblock renderer */
+            qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)&ok, sizeof(ok));
+        } else {
+            virtio_gpu_process_cmdq(g);
+        }
     }
 }
 
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
index cece4aa495..470bf0608c 100644
--- a/hw/display/virtio-gpu-pci.c
+++ b/hw/display/virtio-gpu-pci.c
@@ -18,6 +18,7 @@
 #include "hw/virtio/virtio-bus.h"
 #include "hw/virtio/virtio-pci.h"
 #include "hw/virtio/virtio-gpu.h"
+#include "qapi/error.h"
 
 static Property virtio_gpu_pci_properties[] = {
     DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
@@ -67,6 +68,10 @@ static void virtio_gpu_initfn(Object *obj)
 
     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
                                 TYPE_VIRTIO_GPU);
+
+    object_property_add_alias(obj, "vhost-user",
+                              OBJECT(&dev->vdev), "vhost-user",
+                              &error_abort);
 }
 
 static const TypeInfo virtio_gpu_pci_info = {
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 2dd3c3481a..0dfa08a173 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -22,6 +22,7 @@
 #include "migration/blocker.h"
 #include "qemu/log.h"
 #include "qapi/error.h"
+#include "qemu/error-report.h"
 
 #define VIRTIO_GPU_VM_VERSION 1
 
@@ -1184,11 +1185,16 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
     }
 
     g->use_virgl_renderer = false;
+    if (g->vhost) {
+        /* default to backend virgl=on */
+        have_virgl = true;
+    } else {
 #if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN)
-    have_virgl = false;
+        have_virgl = false;
 #else
-    have_virgl = display_opengl;
+        have_virgl = display_opengl;
 #endif
+    }
     if (!have_virgl) {
         g->conf.flags &= ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED);
     }
@@ -1226,6 +1232,10 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
         g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
     }
 
+    if (g->vhost && vhost_gpu_init(g, errp) < 0) {
+        return;
+    }
+
     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);
@@ -1253,8 +1263,27 @@ static void virtio_gpu_device_unrealize(DeviceState *qdev, Error **errp)
     }
 }
 
+static void virtio_gpu_host_user_is_busy(const Object *obj, const char *name,
+                                         Object *val, Error **errp)
+{
+    VirtIOGPU *g = VIRTIO_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 virtio_gpu_instance_init(Object *obj)
 {
+    VirtIOGPU *g = VIRTIO_GPU(obj);
+
+    object_property_add_link(obj, "vhost-user", TYPE_VHOST_USER_BACKEND,
+                             (Object **)&g->vhost,
+                             virtio_gpu_host_user_is_busy,
+                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
+                             &error_abort);
 }
 
 static void virtio_gpu_reset(VirtIODevice *vdev)
@@ -1264,7 +1293,9 @@ static void virtio_gpu_reset(VirtIODevice *vdev)
     int i;
 
     g->enable = 0;
-
+    if (g->vhost) {
+        vhost_user_backend_stop(g->vhost);
+    }
     QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
         virtio_gpu_resource_destroy(g, res);
     }
@@ -1312,6 +1343,43 @@ static const VMStateDescription vmstate_virtio_gpu = {
     },
 };
 
+static void virtio_gpu_set_status(VirtIODevice *vdev, uint8_t val)
+{
+    VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+    if (g->vhost) {
+        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 virtio_gpu_guest_notifier_pending(VirtIODevice *vdev, int idx)
+{
+    VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+    if (!g->vhost) {
+        return false;
+    }
+
+    return vhost_virtqueue_pending(&g->vhost->dev, idx);
+}
+
+static void virtio_gpu_guest_notifier_mask(VirtIODevice *vdev, int idx,
+                                           bool mask)
+{
+    VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+    if (!g->vhost) {
+        return;
+    }
+
+    vhost_virtqueue_mask(&g->vhost->dev, vdev, idx, mask);
+}
+
 static Property virtio_gpu_properties[] = {
     DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1),
     DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf.max_hostmem,
@@ -1338,6 +1406,9 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data)
     vdc->set_config = virtio_gpu_set_config;
     vdc->get_features = virtio_gpu_get_features;
     vdc->set_features = virtio_gpu_set_features;
+    vdc->set_status   = virtio_gpu_set_status;
+    vdc->guest_notifier_mask = virtio_gpu_guest_notifier_mask;
+    vdc->guest_notifier_pending = virtio_gpu_guest_notifier_pending;
 
     vdc->reset = virtio_gpu_reset;
 
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index baa74ba82c..b16edd9e6d 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -201,6 +201,11 @@ static void virtio_vga_inst_initfn(Object *obj)
 
     virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
                                 TYPE_VIRTIO_GPU);
+
+    /* could eventually be included in qdev_alias_all_properties? */
+    object_property_add_alias(obj, "vhost-user",
+                              OBJECT(&dev->vdev), "vhost-user",
+                              &error_abort);
 }
 
 static TypeInfo virtio_vga_info = {
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;
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index d907b381ae..1c6f3c6bc1 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) += virtio-gpu.o virtio-gpu-3d.o
+obj-$(CONFIG_VIRTIO) += virtio-gpu.o virtio-gpu-3d.o vhost-gpu.o
 obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o
 obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
 virtio-gpu.o-cflags := $(VIRGL_CFLAGS)
-- 
2.17.1.906.g10fd178552

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

* [Qemu-devel] [RFC v2 12/12] contrib: add vhost-user-gpu
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (10 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend Marc-André Lureau
@ 2018-06-01 16:27 ` Marc-André Lureau
  2018-06-01 17:28 ` [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU no-reply
  12 siblings, 0 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-01 16:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, 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
- add/check resize support
- add missing options, such as virgl=off,rendernode config, initial
  resolution etc..
- 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/virgl.h       |  24 +
 contrib/vhost-user-gpu/vugpu.h       | 152 +++++
 contrib/vhost-user-gpu/main.c        | 953 +++++++++++++++++++++++++++
 contrib/vhost-user-gpu/virgl.c       | 537 +++++++++++++++
 Makefile                             |   3 +
 Makefile.objs                        |   1 +
 configure                            |   4 +
 contrib/vhost-user-gpu/Makefile.objs |   7 +
 8 files changed, 1681 insertions(+)
 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/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/virgl.h b/contrib/vhost-user-gpu/virgl.h
new file mode 100644
index 0000000000..c86625b963
--- /dev/null
+++ b/contrib/vhost-user-gpu/virgl.h
@@ -0,0 +1,24 @@
+/*
+ * 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"
+
+int vg_virgl_init(VuGpu *g);
+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..4515703401
--- /dev/null
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -0,0 +1,152 @@
+/*
+ * 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"
+
+typedef enum VhostGpuRequest {
+    VHOST_GPU_NONE = 0,
+    VHOST_GPU_CURSOR_POS,
+    VHOST_GPU_CURSOR_POS_HIDE,
+    VHOST_GPU_CURSOR_UPDATE,
+    VHOST_GPU_SCANOUT,
+    VHOST_GPU_UPDATE,
+    VHOST_GPU_GL_SCANOUT,
+    VHOST_GPU_GL_UPDATE,
+} VhostGpuRequest;
+
+typedef struct VhostGpuCursorPos {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+} QEMU_PACKED VhostGpuCursorPos;
+
+typedef struct VhostGpuCursorUpdate {
+    VhostGpuCursorPos pos;
+    uint32_t hot_x;
+    uint32_t hot_y;
+    uint32_t data[64 * 64];
+} QEMU_PACKED VhostGpuCursorUpdate;
+
+typedef struct VhostGpuScanout {
+    uint32_t scanout_id;
+    uint32_t width;
+    uint32_t height;
+} QEMU_PACKED VhostGpuScanout;
+
+typedef struct VhostGpuUpdate {
+    uint32_t scanout_id;
+    uint32_t x;
+    uint32_t y;
+    uint32_t width;
+    uint32_t height;
+    uint8_t data[];
+} QEMU_PACKED VhostGpuUpdate;
+
+typedef struct VhostGpuGlScanout {
+    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 VhostGpuGlScanout;
+
+typedef struct VhostGpuMsg {
+    VhostGpuRequest request;
+    uint32_t size; /* the following payload size */
+    union {
+        VhostGpuCursorPos cursor_pos;
+        VhostGpuCursorUpdate cursor_update;
+        VhostGpuScanout scanout;
+        VhostGpuUpdate update;
+        VhostGpuGlScanout gl_scanout;
+    } payload;
+} QEMU_PACKED VhostGpuMsg;
+
+static VhostGpuMsg m __attribute__ ((unused));
+#define VHOST_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;
+    int sock_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);
+
+ssize_t vg_sock_fd_write(int sock, void *buf, ssize_t buflen, int fd);
+
+#endif
diff --git a/contrib/vhost-user-gpu/main.c b/contrib/vhost-user-gpu/main.c
new file mode 100644
index 0000000000..e14d0d2e8a
--- /dev/null
+++ b/contrib/vhost-user-gpu/main.c
@@ -0,0 +1,953 @@
+/*
+ * 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 <glib.h>
+#include <pixman.h>
+
+#include "vugpu.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;
+    QTAILQ_ENTRY(virtio_gpu_simple_resource) next;
+};
+
+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 < G_N_ELEMENTS(vg_cmd_str)) {
+        return vg_cmd_str[cmd];
+    } else {
+        return "unknown";
+    }
+}
+
+ssize_t
+vg_sock_fd_write(int sock, void *buf, ssize_t buflen, int fd)
+{
+    ssize_t size;
+    struct msghdr msg;
+    struct iovec iov;
+    union {
+        struct cmsghdr cmsghdr;
+        char control[CMSG_SPACE(sizeof(int))];
+    } cmsgu;
+    struct cmsghdr *cmsg;
+
+    iov.iov_base = 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;
+    }
+
+    size = sendmsg(sock, &msg, 0);
+
+    return size;
+}
+
+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;
+    }
+    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 ,};
+    int i;
+
+    dpy_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO;
+    for (i = 0; i < 1 /* g->conf.max_outputs */; i++) {
+        /* if (g->enabled_output_bitmask & (1 << i)) { */
+        dpy_info.pmodes[i].enabled = 1;
+        dpy_info.pmodes[i].r.width = 1024;
+        dpy_info.pmodes[i].r.height = 768;
+        /* } */
+    }
+
+    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);
+
+    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;
+    }
+    res->image = pixman_image_create_bits(pformat,
+                                          c2d.width,
+                                          c2d.height,
+                                          NULL, 0);
+    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)
+{
+    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);
+
+    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);
+
+    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);
+
+    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);
+
+    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,
+                       (uint8_t *)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);
+
+    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;
+        }
+        /* dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); */
+        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;
+
+    VhostGpuMsg msg = {
+        .request = VHOST_GPU_SCANOUT,
+        .size = sizeof(VhostGpuScanout),
+        .payload.scanout.scanout_id = ss.scanout_id,
+        .payload.scanout.width = scanout->width,
+        .payload.scanout.height = scanout->height
+    };
+    vg_sock_fd_write(g->sock_fd, &msg, VHOST_GPU_HDR_SIZE + msg.size, -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);
+
+    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;
+        size_t bpp = PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8;
+        size_t size = width * height * bpp;
+
+        VhostGpuMsg *msg = g_malloc(VHOST_GPU_HDR_SIZE +
+                                    sizeof(VhostGpuUpdate) + size);
+        msg->request = VHOST_GPU_UPDATE;
+        msg->size = sizeof(VhostGpuUpdate) + 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_sock_fd_write(g->sock_fd, msg, VHOST_GPU_HDR_SIZE + msg->size, -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_debug("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;
+
+    g_debug("%s\n", __func__);
+
+    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));
+        }
+
+        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_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));
+        }
+        bool move = cursor.hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR;
+        g_debug("%s move:%d\n", __func__, move);
+
+        if (move) {
+            VhostGpuMsg msg = {
+                .request = cursor.resource_id ?
+                    VHOST_GPU_CURSOR_POS : VHOST_GPU_CURSOR_POS_HIDE,
+                .size = sizeof(VhostGpuCursorPos),
+                .payload.cursor_pos = {
+                    .scanout_id = cursor.pos.scanout_id,
+                    .x = cursor.pos.x,
+                    .y = cursor.pos.y,
+                }
+            };
+            vg_sock_fd_write(g->sock_fd, &msg,
+                             VHOST_GPU_HDR_SIZE + msg.size, -1);
+        } else {
+            VhostGpuMsg msg = {
+                .request = VHOST_GPU_CURSOR_UPDATE,
+                .size = sizeof(VhostGpuCursorUpdate),
+                .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_sock_fd_write(g->sock_fd, &msg,
+                             VHOST_GPU_HDR_SIZE + msg.size, -1);
+        }
+
+        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 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];
+        return 1;
+    default:
+        return 0;
+    }
+
+    return 0;
+}
+
+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) {
+        vg_virgl_init(g);
+        g->virgl_inited = true;
+    }
+
+    g->virgl = virgl;
+}
+
+static const VuDevIface vuiface = {
+    .set_features = vg_set_features,
+    .queue_set_started = vg_queue_set_started,
+    .process_msg = vg_process_msg,
+};
+
+static void
+vg_reset(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);
+    }
+}
+
+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;
+}
+
+char *socket_path;
+
+static GOptionEntry entries[] = {
+    { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &socket_path,
+      "Use UNIX socket path", "" },
+    { NULL, }
+};
+
+int
+main(int argc, char *argv[])
+{
+    GOptionContext *context;
+    GError *error = NULL;
+    GMainLoop *loop = NULL;
+    int fd;
+    VuGpu g = { .sock_fd = -1 };
+
+    QTAILQ_INIT(&g.reslist);
+    QTAILQ_INIT(&g.fenceq);
+
+    context = g_option_context_new("vhost-user-gpu 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);
+    }
+    g_option_context_free(context);
+
+    if (socket_path) {
+        int lsock = unix_sock_new(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_reset(&g);
+
+    return 0;
+}
diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c
new file mode 100644
index 0000000000..38fd5c0198
--- /dev/null
+++ b/contrib/vhost-user-gpu/virgl.c
@@ -0,0 +1,537 @@
+/*
+ * 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);
+}
+
+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;
+
+    g_debug("%s\n", __func__);
+    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 {
+        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));
+}
+
+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;
+        }
+
+        VhostGpuMsg msg = {
+            .request = VHOST_GPU_GL_SCANOUT,
+            .size = sizeof(VhostGpuGlScanout),
+            .payload.gl_scanout.scanout_id = ss.scanout_id,
+            .payload.gl_scanout.x =  ss.r.x,
+            .payload.gl_scanout.y =  ss.r.y,
+            .payload.gl_scanout.width = ss.r.width,
+            .payload.gl_scanout.height = ss.r.height,
+            .payload.gl_scanout.fd_width = info.width,
+            .payload.gl_scanout.fd_height = info.height,
+            .payload.gl_scanout.fd_stride = info.stride,
+            .payload.gl_scanout.fd_flags = info.flags,
+            .payload.gl_scanout.fd_drm_fourcc = info.drm_fourcc
+        };
+        vg_sock_fd_write(g->sock_fd, &msg, VHOST_GPU_HDR_SIZE + msg.size, fd);
+        close(fd);
+    } else {
+        VhostGpuMsg msg = {
+            .request = VHOST_GPU_GL_SCANOUT,
+            .size = sizeof(VhostGpuGlScanout),
+            .payload.gl_scanout.scanout_id = ss.scanout_id,
+        };
+        vg_sock_fd_write(g->sock_fd, &msg, VHOST_GPU_HDR_SIZE + msg.size, -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, ret;
+    uint32_t ok;
+
+    VUGPU_FILL_CMD(rf);
+
+    for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
+        if (g->scanout[i].resource_id != rf.resource_id) {
+            continue;
+        }
+        VhostGpuMsg msg = {
+            .request = VHOST_GPU_GL_UPDATE,
+            .size = sizeof(VhostGpuUpdate),
+            .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
+        };
+        ret = vg_sock_fd_write(g->sock_fd, &msg,
+                               VHOST_GPU_HDR_SIZE + msg.size, -1);
+        g_return_if_fail(ret == VHOST_GPU_HDR_SIZE + msg.size);
+        ret = read(g->sock_fd, &ok, sizeof(ok));
+        g_return_if_fail(ret == sizeof(ok));
+    }
+}
+
+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--;
+    }
+}
+
+static struct virgl_renderer_callbacks virgl_cbs = {
+    .version     = 1,
+    .write_fence = virgl_write_fence,
+};
+
+static void
+vg_virgl_poll(VuDev *dev, int condition, void *data)
+{
+    virgl_renderer_poll();
+}
+
+int
+vg_virgl_init(VuGpu *g)
+{
+    int ret;
+
+    ret = virgl_renderer_init(g,
+                              VIRGL_RENDERER_USE_EGL |
+                              VIRGL_RENDERER_THREAD_SYNC,
+                              &virgl_cbs);
+    if (ret != 0) {
+        return ret;
+    }
+
+    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 ret;
+}
diff --git a/Makefile b/Makefile
index d1222da359..fb2359f877 100644
--- a/Makefile
+++ b/Makefile
@@ -421,6 +421,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 c20f8d35d7..417c115f26 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 1d1caea9e7..675a4ba626 100755
--- a/configure
+++ b/configure
@@ -5533,6 +5533,7 @@ if test "$want_tools" = "yes" ; then
   if [ "$ivshmem" = "yes" ]; then
     tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools"
     tools="vhost-user-input\$(EXESUF) $tools"
+    tools="vhost-user-gpu\$(EXESUF) $tools"
   fi
 fi
 if test "$softmmu" = yes ; then
@@ -7141,6 +7142,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..da7c4b8ba9
--- /dev/null
+++ b/contrib/vhost-user-gpu/Makefile.objs
@@ -0,0 +1,7 @@
+vhost-user-gpu-obj-y = main.o virgl.o
+
+main.o-cflags := $(PIXMAN_CFLAGS)
+main.o-libs := $(PIXMAN_LIBS)
+
+virgl.o-cflags := $(VIRGL_CFLAGS)
+virgl.o-libs := $(VIRGL_LIBS)
-- 
2.17.1.906.g10fd178552

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

* Re: [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU
  2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
                   ` (11 preceding siblings ...)
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 12/12] contrib: add vhost-user-gpu Marc-André Lureau
@ 2018-06-01 17:28 ` no-reply
  12 siblings, 0 replies; 38+ messages in thread
From: no-reply @ 2018-06-01 17:28 UTC (permalink / raw)
  To: marcandre.lureau; +Cc: famz, qemu-devel, kraxel

Hi,

This series failed docker-mingw@fedora build test. Please find the testing commands and
their output below. If you have Docker installed, you can probably reproduce it
locally.

Type: series
Message-id: 20180601162749.27406-1-marcandre.lureau@redhat.com
Subject: [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU

=== TEST SCRIPT BEGIN ===
#!/bin/bash
set -e
git submodule update --init dtc
# Let docker tests dump environment info
export SHOW_ENV=1
export J=8
time make docker-test-mingw@fedora
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
Switched to a new branch 'test'
7e619ea0fc contrib: add vhost-user-gpu
a65b8cf410 Add virtio-gpu vhost-user backend
28ff828eb4 vhost-user: add vhost_user_gpu_set_socket()
caa5a91d56 Add vhost-input-pci
b1640c571e contrib: add vhost-user-input
404d16b19b libvhost-user: export vug_source_new
6c2fba2e53 vhost-user: add vhost_user_input_get_config()
fd2e0d8cf9 vhost-user: split vhost_user_read()
3ddd2476bb Add vhost-user-backend
cfe338e0f1 vhost-user: wrap some read/write with retry handling
8bce1b48c3 libvhost-user: exit by default on VHOST_USER_NONE
b249db152e chardev: avoid crash if no associated address

=== OUTPUT BEGIN ===
Submodule 'dtc' (git://git.qemu-project.org/dtc.git) registered for path 'dtc'
Cloning into '/var/tmp/patchew-tester-tmp-q1mu68mb/src/dtc'...
Submodule path 'dtc': checked out 'e54388015af1fb4bf04d0bca99caba1074d9cc42'
  BUILD   fedora
make[1]: Entering directory '/var/tmp/patchew-tester-tmp-q1mu68mb/src'
  GEN     /var/tmp/patchew-tester-tmp-q1mu68mb/src/docker-src.2018-06-01-13.26.35.22520/qemu.tar
Cloning into '/var/tmp/patchew-tester-tmp-q1mu68mb/src/docker-src.2018-06-01-13.26.35.22520/qemu.tar.vroot'...
done.
Your branch is up-to-date with 'origin/test'.
Submodule 'dtc' (git://git.qemu-project.org/dtc.git) registered for path 'dtc'
Cloning into '/var/tmp/patchew-tester-tmp-q1mu68mb/src/docker-src.2018-06-01-13.26.35.22520/qemu.tar.vroot/dtc'...
Submodule path 'dtc': checked out 'e54388015af1fb4bf04d0bca99caba1074d9cc42'
Submodule 'ui/keycodemapdb' (git://git.qemu.org/keycodemapdb.git) registered for path 'ui/keycodemapdb'
Cloning into '/var/tmp/patchew-tester-tmp-q1mu68mb/src/docker-src.2018-06-01-13.26.35.22520/qemu.tar.vroot/ui/keycodemapdb'...
Submodule path 'ui/keycodemapdb': checked out '6b3d716e2b6472eb7189d3220552280ef3d832ce'
  COPY    RUNNER
    RUN test-mingw in qemu:fedora 
Packages installed:
PyYAML-3.12-5.fc27.x86_64
SDL2-devel-2.0.7-2.fc27.x86_64
bc-1.07.1-3.fc27.x86_64
bison-3.0.4-8.fc27.x86_64
bluez-libs-devel-5.48-3.fc27.x86_64
brlapi-devel-0.6.6-8.fc27.x86_64
bzip2-1.0.6-24.fc27.x86_64
bzip2-devel-1.0.6-24.fc27.x86_64
ccache-3.3.6-1.fc27.x86_64
clang-5.0.1-5.fc27.x86_64
device-mapper-multipath-devel-0.7.1-9.git847cc43.fc27.x86_64
findutils-4.6.0-16.fc27.x86_64
flex-2.6.1-5.fc27.x86_64
gcc-7.3.1-5.fc27.x86_64
gcc-c++-7.3.1-5.fc27.x86_64
gettext-0.19.8.1-12.fc27.x86_64
git-2.14.3-3.fc27.x86_64
glib2-devel-2.54.3-2.fc27.x86_64
glusterfs-api-devel-3.12.7-1.fc27.x86_64
gnutls-devel-3.5.18-2.fc27.x86_64
gtk3-devel-3.22.26-2.fc27.x86_64
hostname-3.18-4.fc27.x86_64
libaio-devel-0.3.110-9.fc27.x86_64
libasan-7.3.1-5.fc27.x86_64
libattr-devel-2.4.47-21.fc27.x86_64
libcap-devel-2.25-7.fc27.x86_64
libcap-ng-devel-0.7.8-5.fc27.x86_64
libcurl-devel-7.55.1-10.fc27.x86_64
libfdt-devel-1.4.6-1.fc27.x86_64
libpng-devel-1.6.31-1.fc27.x86_64
librbd-devel-12.2.4-1.fc27.x86_64
libssh2-devel-1.8.0-5.fc27.x86_64
libubsan-7.3.1-5.fc27.x86_64
libusbx-devel-1.0.21-4.fc27.x86_64
libxml2-devel-2.9.7-1.fc27.x86_64
llvm-5.0.1-6.fc27.x86_64
lzo-devel-2.08-11.fc27.x86_64
make-4.2.1-4.fc27.x86_64
mingw32-SDL-1.2.15-9.fc27.noarch
mingw32-bzip2-1.0.6-9.fc27.noarch
mingw32-curl-7.54.1-2.fc27.noarch
mingw32-glib2-2.54.1-1.fc27.noarch
mingw32-gmp-6.1.2-2.fc27.noarch
mingw32-gnutls-3.5.13-2.fc27.noarch
mingw32-gtk2-2.24.31-4.fc27.noarch
mingw32-gtk3-3.22.16-1.fc27.noarch
mingw32-libjpeg-turbo-1.5.1-3.fc27.noarch
mingw32-libpng-1.6.29-2.fc27.noarch
mingw32-libssh2-1.8.0-3.fc27.noarch
mingw32-libtasn1-4.13-1.fc27.noarch
mingw32-nettle-3.3-3.fc27.noarch
mingw32-pixman-0.34.0-3.fc27.noarch
mingw32-pkg-config-0.28-9.fc27.x86_64
mingw64-SDL-1.2.15-9.fc27.noarch
mingw64-bzip2-1.0.6-9.fc27.noarch
mingw64-curl-7.54.1-2.fc27.noarch
mingw64-glib2-2.54.1-1.fc27.noarch
mingw64-gmp-6.1.2-2.fc27.noarch
mingw64-gnutls-3.5.13-2.fc27.noarch
mingw64-gtk2-2.24.31-4.fc27.noarch
mingw64-gtk3-3.22.16-1.fc27.noarch
mingw64-libjpeg-turbo-1.5.1-3.fc27.noarch
mingw64-libpng-1.6.29-2.fc27.noarch
mingw64-libssh2-1.8.0-3.fc27.noarch
mingw64-libtasn1-4.13-1.fc27.noarch
mingw64-nettle-3.3-3.fc27.noarch
mingw64-pixman-0.34.0-3.fc27.noarch
mingw64-pkg-config-0.28-9.fc27.x86_64
ncurses-devel-6.0-13.20170722.fc27.x86_64
nettle-devel-3.4-1.fc27.x86_64
nss-devel-3.36.0-1.0.fc27.x86_64
numactl-devel-2.0.11-5.fc27.x86_64
package libjpeg-devel is not installed
perl-5.26.1-403.fc27.x86_64
pixman-devel-0.34.0-4.fc27.x86_64
python3-3.6.2-13.fc27.x86_64
snappy-devel-1.1.4-5.fc27.x86_64
sparse-0.5.1-2.fc27.x86_64
spice-server-devel-0.14.0-1.fc27.x86_64
systemtap-sdt-devel-3.2-3.fc27.x86_64
tar-1.29-7.fc27.x86_64
usbredir-devel-0.7.1-5.fc27.x86_64
virglrenderer-devel-0.6.0-3.20170210git76b3da97b.fc27.x86_64
vte3-devel-0.36.5-5.fc27.x86_64
which-2.21-4.fc27.x86_64
xen-devel-4.9.1-5.fc27.x86_64
zlib-devel-1.2.11-4.fc27.x86_64

Environment variables:
TARGET_LIST=
PACKAGES=ccache gettext git tar PyYAML sparse flex bison python3 bzip2 hostname     gcc gcc-c++ llvm clang make perl which bc findutils glib2-devel     libaio-devel pixman-devel zlib-devel libfdt-devel libasan libubsan     bluez-libs-devel brlapi-devel bzip2-devel     device-mapper-multipath-devel glusterfs-api-devel gnutls-devel     gtk3-devel libattr-devel libcap-devel libcap-ng-devel libcurl-devel     libjpeg-devel libpng-devel librbd-devel libssh2-devel libusbx-devel     libxml2-devel lzo-devel ncurses-devel nettle-devel nss-devel     numactl-devel SDL2-devel snappy-devel spice-server-devel     systemtap-sdt-devel usbredir-devel virglrenderer-devel vte3-devel     xen-devel     mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL mingw32-pkg-config     mingw32-gtk2 mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1     mingw32-libjpeg-turbo mingw32-libpng mingw32-curl mingw32-libssh2     mingw32-bzip2     mingw64-pixman mingw64-glib2 mingw64-gmp mingw64-SDL mingw64-pkg-config     mingw64-gtk2 mingw64-gtk3 mingw64-gnutls mingw64-nettle mingw64-libtasn1     mingw64-libjpeg-turbo mingw64-libpng mingw64-curl mingw64-libssh2     mingw64-bzip2
J=8
V=
HOSTNAME=1b9bd3175d30
DEBUG=
SHOW_ENV=1
PWD=/
HOME=/root
CCACHE_DIR=/var/tmp/ccache
DISTTAG=f27container
QEMU_CONFIGURE_OPTS=--python=/usr/bin/python3
FGC=f27
TEST_DIR=/tmp/qemu-test
SHLVL=1
FEATURES=mingw clang pyyaml asan dtc
PATH=/usr/lib/ccache:/usr/lib64/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAKEFLAGS= -j8
EXTRA_CONFIGURE_OPTS=
_=/usr/bin/env

Configure options:
--enable-werror --target-list=x86_64-softmmu,aarch64-softmmu --prefix=/tmp/qemu-test/install --python=/usr/bin/python3 --cross-prefix=x86_64-w64-mingw32- --enable-trace-backends=simple --enable-gnutls --enable-nettle --enable-curl --enable-vnc --enable-bzip2 --enable-guest-agent --with-sdlabi=1.2 --with-gtkabi=2.0
Install prefix    /tmp/qemu-test/install
BIOS directory    /tmp/qemu-test/install
firmware path     /tmp/qemu-test/install/share/qemu-firmware
binary directory  /tmp/qemu-test/install
library directory /tmp/qemu-test/install/lib
module directory  /tmp/qemu-test/install/lib
libexec directory /tmp/qemu-test/install/libexec
include directory /tmp/qemu-test/install/include
config directory  /tmp/qemu-test/install
local state directory   queried at runtime
Windows SDK       no
Source path       /tmp/qemu-test/src
GIT binary        git
GIT submodules    
C compiler        x86_64-w64-mingw32-gcc
Host C compiler   cc
C++ compiler      x86_64-w64-mingw32-g++
Objective-C compiler clang
ARFLAGS           rv
CFLAGS            -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -g 
QEMU_CFLAGS       -I/usr/x86_64-w64-mingw32/sys-root/mingw/include/pixman-1  -I$(SRC_PATH)/dtc/libfdt -Werror -DHAS_LIBSSH2_SFTP_FSYNC -mms-bitfields -I/usr/x86_64-w64-mingw32/sys-root/mingw/include/glib-2.0 -I/usr/x86_64-w64-mingw32/sys-root/mingw/lib/glib-2.0/include -I/usr/x86_64-w64-mingw32/sys-root/mingw/include  -m64 -mcx16 -mthreads -D__USE_MINGW_ANSI_STDIO=1 -DWIN32_LEAN_AND_MEAN -DWINVER=0x501 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -Wstrict-prototypes -Wredundant-decls -Wall -Wundef -Wwrite-strings -Wmissing-prototypes -fno-strict-aliasing -fno-common -fwrapv  -Wexpansion-to-defined -Wendif-labels -Wno-shift-negative-value -Wno-missing-include-dirs -Wempty-body -Wnested-externs -Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers -Wold-style-declaration -Wold-style-definition -Wtype-limits -fstack-protector-strong -I/usr/x86_64-w64-mingw32/sys-root/mingw/include -I/usr/x86_64-w64-mingw32/sys-root/mingw/include/p11-kit-1 -I/usr/x86_64-w64-mingw32/sys-root/mingw/include  -I/usr/x86_64-w64-mingw32/sys-root/mingw/include   -I/usr/x86_64-w64-mingw32/sys-root/mingw/include/libpng16 
LDFLAGS           -Wl,--nxcompat -Wl,--no-seh -Wl,--dynamicbase -Wl,--warn-common -m64 -g 
QEMU_LDFLAGS      -L$(BUILD_DIR)/dtc/libfdt 
make              make
install           install
python            /usr/bin/python3 -B
smbd              /usr/sbin/smbd
module support    no
host CPU          x86_64
host big endian   no
target list       x86_64-softmmu aarch64-softmmu
gprof enabled     no
sparse enabled    no
strip binaries    yes
profiler          no
static build      no
SDL support       yes (1.2.15)
GTK support       yes (2.24.31)
GTK GL support    no
VTE support       no 
TLS priority      NORMAL
GNUTLS support    yes
GNUTLS rnd        yes
libgcrypt         no
libgcrypt kdf     no
nettle            yes (3.3)
nettle kdf        yes
libtasn1          yes
curses support    no
virgl support     no
curl support      yes
mingw32 support   yes
Audio drivers     dsound
Block whitelist (rw) 
Block whitelist (ro) 
VirtFS support    no
Multipath support no
VNC support       yes
VNC SASL support  no
VNC JPEG support  yes
VNC PNG support   yes
xen support       no
brlapi support    no
bluez  support    no
Documentation     no
PIE               no
vde support       no
netmap support    no
Linux AIO support no
ATTR/XATTR support no
Install blobs     yes
KVM support       no
HAX support       yes
HVF support       no
WHPX support      no
TCG support       yes
TCG debug enabled no
TCG interpreter   no
malloc trim support no
RDMA support      no
fdt support       git
membarrier        no
preadv support    no
fdatasync         no
madvise           no
posix_madvise     no
posix_memalign    no
libcap-ng support no
vhost-net support no
vhost-crypto support no
vhost-scsi support no
vhost-vsock support no
vhost-user support no
Trace backends    simple
Trace output file trace-<pid>
spice support     no 
rbd support       no
xfsctl support    no
smartcard support no
libusb            no
usb net redir     no
OpenGL support    no
OpenGL dmabufs    no
libiscsi support  no
libnfs support    no
build guest agent yes
QGA VSS support   no
QGA w32 disk info yes
QGA MSI support   no
seccomp support   no
coroutine backend win32
coroutine pool    yes
debug stack usage no
crypto afalg      no
GlusterFS support no
gcov              gcov
gcov enabled      no
TPM support       yes
libssh2 support   yes
TPM passthrough   no
TPM emulator      no
QOM debugging     yes
Live block migration yes
lzo support       no
snappy support    no
bzip2 support     yes
NUMA host support no
libxml2           no
tcmalloc support  no
jemalloc support  no
avx2 optimization yes
replication support yes
VxHS block device no
capstone          no

WARNING: Use of GTK 2.0 is deprecated and will be removed in
WARNING: future releases. Please switch to using GTK 3.0

WARNING: Use of SDL 1.2 is deprecated and will be removed in
WARNING: future releases. Please switch to using SDL 2.0
  GEN     x86_64-softmmu/config-devices.mak.tmp
  GEN     config-host.h
  GEN     aarch64-softmmu/config-devices.mak.tmp
  GEN     qemu-options.def
  GEN     qapi-gen
  GEN     trace/generated-tcg-tracers.h
  GEN     trace/generated-helpers-wrappers.h
  GEN     trace/generated-helpers.h
  GEN     x86_64-softmmu/config-devices.mak
  GEN     trace/generated-helpers.c
  GEN     module_block.h
  GEN     aarch64-softmmu/config-devices.mak
  GEN     ui/input-keymap-atset1-to-qcode.c
  GEN     ui/input-keymap-linux-to-qcode.c
  GEN     ui/input-keymap-qcode-to-atset1.c
  GEN     ui/input-keymap-qcode-to-atset2.c
  GEN     ui/input-keymap-qcode-to-atset3.c
  GEN     ui/input-keymap-qcode-to-linux.c
  GEN     ui/input-keymap-qcode-to-qnum.c
  GEN     ui/input-keymap-qcode-to-sun.c
  GEN     ui/input-keymap-qnum-to-qcode.c
  GEN     ui/input-keymap-usb-to-qcode.c
  GEN     ui/input-keymap-win32-to-qcode.c
  GEN     ui/input-keymap-x11-to-qcode.c
  GEN     ui/input-keymap-xorgevdev-to-qcode.c
  GEN     ui/input-keymap-xorgkbd-to-qcode.c
  GEN     ui/input-keymap-xorgxquartz-to-qcode.c
  GEN     ui/input-keymap-xorgxwin-to-qcode.c
  GEN     tests/test-qapi-gen
  GEN     trace-root.h
  GEN     accel/kvm/trace.h
  GEN     accel/tcg/trace.h
  GEN     audio/trace.h
  GEN     block/trace.h
  GEN     chardev/trace.h
  GEN     crypto/trace.h
  GEN     hw/9pfs/trace.h
  GEN     hw/acpi/trace.h
  GEN     hw/alpha/trace.h
  GEN     hw/arm/trace.h
  GEN     hw/audio/trace.h
  GEN     hw/block/trace.h
  GEN     hw/block/dataplane/trace.h
  GEN     hw/char/trace.h
  GEN     hw/display/trace.h
  GEN     hw/dma/trace.h
  GEN     hw/hppa/trace.h
  GEN     hw/i386/trace.h
  GEN     hw/i386/xen/trace.h
  GEN     hw/ide/trace.h
  GEN     hw/input/trace.h
  GEN     hw/intc/trace.h
  GEN     hw/isa/trace.h
  GEN     hw/mem/trace.h
  GEN     hw/misc/trace.h
  GEN     hw/misc/macio/trace.h
  GEN     hw/net/trace.h
  GEN     hw/nvram/trace.h
  GEN     hw/pci/trace.h
  GEN     hw/pci-host/trace.h
  GEN     hw/ppc/trace.h
  GEN     hw/rdma/trace.h
  GEN     hw/rdma/vmw/trace.h
  GEN     hw/s390x/trace.h
  GEN     hw/scsi/trace.h
  GEN     hw/sd/trace.h
  GEN     hw/sparc/trace.h
  GEN     hw/sparc64/trace.h
  GEN     hw/timer/trace.h
  GEN     hw/tpm/trace.h
  GEN     hw/usb/trace.h
  GEN     hw/vfio/trace.h
  GEN     hw/virtio/trace.h
  GEN     hw/xen/trace.h
  GEN     io/trace.h
  GEN     linux-user/trace.h
  GEN     migration/trace.h
  GEN     nbd/trace.h
  GEN     net/trace.h
  GEN     qapi/trace.h
  GEN     qom/trace.h
  GEN     scsi/trace.h
  GEN     target/arm/trace.h
  GEN     target/i386/trace.h
  GEN     target/mips/trace.h
  GEN     target/ppc/trace.h
  GEN     target/s390x/trace.h
  GEN     target/sparc/trace.h
  GEN     ui/trace.h
  GEN     util/trace.h
  GEN     trace-root.c
  GEN     accel/kvm/trace.c
  GEN     accel/tcg/trace.c
  GEN     audio/trace.c
  GEN     block/trace.c
  GEN     chardev/trace.c
  GEN     crypto/trace.c
  GEN     hw/9pfs/trace.c
  GEN     hw/acpi/trace.c
  GEN     hw/alpha/trace.c
  GEN     hw/arm/trace.c
  GEN     hw/audio/trace.c
  GEN     hw/block/trace.c
  GEN     hw/block/dataplane/trace.c
  GEN     hw/char/trace.c
  GEN     hw/display/trace.c
  GEN     hw/dma/trace.c
  GEN     hw/hppa/trace.c
  GEN     hw/i386/trace.c
  GEN     hw/i386/xen/trace.c
  GEN     hw/ide/trace.c
  GEN     hw/input/trace.c
  GEN     hw/intc/trace.c
  GEN     hw/isa/trace.c
  GEN     hw/mem/trace.c
  GEN     hw/misc/trace.c
  GEN     hw/misc/macio/trace.c
  GEN     hw/net/trace.c
  GEN     hw/nvram/trace.c
  GEN     hw/pci/trace.c
  GEN     hw/pci-host/trace.c
  GEN     hw/ppc/trace.c
  GEN     hw/rdma/trace.c
  GEN     hw/rdma/vmw/trace.c
  GEN     hw/s390x/trace.c
  GEN     hw/scsi/trace.c
  GEN     hw/sd/trace.c
  GEN     hw/sparc/trace.c
  GEN     hw/sparc64/trace.c
  GEN     hw/timer/trace.c
  GEN     hw/tpm/trace.c
  GEN     hw/usb/trace.c
  GEN     hw/vfio/trace.c
  GEN     hw/virtio/trace.c
  GEN     hw/xen/trace.c
  GEN     io/trace.c
  GEN     linux-user/trace.c
  GEN     migration/trace.c
  GEN     nbd/trace.c
  GEN     net/trace.c
  GEN     qapi/trace.c
  GEN     qom/trace.c
  GEN     scsi/trace.c
  GEN     target/arm/trace.c
  GEN     target/i386/trace.c
  GEN     target/mips/trace.c
  GEN     target/ppc/trace.c
  GEN     target/s390x/trace.c
  GEN     target/sparc/trace.c
  GEN     ui/trace.c
  GEN     util/trace.c
  GEN     config-all-devices.mak
	 DEP /tmp/qemu-test/src/dtc/tests/dumptrees.c
	 DEP /tmp/qemu-test/src/dtc/tests/trees.S
	 DEP /tmp/qemu-test/src/dtc/tests/testutils.c
	 DEP /tmp/qemu-test/src/dtc/tests/value-labels.c
	 DEP /tmp/qemu-test/src/dtc/tests/asm_tree_dump.c
	 DEP /tmp/qemu-test/src/dtc/tests/truncated_property.c
	 DEP /tmp/qemu-test/src/dtc/tests/check_path.c
	 DEP /tmp/qemu-test/src/dtc/tests/overlay_bad_fixup.c
	 DEP /tmp/qemu-test/src/dtc/tests/overlay.c
	 DEP /tmp/qemu-test/src/dtc/tests/subnode_iterate.c
	 DEP /tmp/qemu-test/src/dtc/tests/property_iterate.c
	 DEP /tmp/qemu-test/src/dtc/tests/integer-expressions.c
	 DEP /tmp/qemu-test/src/dtc/tests/utilfdt_test.c
	 DEP /tmp/qemu-test/src/dtc/tests/path_offset_aliases.c
	 DEP /tmp/qemu-test/src/dtc/tests/add_subnode_with_nops.c
	 DEP /tmp/qemu-test/src/dtc/tests/dtbs_equal_unordered.c
	 DEP /tmp/qemu-test/src/dtc/tests/dtb_reverse.c
	 DEP /tmp/qemu-test/src/dtc/tests/dtbs_equal_ordered.c
	 DEP /tmp/qemu-test/src/dtc/tests/extra-terminating-null.c
	 DEP /tmp/qemu-test/src/dtc/tests/incbin.c
	 DEP /tmp/qemu-test/src/dtc/tests/boot-cpuid.c
	 DEP /tmp/qemu-test/src/dtc/tests/phandle_format.c
	 DEP /tmp/qemu-test/src/dtc/tests/path-references.c
	 DEP /tmp/qemu-test/src/dtc/tests/references.c
	 DEP /tmp/qemu-test/src/dtc/tests/string_escapes.c
	 DEP /tmp/qemu-test/src/dtc/tests/propname_escapes.c
	 DEP /tmp/qemu-test/src/dtc/tests/appendprop2.c
	 DEP /tmp/qemu-test/src/dtc/tests/appendprop1.c
	 DEP /tmp/qemu-test/src/dtc/tests/del_node.c
	 DEP /tmp/qemu-test/src/dtc/tests/del_property.c
	 DEP /tmp/qemu-test/src/dtc/tests/setprop.c
	 DEP /tmp/qemu-test/src/dtc/tests/set_name.c
	 DEP /tmp/qemu-test/src/dtc/tests/rw_tree1.c
	 DEP /tmp/qemu-test/src/dtc/tests/open_pack.c
	 DEP /tmp/qemu-test/src/dtc/tests/nopulate.c
	 DEP /tmp/qemu-test/src/dtc/tests/mangle-layout.c
	 DEP /tmp/qemu-test/src/dtc/tests/move_and_save.c
	 DEP /tmp/qemu-test/src/dtc/tests/sw_tree1.c
	 DEP /tmp/qemu-test/src/dtc/tests/nop_node.c
	 DEP /tmp/qemu-test/src/dtc/tests/nop_property.c
	 DEP /tmp/qemu-test/src/dtc/tests/setprop_inplace.c
	 DEP /tmp/qemu-test/src/dtc/tests/stringlist.c
	 DEP /tmp/qemu-test/src/dtc/tests/addr_size_cells.c
	 DEP /tmp/qemu-test/src/dtc/tests/notfound.c
	 DEP /tmp/qemu-test/src/dtc/tests/sized_cells.c
	 DEP /tmp/qemu-test/src/dtc/tests/char_literal.c
	 DEP /tmp/qemu-test/src/dtc/tests/get_alias.c
	 DEP /tmp/qemu-test/src/dtc/tests/node_offset_by_compatible.c
	 DEP /tmp/qemu-test/src/dtc/tests/node_check_compatible.c
	 DEP /tmp/qemu-test/src/dtc/tests/node_offset_by_prop_value.c
	 DEP /tmp/qemu-test/src/dtc/tests/node_offset_by_phandle.c
	 DEP /tmp/qemu-test/src/dtc/tests/parent_offset.c
	 DEP /tmp/qemu-test/src/dtc/tests/supernode_atdepth_offset.c
	 DEP /tmp/qemu-test/src/dtc/tests/get_path.c
	 DEP /tmp/qemu-test/src/dtc/tests/get_phandle.c
	 DEP /tmp/qemu-test/src/dtc/tests/getprop.c
	 DEP /tmp/qemu-test/src/dtc/tests/get_name.c
	 DEP /tmp/qemu-test/src/dtc/tests/path_offset.c
	 DEP /tmp/qemu-test/src/dtc/tests/subnode_offset.c
	 DEP /tmp/qemu-test/src/dtc/tests/find_property.c
	 DEP /tmp/qemu-test/src/dtc/tests/root_node.c
	 DEP /tmp/qemu-test/src/dtc/tests/get_mem_rsv.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt_overlay.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt_addresses.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt_empty_tree.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt_strerror.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt_rw.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt_sw.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt_wip.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt.c
	 DEP /tmp/qemu-test/src/dtc/libfdt/fdt_ro.c
	 DEP /tmp/qemu-test/src/dtc/util.c
	 DEP /tmp/qemu-test/src/dtc/fdtoverlay.c
	 DEP /tmp/qemu-test/src/dtc/fdtput.c
	 DEP /tmp/qemu-test/src/dtc/fdtget.c
	 DEP /tmp/qemu-test/src/dtc/fdtdump.c
	 LEX convert-dtsv0-lexer.lex.c
	 DEP /tmp/qemu-test/src/dtc/srcpos.c
	 BISON dtc-parser.tab.c
	 LEX dtc-lexer.lex.c
	 DEP /tmp/qemu-test/src/dtc/treesource.c
	 DEP /tmp/qemu-test/src/dtc/fstree.c
	 DEP /tmp/qemu-test/src/dtc/livetree.c
	 DEP /tmp/qemu-test/src/dtc/flattree.c
	 DEP /tmp/qemu-test/src/dtc/dtc.c
	 DEP /tmp/qemu-test/src/dtc/data.c
	 DEP /tmp/qemu-test/src/dtc/checks.c
	 DEP convert-dtsv0-lexer.lex.c
	 DEP dtc-parser.tab.c
	 DEP dtc-lexer.lex.c
	CHK version_gen.h
	UPD version_gen.h
	 DEP /tmp/qemu-test/src/dtc/util.c
	 CC libfdt/fdt.o
	 CC libfdt/fdt_ro.o
	 CC libfdt/fdt_wip.o
	 CC libfdt/fdt_sw.o
	 CC libfdt/fdt_strerror.o
	 CC libfdt/fdt_rw.o
	 CC libfdt/fdt_addresses.o
	 CC libfdt/fdt_empty_tree.o
	 CC libfdt/fdt_overlay.o
	 AR libfdt/libfdt.a
x86_64-w64-mingw32-ar: creating libfdt/libfdt.a
a - libfdt/fdt.o
a - libfdt/fdt_ro.o
a - libfdt/fdt_wip.o
a - libfdt/fdt_sw.o
a - libfdt/fdt_rw.o
a - libfdt/fdt_strerror.o
a - libfdt/fdt_empty_tree.o
a - libfdt/fdt_addresses.o
a - libfdt/fdt_overlay.o
  RC      version.o
  GEN     qga/qapi-generated/qapi-gen
  CC      qapi/qapi-builtin-types.o
  CC      qapi/qapi-types-block-core.o
  CC      qapi/qapi-types-char.o
  CC      qapi/qapi-types-common.o
  CC      qapi/qapi-types-crypto.o
  CC      qapi/qapi-types-block.o
  CC      qapi/qapi-types.o
  CC      qapi/qapi-types-introspect.o
  CC      qapi/qapi-types-job.o
  CC      qapi/qapi-types-migration.o
  CC      qapi/qapi-types-misc.o
  CC      qapi/qapi-types-net.o
  CC      qapi/qapi-types-rocker.o
  CC      qapi/qapi-types-run-state.o
  CC      qapi/qapi-types-sockets.o
  CC      qapi/qapi-types-tpm.o
  CC      qapi/qapi-types-trace.o
  CC      qapi/qapi-types-transaction.o
  CC      qapi/qapi-types-ui.o
  CC      qapi/qapi-builtin-visit.o
  CC      qapi/qapi-visit.o
  CC      qapi/qapi-visit-block-core.o
  CC      qapi/qapi-visit-block.o
  CC      qapi/qapi-visit-char.o
  CC      qapi/qapi-visit-common.o
  CC      qapi/qapi-visit-crypto.o
  CC      qapi/qapi-visit-introspect.o
  CC      qapi/qapi-visit-job.o
  CC      qapi/qapi-visit-migration.o
  CC      qapi/qapi-visit-misc.o
  CC      qapi/qapi-visit-net.o
  CC      qapi/qapi-visit-rocker.o
  CC      qapi/qapi-visit-run-state.o
  CC      qapi/qapi-visit-sockets.o
  CC      qapi/qapi-visit-tpm.o
  CC      qapi/qapi-visit-trace.o
  CC      qapi/qapi-visit-transaction.o
  CC      qapi/qapi-visit-ui.o
  CC      qapi/qapi-events.o
  CC      qapi/qapi-events-block-core.o
  CC      qapi/qapi-events-block.o
  CC      qapi/qapi-events-char.o
  CC      qapi/qapi-events-common.o
  CC      qapi/qapi-events-crypto.o
  CC      qapi/qapi-events-introspect.o
  CC      qapi/qapi-events-job.o
  CC      qapi/qapi-events-migration.o
  CC      qapi/qapi-events-misc.o
  CC      qapi/qapi-events-net.o
  CC      qapi/qapi-events-rocker.o
  CC      qapi/qapi-events-run-state.o
  CC      qapi/qapi-events-sockets.o
  CC      qapi/qapi-events-tpm.o
  CC      qapi/qapi-events-trace.o
  CC      qapi/qapi-events-transaction.o
  CC      qapi/qapi-events-ui.o
  CC      qapi/qapi-introspect.o
  CC      qapi/qapi-visit-core.o
  CC      qapi/qapi-dealloc-visitor.o
  CC      qapi/qobject-input-visitor.o
  CC      qapi/qobject-output-visitor.o
  CC      qapi/qmp-registry.o
  CC      qapi/qmp-dispatch.o
  CC      qapi/string-input-visitor.o
  CC      qapi/string-output-visitor.o
  CC      qapi/opts-visitor.o
  CC      qapi/qapi-clone-visitor.o
  CC      qapi/qmp-event.o
  CC      qapi/qapi-util.o
  CC      qobject/qnull.o
  CC      qobject/qnum.o
  CC      qobject/qstring.o
  CC      qobject/qdict.o
  CC      qobject/qlist.o
  CC      qobject/qbool.o
  CC      qobject/qlit.o
  CC      qobject/qjson.o
  CC      qobject/qobject.o
  CC      qobject/json-lexer.o
  CC      qobject/json-streamer.o
  CC      qobject/json-parser.o
  CC      trace/simple.o
  CC      trace/qmp.o
  CC      trace/control.o
  CC      util/osdep.o
  CC      util/cutils.o
  CC      util/unicode.o
  CC      util/qemu-timer-common.o
  CC      util/bufferiszero.o
  CC      util/lockcnt.o
  CC      util/aiocb.o
  CC      util/async.o
  CC      util/aio-wait.o
  CC      util/thread-pool.o
  CC      util/main-loop.o
  CC      util/qemu-timer.o
  CC      util/iohandler.o
  CC      util/aio-win32.o
  CC      util/event_notifier-win32.o
  CC      util/oslib-win32.o
  CC      util/qemu-thread-win32.o
  CC      util/envlist.o
  CC      util/path.o
  CC      util/module.o
  CC      util/host-utils.o
  CC      util/bitmap.o
  CC      util/bitops.o
  CC      util/hbitmap.o
  CC      util/fifo8.o
  CC      util/acl.o
  CC      util/cacheinfo.o
  CC      util/error.o
  CC      util/qemu-error.o
  CC      util/id.o
  CC      util/iov.o
  CC      util/qemu-config.o
  CC      util/qemu-sockets.o
  CC      util/uri.o
  CC      util/notify.o
  CC      util/qemu-option.o
  CC      util/qemu-progress.o
  CC      util/keyval.o
  CC      util/hexdump.o
  CC      util/uuid.o
  CC      util/crc32c.o
  CC      util/throttle.o
  CC      util/getauxval.o
  CC      util/readline.o
  CC      util/rcu.o
  CC      util/qemu-coroutine.o
  CC      util/qemu-coroutine-lock.o
  CC      util/qemu-coroutine-io.o
  CC      util/qemu-coroutine-sleep.o
  CC      util/coroutine-win32.o
  CC      util/buffer.o
  CC      util/timed-average.o
  CC      util/base64.o
  CC      util/log.o
  CC      util/pagesize.o
  CC      util/qdist.o
  CC      util/qht.o
  CC      util/range.o
  CC      util/stats64.o
  CC      util/systemd.o
  CC      util/iova-tree.o
  CC      trace-root.o
  CC      accel/kvm/trace.o
  CC      accel/tcg/trace.o
  CC      audio/trace.o
  CC      block/trace.o
  CC      chardev/trace.o
  CC      crypto/trace.o
  CC      hw/9pfs/trace.o
  CC      hw/acpi/trace.o
  CC      hw/alpha/trace.o
  CC      hw/arm/trace.o
  CC      hw/audio/trace.o
  CC      hw/block/trace.o
  CC      hw/block/dataplane/trace.o
  CC      hw/char/trace.o
  CC      hw/display/trace.o
  CC      hw/dma/trace.o
  CC      hw/hppa/trace.o
  CC      hw/i386/trace.o
  CC      hw/i386/xen/trace.o
  CC      hw/ide/trace.o
  CC      hw/input/trace.o
  CC      hw/intc/trace.o
  CC      hw/isa/trace.o
  CC      hw/mem/trace.o
  CC      hw/misc/trace.o
  CC      hw/misc/macio/trace.o
  CC      hw/net/trace.o
  CC      hw/nvram/trace.o
  CC      hw/pci/trace.o
  CC      hw/pci-host/trace.o
  CC      hw/ppc/trace.o
  CC      hw/rdma/trace.o
  CC      hw/rdma/vmw/trace.o
  CC      hw/s390x/trace.o
  CC      hw/scsi/trace.o
  CC      hw/sd/trace.o
  CC      hw/sparc/trace.o
  CC      hw/sparc64/trace.o
  CC      hw/timer/trace.o
  CC      hw/tpm/trace.o
  CC      hw/usb/trace.o
  CC      hw/vfio/trace.o
  CC      hw/virtio/trace.o
  CC      hw/xen/trace.o
  CC      io/trace.o
  CC      linux-user/trace.o
  CC      migration/trace.o
  CC      nbd/trace.o
  CC      net/trace.o
  CC      qapi/trace.o
  CC      qom/trace.o
  CC      scsi/trace.o
  CC      target/arm/trace.o
  CC      target/i386/trace.o
  CC      target/mips/trace.o
  CC      target/ppc/trace.o
  CC      target/s390x/trace.o
  CC      target/sparc/trace.o
  CC      ui/trace.o
  CC      util/trace.o
  CC      crypto/pbkdf-stub.o
  CC      stubs/arch-query-cpu-def.o
  CC      stubs/arch-query-cpu-model-expansion.o
  CC      stubs/arch-query-cpu-model-comparison.o
  CC      stubs/arch-query-cpu-model-baseline.o
  CC      stubs/bdrv-next-monitor-owned.o
  CC      stubs/blk-commit-all.o
  CC      stubs/blockdev-close-all-bdrv-states.o
  CC      stubs/clock-warp.o
  CC      stubs/cpu-get-clock.o
  CC      stubs/cpu-get-icount.o
  CC      stubs/dump.o
  CC      stubs/error-printf.o
  CC      stubs/fdset.o
  CC      stubs/gdbstub.o
  CC      stubs/get-vm-name.o
  CC      stubs/iothread.o
  CC      stubs/iothread-lock.o
  CC      stubs/is-daemonized.o
  CC      stubs/machine-init-done.o
  CC      stubs/migr-blocker.o
  CC      stubs/change-state-handler.o
  CC      stubs/monitor.o
  CC      stubs/notify-event.o
  CC      stubs/qtest.o
  CC      stubs/replay.o
  CC      stubs/runstate-check.o
  CC      stubs/set-fd-handler.o
  CC      stubs/slirp.o
  CC      stubs/sysbus.o
  CC      stubs/tpm.o
  CC      stubs/trace-control.o
  CC      stubs/uuid.o
  CC      stubs/vm-stop.o
  CC      stubs/fd-register.o
  CC      stubs/vmstate.o
  CC      stubs/qmp_memory_device.o
  CC      stubs/target-monitor-defs.o
  CC      stubs/target-get-monitor-def.o
  CC      stubs/pc_madt_cpu_entry.o
  CC      stubs/vmgenid.o
  CC      stubs/xen-common.o
  CC      stubs/xen-hvm.o
  CC      stubs/pci-host-piix.o
  CC      stubs/ram-block.o
  GEN     qemu-img-cmds.h
  CC      block.o
  CC      blockjob.o
  CC      job.o
  CC      qemu-io-cmds.o
  CC      replication.o
  CC      block/raw-format.o
  CC      block/qcow.o
  CC      block/vdi.o
  CC      block/cloop.o
  CC      block/vmdk.o
  CC      block/bochs.o
  CC      block/vpc.o
  CC      block/vvfat.o
  CC      block/dmg.o
  CC      block/qcow2.o
  CC      block/qcow2-refcount.o
  CC      block/qcow2-cluster.o
  CC      block/qcow2-snapshot.o
  CC      block/qcow2-cache.o
  CC      block/qcow2-bitmap.o
  CC      block/qed.o
  CC      block/qed-l2-cache.o
  CC      block/qed-table.o
  CC      block/qed-cluster.o
  CC      block/qed-check.o
  CC      block/vhdx.o
  CC      block/vhdx-endian.o
  CC      block/vhdx-log.o
  CC      block/quorum.o
  CC      block/parallels.o
  CC      block/blkdebug.o
  CC      block/blkverify.o
  CC      block/blkreplay.o
  CC      block/block-backend.o
  CC      block/snapshot.o
  CC      block/qapi.o
  CC      block/file-win32.o
  CC      block/win32-aio.o
  CC      block/null.o
  CC      block/mirror.o
  CC      block/commit.o
  CC      block/io.o
  CC      block/create.o
  CC      block/throttle-groups.o
  CC      block/nbd.o
  CC      block/nbd-client.o
  CC      block/sheepdog.o
  CC      block/accounting.o
  CC      block/dirty-bitmap.o
  CC      block/write-threshold.o
  CC      block/backup.o
  CC      block/replication.o
  CC      block/throttle.o
  CC      block/copy-on-read.o
  CC      block/crypto.o
  CC      nbd/server.o
  CC      nbd/client.o
  CC      nbd/common.o
  CC      scsi/utils.o
  CC      block/curl.o
  CC      block/ssh.o
  CC      block/dmg-bz2.o
  CC      crypto/init.o
  CC      crypto/hash.o
  CC      crypto/hash-nettle.o
  CC      crypto/hmac.o
  CC      crypto/hmac-nettle.o
  CC      crypto/aes.o
  CC      crypto/desrfb.o
  CC      crypto/cipher.o
  CC      crypto/tlscreds.o
  CC      crypto/tlscredsanon.o
  CC      crypto/tlscredsx509.o
  CC      crypto/tlssession.o
  CC      crypto/secret.o
  CC      crypto/random-gnutls.o
  CC      crypto/pbkdf.o
  CC      crypto/pbkdf-nettle.o
  CC      crypto/ivgen.o
  CC      crypto/ivgen-essiv.o
  CC      crypto/ivgen-plain.o
  CC      crypto/ivgen-plain64.o
  CC      crypto/afsplit.o
  CC      crypto/xts.o
  CC      crypto/block.o
  CC      crypto/block-qcow.o
  CC      crypto/block-luks.o
  CC      io/channel.o
  CC      io/channel-buffer.o
  CC      io/channel-command.o
  CC      io/channel-file.o
  CC      io/channel-socket.o
  CC      io/channel-tls.o
  CC      io/channel-watch.o
  CC      io/channel-websock.o
  CC      io/channel-util.o
  CC      io/dns-resolver.o
  CC      io/net-listener.o
  CC      io/task.o
  CC      qom/object.o
  CC      qom/container.o
  CC      qom/qom-qobject.o
  CC      qom/object_interfaces.o
  CC      qemu-io.o
  CC      blockdev.o
  CC      blockdev-nbd.o
  CC      bootdevice.o
  CC      iothread.o
  CC      job-qmp.o
  CC      qdev-monitor.o
  CC      os-win32.o
  CC      device-hotplug.o
  CC      bt-host.o
  CC      bt-vhci.o
  CC      dma-helpers.o
  CC      vl.o
  CC      tpm.o
  CC      device_tree.o
  CC      qapi/qapi-commands.o
  CC      qapi/qapi-commands-block-core.o
  CC      qapi/qapi-commands-block.o
  CC      qapi/qapi-commands-char.o
  CC      qapi/qapi-commands-common.o
  CC      qapi/qapi-commands-crypto.o
  CC      qapi/qapi-commands-introspect.o
  CC      qapi/qapi-commands-job.o
  CC      qapi/qapi-commands-misc.o
  CC      qapi/qapi-commands-migration.o
  CC      qapi/qapi-commands-net.o
  CC      qapi/qapi-commands-rocker.o
  CC      qapi/qapi-commands-run-state.o
  CC      qapi/qapi-commands-sockets.o
  CC      qapi/qapi-commands-tpm.o
  CC      qapi/qapi-commands-trace.o
  CC      qapi/qapi-commands-transaction.o
  CC      qapi/qapi-commands-ui.o
  CC      qmp.o
  CC      hmp.o
  CC      cpus-common.o
  CC      audio/audio.o
  CC      audio/noaudio.o
  CC      audio/wavaudio.o
  CC      audio/mixeng.o
  CC      audio/dsoundaudio.o
  CC      audio/audio_win_int.o
  CC      audio/wavcapture.o
  CC      backends/rng.o
  CC      backends/rng-egd.o
  CC      backends/tpm.o
  CC      backends/hostmem.o
  CC      backends/hostmem-ram.o
  CC      backends/cryptodev.o
  CC      backends/cryptodev-builtin.o
  CC      backends/cryptodev-vhost.o
  CC      block/stream.o
  CC      chardev/msmouse.o
  CC      chardev/wctablet.o
  CC      chardev/testdev.o
  CC      disas/arm.o
  CXX     disas/arm-a64.o
  CC      disas/i386.o
  CXX     disas/libvixl/vixl/utils.o
  CXX     disas/libvixl/vixl/compiler-intrinsics.o
  CXX     disas/libvixl/vixl/a64/instructions-a64.o
  CXX     disas/libvixl/vixl/a64/decoder-a64.o
  CXX     disas/libvixl/vixl/a64/disasm-a64.o
  CC      hw/acpi/core.o
  CC      hw/acpi/piix4.o
  CC      hw/acpi/pcihp.o
  CC      hw/acpi/ich9.o
  CC      hw/acpi/tco.o
  CC      hw/acpi/cpu_hotplug.o
  CC      hw/acpi/memory_hotplug.o
  CC      hw/acpi/cpu.o
  CC      hw/acpi/nvdimm.o
  CC      hw/acpi/vmgenid.o
  CC      hw/acpi/acpi_interface.o
  CC      hw/acpi/bios-linker-loader.o
  CC      hw/acpi/aml-build.o
  CC      hw/acpi/ipmi.o
  CC      hw/acpi/acpi-stub.o
  CC      hw/acpi/ipmi-stub.o
  CC      hw/audio/sb16.o
  CC      hw/audio/es1370.o
  CC      hw/audio/ac97.o
  CC      hw/audio/fmopl.o
  CC      hw/audio/adlib.o
  CC      hw/audio/gus.o
  CC      hw/audio/gusemu_hal.o
  CC      hw/audio/gusemu_mixer.o
  CC      hw/audio/cs4231a.o
  CC      hw/audio/intel-hda.o
  CC      hw/audio/hda-codec.o
  CC      hw/audio/pcspk.o
  CC      hw/audio/wm8750.o
  CC      hw/audio/pl041.o
  CC      hw/audio/lm4549.o
  CC      hw/audio/marvell_88w8618.o
  CC      hw/audio/soundhw.o
  CC      hw/block/block.o
  CC      hw/block/cdrom.o
  CC      hw/block/hd-geometry.o
  CC      hw/block/fdc.o
  CC      hw/block/m25p80.o
  CC      hw/block/nand.o
  CC      hw/block/pflash_cfi01.o
  CC      hw/block/pflash_cfi02.o
  CC      hw/block/ecc.o
  CC      hw/block/onenand.o
  CC      hw/block/nvme.o
  CC      hw/bt/core.o
  CC      hw/bt/l2cap.o
  CC      hw/bt/sdp.o
  CC      hw/bt/hci.o
  CC      hw/bt/hid.o
  CC      hw/bt/hci-csr.o
  CC      hw/char/ipoctal232.o
  CC      hw/char/parallel.o
  CC      hw/char/parallel-isa.o
  CC      hw/char/pl011.o
  CC      hw/char/serial.o
  CC      hw/char/serial-isa.o
  CC      hw/char/serial-pci.o
  CC      hw/char/virtio-console.o
  CC      hw/char/cadence_uart.o
  CC      hw/char/cmsdk-apb-uart.o
  CC      hw/char/debugcon.o
  CC      hw/char/imx_serial.o
  CC      hw/core/qdev.o
  CC      hw/core/qdev-properties.o
  CC      hw/core/bus.o
  CC      hw/core/reset.o
  CC      hw/core/qdev-fw.o
  CC      hw/core/fw-path-provider.o
  CC      hw/core/irq.o
  CC      hw/core/hotplug.o
  CC      hw/core/nmi.o
  CC      hw/core/stream.o
  CC      hw/core/ptimer.o
  CC      hw/core/machine.o
  CC      hw/core/sysbus.o
  CC      hw/core/loader.o
  CC      hw/core/qdev-properties-system.o
  CC      hw/core/register.o
  CC      hw/core/or-irq.o
  CC      hw/core/split-irq.o
  CC      hw/core/platform-bus.o
  CC      hw/cpu/core.o
  CC      hw/display/ads7846.o
  CC      hw/display/cirrus_vga.o
  CC      hw/display/pl110.o
  CC      hw/display/sii9022.o
  CC      hw/display/ssd0303.o
  CC      hw/display/ssd0323.o
  CC      hw/display/vga-pci.o
  CC      hw/display/bochs-display.o
  CC      hw/display/vga-isa.o
  CC      hw/display/vmware_vga.o
  CC      hw/display/blizzard.o
  CC      hw/display/exynos4210_fimd.o
  CC      hw/display/framebuffer.o
  CC      hw/display/tc6393xb.o
  CC      hw/dma/pl080.o
  CC      hw/dma/pl330.o
  CC      hw/dma/i8257.o
  CC      hw/dma/xilinx_axidma.o
  CC      hw/dma/xlnx-zynq-devcfg.o
  CC      hw/dma/xlnx-zdma.o
  CC      hw/gpio/max7310.o
  CC      hw/gpio/pl061.o
  CC      hw/gpio/zaurus.o
  CC      hw/gpio/gpio_key.o
  CC      hw/i2c/core.o
  CC      hw/i2c/smbus.o
  CC      hw/i2c/smbus_eeprom.o
  CC      hw/i2c/i2c-ddc.o
  CC      hw/i2c/versatile_i2c.o
  CC      hw/i2c/smbus_ich9.o
  CC      hw/i2c/pm_smbus.o
  CC      hw/i2c/bitbang_i2c.o
  CC      hw/i2c/exynos4210_i2c.o
  CC      hw/i2c/imx_i2c.o
  CC      hw/i2c/aspeed_i2c.o
  CC      hw/ide/core.o
  CC      hw/ide/atapi.o
  CC      hw/ide/qdev.o
  CC      hw/ide/pci.o
  CC      hw/ide/isa.o
  CC      hw/ide/piix.o
  CC      hw/ide/microdrive.o
  CC      hw/ide/ahci.o
  CC      hw/ide/ich.o
  CC      hw/ide/ahci-allwinner.o
  CC      hw/input/hid.o
  CC      hw/input/lm832x.o
  CC      hw/input/pckbd.o
  CC      hw/input/pl050.o
  CC      hw/input/ps2.o
  CC      hw/input/stellaris_input.o
  CC      hw/input/tsc2005.o
  CC      hw/input/virtio-input.o
  CC      hw/input/virtio-input-hid.o
  CC      hw/input/vhost-user-input.o
  CC      hw/intc/i8259_common.o
  CC      hw/intc/i8259.o
  CC      hw/intc/pl190.o
  CC      hw/intc/xlnx-pmu-iomod-intc.o
  CC      hw/intc/xlnx-zynqmp-ipi.o
  CC      hw/intc/imx_avic.o
  CC      hw/intc/imx_gpcv2.o
  CC      hw/intc/realview_gic.o
  CC      hw/intc/ioapic_common.o
  CC      hw/intc/arm_gic_common.o
  CC      hw/intc/arm_gic.o
  CC      hw/intc/arm_gicv2m.o
  CC      hw/intc/arm_gicv3_common.o
  CC      hw/intc/arm_gicv3.o
  CC      hw/intc/arm_gicv3_dist.o
  CC      hw/intc/arm_gicv3_redist.o
  CC      hw/intc/arm_gicv3_its_common.o
  CC      hw/intc/intc.o
  CC      hw/ipack/ipack.o
  CC      hw/ipack/tpci200.o
  CC      hw/ipmi/ipmi.o
  CC      hw/ipmi/ipmi_bmc_sim.o
  CC      hw/ipmi/ipmi_bmc_extern.o
  CC      hw/ipmi/isa_ipmi_bt.o
  CC      hw/ipmi/isa_ipmi_kcs.o
  CC      hw/isa/isa-bus.o
  CC      hw/isa/isa-superio.o
  CC      hw/isa/smc37c669-superio.o
  CC      hw/isa/apm.o
  CC      hw/mem/pc-dimm.o
  CC      hw/mem/memory-device.o
  CC      hw/mem/nvdimm.o
  CC      hw/misc/applesmc.o
  CC      hw/misc/max111x.o
  CC      hw/misc/tmp105.o
  CC      hw/misc/tmp421.o
  CC      hw/misc/debugexit.o
  CC      hw/misc/sga.o
  CC      hw/misc/pc-testdev.o
  CC      hw/misc/pci-testdev.o
  CC      hw/misc/edu.o
  CC      hw/misc/unimp.o
  CC      hw/misc/vmcoreinfo.o
  CC      hw/misc/arm_l2x0.o
  CC      hw/misc/arm_integrator_debug.o
/tmp/qemu-test/src/hw/input/vhost-user-input.c:16:10: fatal error: sys/ioctl.h: No such file or directory
 #include <sys/ioctl.h>
          ^~~~~~~~~~~~~
compilation terminated.
make: *** [/tmp/qemu-test/src/rules.mak:66: hw/input/vhost-user-input.o] Error 1
make: *** Waiting for unfinished jobs....
  CC      hw/misc/a9scu.o
Traceback (most recent call last):
  File "./tests/docker/docker.py", line 407, in <module>
    sys.exit(main())
  File "./tests/docker/docker.py", line 404, in main
    return args.cmdobj.run(args, argv)
  File "./tests/docker/docker.py", line 261, in run
    return Docker().run(argv, args.keep, quiet=args.quiet)
  File "./tests/docker/docker.py", line 229, in run
    quiet=quiet)
  File "./tests/docker/docker.py", line 147, in _do_check
    return subprocess.check_call(self._command + cmd, **kwargs)
  File "/usr/lib64/python2.7/subprocess.py", line 186, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['docker', 'run', '--label', 'com.qemu.instance.uuid=f5a60be865c011e8818752540069c830', '-u', '0', '--security-opt', 'seccomp=unconfined', '--rm', '--net=none', '-e', 'TARGET_LIST=', '-e', 'EXTRA_CONFIGURE_OPTS=', '-e', 'V=', '-e', 'J=8', '-e', 'DEBUG=', '-e', 'SHOW_ENV=1', '-e', 'CCACHE_DIR=/var/tmp/ccache', '-v', '/root/.cache/qemu-docker-ccache:/var/tmp/ccache:z', '-v', '/var/tmp/patchew-tester-tmp-q1mu68mb/src/docker-src.2018-06-01-13.26.35.22520:/var/tmp/qemu:z,ro', 'qemu:fedora', '/var/tmp/qemu/run', 'test-mingw']' returned non-zero exit status 2
make[1]: *** [tests/docker/Makefile.include:129: docker-run] Error 1
make[1]: Leaving directory '/var/tmp/patchew-tester-tmp-q1mu68mb/src'
make: *** [tests/docker/Makefile.include:163: docker-run-test-mingw@fedora] Error 2

real	1m31.301s
user	0m4.561s
sys	0m3.852s
=== OUTPUT END ===

Test command exited with code: 2


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

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

* Re: [Qemu-devel] [RFC v2 09/12] Add vhost-input-pci
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 09/12] Add vhost-input-pci Marc-André Lureau
@ 2018-06-04  8:58   ` Gerd Hoffmann
  2018-06-07 22:22     ` Marc-André Lureau
  0 siblings, 1 reply; 38+ messages in thread
From: Gerd Hoffmann @ 2018-06-04  8:58 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

> +#define TYPE_VHOST_USER_INPUT_PCI "vhost-user-input-pci"

Patch $subject mismatch.

> +struct VHostUserInput {
> +    VirtIOInput                       parent_obj;
> +
> +    VhostUserBackend                  *vhost;
> +};

Nothing input specific here ...

> +static void vhost_input_change_active(VirtIOInput *vinput)
> +{
> +    VHostUserInput *vhi = VHOST_USER_INPUT(vinput);
> +
> +    if (!vhi->vhost) {
> +        return;
> +    }
> +
> +    if (vinput->active) {
> +        vhost_user_backend_start(vhi->vhost);
> +    } else {
> +        vhost_user_backend_stop(vhi->vhost);
> +    }
> +}

... and here ...

> +static const VMStateDescription vmstate_vhost_input = {
> +    .name = "vhost-user-input",
> +    .unmigratable = 1,
> +};

... and here ...

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

... likewise ...

So, maybe it makes sense to have a abstact base class for vhost-user
devices?  And possibly move the vhost-backend code to the base class
then?

cheers,
  Gerd

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

* Re: [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config()
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
@ 2018-06-04  9:07   ` Dr. David Alan Gilbert
  2018-06-04  9:18     ` Marc-André Lureau
  0 siblings, 1 reply; 38+ messages in thread
From: Dr. David Alan Gilbert @ 2018-06-04  9:07 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Gerd Hoffmann

* Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
> Ask vhost user input backend the list of virtio_input_config.

Why is this vhost-user specific; shouldn't the vhost-input
behaviour be the same irrespective of if it's vhost-user or plain vhost?

Dave


> 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       |  6 ++++
>  3 files changed, 69 insertions(+)
> 
> diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
> index 5dac61f9ea..6cc2edacc5 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,
> @@ -156,4 +157,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 a87db01e55..19ed87d07c 100644
> --- a/hw/virtio/vhost-user.c
> +++ b/hw/virtio/vhost-user.c
> @@ -84,6 +84,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;
>  
> @@ -324,6 +325,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->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 534caab18a..3df9927386 100644
> --- a/docs/interop/vhost-user.txt
> +++ b/docs/interop/vhost-user.txt
> @@ -744,6 +744,12 @@ Master message types
>        was previously sent.
>        The value returned is an error indication; 0 is success.
>  
> + * VHOST_USER_INPUT_GET_CONFIG
> +
> +      Slave payload: (struct virtio_input_config)*
> +
> +      Ask vhost user input backend the list of virtio_input_config.
> +
>  Slave message types
>  -------------------
>  
> -- 
> 2.17.1.906.g10fd178552
> 
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

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

* Re: [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config()
  2018-06-04  9:07   ` Dr. David Alan Gilbert
@ 2018-06-04  9:18     ` Marc-André Lureau
  2018-06-04  9:59       ` Dr. David Alan Gilbert
  0 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-04  9:18 UTC (permalink / raw)
  To: Dr. David Alan Gilbert; +Cc: qemu-devel, Gerd Hoffmann

Hi

On Mon, Jun 4, 2018 at 11:07 AM, Dr. David Alan Gilbert
<dgilbert@redhat.com> wrote:
> * Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
>> Ask vhost user input backend the list of virtio_input_config.
>
> Why is this vhost-user specific; shouldn't the vhost-input
> behaviour be the same irrespective of if it's vhost-user or plain vhost?
>

A similar message could be used for plain vhost, in some ioctl form.
Here we are documenting vhost-user protocol only.


Thanks

> Dave
>
>
>> 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       |  6 ++++
>>  3 files changed, 69 insertions(+)
>>
>> diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
>> index 5dac61f9ea..6cc2edacc5 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,
>> @@ -156,4 +157,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 a87db01e55..19ed87d07c 100644
>> --- a/hw/virtio/vhost-user.c
>> +++ b/hw/virtio/vhost-user.c
>> @@ -84,6 +84,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;
>>
>> @@ -324,6 +325,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->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 534caab18a..3df9927386 100644
>> --- a/docs/interop/vhost-user.txt
>> +++ b/docs/interop/vhost-user.txt
>> @@ -744,6 +744,12 @@ Master message types
>>        was previously sent.
>>        The value returned is an error indication; 0 is success.
>>
>> + * VHOST_USER_INPUT_GET_CONFIG
>> +
>> +      Slave payload: (struct virtio_input_config)*
>> +
>> +      Ask vhost user input backend the list of virtio_input_config.
>> +
>>  Slave message types
>>  -------------------
>>
>> --
>> 2.17.1.906.g10fd178552
>>
>>
> --
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

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

* Re: [Qemu-devel] [RFC v2 10/12] vhost-user: add vhost_user_gpu_set_socket()
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 10/12] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
@ 2018-06-04  9:28   ` Gerd Hoffmann
  0 siblings, 0 replies; 38+ messages in thread
From: Gerd Hoffmann @ 2018-06-04  9:28 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Fri, Jun 01, 2018 at 06:27:47PM +0200, Marc-André Lureau wrote:
> 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 

Yes.

> and may not need the same kind of
> stability guarantees,

Why?

> thus I recommend to have a new dedicated
> channel.
> 
> FIXME: document the GPU protocol.

Yes, please.

cheers,
  Gerd

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

* Re: [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend Marc-André Lureau
@ 2018-06-04  9:36   ` Daniel P. Berrangé
  2018-06-07 22:34     ` Marc-André Lureau
  0 siblings, 1 reply; 38+ messages in thread
From: Daniel P. Berrangé @ 2018-06-04  9:36 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Gerd Hoffmann

On Fri, Jun 01, 2018 at 06:27:41PM +0200, Marc-André Lureau 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.
> 
> Alternatively, 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.

Passing a binary and args as a string blob.....

> +static int
> +vhost_user_backend_spawn_cmd(VhostUserBackend *b, int vhostfd, Error **errp)
> +{
> +    int devnull = open("/dev/null", O_RDWR);
> +    pid_t pid;
> +
> +    assert(!b->child);
> +
> +    if (!b->cmd) {
> +        error_setg_errno(errp, errno, "Missing cmd property");
> +        return -1;
> +    }
> +    if (devnull < 0) {
> +        error_setg_errno(errp, errno, "Unable to open /dev/null");
> +        return -1;
> +    }
> +
> +    pid = qemu_fork(errp);
> +    if (pid < 0) {
> +        close(devnull);
> +        return -1;
> +    }
> +
> +    if (pid == 0) { /* child */
> +        int fd, maxfd = sysconf(_SC_OPEN_MAX);
> +
> +        dup2(devnull, STDIN_FILENO);
> +        dup2(devnull, STDOUT_FILENO);
> +        dup2(vhostfd, 3);
> +
> +        signal(SIGINT, SIG_IGN);

Why ignore SIGINT ?  Surely we want this extra process to be killed
someone ctrl-c's the parent QEMU.

> +
> +        for (fd = 4; fd < maxfd; fd++) {
> +            close(fd);
> +        }
> +
> +        execlp("/bin/sh", "sh", "-c", b->cmd, NULL);

...which is then interpreted by the shell is a recipe for security
flaws. There needs to be a way to pass the command + arguments
to QEMU as an argv[] we can directly exec without involving the
shell.

> +        _exit(1);
> +    }
> +
> +    b->child = QIO_CHANNEL(qio_channel_command_new_pid(devnull, devnull, pid));

Overall this method overall duplicates much of the
qio_channel_command_new_argv(), though you do have a few differences.

I'd prefer if we could make qio_channel_command_new_argv more flexible to
handle these extra needs though.

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] 38+ messages in thread

* Re: [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend Marc-André Lureau
@ 2018-06-04  9:37   ` Gerd Hoffmann
  2018-06-08 17:25     ` Marc-André Lureau
  0 siblings, 1 reply; 38+ messages in thread
From: Gerd Hoffmann @ 2018-06-04  9:37 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

On Fri, Jun 01, 2018 at 06:27:48PM +0200, Marc-André Lureau wrote:
> Add to virtio-gpu devices a "vhost-user" property. When set, the
> associated vhost-user backend is used to handle the virtio rings.
> 
> For now, a socketpair is created for the backend to share the rendering
> results with qemu via a simple VHOST_GPU protocol.

Why this isn't a separate device, like vhost-user-input-pci?

> +typedef struct VhostGpuUpdate {
> +    uint32_t scanout_id;
> +    uint32_t x;
> +    uint32_t y;
> +    uint32_t width;
> +    uint32_t height;
> +    uint8_t data[];
> +} QEMU_PACKED VhostGpuUpdate;

Hmm, when designing a new protocol I think we can do better than just
squeering the pixels into a tcp stream.  Use shared memory instead?  Due
to vhost we are limited to linux anyway, so we might even consider stuff
like dmabufs here.

cheers,
  Gerd

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

* Re: [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config()
  2018-06-04  9:18     ` Marc-André Lureau
@ 2018-06-04  9:59       ` Dr. David Alan Gilbert
  2018-06-12 12:46         ` Marc-André Lureau
  0 siblings, 1 reply; 38+ messages in thread
From: Dr. David Alan Gilbert @ 2018-06-04  9:59 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Gerd Hoffmann

* Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
> Hi
> 
> On Mon, Jun 4, 2018 at 11:07 AM, Dr. David Alan Gilbert
> <dgilbert@redhat.com> wrote:
> > * Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
> >> Ask vhost user input backend the list of virtio_input_config.
> >
> > Why is this vhost-user specific; shouldn't the vhost-input
> > behaviour be the same irrespective of if it's vhost-user or plain vhost?
> >
> 
> A similar message could be used for plain vhost, in some ioctl form.
> Here we are documenting vhost-user protocol only.

The vhost-user protocol seems to be mostly independent of the device
it's carrying; for example I see there's a 'net_set_mtu' and 'send_rarp'
but I don't think there's anything for disk storage;  what are the cases
whether these have to be done through a change to the vhost-user
protocol as opposed to stuff carried over it?

Dave

> 
> Thanks
> 
> > Dave
> >
> >
> >> 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       |  6 ++++
> >>  3 files changed, 69 insertions(+)
> >>
> >> diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
> >> index 5dac61f9ea..6cc2edacc5 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,
> >> @@ -156,4 +157,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 a87db01e55..19ed87d07c 100644
> >> --- a/hw/virtio/vhost-user.c
> >> +++ b/hw/virtio/vhost-user.c
> >> @@ -84,6 +84,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;
> >>
> >> @@ -324,6 +325,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->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 534caab18a..3df9927386 100644
> >> --- a/docs/interop/vhost-user.txt
> >> +++ b/docs/interop/vhost-user.txt
> >> @@ -744,6 +744,12 @@ Master message types
> >>        was previously sent.
> >>        The value returned is an error indication; 0 is success.
> >>
> >> + * VHOST_USER_INPUT_GET_CONFIG
> >> +
> >> +      Slave payload: (struct virtio_input_config)*
> >> +
> >> +      Ask vhost user input backend the list of virtio_input_config.
> >> +
> >>  Slave message types
> >>  -------------------
> >>
> >> --
> >> 2.17.1.906.g10fd178552
> >>
> >>
> > --
> > Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

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

* Re: [Qemu-devel] [RFC v2 09/12] Add vhost-input-pci
  2018-06-04  8:58   ` Gerd Hoffmann
@ 2018-06-07 22:22     ` Marc-André Lureau
  2018-06-08  5:41       ` Gerd Hoffmann
  0 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-07 22:22 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU

Hi

On Mon, Jun 4, 2018 at 10:58 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> +#define TYPE_VHOST_USER_INPUT_PCI "vhost-user-input-pci"
>
> Patch $subject mismatch.
>
>> +struct VHostUserInput {
>> +    VirtIOInput                       parent_obj;
>> +
>> +    VhostUserBackend                  *vhost;
>> +};
>
> Nothing input specific here ...

Except VirtIOInput

>
>> +static void vhost_input_change_active(VirtIOInput *vinput)
>> +{
>> +    VHostUserInput *vhi = VHOST_USER_INPUT(vinput);
>> +
>> +    if (!vhi->vhost) {
>> +        return;
>> +    }
>> +
>> +    if (vinput->active) {
>> +        vhost_user_backend_start(vhi->vhost);
>> +    } else {
>> +        vhost_user_backend_stop(vhi->vhost);
>> +    }
>> +}
>
> ... and here ...

Except it's a VirtIOInputClass callback
>
>> +static const VMStateDescription vmstate_vhost_input = {
>> +    .name = "vhost-user-input",
>> +    .unmigratable = 1,
>> +};
>
> ... and here ...
>
>> +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);
>> +    }
>> +}
>
> ... likewise ...
>
> So, maybe it makes sense to have a abstact base class for vhost-user
> devices?  And possibly move the vhost-backend code to the base class
> then?

The device inherits from virtio-input type. So we could somehow not
expose -object vhost-user and instead have internal vhost-user object
& properties aliased & duplicated on each -device vhost-user*. I would
rather keep the -object solution, since it's somehow cleaner, more
flexible and simpler to document that way. Or do you have a better
idea?

-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend
  2018-06-04  9:36   ` Daniel P. Berrangé
@ 2018-06-07 22:34     ` Marc-André Lureau
  2018-06-08  8:43       ` Daniel P. Berrangé
  0 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-07 22:34 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: QEMU, Gerd Hoffmann

Hi

On Mon, Jun 4, 2018 at 11:36 AM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> On Fri, Jun 01, 2018 at 06:27:41PM +0200, Marc-André Lureau 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.
>>
>> Alternatively, 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.
>
> Passing a binary and args as a string blob.....
>
>> +static int
>> +vhost_user_backend_spawn_cmd(VhostUserBackend *b, int vhostfd, Error **errp)
>> +{
>> +    int devnull = open("/dev/null", O_RDWR);
>> +    pid_t pid;
>> +
>> +    assert(!b->child);
>> +
>> +    if (!b->cmd) {
>> +        error_setg_errno(errp, errno, "Missing cmd property");
>> +        return -1;
>> +    }
>> +    if (devnull < 0) {
>> +        error_setg_errno(errp, errno, "Unable to open /dev/null");
>> +        return -1;
>> +    }
>> +
>> +    pid = qemu_fork(errp);
>> +    if (pid < 0) {
>> +        close(devnull);
>> +        return -1;
>> +    }
>> +
>> +    if (pid == 0) { /* child */
>> +        int fd, maxfd = sysconf(_SC_OPEN_MAX);
>> +
>> +        dup2(devnull, STDIN_FILENO);
>> +        dup2(devnull, STDOUT_FILENO);
>> +        dup2(vhostfd, 3);
>> +
>> +        signal(SIGINT, SIG_IGN);
>
> Why ignore SIGINT ?  Surely we want this extra process to be killed
> someone ctrl-c's the parent QEMU.

leftover, removed

>
>> +
>> +        for (fd = 4; fd < maxfd; fd++) {
>> +            close(fd);
>> +        }
>> +
>> +        execlp("/bin/sh", "sh", "-c", b->cmd, NULL);
>
> ...which is then interpreted by the shell is a recipe for security
> flaws. There needs to be a way to pass the command + arguments
> to QEMU as an argv[] we can directly exec without involving the
> shell.
>

For now, I use g_shell_parse_argv(). Do you have a better idea?

>> +        _exit(1);
>> +    }
>> +
>> +    b->child = QIO_CHANNEL(qio_channel_command_new_pid(devnull, devnull, pid));
>
> Overall this method overall duplicates much of the
> qio_channel_command_new_argv(), though you do have a few differences.
>
> I'd prefer if we could make qio_channel_command_new_argv more flexible to
> handle these extra needs though.
>

Ok I added a pre-exec callback for the extra dup2() & close().

Thanks,




-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [RFC v2 09/12] Add vhost-input-pci
  2018-06-07 22:22     ` Marc-André Lureau
@ 2018-06-08  5:41       ` Gerd Hoffmann
  0 siblings, 0 replies; 38+ messages in thread
From: Gerd Hoffmann @ 2018-06-08  5:41 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU

On Fri, Jun 08, 2018 at 12:22:38AM +0200, Marc-André Lureau wrote:
> Hi
> 
> On Mon, Jun 4, 2018 at 10:58 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> >> +#define TYPE_VHOST_USER_INPUT_PCI "vhost-user-input-pci"
> >
> > Patch $subject mismatch.
> >
> >> +struct VHostUserInput {
> >> +    VirtIOInput                       parent_obj;
> >> +
> >> +    VhostUserBackend                  *vhost;
> >> +};
> >
> > Nothing input specific here ...
> 
> Except VirtIOInput

Oops, missed that.  Scratch the idea then.

cheers,
  Gerd

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

* Re: [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend
  2018-06-07 22:34     ` Marc-André Lureau
@ 2018-06-08  8:43       ` Daniel P. Berrangé
  2018-06-12 14:53         ` Marc-André Lureau
  0 siblings, 1 reply; 38+ messages in thread
From: Daniel P. Berrangé @ 2018-06-08  8:43 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Gerd Hoffmann

On Fri, Jun 08, 2018 at 12:34:15AM +0200, Marc-André Lureau wrote:
> Hi
> 
> On Mon, Jun 4, 2018 at 11:36 AM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> > On Fri, Jun 01, 2018 at 06:27:41PM +0200, Marc-André Lureau 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.
> >>
> >> Alternatively, 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.
> >
> > Passing a binary and args as a string blob.....
> >
> >> +static int
> >> +vhost_user_backend_spawn_cmd(VhostUserBackend *b, int vhostfd, Error **errp)
> >> +{
> >> +    int devnull = open("/dev/null", O_RDWR);
> >> +    pid_t pid;
> >> +
> >> +    assert(!b->child);
> >> +
> >> +    if (!b->cmd) {
> >> +        error_setg_errno(errp, errno, "Missing cmd property");
> >> +        return -1;
> >> +    }
> >> +    if (devnull < 0) {
> >> +        error_setg_errno(errp, errno, "Unable to open /dev/null");
> >> +        return -1;
> >> +    }
> >> +
> >> +    pid = qemu_fork(errp);
> >> +    if (pid < 0) {
> >> +        close(devnull);
> >> +        return -1;
> >> +    }
> >> +
> >> +    if (pid == 0) { /* child */
> >> +        int fd, maxfd = sysconf(_SC_OPEN_MAX);
> >> +
> >> +        dup2(devnull, STDIN_FILENO);
> >> +        dup2(devnull, STDOUT_FILENO);
> >> +        dup2(vhostfd, 3);
> >> +
> >> +        signal(SIGINT, SIG_IGN);
> >
> > Why ignore SIGINT ?  Surely we want this extra process to be killed
> > someone ctrl-c's the parent QEMU.
> 
> leftover, removed
> 
> >
> >> +
> >> +        for (fd = 4; fd < maxfd; fd++) {
> >> +            close(fd);
> >> +        }
> >> +
> >> +        execlp("/bin/sh", "sh", "-c", b->cmd, NULL);
> >
> > ...which is then interpreted by the shell is a recipe for security
> > flaws. There needs to be a way to pass the command + arguments
> > to QEMU as an argv[] we can directly exec without involving the
> > shell.
> >
> 
> For now, I use g_shell_parse_argv(). Do you have a better idea?

Accept individual args at the cli level is far preferrable - we don't
want anything to be parsing shell strings:

 vhost-user-backend,id=vui,binary=/sbin/vhost-user-input,arg=/dev/input,arg=foo,arg=bar



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] 38+ messages in thread

* Re: [Qemu-devel] [RFC v2 02/12] libvhost-user: exit by default on VHOST_USER_NONE
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 02/12] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
@ 2018-06-08 14:48   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-06-08 14:48 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel; +Cc: Gerd Hoffmann

On 06/01/2018 01:27 PM, Marc-André Lureau wrote:
> 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 | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c
> index 54e643d871..ebe30c7dab 100644
> --- a/contrib/libvhost-user/libvhost-user.c
> +++ b/contrib/libvhost-user/libvhost-user.c
> @@ -1220,6 +1220,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
>      case VHOST_USER_SET_CONFIG:
>          return vu_set_config(dev, vmsg);
>      case VHOST_USER_NONE:
> +        /* if you need processing before exit, override iface->process_msg */
> +        exit(0);
>          break;

Remove the 'break'?

>      case VHOST_USER_POSTCOPY_ADVISE:
>          return vu_set_postcopy_advise(dev, vmsg);
> 

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

* Re: [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address Marc-André Lureau
@ 2018-06-08 14:52   ` Philippe Mathieu-Daudé
  2018-06-11  8:59     ` Daniel P. Berrangé
  2018-06-11  9:04   ` Daniel P. Berrangé
  1 sibling, 1 reply; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-06-08 14:52 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel; +Cc: Gerd Hoffmann

On 06/01/2018 01:27 PM, 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
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  chardev/char-socket.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/chardev/char-socket.c b/chardev/char-socket.c
> index 159e69c3b1..f1b7907798 100644
> --- a/chardev/char-socket.c
> +++ b/chardev/char-socket.c
> @@ -416,8 +416,11 @@ 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);
> +    chr->filename = NULL;
> +    if (s->addr) {

Isn't it more robust to add this check in SocketAddress_to_str()?

    static char *SocketAddress_to_str(const char *prefix, SocketAddress
*addr,
                                      bool is_listen, bool is_telnet)
    {
        if (!addr) {
            return NULL;
        }
        switch (addr->type) {
            ...

> +        chr->filename = SocketAddress_to_str("disconnected:", s->addr,
> +                                             s->is_listen, s->is_telnet);
> +    }
>  }
>  
>  /* NB may be called even if tcp_chr_connect has not been
> 

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

* Re: [Qemu-devel] [RFC v2 03/12] vhost-user: wrap some read/write with retry handling
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 03/12] vhost-user: wrap some read/write with retry handling Marc-André Lureau
@ 2018-06-08 14:53   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-06-08 14:53 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel; +Cc: Gerd Hoffmann

On 06/01/2018 01:27 PM, Marc-André Lureau wrote:
> 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 ca554d4ff1..cc9298792d 100644
> --- a/hw/virtio/vhost-user.c
> +++ b/hw/virtio/vhost-user.c
> @@ -869,7 +869,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;
> @@ -898,7 +901,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;
> @@ -941,7 +947,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;
> 

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

* Re: [Qemu-devel] [RFC v2 05/12] vhost-user: split vhost_user_read()
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 05/12] vhost-user: split vhost_user_read() Marc-André Lureau
@ 2018-06-08 14:57   ` Philippe Mathieu-Daudé
  2018-06-12 14:58     ` Marc-André Lureau
  0 siblings, 1 reply; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2018-06-08 14:57 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel; +Cc: Gerd Hoffmann

On 06/01/2018 01:27 PM, Marc-André Lureau wrote:
> Split vhost_user_read(), so only header can be read with
> vhost_user_read_header().
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  hw/virtio/vhost-user.c | 27 +++++++++++++++++++--------
>  1 file changed, 19 insertions(+), 8 deletions(-)
> 
> diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
> index cc9298792d..a87db01e55 100644
> --- a/hw/virtio/vhost-user.c
> +++ b/hw/virtio/vhost-user.c
> @@ -196,7 +196,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)

Can this be simply:

static int vhost_user_read_header(CharBackend *chr, VhostUserMsg *msg) ?

>  {
>      struct vhost_user *u = dev->opaque;
>      CharBackend *chr = u->chr;
> @@ -207,7 +207,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 */
> @@ -215,7 +215,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->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 */
> @@ -223,7 +237,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) {
> @@ -233,14 +247,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,
> 

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

* Re: [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend
  2018-06-04  9:37   ` Gerd Hoffmann
@ 2018-06-08 17:25     ` Marc-André Lureau
  2018-06-09  1:02       ` Marc-André Lureau
  2018-06-11  6:49       ` Gerd Hoffmann
  0 siblings, 2 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-08 17:25 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU, Dave Airlie

Hi

On Mon, Jun 4, 2018 at 11:37 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
> On Fri, Jun 01, 2018 at 06:27:48PM +0200, Marc-André Lureau wrote:
>> Add to virtio-gpu devices a "vhost-user" property. When set, the
>> associated vhost-user backend is used to handle the virtio rings.
>>
>> For now, a socketpair is created for the backend to share the rendering
>> results with qemu via a simple VHOST_GPU protocol.
>
> Why this isn't a separate device, like vhost-user-input-pci?

Ok, let's have vhost-user-gpu-pci and vhost-user-vga, inheriting from
existing devices.

>> +typedef struct VhostGpuUpdate {
>> +    uint32_t scanout_id;
>> +    uint32_t x;
>> +    uint32_t y;
>> +    uint32_t width;
>> +    uint32_t height;
>> +    uint8_t data[];
>> +} QEMU_PACKED VhostGpuUpdate;
>
> Hmm, when designing a new protocol I think we can do better than just
> squeering the pixels into a tcp stream.  Use shared memory instead?  Due
> to vhost we are limited to linux anyway, so we might even consider stuff
> like dmabufs here.

Well, my goal is not to invent a new spice or wayland protocol :) I
don't care much about 2d performance at this point, more about 3d. Can
we leave 2d improvements for another day? Beside, what would dmabuf
bring us for 2d compared to shmem?

There seems to be a lot of overhead with the roundtrip vhost-user ->
qemu -> spice worker -> spice client -> wayland/x11 -> gpu already
(but this isn't necessarily so bad at 60fps or less).
Ideally, I would like to bypass qemu & spice for local rendering, but
I don't think wayland support that kind of nested window composition
(at least tracking messages weston --nested doesn't show that kind of
optimization).

FWIW, here are some Unigine Heaven 4.0 benchmarks (probably within +-10%):

qemu-gtk/egl+virtio-gpu: fps:2.6/ score: 64
qemu-gtk/egl+vhost-user-gpu: fps:12.9 / score: 329

spice+virtio-gpu: fps:2.8 / score: 70
spice+vhost-user-gpu: fps:12.1 / score: 304

That should give an extra motivation :)

-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend
  2018-06-08 17:25     ` Marc-André Lureau
@ 2018-06-09  1:02       ` Marc-André Lureau
  2018-06-11  6:49       ` Gerd Hoffmann
  1 sibling, 0 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-09  1:02 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU, Dave Airlie

On Fri, Jun 8, 2018 at 7:25 PM, Marc-André Lureau
<marcandre.lureau@gmail.com> wrote:
> Hi
>
> On Mon, Jun 4, 2018 at 11:37 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> On Fri, Jun 01, 2018 at 06:27:48PM +0200, Marc-André Lureau wrote:
>>> Add to virtio-gpu devices a "vhost-user" property. When set, the
>>> associated vhost-user backend is used to handle the virtio rings.
>>>
>>> For now, a socketpair is created for the backend to share the rendering
>>> results with qemu via a simple VHOST_GPU protocol.
>>
>> Why this isn't a separate device, like vhost-user-input-pci?
>
> Ok, let's have vhost-user-gpu-pci and vhost-user-vga, inheriting from
> existing devices.
>
>>> +typedef struct VhostGpuUpdate {
>>> +    uint32_t scanout_id;
>>> +    uint32_t x;
>>> +    uint32_t y;
>>> +    uint32_t width;
>>> +    uint32_t height;
>>> +    uint8_t data[];
>>> +} QEMU_PACKED VhostGpuUpdate;
>>
>> Hmm, when designing a new protocol I think we can do better than just
>> squeering the pixels into a tcp stream.  Use shared memory instead?  Due
>> to vhost we are limited to linux anyway, so we might even consider stuff
>> like dmabufs here.
>
> Well, my goal is not to invent a new spice or wayland protocol :) I
> don't care much about 2d performance at this point, more about 3d. Can
> we leave 2d improvements for another day? Beside, what would dmabuf
> bring us for 2d compared to shmem?
>
> There seems to be a lot of overhead with the roundtrip vhost-user ->
> qemu -> spice worker -> spice client -> wayland/x11 -> gpu already
> (but this isn't necessarily so bad at 60fps or less).
> Ideally, I would like to bypass qemu & spice for local rendering, but
> I don't think wayland support that kind of nested window composition
> (at least tracking messages weston --nested doesn't show that kind of
> optimization).
>
> FWIW, here are some Unigine Heaven 4.0 benchmarks (probably within +-10%):
>
> qemu-gtk/egl+virtio-gpu: fps:2.6/ score: 64
> qemu-gtk/egl+vhost-user-gpu: fps:12.9 / score: 329
>
> spice+virtio-gpu: fps:2.8 / score: 70
> spice+vhost-user-gpu: fps:12.1 / score: 304
>
> That should give an extra motivation :)
>

(host is fps:31.1 / score:784



-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend
  2018-06-08 17:25     ` Marc-André Lureau
  2018-06-09  1:02       ` Marc-André Lureau
@ 2018-06-11  6:49       ` Gerd Hoffmann
  1 sibling, 0 replies; 38+ messages in thread
From: Gerd Hoffmann @ 2018-06-11  6:49 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Dave Airlie

> >> For now, a socketpair is created for the backend to share the rendering
> >> results with qemu via a simple VHOST_GPU protocol.
> >
> > Why this isn't a separate device, like vhost-user-input-pci?
> 
> Ok, let's have vhost-user-gpu-pci and vhost-user-vga, inheriting from
> existing devices.

I'd tend to create separate devices instead of inheriting from the
existing devices.  Arn't the code paths more or less completely
different?  What code is shared between builtin and vhost-user versions
of the devices?

> >> +typedef struct VhostGpuUpdate {
> >> +    uint32_t scanout_id;
> >> +    uint32_t x;
> >> +    uint32_t y;
> >> +    uint32_t width;
> >> +    uint32_t height;
> >> +    uint8_t data[];
> >> +} QEMU_PACKED VhostGpuUpdate;
> >
> > Hmm, when designing a new protocol I think we can do better than just
> > squeering the pixels into a tcp stream.  Use shared memory instead?  Due
> > to vhost we are limited to linux anyway, so we might even consider stuff
> > like dmabufs here.
> 
> Well, my goal is not to invent a new spice or wayland protocol :) I
> don't care much about 2d performance at this point, more about 3d. Can
> we leave 2d improvements for another day? Beside, what would dmabuf
> bring us for 2d compared to shmem?

Well, you need dma-bufs for 3d anyway, so why not use them for 2d too?
I don't think we need separate code paths for 2d vs. 3d updates.

> There seems to be a lot of overhead with the roundtrip vhost-user ->
> qemu -> spice worker -> spice client -> wayland/x11 -> gpu already
> (but this isn't necessarily so bad at 60fps or less).
> Ideally, I would like to bypass qemu & spice for local rendering, but
> I don't think wayland support that kind of nested window composition
> (at least tracking messages weston --nested doesn't show that kind of
> optimization).

Yep, a direct vhost-user -> wayland path makes sense.  Using dma-bufs
for both 2d and 3d should simplify that too (again: one code path
instead of two).

What do you mean with nested window composition?

cheers,
  Gerd

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

* Re: [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address
  2018-06-08 14:52   ` Philippe Mathieu-Daudé
@ 2018-06-11  8:59     ` Daniel P. Berrangé
  0 siblings, 0 replies; 38+ messages in thread
From: Daniel P. Berrangé @ 2018-06-11  8:59 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Marc-André Lureau, qemu-devel, Gerd Hoffmann

On Fri, Jun 08, 2018 at 11:52:11AM -0300, Philippe Mathieu-Daudé wrote:
> On 06/01/2018 01:27 PM, 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
> > 
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  chardev/char-socket.c | 7 +++++--
> >  1 file changed, 5 insertions(+), 2 deletions(-)
> > 
> > diff --git a/chardev/char-socket.c b/chardev/char-socket.c
> > index 159e69c3b1..f1b7907798 100644
> > --- a/chardev/char-socket.c
> > +++ b/chardev/char-socket.c
> > @@ -416,8 +416,11 @@ 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);
> > +    chr->filename = NULL;
> > +    if (s->addr) {
> 
> Isn't it more robust to add this check in SocketAddress_to_str()?

IMHO that just shifts the problem elsewhere - currently SocketAddress_to_str
is assumed to return non-NULL, or to abort(). Shifting the check means it
can now return NULL, so there's every chance the caller will now reference
the NULL pointer that's returned.

> 
>     static char *SocketAddress_to_str(const char *prefix, SocketAddress
> *addr,
>                                       bool is_listen, bool is_telnet)
>     {
>         if (!addr) {
>             return NULL;
>         }
>         switch (addr->type) {
>             ...
> 
> > +        chr->filename = SocketAddress_to_str("disconnected:", s->addr,
> > +                                             s->is_listen, s->is_telnet);
> > +    }
> >  }
> >  
> >  /* NB may be called even if tcp_chr_connect has not been
> > 
> 

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] 38+ messages in thread

* Re: [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address
  2018-06-01 16:27 ` [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address Marc-André Lureau
  2018-06-08 14:52   ` Philippe Mathieu-Daudé
@ 2018-06-11  9:04   ` Daniel P. Berrangé
  1 sibling, 0 replies; 38+ messages in thread
From: Daniel P. Berrangé @ 2018-06-11  9:04 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Gerd Hoffmann

On Fri, Jun 01, 2018 at 06:27:38PM +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
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  chardev/char-socket.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/chardev/char-socket.c b/chardev/char-socket.c
> index 159e69c3b1..f1b7907798 100644
> --- a/chardev/char-socket.c
> +++ b/chardev/char-socket.c
> @@ -416,8 +416,11 @@ 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);
> +    chr->filename = NULL;
> +    if (s->addr) {
> +        chr->filename = SocketAddress_to_str("disconnected:", s->addr,
> +                                             s->is_listen, s->is_telnet);
> +    }
>  }

This will mean 'chr->filename' as NULL, which means other code using this
field may get NULL - especially the monitor looks like it will printf()
passing a NULL, which is a crash on some platforms. So I think you need
an else clause that will set it to some dummy value.


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] 38+ messages in thread

* Re: [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config()
  2018-06-04  9:59       ` Dr. David Alan Gilbert
@ 2018-06-12 12:46         ` Marc-André Lureau
  0 siblings, 0 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-12 12:46 UTC (permalink / raw)
  To: Dr. David Alan Gilbert; +Cc: qemu-devel, Gerd Hoffmann

Hi

On Mon, Jun 4, 2018 at 11:59 AM, Dr. David Alan Gilbert
<dgilbert@redhat.com> wrote:
> * Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
>> Hi
>>
>> On Mon, Jun 4, 2018 at 11:07 AM, Dr. David Alan Gilbert
>> <dgilbert@redhat.com> wrote:
>> > * Marc-André Lureau (marcandre.lureau@redhat.com) wrote:
>> >> Ask vhost user input backend the list of virtio_input_config.
>> >
>> > Why is this vhost-user specific; shouldn't the vhost-input
>> > behaviour be the same irrespective of if it's vhost-user or plain vhost?
>> >
>>
>> A similar message could be used for plain vhost, in some ioctl form.
>> Here we are documenting vhost-user protocol only.
>
> The vhost-user protocol seems to be mostly independent of the device
> it's carrying; for example I see there's a 'net_set_mtu' and 'send_rarp'
> but I don't think there's anything for disk storage;  what are the cases
> whether these have to be done through a change to the vhost-user
> protocol as opposed to stuff carried over it?

It's necessary for whatever is out-of-band, like configuring the
device, notifying of device/qemu events,... Thanks to virtio design,
most of the messages are generic. But some of them are more specific
(like here for input config, or later to hand over the gpu socket).
The gpu socket/channel/protocol is seperated from "main" vhost-user
protocol, because it's quite orthogonal to virtio/vhost: it's only
used for the gpu/display updates, and it's driven by the backend.

I hope that answer your question.

> Dave
>
>>
>> Thanks
>>
>> > Dave
>> >
>> >
>> >> 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       |  6 ++++
>> >>  3 files changed, 69 insertions(+)
>> >>
>> >> diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
>> >> index 5dac61f9ea..6cc2edacc5 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,
>> >> @@ -156,4 +157,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 a87db01e55..19ed87d07c 100644
>> >> --- a/hw/virtio/vhost-user.c
>> >> +++ b/hw/virtio/vhost-user.c
>> >> @@ -84,6 +84,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;
>> >>
>> >> @@ -324,6 +325,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->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 534caab18a..3df9927386 100644
>> >> --- a/docs/interop/vhost-user.txt
>> >> +++ b/docs/interop/vhost-user.txt
>> >> @@ -744,6 +744,12 @@ Master message types
>> >>        was previously sent.
>> >>        The value returned is an error indication; 0 is success.
>> >>
>> >> + * VHOST_USER_INPUT_GET_CONFIG
>> >> +
>> >> +      Slave payload: (struct virtio_input_config)*
>> >> +
>> >> +      Ask vhost user input backend the list of virtio_input_config.
>> >> +
>> >>  Slave message types
>> >>  -------------------
>> >>
>> >> --
>> >> 2.17.1.906.g10fd178552
>> >>
>> >>
>> > --
>> > Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
> --
> Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

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

* Re: [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend
  2018-06-08  8:43       ` Daniel P. Berrangé
@ 2018-06-12 14:53         ` Marc-André Lureau
  2018-06-12 15:08           ` Daniel P. Berrangé
  0 siblings, 1 reply; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-12 14:53 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: QEMU, Gerd Hoffmann, Markus Armbruster

Hi

On Fri, Jun 8, 2018 at 10:43 AM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> On Fri, Jun 08, 2018 at 12:34:15AM +0200, Marc-André Lureau wrote:
>> Hi
>>
>> On Mon, Jun 4, 2018 at 11:36 AM, Daniel P. Berrangé <berrange@redhat.com> wrote:
>> > On Fri, Jun 01, 2018 at 06:27:41PM +0200, Marc-André Lureau 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.
>> >>
>> >> Alternatively, 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.
>> >
>> > Passing a binary and args as a string blob.....
>> >
>> >> +static int
>> >> +vhost_user_backend_spawn_cmd(VhostUserBackend *b, int vhostfd, Error **errp)
>> >> +{
>> >> +    int devnull = open("/dev/null", O_RDWR);
>> >> +    pid_t pid;
>> >> +
>> >> +    assert(!b->child);
>> >> +
>> >> +    if (!b->cmd) {
>> >> +        error_setg_errno(errp, errno, "Missing cmd property");
>> >> +        return -1;
>> >> +    }
>> >> +    if (devnull < 0) {
>> >> +        error_setg_errno(errp, errno, "Unable to open /dev/null");
>> >> +        return -1;
>> >> +    }
>> >> +
>> >> +    pid = qemu_fork(errp);
>> >> +    if (pid < 0) {
>> >> +        close(devnull);
>> >> +        return -1;
>> >> +    }
>> >> +
>> >> +    if (pid == 0) { /* child */
>> >> +        int fd, maxfd = sysconf(_SC_OPEN_MAX);
>> >> +
>> >> +        dup2(devnull, STDIN_FILENO);
>> >> +        dup2(devnull, STDOUT_FILENO);
>> >> +        dup2(vhostfd, 3);
>> >> +
>> >> +        signal(SIGINT, SIG_IGN);
>> >
>> > Why ignore SIGINT ?  Surely we want this extra process to be killed
>> > someone ctrl-c's the parent QEMU.
>>
>> leftover, removed
>>
>> >
>> >> +
>> >> +        for (fd = 4; fd < maxfd; fd++) {
>> >> +            close(fd);
>> >> +        }
>> >> +
>> >> +        execlp("/bin/sh", "sh", "-c", b->cmd, NULL);
>> >
>> > ...which is then interpreted by the shell is a recipe for security
>> > flaws. There needs to be a way to pass the command + arguments
>> > to QEMU as an argv[] we can directly exec without involving the
>> > shell.
>> >
>>
>> For now, I use g_shell_parse_argv(). Do you have a better idea?
>
> Accept individual args at the cli level is far preferrable - we don't
> want anything to be parsing shell strings:
>
>  vhost-user-backend,id=vui,binary=/sbin/vhost-user-input,arg=/dev/input,arg=foo,arg=bar

Object arguments are populated in a dictionary. Only the last value
specified is used.

g_shell_parse_argv() isn't that scary imho. But if there is a
blacklist of functions, it would be worth to have them listed
somewhere.

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

* Re: [Qemu-devel] [RFC v2 05/12] vhost-user: split vhost_user_read()
  2018-06-08 14:57   ` Philippe Mathieu-Daudé
@ 2018-06-12 14:58     ` Marc-André Lureau
  0 siblings, 0 replies; 38+ messages in thread
From: Marc-André Lureau @ 2018-06-12 14:58 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: qemu-devel, Gerd Hoffmann

Hi

On Fri, Jun 8, 2018 at 4:57 PM, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> On 06/01/2018 01:27 PM, Marc-André Lureau wrote:
>> Split vhost_user_read(), so only header can be read with
>> vhost_user_read_header().
>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  hw/virtio/vhost-user.c | 27 +++++++++++++++++++--------
>>  1 file changed, 19 insertions(+), 8 deletions(-)
>>
>> diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
>> index cc9298792d..a87db01e55 100644
>> --- a/hw/virtio/vhost-user.c
>> +++ b/hw/virtio/vhost-user.c
>> @@ -196,7 +196,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)
>
> Can this be simply:
>
> static int vhost_user_read_header(CharBackend *chr, VhostUserMsg *msg) ?

It's more convenient and uniform with vhost_user_read() and friends if
it takes vhost_dev as argument. You can also see in patch "vhost-user:
add vhost_user_input_get_config()" that vhost_user_read_header() is
called fromwithout CharBackend* as variable in the caller.

>>  {
>>      struct vhost_user *u = dev->opaque;
>>      CharBackend *chr = u->chr;
>> @@ -207,7 +207,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 */
>> @@ -215,7 +215,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->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 */
>> @@ -223,7 +237,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) {
>> @@ -233,14 +247,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,
>>

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

* Re: [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend
  2018-06-12 14:53         ` Marc-André Lureau
@ 2018-06-12 15:08           ` Daniel P. Berrangé
  0 siblings, 0 replies; 38+ messages in thread
From: Daniel P. Berrangé @ 2018-06-12 15:08 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Gerd Hoffmann, Markus Armbruster

On Tue, Jun 12, 2018 at 04:53:54PM +0200, Marc-André Lureau wrote:
> Hi
> 
> On Fri, Jun 8, 2018 at 10:43 AM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> > On Fri, Jun 08, 2018 at 12:34:15AM +0200, Marc-André Lureau wrote:
> >> Hi
> >>
> >> On Mon, Jun 4, 2018 at 11:36 AM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> >> > On Fri, Jun 01, 2018 at 06:27:41PM +0200, Marc-André Lureau 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.
> >> >>
> >> >> Alternatively, 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.
> >> >
> >> > Passing a binary and args as a string blob.....
> >> >
> >> >> +static int
> >> >> +vhost_user_backend_spawn_cmd(VhostUserBackend *b, int vhostfd, Error **errp)
> >> >> +{
> >> >> +    int devnull = open("/dev/null", O_RDWR);
> >> >> +    pid_t pid;
> >> >> +
> >> >> +    assert(!b->child);
> >> >> +
> >> >> +    if (!b->cmd) {
> >> >> +        error_setg_errno(errp, errno, "Missing cmd property");
> >> >> +        return -1;
> >> >> +    }
> >> >> +    if (devnull < 0) {
> >> >> +        error_setg_errno(errp, errno, "Unable to open /dev/null");
> >> >> +        return -1;
> >> >> +    }
> >> >> +
> >> >> +    pid = qemu_fork(errp);
> >> >> +    if (pid < 0) {
> >> >> +        close(devnull);
> >> >> +        return -1;
> >> >> +    }
> >> >> +
> >> >> +    if (pid == 0) { /* child */
> >> >> +        int fd, maxfd = sysconf(_SC_OPEN_MAX);
> >> >> +
> >> >> +        dup2(devnull, STDIN_FILENO);
> >> >> +        dup2(devnull, STDOUT_FILENO);
> >> >> +        dup2(vhostfd, 3);
> >> >> +
> >> >> +        signal(SIGINT, SIG_IGN);
> >> >
> >> > Why ignore SIGINT ?  Surely we want this extra process to be killed
> >> > someone ctrl-c's the parent QEMU.
> >>
> >> leftover, removed
> >>
> >> >
> >> >> +
> >> >> +        for (fd = 4; fd < maxfd; fd++) {
> >> >> +            close(fd);
> >> >> +        }
> >> >> +
> >> >> +        execlp("/bin/sh", "sh", "-c", b->cmd, NULL);
> >> >
> >> > ...which is then interpreted by the shell is a recipe for security
> >> > flaws. There needs to be a way to pass the command + arguments
> >> > to QEMU as an argv[] we can directly exec without involving the
> >> > shell.
> >> >
> >>
> >> For now, I use g_shell_parse_argv(). Do you have a better idea?
> >
> > Accept individual args at the cli level is far preferrable - we don't
> > want anything to be parsing shell strings:
> >
> >  vhost-user-backend,id=vui,binary=/sbin/vhost-user-input,arg=/dev/input,arg=foo,arg=bar
> 
> Object arguments are populated in a dictionary. Only the last value
> specified is used.

Sigh, yes, this goes back to -object not having a full syntax for
non-scalar properties.

The quickest short term solution to this is to get the support for
"-object json:..."  working probably, then it will be just as
expressive as QMP's object_add.

> g_shell_parse_argv() isn't that scary imho. But if there is a
> blacklist of functions, it would be worth to have them listed
> somewhere.

We don't want to be using shell at all - we are going to be directly
passing the binary to execve(), so shell won't be involved. The
g_shell_parse_argv() is not simply splitting a string based on
whitespace, it is intepreting shell quoting rules, comments and
more. IOW, it is mangling args we'll be passing to execve() based
on shell rules that we're not intending to be using. It is simply
wrong to use g_shell_parse_argv() for this.


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] 38+ messages in thread

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

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-01 16:27 [Qemu-devel] [RFC v2 00/12] vhost-user for input & GPU Marc-André Lureau
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address Marc-André Lureau
2018-06-08 14:52   ` Philippe Mathieu-Daudé
2018-06-11  8:59     ` Daniel P. Berrangé
2018-06-11  9:04   ` Daniel P. Berrangé
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 02/12] libvhost-user: exit by default on VHOST_USER_NONE Marc-André Lureau
2018-06-08 14:48   ` Philippe Mathieu-Daudé
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 03/12] vhost-user: wrap some read/write with retry handling Marc-André Lureau
2018-06-08 14:53   ` Philippe Mathieu-Daudé
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 04/12] Add vhost-user-backend Marc-André Lureau
2018-06-04  9:36   ` Daniel P. Berrangé
2018-06-07 22:34     ` Marc-André Lureau
2018-06-08  8:43       ` Daniel P. Berrangé
2018-06-12 14:53         ` Marc-André Lureau
2018-06-12 15:08           ` Daniel P. Berrangé
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 05/12] vhost-user: split vhost_user_read() Marc-André Lureau
2018-06-08 14:57   ` Philippe Mathieu-Daudé
2018-06-12 14:58     ` Marc-André Lureau
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_get_config() Marc-André Lureau
2018-06-04  9:07   ` Dr. David Alan Gilbert
2018-06-04  9:18     ` Marc-André Lureau
2018-06-04  9:59       ` Dr. David Alan Gilbert
2018-06-12 12:46         ` Marc-André Lureau
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 07/12] libvhost-user: export vug_source_new Marc-André Lureau
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 08/12] contrib: add vhost-user-input Marc-André Lureau
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 09/12] Add vhost-input-pci Marc-André Lureau
2018-06-04  8:58   ` Gerd Hoffmann
2018-06-07 22:22     ` Marc-André Lureau
2018-06-08  5:41       ` Gerd Hoffmann
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 10/12] vhost-user: add vhost_user_gpu_set_socket() Marc-André Lureau
2018-06-04  9:28   ` Gerd Hoffmann
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 11/12] Add virtio-gpu vhost-user backend Marc-André Lureau
2018-06-04  9:37   ` Gerd Hoffmann
2018-06-08 17:25     ` Marc-André Lureau
2018-06-09  1:02       ` Marc-André Lureau
2018-06-11  6:49       ` Gerd Hoffmann
2018-06-01 16:27 ` [Qemu-devel] [RFC v2 12/12] contrib: add vhost-user-gpu Marc-André Lureau
2018-06-01 17:28 ` [Qemu-devel] [RFC v2 00/12] 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.