From patchwork Sat Jun 4 21:33:11 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marc-Andr=C3=A9_Lureau?= X-Patchwork-Id: 630297 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3rMZ4T3FdYz9t61 for ; Sun, 5 Jun 2016 07:35:05 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=1F5NK+VU; dkim-atps=neutral Received: from localhost ([::1]:34194 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b9JDj-0007Ic-Es for incoming@patchwork.ozlabs.org; Sat, 04 Jun 2016 17:35:03 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44697) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b9JCI-0005nM-S7 for qemu-devel@nongnu.org; Sat, 04 Jun 2016 17:33:36 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1b9JCG-0005iE-97 for qemu-devel@nongnu.org; Sat, 04 Jun 2016 17:33:33 -0400 Received: from mail-wm0-x243.google.com ([2a00:1450:400c:c09::243]:34803) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b9JCF-0005i2-EH for qemu-devel@nongnu.org; Sat, 04 Jun 2016 17:33:32 -0400 Received: by mail-wm0-x243.google.com with SMTP id n184so7287049wmn.1 for ; Sat, 04 Jun 2016 14:33:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=8kg+MMWcVrbf3tz3lp2gHKvaAbRH/LaeRKT8IFdgMHM=; b=1F5NK+VUZ/jjC4dh+YLVQt+wpIYN2VWonwp5cinfYZJTz6DTYNvUrlfuYYonsm+puZ 0Cprktw6wYvCkXL4OnBmYQrOQ+dcPIQI52sCY9ZMUMSFDAedaj56O+K6crtWrcPAMvfP vgkybRLPEDKhkYWE4qZeTYIMEF8dpZEIViLFwzugekdzRVHZ4xkZj57QsvQ6oD/avB7a 1fugkrWB9aKC4CLnWOey6/cJdYm3v6sp3NT7wsoQlPvY2CaKZBlYe/1fk3prcvqmMCSn fUZNiSVyLfyp9yearc5n5zVeqTAoLisTfZ1mduNSukliMLfXwIYs8Qqb8grLk8LmGwU+ +J4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding; bh=8kg+MMWcVrbf3tz3lp2gHKvaAbRH/LaeRKT8IFdgMHM=; b=KrtmFQeR9DPueAtwvX7XyZEYD94uTKYK2gRaIHQJokduSQ99Z2RrfFXTEW8jPc0n4W gumkdSwtjEIFRNyFsLOWql+hPIdYdiJc3Y14ItRfK9KDm41Xe8Ff33htnRf79iifxa77 Nc0j0dkIt6AuugFLVZphvDfLI0o1i6Vn8VawN829Rfw0iTyATwmED3/qcxQtmfeK3znz 1+XrMJzdw4s59BilNbFolZ80jKLyWpAfUxEbXnUvQHcZFhGhsr2WggwsHmNn/OAiKmbU apHpUSt2afDcsaIR+jrm+P+YomFtUKdAqup8DeNiZpf7YNBuQAAQ+buvnTJSnJ0w358J OGRQ== X-Gm-Message-State: ALyK8tLvtVPoCbMRnAI2cnRmX/UmVd11BgIKs++BMDILAnOTlY0d7ncTTZWyrwv1CaLGCg== X-Received: by 10.194.133.98 with SMTP id pb2mr8819206wjb.144.1465076010743; Sat, 04 Jun 2016 14:33:30 -0700 (PDT) Received: from localhost (APoitiers-110-1-1-235.w193-248.abo.wanadoo.fr. [193.248.103.235]) by smtp.gmail.com with ESMTPSA id d5sm12353720wje.20.2016.06.04.14.33.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 04 Jun 2016 14:33:29 -0700 (PDT) From: marcandre.lureau@redhat.com To: qemu-devel@nongnu.org Date: Sat, 4 Jun 2016 23:33:11 +0200 Message-Id: <1465076003-26291-3-git-send-email-marcandre.lureau@redhat.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1465076003-26291-1-git-send-email-marcandre.lureau@redhat.com> References: <1465076003-26291-1-git-send-email-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::243 Subject: [Qemu-devel] [RFC 02/14] Add vhost-user-backend X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , kraxel@redhat.com Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" From: 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. 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 --- 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 + * + * 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 + * + * 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