[PATCH 06/10] KVM: Add a guest side driver for IOQ

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

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

drivers/kvm/Kconfig | 28 +++
drivers/kvm/Makefile | 3
drivers/kvm/ioq.h | 39 +++++
drivers/kvm/ioq_guest.c | 195 +++++++++++++++++++++++
drivers/kvm/pvbus.h | 63 +++++++
drivers/kvm/pvbus_guest.c | 382 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/kvm.h | 4
7 files changed, 706 insertions(+), 8 deletions(-)

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

-config KVM_NET
- tristate "Para virtual network device"
- depends on KVM
- ---help---
- Provides support for guest paravirtualization networking
-
config KVM_NET_HOST
tristate "Para virtual network host device"
depends on KVM
---help---
Provides support for host paravirtualization networking

+config KVM_GUEST
+ bool "KVM Guest support"
+ depends on X86
+ default y
+
+config KVM_PVBUS_GUEST
+ tristate "Paravirtualized Bus (PVBUS) support"
+ depends on KVM_GUEST
+ select IOQ
+ select PVBUS
+ ---help---
+ PVBUS is an infrastructure for generic PV drivers to take advantage
+ of an underlying hypervisor without having to understand the details
+ of the hypervisor itself. You only need this option if you plan to
+ run this kernel as a KVM guest.
+
+config KVM_NET
+ tristate "Para virtual network device"
+ depends on KVM && KVM_GUEST
+ ---help---
+ Provides support for guest paravirtualization networking
+
endif # VIRTUALIZATION
diff --git a/drivers/kvm/Makefile b/drivers/kvm/Makefile
index 92600d8..c6a59bb 100644
--- a/drivers/kvm/Makefile
+++ b/drivers/kvm/Makefile
@@ -14,4 +14,5 @@ kvm-net-objs = kvm_net.o
obj-$(CONFIG_KVM_NET) += kvm-net.o
kvm-net-host-objs = kvm_net_host.o
obj-$(CONFIG_KVM_NET_HOST) += kvm_net_host.o
-
+kvm-pvbus-objs := ioq_guest.o pvbus_guest.o
+obj-$(CONFIG_KVM_PVBUS_GUEST) += kvm-pvbus.o
diff --git a/drivers/kvm/ioq.h b/drivers/kvm/ioq.h
new file mode 100644
index 0000000..7e955f1
--- /dev/null
+++ b/drivers/kvm/ioq.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef _KVM_IOQ_H_
+#define _KVM_IOQ_H_
+
+#include <linux/ioq.h>
+
+#define IOQHC_REGISTER 1
+#define IOQHC_UNREGISTER 2
+#define IOQHC_SIGNAL 3
+
+struct ioq_register {
+ ioq_id_t id;
+ u32 irq;
+ u64 ring;
+};
+
+
+#endif /* _KVM_IOQ_H_ */
diff --git a/drivers/kvm/ioq_guest.c b/drivers/kvm/ioq_guest.c
new file mode 100644
index 0000000..068aeb1
--- /dev/null
+++ b/drivers/kvm/ioq_guest.c
@@ -0,0 +1,195 @@
+/*
+ * 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 <asm/hypercall.h>
+
+#include "ioq.h"
+#include "kvm.h"
+
+struct kvmguest_ioq {
+ struct ioq ioq;
+ int irq;
+};
+
+struct kvmguest_ioq* to_ioq(struct ioq *ioq)
+{
+ return container_of(ioq, struct kvmguest_ioq, ioq);
+}
+
+static int ioq_hypercall(unsigned long nr, void *data)
+{
+ return hypercall(2, __NR_hypercall_ioq, nr, __pa(data));
+}
+
+/*
+ * ------------------
+ * interrupt handler
+ * ------------------
+ */
+irqreturn_t kvmguest_ioq_intr(int irq, void *dev)
+{
+ struct kvmguest_ioq *_ioq = to_ioq(dev);
+
+ ioq_wakeup(&_ioq->ioq);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * ------------------
+ * ioq implementation
+ * ------------------
+ */
+
+static int kvmguest_ioq_signal(struct ioq *ioq)
+{
+ return ioq_hypercall(IOQHC_SIGNAL, &ioq->id);
+}
+
+static void kvmguest_ioq_destroy(struct ioq *ioq)
+{
+ struct kvmguest_ioq *_ioq = to_ioq(ioq);
+ int ret;
+
+ ret = ioq_hypercall(IOQHC_UNREGISTER, &ioq->id);
+ BUG_ON (ret < 0);
+
+ free_irq(_ioq->irq, NULL);
+ destroy_irq(_ioq->irq);
+
+ kfree(_ioq->ioq.ring);
+ kfree(_ioq->ioq.head_desc);
+ kfree(_ioq);
+}
+
+/*
+ * ------------------
+ * ioqmgr implementation
+ * ------------------
+ */
+static int kvmguest_ioq_register(struct kvmguest_ioq *ioq, ioq_id_t id,
+ int irq, void *ring)
+{
+ struct ioq_register data = {
+ .id = id,
+ .irq = irq,
+ .ring = (u64)__pa(ring),
+ };
+
+ return ioq_hypercall(IOQHC_REGISTER, &data);
+}
+
+static int kvmguest_ioq_create(struct ioq_mgr *t, struct ioq **ioq,
+ size_t ringsize, int flags)
+{
+ struct kvmguest_ioq *_ioq = NULL;
+ struct ioq_ring_head *head_desc = NULL;
+ void *ring = NULL;
+ size_t ringlen = sizeof(struct ioq_ring_desc) * ringsize;
+ int ret = -ENOMEM;
+
+ _ioq = kzalloc(sizeof(*_ioq), GFP_KERNEL);
+ if (!_ioq)
+ goto error;
+
+ head_desc = kzalloc(sizeof(*head_desc), GFP_KERNEL | GFP_DMA);
+ if (!head_desc)
+ goto error;
+
+ ring = kzalloc(ringlen, GFP_KERNEL | GFP_DMA);
+ if (!ring)
+ goto error;
+
+ head_desc->magic = IOQ_RING_MAGIC;
+ head_desc->ver = IOQ_RING_VER;
+ head_desc->id = (ioq_id_t)_ioq;
+ head_desc->count = ringsize;
+ head_desc->ptr = (u64)__pa(ring);
+
+ /* Dynamically assign a free IRQ to this resource */
+ _ioq->irq = create_irq();
+
+ ioq_init(&_ioq->ioq);
+
+ _ioq->ioq.signal = kvmguest_ioq_signal;
+ _ioq->ioq.destroy = kvmguest_ioq_destroy;
+
+ _ioq->ioq.id = head_desc->id;
+ _ioq->ioq.locale = ioq_locality_north;
+ _ioq->ioq.mgr = t;
+ _ioq->ioq.head_desc = head_desc;
+ _ioq->ioq.ring = ring;
+
+ ret = request_irq(_ioq->irq, kvmguest_ioq_intr, 0, "KVM-IOQ", _ioq);
+ if (ret < 0)
+ goto error;
+
+ ret = kvmguest_ioq_register(_ioq, _ioq->ioq.id, _ioq->irq, ring);
+ if (ret < 0)
+ goto error;
+
+ *ioq = &_ioq->ioq;
+
+ return 0;
+
+ error:
+ if (_ioq)
+ kfree(_ioq);
+ if (head_desc)
+ kfree(head_desc);
+ if (ring)
+ kfree(ring);
+
+ return ret;
+}
+
+static int kvmguest_ioq_connect(struct ioq_mgr *t, ioq_id_t id,
+ struct ioq **ioq, int flags)
+{
+ /* You cannot connect to queues on the guest */
+ return -EINVAL;
+
+}
+
+int kvmguest_ioqmgr_alloc(struct ioq_mgr **mgr)
+{
+ struct ioq_mgr *_mgr = kzalloc(sizeof(*_mgr), GFP_KERNEL);
+ if (!_mgr)
+ return -ENOMEM;
+
+ _mgr->create = kvmguest_ioq_create;
+ _mgr->connect = kvmguest_ioq_connect;
+
+ *mgr = _mgr;
+
+ return 0;
+}
+
+void kvmguest_ioqmgr_free(struct ioq_mgr *mgr)
+{
+ kfree(mgr);
+}
+
+
+
+
diff --git a/drivers/kvm/pvbus.h b/drivers/kvm/pvbus.h
new file mode 100644
index 0000000..3241ef0
--- /dev/null
+++ b/drivers/kvm/pvbus.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 Novell. All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef _KVM_PVBUS_H
+#define _KVM_PVBUS_H
+
+#include <linux/ioq.h>
+
+#define KVM_PVBUS_OP_REGISTER 1
+#define KVM_PVBUS_OP_UNREGISTER 2
+#define KVM_PVBUS_OP_CALL 3
+
+struct pvbus_register_params {
+ ioq_id_t qid;
+};
+
+struct pvbus_call_params {
+ u64 inst;
+ u32 func;
+ u64 data;
+ u64 len;
+};
+
+#define KVM_PVBUS_EVENT_ADD 1
+#define KVM_PVBUS_EVENT_DROP 2
+
+#define PVBUS_MAX_NAME 128
+
+struct pvbus_add_event {
+ char name[PVBUS_MAX_NAME];
+ u64 id;
+};
+
+struct pvbus_drop_event {
+ u64 id;
+};
+
+struct pvbus_event {
+ u32 eventid;
+ union {
+ struct pvbus_add_event add;
+ struct pvbus_drop_event drop;
+ }data;
+};
+
+#endif /* _KVM_PVBUS_H */
diff --git a/drivers/kvm/pvbus_guest.c b/drivers/kvm/pvbus_guest.c
new file mode 100644
index 0000000..56c3b50
--- /dev/null
+++ b/drivers/kvm/pvbus_guest.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2007 Novell. All Rights Reserved.
+ *
+ * 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/module.h>
+#include <linux/pvbus.h>
+#include <linux/kvm_para.h>
+#include <linux/kvm.h>
+#include <linux/mm.h>
+#include <linux/ioq.h>
+#include <linux/interrupt.h>
+
+#include <asm/hypercall.h>
+
+#include "pvbus.h"
+
+MODULE_AUTHOR ("Gregory Haskins");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1");
+
+int kvmguest_ioqmgr_alloc(struct ioq_mgr **mgr);
+void kvmguest_ioqmgr_free(struct ioq_mgr *mgr);
+
+static int kvm_pvbus_hypercall(unsigned long nr, void *data, unsigned long len)
+{
+ return hypercall(3, __NR_hypercall_pvbus, nr, __pa(data), len);
+}
+
+/*
+ * This is the vm-syscall address - to be patched by the host to
+ * VMCALL (Intel) or VMMCALL (AMD), depending on the CPU model:
+ */
+asm (
+ " .globl hypercall_addr \n"
+ " .align 4 \n"
+ " hypercall_addr: \n"
+ " movl $-38, %eax \n"
+ " ret \n"
+);
+
+extern unsigned char hypercall_addr[6];
+
+#ifndef CONFIG_X86_64
+static DEFINE_PER_CPU(struct kvm_vcpu_para_state, para_state);
+#endif
+
+static int __init kvm_pvbus_probe(void)
+{
+ struct page *hypercall_addr_page;
+ struct kvm_vcpu_para_state *para_state;
+
+#ifdef CONFIG_X86_64
+ struct page *pstate_page;
+ if ((pstate_page = alloc_page(GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ para_state = (struct kvm_vcpu_para_state*)page_address(pstate_page);
+#else
+ para_state = &per_cpu(para_state, cpu);
+#endif
+ /*
+ * Try to write to a magic MSR (which is invalid on any real CPU),
+ * and thus signal to KVM that we wish to entering para-virtualized
+ * mode:
+ */
+ para_state->guest_version = KVM_PARA_API_VERSION;
+ para_state->host_version = -1;
+ para_state->size = sizeof(*para_state);
+ para_state->ret = -1;
+
+ hypercall_addr_page = vmalloc_to_page(hypercall_addr);
+ para_state->hypercall_gpa = page_to_pfn(hypercall_addr_page)
+ << PAGE_SHIFT | offset_in_page(hypercall_addr);
+ printk(KERN_DEBUG "kvm guest: hypercall gpa is 0x%lx\n",
+ (long)para_state->hypercall_gpa);
+
+ if (wrmsr_safe(MSR_KVM_API_MAGIC, __pa(para_state), 0)) {
+ printk(KERN_INFO "KVM guest: WRMSR probe failed.\n");
+ return -1;
+ }
+
+ printk(KERN_DEBUG "kvm guest: host returned %d\n",
+ para_state->ret);
+ printk(KERN_DEBUG "kvm guest: host version: %d\n",
+ para_state->host_version);
+ printk(KERN_DEBUG "kvm guest: syscall entry: %02x %02x %02x %02x\n",
+ hypercall_addr[0], hypercall_addr[1],
+ hypercall_addr[2], hypercall_addr[3]);
+
+ if (para_state->ret) {
+ printk(KERN_ERR "kvm guest: host refused registration.\n");
+ return -1;
+ }
+
+ return 0;
+
+}
+
+struct kvm_pvbus {
+ int connected;
+ struct ioq_mgr *ioqmgr;
+ struct ioq *ioq;
+ struct ioq_notifier ioqn;
+ struct tasklet_struct task;
+};
+
+static struct kvm_pvbus kvm_pvbus;
+
+struct kvm_pvbus_device {
+ struct pvbus_device pvbdev;
+ char name[PVBUS_MAX_NAME];
+};
+
+static int kvm_pvbus_createqueue(struct pvbus_device *dev, struct ioq **ioq,
+ size_t ringsize, int flags)
+{
+ struct ioq_mgr *ioqmgr = kvm_pvbus.ioqmgr;
+
+ return ioqmgr->create(ioqmgr, ioq, ringsize, flags);
+}
+
+static int kvm_pvbus_call(struct pvbus_device *dev, u32 func, void *data,
+ size_t len, int flags)
+{
+ struct pvbus_call_params params = {
+ .inst = dev->id,
+ .func = func,
+ .data = (u64)__pa(data),
+ .len = len,
+ };
+
+ return kvm_pvbus_hypercall(KVM_PVBUS_OP_CALL, &params, sizeof(params));
+}
+
+static void kvm_pvbus_add_event(struct pvbus_add_event *event)
+{
+ int ret;
+ struct kvm_pvbus_device *new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!new) {
+ printk("KVM_PVBUS: Out of memory on add_event\n");
+ return;
+ }
+
+ memcpy(new->name, event->name, PVBUS_MAX_NAME);
+ new->pvbdev.name = new->name;
+ new->pvbdev.id = event->id;
+ new->pvbdev.createqueue = kvm_pvbus_createqueue;
+ new->pvbdev.call = kvm_pvbus_call;
+
+ sprintf(new->pvbdev.dev.bus_id, "%lld", event->id);
+
+ ret = pvbus_device_register(&new->pvbdev);
+ BUG_ON(ret < 0);
+}
+
+static void kvm_pvbus_drop_event(struct pvbus_drop_event *event)
+{
+#if 0 /* FIXME */
+ int ret = pvbus_device_unregister(event->id);
+ BUG_ON(ret < 0);
+#endif
+}
+
+/* INTR-Layer2: Invoked whenever layer 1 schedules our tasklet */
+static void kvm_pvbus_intr_l2(unsigned long _data)
+{
+ struct ioq_iterator iter;
+ int ret;
+
+ /* We want to iterate on the tail of the in-use index */
+ ret = ioq_iter_init(kvm_pvbus.ioq, &iter, ioq_idxtype_inuse, 0);
+ BUG_ON(ret < 0);
+
+ ret = ioq_iter_seek(&iter, ioq_seek_tail, 0, 0);
+ BUG_ON(ret < 0);
+
+ /*
+ * The EOM is indicated by finding a packet that is still owned by
+ * the south side.
+ *
+ * FIXME: This in theory could run indefinitely if the host keeps
+ * feeding us events since there is nothing like a NAPI budget. We
+ * might need to address that
+ */
+ while (!iter.desc->sown) {
+ struct ioq_ring_desc *desc = iter.desc;
+ struct pvbus_event *event = (struct pvbus_event*)desc->cookie;
+
+ switch (event->eventid) {
+ case KVM_PVBUS_EVENT_ADD:
+ kvm_pvbus_add_event(&event->data.add);
+ break;
+ case KVM_PVBUS_EVENT_DROP:
+ kvm_pvbus_drop_event(&event->data.drop);
+ break;
+ default:
+ printk(KERN_WARNING "KVM_PVBUS: Unexpected event %d\n",
+ event->eventid);
+ break;
+ };
+
+ memset(event, 0, sizeof(*event));
+
+ mb();
+ desc->sown = 1; /* give ownership back to the south */
+ mb();
+
+ /* Advance the in-use tail */
+ ret = ioq_iter_pop(&iter, 0);
+ BUG_ON(ret < 0);
+ }
+
+ /* And let the south side know that we changed the rx-queue */
+ ioq_signal(kvm_pvbus.ioq, 0);
+}
+
+/* INTR-Layer1: Invoked whenever the host issues an ioq_signal() */
+static void kvm_pvbus_intr_l1(struct ioq_notifier *ioqn)
+{
+ tasklet_schedule(&kvm_pvbus.task);
+}
+
+static int __init kvm_pvbus_register(void)
+{
+ struct pvbus_register_params params = {
+ .qid = kvm_pvbus.ioq->id,
+ };
+
+ return kvm_pvbus_hypercall(KVM_PVBUS_OP_REGISTER,
+ &params, sizeof(params));
+}
+
+static int __init kvm_pvbus_setup_ring(void)
+{
+ struct ioq *ioq = kvm_pvbus.ioq;
+ struct ioq_iterator iter;
+ int ret;
+
+ /*
+ * We want to iterate on the "valid" index. By default the iterator
+ * will not "autoupdate" which means it will not hypercall the host
+ * with our changes. This is good, because we are really just
+ * initializing stuff here anyway. Note that you can always manually
+ * signal the host with ioq_signal() if the autoupdate feature is not
+ * used.
+ */
+ ret = ioq_iter_init(ioq, &iter, ioq_idxtype_valid, 0);
+ BUG_ON(ret < 0);
+
+ /*
+ * Seek to the head of the valid index (which should be our first
+ * item since the queue is brand-new)
+ */
+ ret = ioq_iter_seek(&iter, ioq_seek_head, 0, 0);
+ BUG_ON(ret < 0);
+
+ /*
+ * Now populate each descriptor with an empty pvbus_event and mark it
+ * valid
+ */
+ while (!iter.desc->valid) {
+ struct pvbus_event *event;
+ size_t len = sizeof(*event);
+ struct ioq_ring_desc *desc = iter.desc;
+
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ desc->cookie = (u64)event;
+ desc->ptr = (u64)__pa(event);
+ desc->len = len; /* total length */
+ desc->alen = 0; /* actual length - filled in by host */
+
+ /*
+ * We don't need any barriers here because the ring is not used
+ * yet
+ */
+ desc->valid = 1;
+ desc->sown = 1; /* give ownership to the south */
+
+ /*
+ * This push operation will simultaneously advance the
+ * valid-head index and increment our position in the queue
+ * by one.
+ */
+ ret = ioq_iter_push(&iter, 0);
+ BUG_ON(ret < 0);
+ }
+
+ return 0;
+}
+
+int __init kvm_pvbus_init(void)
+{
+ struct ioq_mgr *ioqmgr = NULL;
+ int ret;
+
+ memset(&kvm_pvbus, 0, sizeof(kvm_pvbus));
+
+ ret = kvm_pvbus_probe();
+ if (ret < 0)
+ return ret;
+
+ kvm_pvbus.connected = 1;
+
+ /* Allocate an IOQ-manager to use for all operations */
+ ret = kvmguest_ioqmgr_alloc(&ioqmgr);
+ if (ret < 0) {
+ printk(KERN_ERR "KVM_PVBUS: Could not create ioqmgr\n");
+ return ret;
+ }
+
+ kvm_pvbus.ioqmgr = ioqmgr;
+
+ /* Now allocate an IOQ to use for hotplug notification */
+ ret = ioqmgr->create(ioqmgr, &kvm_pvbus.ioq, 32, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "KVM_PVBUS: Cound not create hotplug ioq\n");
+ goto out_fail;
+ }
+
+ ret = kvm_pvbus_setup_ring();
+ if (ret < 0) {
+ printk(KERN_ERR "KVM_PVBUS: Cound not setup ring\n");
+ goto out_fail;
+ }
+
+ /* Setup our interrupt callback */
+ kvm_pvbus.ioqn.signal = kvm_pvbus_intr_l1;
+ kvm_pvbus.ioq->notifier = &kvm_pvbus.ioqn;
+ tasklet_init(&kvm_pvbus.task, kvm_pvbus_intr_l2, 0);
+
+ /*
+ * Finally register our queue on the host to start receiving hotplug
+ * updates
+ */
+ ret = kvm_pvbus_register();
+ if (ret < 0) {
+ printk(KERN_ERR "KVM_PVBUS: Could not register with host\n");
+ goto out_fail;
+ }
+
+ return 0;
+
+ out_fail:
+ kvmguest_ioqmgr_free(ioqmgr);
+
+ return ret;
+
+}
+
+static void __exit kvm_pvbus_exit(void)
+{
+ if (kvm_pvbus.connected)
+ kvm_pvbus_hypercall(KVM_PVBUS_OP_UNREGISTER, NULL, 0);
+
+ if (kvm_pvbus.ioq)
+ kvm_pvbus.ioq->destroy(kvm_pvbus.ioq);
+
+ kvmguest_ioqmgr_free(kvm_pvbus.ioqmgr);
+}
+
+module_init(kvm_pvbus_init);
+module_exit(kvm_pvbus_exit);
+
+
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 992aeec..bc2b51e 100755
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -377,13 +377,15 @@ struct kvm_pvnet_config {
* No registers are clobbered by the hypercall, except that the
* return value is in RAX.
*/
-#define KVM_NR_HYPERCALLS 5
+#define KVM_NR_HYPERCALLS 7

#define __NR_hypercall_test 0
#define __NR_hypercall_register_eth 1
#define __NR_hypercall_send_eth 2
#define __NR_hypercall_set_multicast_eth 3
#define __NR_hypercall_start_stop_eth 4
+#define __NR_hypercall_ioq 5
+#define __NR_hypercall_pvbus 6

#define __NR_hypercall_balloon (KVM_NR_HYPERCALLS + 0)