[PATCH 08/10] KVM: Add support for IOQ

Gregory Haskins
2007-08-16T23:14:38Z

IOQ is a shared-memory-queue interface for implmenting PV driver
communication.

Signed-off-by: Gregory Haskins <ghaskins-Et1tbQHTxzrQT0dZR+AlfA@public.gmane.org>
---

drivers/kvm/Kconfig | 5 +
drivers/kvm/Makefile | 3
drivers/kvm/ioq.h | 12 +-
drivers/kvm/ioq_host.c | 365 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/kvm/kvm.h | 5 +
drivers/kvm/kvm_main.c | 3
include/linux/kvm.h | 1
7 files changed, 393 insertions(+), 1 deletions(-)

diff --git a/drivers/kvm/Kconfig b/drivers/kvm/Kconfig
index aca79d1..d9def33 100644
--- a/drivers/kvm/Kconfig
+++ b/drivers/kvm/Kconfig
@@ -47,6 +47,11 @@ config KVM_BALLOON
The driver inflate/deflate guest physical memory on demand.
This ability provides memory over commit for the host

+config KVM_IOQ_HOST
+ boolean "Add IOQ support to KVM"
+ depends on KVM
+ select IOQ
+
config KVM_NET_HOST
tristate "Para virtual network host device"
depends on KVM
diff --git a/drivers/kvm/Makefile b/drivers/kvm/Makefile
index c6a59bb..2095061 100644
--- a/drivers/kvm/Makefile
+++ b/drivers/kvm/Makefile
@@ -4,6 +4,9 @@
EXTRA_CFLAGS :=

kvm-objs := kvm_main.o mmu.o x86_emulate.o
+ifeq ($(CONFIG_KVM_IOQ_HOST),y)
+kvm-objs += ioq_host.o
+endif
obj-$(CONFIG_KVM) += kvm.o
kvm-intel-objs = vmx.o
obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
diff --git a/drivers/kvm/ioq.h b/drivers/kvm/ioq.h
index 7e955f1..b942113 100644
--- a/drivers/kvm/ioq.h
+++ b/drivers/kvm/ioq.h
@@ -25,7 +25,17 @@

#include <linux/ioq.h>

-#define IOQHC_REGISTER 1
+struct kvm;
+
+#ifdef CONFIG_KVM_IOQ_HOST
+int kvmhost_ioqmgr_init(struct kvm *kvm);
+int kvmhost_ioqmgr_module_init(void);
+#else
+#define kvmhost_ioqmgr_init(kvm) {}
+#define kvmhost_ioqmgr_module_init() {}
+#endif
+
+#define IOQHC_REGISTER 1
#define IOQHC_UNREGISTER 2
#define IOQHC_SIGNAL 3

diff --git a/drivers/kvm/ioq_host.c b/drivers/kvm/ioq_host.c
new file mode 100644
index 0000000..413f103
--- /dev/null
+++ b/drivers/kvm/ioq_host.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2007 Novell. All Rights Reserved.
+ *
+ * See include/linux/ioq.h for documentation
+ *
+ * Author:
+ * Gregory Haskins <ghaskins-Et1tbQHTxzrQT0dZR+AlfA@public.gmane.org>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/ioq.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/highmem.h>
+
+#include <asm/atomic.h>
+
+#include "ioq.h"
+#include "kvm.h"
+
+struct kvmhost_ioq {
+ struct ioq ioq;
+ struct rb_node node;
+ atomic_t refcnt;
+ struct kvm_vcpu *vcpu;
+ int irq;
+};
+
+struct kvmhost_map {
+ spinlock_t lock;
+ struct rb_root root;
+};
+
+struct kvmhost_ioq_mgr {
+ struct ioq_mgr mgr;
+ struct kvm *kvm;
+ struct kvmhost_map map;
+};
+
+struct kvmhost_ioq* to_ioq(struct ioq *ioq)
+{
+ return container_of(ioq, struct kvmhost_ioq, ioq);
+}
+
+struct kvmhost_ioq_mgr* to_mgr(struct ioq_mgr *mgr)
+{
+ return container_of(mgr, struct kvmhost_ioq_mgr, mgr);
+}
+
+/*
+ * ------------------
+ * rb map management
+ * ------------------
+ */
+
+static void kvmhost_map_init(struct kvmhost_map *map)
+{
+ spin_lock_init(&map->lock);
+ map->root = RB_ROOT;
+}
+
+static int kvmhost_map_register(struct kvmhost_map *map,
+ struct kvmhost_ioq *ioq)
+{
+ int ret = 0;
+ struct rb_root *root;
+ struct rb_node **new, *parent = NULL;
+
+ spin_lock(&map->lock);
+
+ root = &map->root;
+ new = &(root->rb_node);
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct kvmhost_ioq *this;
+
+ this = container_of(*new, struct kvmhost_ioq, node);
+ parent = *new;
+
+ if (ioq->ioq.id < this->ioq.id)
+ new = &((*new)->rb_left);
+ else if (ioq->ioq.id > this->ioq.id)
+ new = &((*new)->rb_right);
+ else {
+ ret = -EEXIST;
+ break;
+ }
+ }
+
+ if (!ret) {
+ /* Add new node and rebalance tree. */
+ rb_link_node(&ioq->node, parent, new);
+ rb_insert_color(&ioq->node, root);
+ }
+
+ spin_unlock(&map->lock);
+
+ return ret;
+}
+
+static struct kvmhost_ioq* kvmhost_map_find(struct kvmhost_map *map,
+ ioq_id_t id)
+{
+ struct rb_node *node;
+ struct kvmhost_ioq *ioq = NULL;
+
+ spin_lock(&map->lock);
+
+ node = map->root.rb_node;
+
+ while (node) {
+ struct kvmhost_ioq *_ioq;
+
+ _ioq = container_of(node, struct kvmhost_ioq, node);
+
+ if (ioq->ioq.id < id)
+ node = node->rb_left;
+ else if (_ioq->ioq.id > id)
+ node = node->rb_right;
+ else {
+ ioq = _ioq;
+ break;
+ }
+ }
+
+ spin_unlock(&map->lock);
+
+ return ioq;
+}
+
+static void kvmhost_map_erase(struct kvmhost_map *map,
+ struct kvmhost_ioq *ioq)
+{
+ spin_lock(&map->lock);
+ rb_erase(&ioq->node, &map->root);
+ spin_unlock(&map->lock);
+}
+
+/*
+ * ------------------
+ * ioq implementation
+ * ------------------
+ */
+
+static int kvmhost_ioq_signal(struct ioq *ioq)
+{
+ struct kvmhost_ioq *_ioq = to_ioq(ioq);
+ BUG_ON(!_ioq);
+
+ /*
+ * FIXME: Inject an interrupt to the guest for "id"
+ *
+ * We will have to decide if we will have 1:1 IOQ:IRQ, or if we
+ * will aggregate all IOQs through a single IRQ. For purposes of
+ * example, we will assume 1:1.
+ */
+
+ /* kvm_vcpu_send_interrupt(_ioq->vcpu, _ioq->irq); */
+
+ return 0;
+}
+
+static void kvmhost_ioq_destroy(struct ioq *ioq)
+{
+ struct kvmhost_ioq *_ioq = to_ioq(ioq);
+
+ if (atomic_dec_and_test(&_ioq->refcnt))
+ kfree(_ioq);
+}
+
+static struct kvmhost_ioq* kvmhost_ioq_alloc(struct ioq_mgr *t,
+ struct kvm_vcpu *vcpu,
+ ioq_id_t id, int irq, gpa_t ring)
+{
+ struct kvmhost_ioq *_ioq;
+ struct ioq *ioq;
+
+ _ioq = kzalloc(sizeof(*_ioq), GFP_KERNEL);
+ if (!_ioq)
+ return NULL;
+
+ ioq = &_ioq->ioq;
+
+ atomic_set(&_ioq->refcnt, 1);
+ _ioq->vcpu = vcpu;
+ _ioq->irq = irq;
+
+ ioq_init(&_ioq->ioq);
+
+ ioq->signal = kvmhost_ioq_signal;
+ ioq->destroy = kvmhost_ioq_destroy;
+
+ ioq->id = id;
+ ioq->locale = ioq_locality_south;
+ ioq->mgr = t;
+ ioq->head_desc = (struct ioq_ring_head*)gpa_to_hva(vcpu->kvm, ring);
+ ioq->ring = (struct ioq_ring_desc*)gpa_to_hva(vcpu->kvm,
+ ioq->head_desc->ptr);
+
+ return _ioq;
+}
+
+/*
+ * ------------------
+ * hypercall implementation
+ * ------------------
+ */
+
+static int kvmhost_ioq_hc_register(struct ioq_mgr *t, struct kvm_vcpu *vcpu,
+ ioq_id_t id, int irq, gpa_t ring)
+{
+ struct kvmhost_ioq *_ioq = kvmhost_ioq_alloc(t, vcpu, id, irq, ring);
+ int ret;
+
+ if (!_ioq)
+ return -ENOMEM;
+
+ ret = kvmhost_map_register(&to_mgr(t)->map, _ioq);
+ if (ret < 0)
+ kvmhost_ioq_destroy(&_ioq->ioq);
+
+ return 0;
+}
+
+static int kvmhost_ioq_hc_unregister(struct ioq_mgr *t, ioq_id_t id)
+{
+ struct kvmhost_ioq_mgr *_mgr = to_mgr(t);
+ struct kvmhost_ioq *_ioq = kvmhost_map_find(&_mgr->map, id);
+
+ if (!_ioq)
+ return -ENOENT;
+
+ kvmhost_map_erase(&_mgr->map, _ioq);
+ kvmhost_ioq_destroy(&_ioq->ioq);
+
+ return 0;
+}
+
+static int kvmhost_ioq_hc_signal(struct ioq_mgr *t, ioq_id_t id)
+{
+ struct kvmhost_ioq *_ioq = kvmhost_map_find(&to_mgr(t)->map, id);
+
+ if (!_ioq)
+ return -1;
+
+ ioq_wakeup(&_ioq->ioq);
+
+ return 0;
+}
+
+/*
+ * Our hypercall format will always follow with the call-id in arg[0] and
+ * a pointer to the arguments in arg[1]
+ */
+static unsigned long kvmhost_hc(struct kvm_vcpu *vcpu, unsigned long args[])
+{
+ struct ioq_mgr *t = vcpu->kvm->ioqmgr;
+ void *vdata = gpa_to_hva(vcpu->kvm, args[1]);
+ int ret = -EINVAL;
+
+ if (!vdata)
+ return -EINVAL;
+
+ /*
+ * FIXME: we need to make sure that the pointer is sane
+ * so a malicious guest cannot crash the host.
+ */
+
+ switch (args[0])
+ {
+ case IOQHC_REGISTER: {
+ struct ioq_register *data = (struct ioq_register*)vdata;
+ ret = kvmhost_ioq_hc_register(t, vcpu,
+ data->id,
+ data->irq,
+ data->ring);
+ }
+ case IOQHC_UNREGISTER: {
+ ioq_id_t *id = (ioq_id_t*)vdata;
+ ret = kvmhost_ioq_hc_unregister(t, *id);
+ }
+ case IOQHC_SIGNAL: {
+ ioq_id_t *id = (ioq_id_t*)vdata;
+ ret = kvmhost_ioq_hc_signal(t, *id);
+ }
+ }
+
+ /* FIXME: unmap the vdata? */
+
+ return ret;
+}
+
+/*
+ * ------------------
+ * ioqmgr implementation
+ * ------------------
+ */
+
+static int kvmhost_ioq_create(struct ioq_mgr *t, struct ioq **ioq,
+ size_t ringsize, int flags)
+{
+ /* You cannot create queues on the host */
+ return -EINVAL;
+}
+
+static int kvmhost_ioq_connect(struct ioq_mgr *t, ioq_id_t id,
+ struct ioq **ioq, int flags)
+{
+ struct kvmhost_ioq *_ioq = kvmhost_map_find(&to_mgr(t)->map, id);
+
+ if (!_ioq)
+ return -1;
+
+ atomic_inc(&_ioq->refcnt);
+ *ioq = &_ioq->ioq;
+
+ return 0;
+
+}
+
+int kvmhost_ioqmgr_init(struct kvm *kvm)
+{
+ struct kvmhost_ioq_mgr *_mgr = kzalloc(sizeof(*_mgr), GFP_KERNEL);
+ if (!_mgr)
+ return -ENOMEM;
+
+ _mgr->kvm = kvm;
+ kvmhost_map_init(&_mgr->map);
+
+ _mgr->mgr.create = kvmhost_ioq_create;
+ _mgr->mgr.connect = kvmhost_ioq_connect;
+
+ kvm->ioqmgr = &_mgr->mgr;
+
+ return 0;
+}
+
+__init int kvmhost_ioqmgr_module_init(void)
+{
+ struct kvm_hypercall hc;
+
+ hc.hypercall = kvmhost_hc;
+ hc.idx = __NR_hypercall_ioq;
+
+ kvm_register_hypercall(THIS_MODULE, &hc);
+
+ return 0;
+}
+
+
+
+
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h
index 05d5be1..c38c84f 100755
--- a/drivers/kvm/kvm.h
+++ b/drivers/kvm/kvm.h
@@ -16,6 +16,7 @@
#include <linux/netdevice.h>

#include "vmx.h"
+#include "ioq.h"
#include <linux/kvm.h>
#include <linux/kvm_para.h>

@@ -389,6 +390,10 @@ struct kvm {
struct list_head vm_list;
struct net_device *netdev;
struct file *filp;
+#ifdef CONFIG_KVM_IOQ_HOST
+ struct ioq_mgr *ioqmgr;
+#endif
+
};

struct descriptor_table {
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
index f252b39..fbffd2f 100644
--- a/drivers/kvm/kvm_main.c
+++ b/drivers/kvm/kvm_main.c
@@ -349,6 +349,7 @@ static struct kvm *kvm_create_vm(void)
list_add(&kvm->vm_list, &vm_list);
spin_unlock(&kvm_lock);
}
+ kvmhost_ioqmgr_init(kvm);
return kvm;
}

@@ -3614,6 +3615,8 @@ static __init int kvm_init(void)
bad_page_address = page_to_pfn(bad_page) << PAGE_SHIFT;
memset(__va(bad_page_address), 0, PAGE_SIZE);

+ kvmhost_ioqmgr_module_init();
+
return 0;

out:
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index bc2b51e..2cceae3 100755
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -377,6 +377,7 @@ struct kvm_pvnet_config {
* No registers are clobbered by the hypercall, except that the
* return value is in RAX.
*/
+
#define KVM_NR_HYPERCALLS 7

#define __NR_hypercall_test 0