aboutsummaryrefslogtreecommitdiff
path: root/hw/vhost_net.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/vhost_net.c')
-rw-r--r--hw/vhost_net.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/hw/vhost_net.c b/hw/vhost_net.c
new file mode 100644
index 0000000000..39643f1e99
--- /dev/null
+++ b/hw/vhost_net.c
@@ -0,0 +1,195 @@
+/*
+ * vhost-net support
+ *
+ * Copyright Red Hat, Inc. 2010
+ *
+ * Authors:
+ * Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "net.h"
+#include "net/tap.h"
+
+#include "virtio-net.h"
+#include "vhost_net.h"
+
+#include "config.h"
+
+#ifdef CONFIG_VHOST_NET
+#include <linux/vhost.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <linux/kvm.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/virtio_ring.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+
+#include "vhost.h"
+
+struct vhost_net {
+ struct vhost_dev dev;
+ struct vhost_virtqueue vqs[2];
+ int backend;
+ VLANClientState *vc;
+};
+
+unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
+{
+ /* Clear features not supported by host kernel. */
+ if (!(net->dev.features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))) {
+ features &= ~(1 << VIRTIO_F_NOTIFY_ON_EMPTY);
+ }
+ if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) {
+ features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
+ }
+ return features;
+}
+
+void vhost_net_ack_features(struct vhost_net *net, unsigned features)
+{
+ net->dev.acked_features = net->dev.backend_features;
+ if (features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) {
+ net->dev.acked_features |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY);
+ }
+ if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) {
+ net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC);
+ }
+}
+
+static int vhost_net_get_fd(VLANClientState *backend)
+{
+ switch (backend->info->type) {
+ case NET_CLIENT_TYPE_TAP:
+ return tap_get_fd(backend);
+ default:
+ fprintf(stderr, "vhost-net requires tap backend\n");
+ return -EBADFD;
+ }
+}
+
+struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd)
+{
+ int r;
+ struct vhost_net *net = qemu_malloc(sizeof *net);
+ if (!backend) {
+ fprintf(stderr, "vhost-net requires backend to be setup\n");
+ goto fail;
+ }
+ r = vhost_net_get_fd(backend);
+ if (r < 0) {
+ goto fail;
+ }
+ net->vc = backend;
+ net->dev.backend_features = tap_has_vnet_hdr(backend) ? 0 :
+ (1 << VHOST_NET_F_VIRTIO_NET_HDR);
+ net->backend = r;
+
+ r = vhost_dev_init(&net->dev, devfd);
+ if (r < 0) {
+ goto fail;
+ }
+ if (~net->dev.features & net->dev.backend_features) {
+ fprintf(stderr, "vhost lacks feature mask %llu for backend\n",
+ ~net->dev.features & net->dev.backend_features);
+ vhost_dev_cleanup(&net->dev);
+ goto fail;
+ }
+
+ /* Set sane init value. Override when guest acks. */
+ vhost_net_ack_features(net, 0);
+ return net;
+fail:
+ qemu_free(net);
+ return NULL;
+}
+
+int vhost_net_start(struct vhost_net *net,
+ VirtIODevice *dev)
+{
+ struct vhost_vring_file file = { };
+ int r;
+
+ net->dev.nvqs = 2;
+ net->dev.vqs = net->vqs;
+ r = vhost_dev_start(&net->dev, dev);
+ if (r < 0) {
+ return r;
+ }
+
+ net->vc->info->poll(net->vc, false);
+ qemu_set_fd_handler(net->backend, NULL, NULL, NULL);
+ file.fd = net->backend;
+ for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
+ r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ file.fd = -1;
+ while (--file.index >= 0) {
+ int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file);
+ assert(r >= 0);
+ }
+ net->vc->info->poll(net->vc, true);
+ vhost_dev_stop(&net->dev, dev);
+ return r;
+}
+
+void vhost_net_stop(struct vhost_net *net,
+ VirtIODevice *dev)
+{
+ struct vhost_vring_file file = { .fd = -1 };
+
+ for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
+ int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file);
+ assert(r >= 0);
+ }
+ net->vc->info->poll(net->vc, true);
+ vhost_dev_stop(&net->dev, dev);
+}
+
+void vhost_net_cleanup(struct vhost_net *net)
+{
+ vhost_dev_cleanup(&net->dev);
+ qemu_free(net);
+}
+#else
+struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd)
+{
+ return NULL;
+}
+
+int vhost_net_start(struct vhost_net *net,
+ VirtIODevice *dev)
+{
+ return -ENOSYS;
+}
+void vhost_net_stop(struct vhost_net *net,
+ VirtIODevice *dev)
+{
+}
+
+void vhost_net_cleanup(struct vhost_net *net)
+{
+}
+
+unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
+{
+ return features;
+}
+void vhost_net_ack_features(struct vhost_net *net, unsigned features)
+{
+}
+#endif