qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: marcandre.lureau@redhat.com
To: qemu-devel@nongnu.org
Cc: kraxel@redhat.com, "Marc-André Lureau" <marcandre.lureau@redhat.com>
Subject: [Qemu-devel] [RFC 02/14] Add vhost-user-backend
Date: Sat,  4 Jun 2016 23:33:11 +0200	[thread overview]
Message-ID: <1465076003-26291-3-git-send-email-marcandre.lureau@redhat.com> (raw)
In-Reply-To: <1465076003-26291-1-git-send-email-marcandre.lureau@redhat.com>

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

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.

Currently, you may specify the executable to spawn directly from command
line, ex: -object vhost-user-backend,id=vui,cmd="./vhost-user-input
/dev/input.."

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 or directoy. If not acceptable, the object may just
take use a socket chardev instead (like vhost-user-net today).

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 backends/Makefile.objs              |   2 +
 backends/vhost-user.c               | 262 ++++++++++++++++++++++++++++++++++++
 include/sysemu/vhost-user-backend.h |  65 +++++++++
 3 files changed, 329 insertions(+)
 create mode 100644 backends/vhost-user.c
 create mode 100644 include/sysemu/vhost-user-backend.h

diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 31a3a89..902addd 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -9,3 +9,5 @@ common-obj-$(CONFIG_TPM) += tpm.o
 
 common-obj-y += hostmem.o hostmem-ram.o
 common-obj-$(CONFIG_LINUX) += hostmem-file.o
+
+common-obj-y += vhost-user.o
diff --git a/backends/vhost-user.c b/backends/vhost-user.c
new file mode 100644
index 0000000..89121ed
--- /dev/null
+++ b/backends/vhost-user.c
@@ -0,0 +1,262 @@
+/*
+ * QEMU vhost-user backend
+ *
+ * Copyright (C) 2016 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 "qemu/error-report.h"
+#include "qom/object_interfaces.h"
+#include "sysemu/vhost-user-backend.h"
+#include "sysemu/char.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);
+
+    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);
+    if (ret < 0) {
+        error_setg(errp, "vhost initialization failed: %s", strerror(-ret));
+        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;
+
+    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;
+    }
+
+    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_notifiers;
+    }
+
+    ret = k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true);
+    if (ret < 0) {
+        error_report("Error binding guest notifier");
+        goto err_vhost_stop;
+    }
+
+    b->started = true;
+    return;
+
+err_vhost_stop:
+    vhost_dev_stop(&b->dev, b->vdev);
+err_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;
+
+    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);
+        }
+    }
+
+    vhost_dev_stop(&b->dev, b->vdev);
+    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->cmd);
+    assert(!b->child);
+
+    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);
+
+        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);
+    int sv[2];
+
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+        error_setg_errno(errp, errno, "socketpair() failed");
+        return;
+    }
+
+    b->chr = qemu_chr_open_socket(sv[0], errp);
+    if (!b->chr) {
+        return;
+    }
+
+    vhost_user_backend_spawn_cmd(b, sv[1], errp);
+
+    close(sv[1]);
+
+    /* could vhost_dev_init() happen here, so early vhost-user message
+     * can be exchanged */
+    b->dev.opaque = b->chr;
+}
+
+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 vhost_user_backend_init(Object *obj)
+{
+    object_property_add_str(obj, "cmd", get_cmd, set_cmd, NULL);
+}
+
+static void vhost_user_backend_finalize(Object *obj)
+{
+    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
+
+    g_free(b->cmd);
+
+    if (b->chr) {
+        qemu_chr_delete(b->chr);
+    }
+
+    if (b->child) {
+        object_unref(OBJECT(b->child));
+    }
+}
+
+static bool
+vhost_user_backend_can_be_deleted(UserCreatable *uc, Error **errp)
+{
+    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/include/sysemu/vhost-user-backend.h b/include/sysemu/vhost-user-backend.h
new file mode 100644
index 0000000..ab7cdbc
--- /dev/null
+++ b/include/sysemu/vhost-user-backend.h
@@ -0,0 +1,65 @@
+/*
+ * QEMU vhost-user backend
+ *
+ * Copyright (C) 2016 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 "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;
+
+/**
+ * VhostUserBackendClass:
+ * @parent_class: opaque parent class container
+ */
+struct VhostUserBackendClass {
+    ObjectClass parent_class;
+};
+
+/**
+ * @VhostUserBackend
+ *
+ * @parent: opaque parent object container
+ */
+struct VhostUserBackend {
+    /* private */
+    Object parent;
+
+    char *cmd;
+
+    CharDriverState *chr;
+    struct vhost_dev dev;
+    QIOChannel *child;
+    VirtIODevice *vdev;
+
+    bool started;
+};
+
+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
-- 
2.7.4

  parent reply	other threads:[~2016-06-04 21:33 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-04 21:33 [Qemu-devel] [RFC 00/14] vhost-user backends for gpu & input virtio devices marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 01/14] Add qemu_chr_open_socket() marcandre.lureau
2016-06-04 21:33 ` marcandre.lureau [this message]
2016-06-04 21:33 ` [Qemu-devel] [RFC 03/14] vhost-user: split vhost_user_read() marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 04/14] vhost-user: add vhost_user_input_get_config() marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 05/14] Add vhost-user backend to virtio-input-host marcandre.lureau
2016-06-06  6:22   ` Gerd Hoffmann
2016-06-04 21:33 ` [Qemu-devel] [RFC 06/14] contrib: add vhost-user-input marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 07/14] misc: rename virtio-gpu.h header guard marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 08/14] vhost: make sure call fd has been received marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 09/14] qemu-char: use READ_RETRIES marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 10/14] qemu-char: block during sync read marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 11/14] console: add dpy_gl_scanout2() marcandre.lureau
2016-06-06  6:35   ` Gerd Hoffmann
2016-06-06 13:18     ` Marc-André Lureau
2016-06-06 14:04       ` Gerd Hoffmann
2016-06-04 21:33 ` [Qemu-devel] [RFC 12/14] contrib: add vhost-user-gpu marcandre.lureau
2016-06-04 21:33 ` [Qemu-devel] [RFC 13/14] vhost-user: add vhost_user_gpu_set_socket() marcandre.lureau
2016-06-06  6:36   ` Gerd Hoffmann
2016-06-04 21:33 ` [Qemu-devel] [RFC 14/14] Add virtio-gpu vhost-user backend marcandre.lureau
2016-06-06  6:54   ` Gerd Hoffmann
2016-06-06 13:54 ` [Qemu-devel] [RFC 00/14] vhost-user backends for gpu & input virtio devices Marc-André Lureau
2016-06-07 14:47   ` Gerd Hoffmann
2016-06-07 15:01     ` Marc-André Lureau
2016-06-08  6:11       ` Gerd Hoffmann
2016-06-08 12:53         ` Marc-André Lureau

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1465076003-26291-3-git-send-email-marcandre.lureau@redhat.com \
    --to=marcandre.lureau@redhat.com \
    --cc=kraxel@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).