@@ -78,6 +78,7 @@ static void cryptodev_builtin_init(
"cryptodev-builtin", NULL);
cc->info_str = g_strdup_printf("cryptodev-builtin0");
cc->queue_index = 0;
+ cc->type = CRYPTODEV_BACKEND_TYPE_BUILTIN;
backend->conf.peers.ccs[0] = cc;
backend->conf.crypto_services =
@@ -29,6 +29,7 @@
#include "standard-headers/linux/virtio_crypto.h"
#include "sysemu/cryptodev-vhost.h"
#include "chardev/char-fe.h"
+#include "sysemu/cryptodev-vhost-user.h"
/**
@@ -58,6 +59,20 @@ cryptodev_vhost_user_running(
return crypto ? 1 : 0;
}
+CryptoDevBackendVhost *
+cryptodev_vhost_user_get_vhost(
+ CryptoDevBackendClient *cc,
+ CryptoDevBackend *b,
+ uint16_t queue)
+{
+ CryptoDevBackendVhostUser *s =
+ CRYPTODEV_BACKEND_VHOST_USER(b);
+ assert(cc->type == CRYPTODEV_BACKEND_TYPE_VHOST_USER);
+ assert(queue < MAX_CRYPTO_QUEUE_NUM);
+
+ return s->vhost_crypto[queue];
+}
+
static void cryptodev_vhost_user_stop(int queues,
CryptoDevBackendVhostUser *s)
{
@@ -190,6 +205,7 @@ static void cryptodev_vhost_user_init(
cc->info_str = g_strdup_printf("cryptodev-vhost-user%lu to %s ",
i, chr->label);
cc->queue_index = i;
+ cc->type = CRYPTODEV_BACKEND_TYPE_VHOST_USER;
backend->conf.peers.ccs[i] = cc;
@@ -25,7 +25,11 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
#include "sysemu/cryptodev-vhost.h"
+#include "hw/virtio/virtio-crypto.h"
+#include "hw/virtio/virtio-bus.h"
+#include "sysemu/cryptodev-vhost-user.h"
uint64_t
@@ -71,3 +75,223 @@ fail:
g_free(crypto);
return NULL;
}
+
+static int
+cryptodev_vhost_start_one(CryptoDevBackendVhost *crypto,
+ VirtIODevice *dev)
+{
+ int r;
+
+ crypto->dev.nvqs = 1;
+ crypto->dev.vqs = crypto->vqs;
+
+ r = vhost_dev_enable_notifiers(&crypto->dev, dev);
+ if (r < 0) {
+ goto fail_notifiers;
+ }
+
+ r = vhost_dev_start(&crypto->dev, dev);
+ if (r < 0) {
+ goto fail_start;
+ }
+
+ return 0;
+
+fail_start:
+ vhost_dev_disable_notifiers(&crypto->dev, dev);
+fail_notifiers:
+ return r;
+}
+
+static void
+cryptodev_vhost_stop_one(CryptoDevBackendVhost *crypto,
+ VirtIODevice *dev)
+{
+ vhost_dev_stop(&crypto->dev, dev);
+ vhost_dev_disable_notifiers(&crypto->dev, dev);
+}
+
+CryptoDevBackendVhost *
+cryptodev_get_vhost(CryptoDevBackendClient *cc,
+ CryptoDevBackend *b,
+ uint16_t queue)
+{
+ CryptoDevBackendVhost *vhost_crypto = NULL;
+
+ if (!cc) {
+ return NULL;
+ }
+
+ switch (cc->type) {
+ case CRYPTODEV_BACKEND_TYPE_VHOST_USER:
+ vhost_crypto = cryptodev_vhost_user_get_vhost(cc, b, queue);
+ break;
+ default:
+ break;
+ }
+
+ return vhost_crypto;
+}
+
+static void
+cryptodev_vhost_set_vq_index(CryptoDevBackendVhost *crypto,
+ int vq_index)
+{
+ crypto->dev.vq_index = vq_index;
+}
+
+static int
+vhost_set_vring_enable(CryptoDevBackendClient *cc,
+ CryptoDevBackend *b,
+ uint16_t queue, int enable)
+{
+ CryptoDevBackendVhost *crypto =
+ cryptodev_get_vhost(cc, b, queue);
+ const VhostOps *vhost_ops;
+
+ cc->vring_enable = enable;
+
+ if (!crypto) {
+ return 0;
+ }
+
+ vhost_ops = crypto->dev.vhost_ops;
+ if (vhost_ops->vhost_set_vring_enable) {
+ return vhost_ops->vhost_set_vring_enable(&crypto->dev, enable);
+ }
+
+ return 0;
+}
+
+int cryptodev_vhost_start(VirtIODevice *dev, int total_queues)
+{
+ VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
+ VirtioBusState *vbus = VIRTIO_BUS(qbus);
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
+ int r, e;
+ int i;
+ CryptoDevBackend *b = vcrypto->cryptodev;
+ CryptoDevBackendVhost *vhost_crypto;
+ CryptoDevBackendClient *cc;
+
+ if (!k->set_guest_notifiers) {
+ error_report("binding does not support guest notifiers");
+ return -ENOSYS;
+ }
+
+ for (i = 0; i < total_queues; i++) {
+ cc = b->conf.peers.ccs[i];
+
+ vhost_crypto = cryptodev_get_vhost(cc, b, i);
+ cryptodev_vhost_set_vq_index(vhost_crypto, i);
+
+ /* Suppress the masking guest notifiers on vhost user
+ * because vhost user doesn't interrupt masking/unmasking
+ * properly.
+ */
+ if (cc->type == CRYPTODEV_BACKEND_TYPE_VHOST_USER) {
+ dev->use_guest_notifier_mask = false;
+ }
+ }
+
+ r = k->set_guest_notifiers(qbus->parent, total_queues, true);
+ if (r < 0) {
+ error_report("error binding guest notifier: %d", -r);
+ goto err;
+ }
+
+ for (i = 0; i < total_queues; i++) {
+ cc = b->conf.peers.ccs[i];
+
+ vhost_crypto = cryptodev_get_vhost(cc, b, i);
+ r = cryptodev_vhost_start_one(vhost_crypto, dev);
+
+ if (r < 0) {
+ goto err_start;
+ }
+
+ if (cc->vring_enable) {
+ /* restore vring enable state */
+ r = vhost_set_vring_enable(cc, b, i, cc->vring_enable);
+
+ if (r < 0) {
+ goto err_start;
+ }
+ }
+ }
+
+ return 0;
+
+err_start:
+ while (--i >= 0) {
+ cc = b->conf.peers.ccs[i];
+ vhost_crypto = cryptodev_get_vhost(cc, b, i);
+ cryptodev_vhost_stop_one(vhost_crypto, dev);
+ }
+ e = k->set_guest_notifiers(qbus->parent, total_queues, false);
+ if (e < 0) {
+ error_report("vhost guest notifier cleanup failed: %d", e);
+ }
+err:
+ return r;
+}
+
+void cryptodev_vhost_stop(VirtIODevice *dev, int total_queues)
+{
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
+ VirtioBusState *vbus = VIRTIO_BUS(qbus);
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
+ VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev);
+ CryptoDevBackend *b = vcrypto->cryptodev;
+ CryptoDevBackendVhost *vhost_crypto;
+ CryptoDevBackendClient *cc;
+ size_t i;
+ int r;
+
+ for (i = 0; i < total_queues; i++) {
+ cc = b->conf.peers.ccs[i];
+
+ vhost_crypto = cryptodev_get_vhost(cc, b, i);
+ cryptodev_vhost_stop_one(vhost_crypto, dev);
+ }
+
+ r = k->set_guest_notifiers(qbus->parent, total_queues, false);
+ if (r < 0) {
+ error_report("vhost guest notifier cleanup failed: %d", r);
+ }
+ assert(r >= 0);
+}
+
+void cryptodev_vhost_virtqueue_mask(VirtIODevice *dev,
+ int queue,
+ int idx, bool mask)
+{
+ VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev);
+ CryptoDevBackend *b = vcrypto->cryptodev;
+ CryptoDevBackendVhost *vhost_crypto;
+ CryptoDevBackendClient *cc;
+
+ assert(queue < MAX_CRYPTO_QUEUE_NUM);
+
+ cc = b->conf.peers.ccs[queue];
+ vhost_crypto = cryptodev_get_vhost(cc, b, queue);
+
+ vhost_virtqueue_mask(&vhost_crypto->dev, dev, idx, mask);
+}
+
+bool cryptodev_vhost_virtqueue_pending(VirtIODevice *dev,
+ int queue, int idx)
+{
+ VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev);
+ CryptoDevBackend *b = vcrypto->cryptodev;
+ CryptoDevBackendVhost *vhost_crypto;
+ CryptoDevBackendClient *cc;
+
+ assert(queue < MAX_CRYPTO_QUEUE_NUM);
+
+ cc = b->conf.peers.ccs[queue];
+ vhost_crypto = cryptodev_get_vhost(cc, b, queue);
+
+ return vhost_virtqueue_pending(&vhost_crypto->dev, idx);
+}
@@ -7,7 +7,7 @@ common-obj-y += virtio-mmio.o
obj-y += virtio.o virtio-balloon.o
obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o
-obj-y += virtio-crypto.o
+obj-$(CONFIG_LINUX) += virtio-crypto.o
obj-$(CONFIG_VIRTIO_PCI) += virtio-crypto-pci.o
endif
@@ -20,6 +20,7 @@
#include "hw/virtio/virtio-crypto.h"
#include "hw/virtio/virtio-access.h"
#include "standard-headers/linux/virtio_ids.h"
+#include "sysemu/cryptodev-vhost.h"
#define VIRTIO_CRYPTO_VM_VERSION 1
@@ -880,6 +881,72 @@ static void virtio_crypto_get_config(VirtIODevice *vdev, uint8_t *config)
memcpy(config, &crypto_cfg, c->config_size);
}
+static bool virtio_crypto_started(VirtIOCrypto *c, uint8_t status)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(c);
+ return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ (c->status & VIRTIO_CRYPTO_S_HW_READY) && vdev->vm_running;
+}
+
+static void virtio_crypto_vhost_status(VirtIOCrypto *c, uint8_t status)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(c);
+ int queues = c->multiqueue ? c->max_queues : 1;
+ CryptoDevBackend *b = c->cryptodev;
+ CryptoDevBackendClient *cc = b->conf.peers.ccs[0];
+
+ if (!cryptodev_get_vhost(cc, b, 0)) {
+ return;
+ }
+
+ if ((virtio_crypto_started(c, status)) == !!c->vhost_started) {
+ return;
+ }
+
+ if (!c->vhost_started) {
+ int r;
+
+ c->vhost_started = 1;
+ r = cryptodev_vhost_start(vdev, queues);
+ if (r < 0) {
+ error_report("unable to start vhost crypto: %d: "
+ "falling back on userspace virtio", -r);
+ c->vhost_started = 0;
+ }
+ } else {
+ cryptodev_vhost_stop(vdev, queues);
+ c->vhost_started = 0;
+ }
+}
+
+static void virtio_crypto_set_status(VirtIODevice *vdev, uint8_t status)
+{
+ VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
+
+ virtio_crypto_vhost_status(vcrypto, status);
+}
+
+static void virtio_crypto_guest_notifier_mask(VirtIODevice *vdev, int idx,
+ bool mask)
+{
+ VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
+ int queue = virtio_crypto_vq2q(idx);
+
+ assert(vcrypto->vhost_started);
+
+ cryptodev_vhost_virtqueue_mask(vdev, queue, idx, mask);
+}
+
+static bool virtio_crypto_guest_notifier_pending(VirtIODevice *vdev, int idx)
+{
+ VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
+ int queue = virtio_crypto_vq2q(idx);
+
+ assert(vcrypto->vhost_started);
+
+ return cryptodev_vhost_virtqueue_pending(vdev, queue, idx);
+}
+
static void virtio_crypto_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -893,6 +960,9 @@ static void virtio_crypto_class_init(ObjectClass *klass, void *data)
vdc->get_config = virtio_crypto_get_config;
vdc->get_features = virtio_crypto_get_features;
vdc->reset = virtio_crypto_reset;
+ vdc->set_status = virtio_crypto_set_status;
+ vdc->guest_notifier_mask = virtio_crypto_guest_notifier_mask;
+ vdc->guest_notifier_pending = virtio_crypto_guest_notifier_pending;
}
static void virtio_crypto_instance_init(Object *obj)
@@ -96,6 +96,7 @@ typedef struct VirtIOCrypto {
int multiqueue;
uint32_t curr_queues;
size_t config_size;
+ uint8_t vhost_started;
} VirtIOCrypto;
#endif /* _QEMU_VIRTIO_CRYPTO_H */
new file mode 100644
@@ -0,0 +1,44 @@
+/*
+ * QEMU Crypto Device Common Vhost User Implement
+ *
+ * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
+ *
+ * Authors:
+ * Gonglei <arei.gonglei@huawei.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef CRYPTODEV_VHOST_USER_H
+#define CRYPTODEV_VHOST_USER_H
+
+
+/**
+ * cryptodev_vhost_user_get_vhost:
+ * @cc: the client object for each queue
+ * @b: the cryptodev backend common vhost object
+ * @queue: the queue index
+ *
+ * Gets a new cryptodev backend common vhost object based on
+ * @b and @queue
+ *
+ * Returns: the cryptodev backend common vhost object
+ */
+CryptoDevBackendVhost *
+cryptodev_vhost_user_get_vhost(
+ CryptoDevBackendClient *cc,
+ CryptoDevBackend *b,
+ uint16_t queue);
+
+#endif /* CRYPTODEV_VHOST_USER_H */
@@ -163,12 +163,20 @@ typedef struct CryptoDevBackendClass {
uint32_t queue_index, Error **errp);
} CryptoDevBackendClass;
+typedef enum CryptoDevBackendOptionsType {
+ CRYPTODEV_BACKEND_TYPE_NONE = 0,
+ CRYPTODEV_BACKEND_TYPE_BUILTIN = 1,
+ CRYPTODEV_BACKEND_TYPE_VHOST_USER = 2,
+ CRYPTODEV_BACKEND_TYPE__MAX,
+} CryptoDevBackendOptionsType;
struct CryptoDevBackendClient {
+ CryptoDevBackendOptionsType type;
char *model;
char *name;
char *info_str;
unsigned int queue_index;
+ int vring_enable;
QTAILQ_ENTRY(CryptoDevBackendClient) next;
};