IOQ is a shared-memory-queue interface for implmenting PV driver communication.
Signed-off-by: Gregory Haskins <[EMAIL PROTECTED]> --- 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 cba03d2..0f81af1 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 <[EMAIL PROTECTED]> + * + * 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 ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ _______________________________________________ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel