PVBUS allows VMM agnostic PV drivers to discover/configure virtual resources

Signed-off-by: Gregory Haskins <[EMAIL PROTECTED]>
---

 drivers/kvm/Kconfig      |   10 +
 drivers/kvm/Makefile     |    3 
 drivers/kvm/kvm.h        |    4 
 drivers/kvm/kvm_main.c   |    4 
 drivers/kvm/pvbus_host.c |  459 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/kvm/pvbus_host.h |   66 +++++++
 6 files changed, 546 insertions(+), 0 deletions(-)

diff --git a/drivers/kvm/Kconfig b/drivers/kvm/Kconfig
index 0f81af1..cb674bb 100644
--- a/drivers/kvm/Kconfig
+++ b/drivers/kvm/Kconfig
@@ -52,6 +52,16 @@ config KVM_IOQ_HOST
        depends on KVM
        select IOQ
 
+config KVM_PVBUS_HOST
+       boolean "Paravirtualized Bus (PVBUS) host support"
+       depends on KVM
+       select KVM_IOQ_HOST
+       ---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 PVBUS based PV guests in KVM.
+
 config KVM_NET_HOST
        tristate "Para virtual network host device"
        depends on KVM
diff --git a/drivers/kvm/Makefile b/drivers/kvm/Makefile
index 2095061..8926fa9 100644
--- a/drivers/kvm/Makefile
+++ b/drivers/kvm/Makefile
@@ -7,6 +7,9 @@ kvm-objs := kvm_main.o mmu.o x86_emulate.o
 ifeq ($(CONFIG_KVM_IOQ_HOST),y)
 kvm-objs += ioq_host.o
 endif
+ifeq ($(CONFIG_KVM_PVBUS_HOST),y)
+kvm-objs += pvbus_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/kvm.h b/drivers/kvm/kvm.h
index c38c84f..8dc9ac3 100755
--- a/drivers/kvm/kvm.h
+++ b/drivers/kvm/kvm.h
@@ -14,6 +14,7 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/netdevice.h>
+#include <linux/pvbus.h>
 
 #include "vmx.h"
 #include "ioq.h"
@@ -393,6 +394,9 @@ struct kvm {
 #ifdef CONFIG_KVM_IOQ_HOST
        struct ioq_mgr *ioqmgr;
 #endif
+#ifdef CONFIG_KVM_PVBUS_HOST
+       struct kvm_pvbus *pvbus;
+#endif
 
 };
 
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
index fbffd2f..d35ce8d 100644
--- a/drivers/kvm/kvm_main.c
+++ b/drivers/kvm/kvm_main.c
@@ -44,6 +44,7 @@
 
 #include "x86_emulate.h"
 #include "segment_descriptor.h"
+#include "pvbus_host.h"
 
 MODULE_AUTHOR("Qumranet");
 MODULE_LICENSE("GPL");
@@ -350,6 +351,7 @@ static struct kvm *kvm_create_vm(void)
                spin_unlock(&kvm_lock);
        }
        kvmhost_ioqmgr_init(kvm);
+       kvm_pvbus_init(kvm);
        return kvm;
 }
 
@@ -3616,6 +3618,7 @@ static __init int kvm_init(void)
        memset(__va(bad_page_address), 0, PAGE_SIZE);
 
        kvmhost_ioqmgr_module_init();
+       kvm_pvbus_module_init();
 
        return 0;
 
@@ -3637,6 +3640,7 @@ static __exit void kvm_exit(void)
        mntput(kvmfs_mnt);
        unregister_filesystem(&kvm_fs_type);
        kvm_mmu_module_exit();
+       kvm_pvbus_module_exit();
 }
 
 module_init(kvm_init)
diff --git a/drivers/kvm/pvbus_host.c b/drivers/kvm/pvbus_host.c
new file mode 100644
index 0000000..574ca4e
--- /dev/null
+++ b/drivers/kvm/pvbus_host.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2007 Novell.  All Rights Reserved.
+ *
+ * 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/module.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/highmem.h>
+
+#include "pvbus.h"
+#include "pvbus_host.h"
+#include "kvm.h"
+
+struct pvbus_map {
+       int         (*compare)(const void *left, const void *right);
+       const void* (*getkey)(struct rb_node *node);
+
+       spinlock_t     lock;
+       struct rb_root root;
+};
+
+struct _pv_devtype {
+       struct kvm_pv_devtype *item;
+       struct rb_node         node;
+       struct list_head       devlist;
+};
+
+struct _pv_device {
+       struct kvm_pv_device  *item;
+       struct rb_node         node;
+       struct list_head       list;
+       struct _pv_devtype    *parent;
+};
+
+static struct pvbus_map pvbus_typemap; /* stores globally available types */
+
+struct kvm_pvbus {
+       spinlock_t       lock;
+       struct pvbus_map typemap; /* stores locally instantiated types */
+       struct pvbus_map devmap;
+};
+
+/*
+ * ------------------
+ * generic rb map management
+ * ------------------
+ */
+
+static void pvbus_map_init(struct pvbus_map *map)
+{
+       spin_lock_init(&map->lock);
+       map->root = RB_ROOT;
+}
+
+static int pvbus_map_register(struct pvbus_map *map, struct rb_node *node)
+{
+       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) {
+               int result = map->compare(map->getkey(node),
+                                         map->getkey(*new));
+
+               parent = *new;
+
+               if (result < 0)
+                       new = &((*new)->rb_left);
+               else if (result > 0)
+                       new = &((*new)->rb_right);
+               else {
+                       ret = -EEXIST;
+                       break;
+               }
+       }
+
+       if (!ret) {
+               /* Add new node and rebalance tree. */
+               rb_link_node(node, parent, new);
+               rb_insert_color(node, root);
+       }
+
+       spin_unlock(&map->lock);
+
+       return ret;
+}
+
+static struct rb_node* pvbus_map_find(struct pvbus_map *map, const void *key)
+{
+       struct rb_node *node;
+
+       spin_lock(&map->lock);
+
+       node = map->root.rb_node;
+
+       while (node) {
+               int result = map->compare(map->getkey(node), key);
+
+               if (result < 0)
+                       node = node->rb_left;
+               else if (result > 0)
+                       node = node->rb_right;
+               else {
+                       break;
+               }
+       }
+
+       spin_unlock(&map->lock);
+
+       return node;
+}
+
+static void pvbus_map_erase(struct pvbus_map *map, struct rb_node *node)
+{
+       spin_lock(&map->lock);
+       rb_erase(node, &map->root);
+       spin_unlock(&map->lock);
+}
+
+/*
+ * ------------------
+ * pv_devtype rb map
+ * ------------------
+ */
+static int pv_devtype_map_compare(const void *left, const void *right)
+{
+       return strcmp((char*)left, (char*)right);
+}
+
+static const void* pv_devtype_map_getkey(struct rb_node *node)
+{
+       struct _pv_devtype *dt;
+
+       dt = container_of(node, struct _pv_devtype, node);
+
+       return dt->item->name;
+}
+
+static void pv_devtype_map_init(struct pvbus_map *map)
+{
+       pvbus_map_init(map);
+
+       map->compare   = pv_devtype_map_compare;
+       map->getkey    = pv_devtype_map_getkey;
+}
+
+static struct _pv_devtype* devtype_map_find(struct pvbus_map *map,
+                                           const void *key)
+{
+       struct rb_node *node = pvbus_map_find(map, key);
+       if (!node)
+               return NULL;
+
+       return container_of(node, struct _pv_devtype, node);
+}
+
+/*
+ * ------------------
+ * pv_device rb map
+ * ------------------
+ */
+static int pv_device_map_compare(const void *left, const void *right)
+{
+       u64 lid = *(const u64*)left;
+       u64 rid = *(const u64*)right;
+
+       return lid - rid;
+}
+
+static const void* pv_device_map_getkey(struct rb_node *node)
+{
+       struct _pv_device *dev;
+
+       dev = container_of(node, struct _pv_device, node);
+
+       return &dev->item->id;
+}
+
+static void pv_device_map_init(struct pvbus_map *map)
+{
+       pvbus_map_init(map);
+
+       map->compare   = pv_device_map_compare;
+       map->getkey    = pv_device_map_getkey;
+}
+
+static struct _pv_device* device_map_find(struct pvbus_map *map,
+                                         const void *key)
+{
+       struct rb_node *node = pvbus_map_find(map, key);
+       if (!node)
+               return NULL;
+
+       return container_of(node, struct _pv_device, node);
+}
+
+/*
+ * ------------------
+ * hypercall implementation
+ * ------------------
+ */
+
+static int kvm_pvbus_enumerate(struct kvm_pvbus *pvbus,
+                              const char* name,
+                              struct pvbus_dev inst[],
+                              u32 *cnt)
+{
+       struct _pv_devtype    *_devtype;
+       struct list_head      *pos;
+       u32                    avail = *cnt;
+
+       *cnt = 0;
+
+       _devtype = devtype_map_find(&pvbus->typemap, name);
+       if (!_devtype)
+               return -ENOENT;
+
+       list_for_each(pos, &_devtype->devlist) {
+
+               /*
+                * If avail drops to 0 or below, it means we have more
+                * instances than the client has space for.  So we simply stop
+                * recording new instances, but allow the loop to continue to
+                * get an accurate count.
+                */
+               if (avail > 0) {
+                       struct _pv_device     *_dev;
+                       struct kvm_pv_device  *dev;
+
+                       _dev = container_of(pos, struct _pv_device, list);
+                       dev  = _dev->item;
+
+                       inst[*cnt].instance = dev->id;
+                       inst[*cnt].version  = dev->ver;
+               }
+
+               (*cnt)++;
+               avail--;
+
+       }
+
+       /*
+        * We fail the call if we ran out of space to give the caller the
+        * opportunity to call again with a larger array
+        */
+       if (avail < 0)
+               return -ENOSPC;
+
+       return 0;
+}
+
+static int kvm_pvbus_call(struct kvm_pvbus *pvbus,
+                         u64 instance, u32 func, void *data, size_t len)
+{
+       struct kvm_pv_device *dev;
+       struct _pv_device    *_dev = device_map_find(&pvbus->devmap,
+                                                    &instance);
+       if (!_dev)
+               return -ENOENT;
+
+       dev = _dev->item;
+
+       return dev->call(dev, func, data, len);
+}
+
+/*
+ * Our hypercall format will always follow with the call-id in arg[0],
+ * a pointer to the arguments in arg[1], and the argument length in arg[2]
+ */
+static unsigned long kvm_pvbus_hc(struct kvm_vcpu *vcpu,
+                                 unsigned long args[])
+{
+       struct kvm_pvbus *pvbus = vcpu->kvm->pvbus;
+       void *vdata = (void*)gpa_to_hva(vcpu->kvm, args[1]);
+       int ret = -EINVAL;
+
+       /* FIXME: We need to validate vdata so that malicious guests cannot
+          cause the host to segfault */
+
+       switch (args[0])
+       {
+       case KVM_PVBUS_OP_ENUM: {
+               struct enumerate_params *params;
+               struct pvbus_dev *inst;
+
+               params = (struct enumerate_params*)vdata;
+
+               inst = (struct pvbus_dev*)&params->data[params->inst_offset];
+
+               ret = kvm_pvbus_enumerate(pvbus,
+                                         &params->data[params->name_offset],
+                                         inst,
+                                         &params->count);
+       }
+       case KVM_PVBUS_OP_CALL: {
+               struct call_params *params;
+               void *data;
+
+               params = (struct call_params*)vdata;
+               data = gpa_to_hva(vcpu->kvm, params->data);
+
+               /*
+                * FIXME: Again, we should validate that
+                *
+                *    params->data to params->data+len
+                *
+                * is a valid region owned by the guest
+                */
+
+               ret = kvm_pvbus_call(pvbus, params->inst, params->func,
+                                    data, params->len);
+
+               /* FIXME: Do we need to unmap the data */
+       }
+       }
+
+       /* FIXME: Do we need to kunmap the vdata? */
+
+       return ret;
+}
+
+int kvm_pvbus_registertype(struct kvm_pv_devtype *devtype)
+{
+       struct _pv_devtype *_devtype = kzalloc(sizeof(*_devtype), GFP_KERNEL);
+       if (!_devtype)
+               return -ENOMEM;
+
+       _devtype->item = devtype;
+       INIT_LIST_HEAD(&_devtype->devlist);
+
+       return pvbus_map_register(&pvbus_typemap, &_devtype->node);
+}
+EXPORT_SYMBOL_GPL(kvm_pvbus_registertype);
+
+int kvm_pvbus_createinstance(struct kvm *kvm, const char *name,
+                            const char *cfg, u64 *id)
+{
+       struct kvm_pvbus      *pvbus = kvm->pvbus;
+       struct _pv_devtype    *_devtype;
+       struct kvm_pv_devtype *devtype;
+       struct _pv_device     *_dev;
+       struct kvm_pv_device  *dev;
+       u64                    _id;
+       int                    ret;
+
+       _devtype = devtype_map_find(&pvbus->typemap, name);
+       if (!_devtype) {
+               struct _pv_devtype    *gdevtype;
+               /*
+                * If we get here it could be because this is the first
+                * time this devtype has been accessed by this particular
+                * vm.  So we need to check the global typemap also
+                */
+               gdevtype = devtype_map_find(&pvbus_typemap, name);
+               if (!gdevtype)
+                       /* I guess this is plain old fashioned not present */
+                       return -ENOENT;
+
+               /* Else, we need to promote this devtype to the current VM */
+               _devtype = kzalloc(sizeof(*_devtype), GFP_KERNEL);
+               _devtype->item = gdevtype->item;
+               INIT_LIST_HEAD(&_devtype->devlist);
+
+               /* Note that we register it with the pvbus context */
+               ret = pvbus_map_register(&pvbus->typemap, &_devtype->node);
+               if (ret < 0)
+                       return ret;
+       }
+
+       devtype = _devtype->item;
+
+       _dev = kzalloc(sizeof(*_dev), GFP_KERNEL);
+       if (!_dev)
+               return -ENOMEM;
+
+       /* We just use the pointer address as a unique id */
+       _id = (u64)_dev;
+
+       ret = devtype->create(kvm, devtype, _id, cfg, &dev);
+       if (ret < 0)
+               goto out_err;
+
+       _dev->item   = dev;
+       INIT_LIST_HEAD(&_dev->list);
+       _dev->parent = _devtype;
+
+       pvbus_map_register(&pvbus->devmap, &_dev->node);
+       list_add_tail(&_devtype->devlist, &_dev->list);
+
+       *id = _id;
+
+       return 0;
+
+ out_err:
+       kfree(_dev);
+
+       return ret;
+}
+
+int kvm_pvbus_init(struct kvm *kvm)
+{
+       struct kvm_pvbus *pvbus = kzalloc(sizeof(*pvbus), GFP_KERNEL);
+       if (!pvbus)
+               return -ENOMEM;
+
+       spin_lock_init(&pvbus->lock);
+       pv_devtype_map_init(&pvbus->typemap);
+       pv_device_map_init(&pvbus->devmap);
+
+       kvm->pvbus = pvbus;
+
+       return 0;
+}
+
+__init int kvm_pvbus_module_init(void)
+{
+       struct kvm_hypercall hc;
+
+       pv_devtype_map_init(&pvbus_typemap);
+
+       /* Register our hypercall */
+       hc.hypercall      = kvm_pvbus_hc;
+       hc.idx            = __NR_hypercall_pvbus;
+
+       kvm_register_hypercall(THIS_MODULE, &hc);
+
+       return 0;
+}
+
+ __exit void kvm_pvbus_module_exit(void)
+{
+       /* FIXME: Unregister our hypercall */
+}
+
+
+
+
diff --git a/drivers/kvm/pvbus_host.h b/drivers/kvm/pvbus_host.h
new file mode 100644
index 0000000..a3cc7a0
--- /dev/null
+++ b/drivers/kvm/pvbus_host.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2007 Novell.  All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef _KVM_PVBUS_HOST_H
+#define _KVM_PVBUS_HOST_H
+
+#include <linux/rbtree.h>
+
+#ifdef CONFIG_KVM_PVBUS_HOST
+
+struct kvm;
+
+struct kvm_pvbus;
+
+struct kvm_pv_device {
+       int (*call)(struct kvm_pv_device *t, u32 func, void *data, size_t len);
+       void (*destroy)(struct kvm_pv_device *t);
+
+       u64            id;
+       u32            ver;
+
+};
+
+struct kvm_pv_devtype {
+       int (*create)(struct kvm *kvm,
+                     struct kvm_pv_devtype *t, u64 id, const char *cfg,
+                     struct kvm_pv_device **dev);
+       void (*destroy)(struct kvm_pv_devtype *t);
+
+       const char     *name;
+};
+
+int kvm_pvbus_init(struct kvm *kvm);
+int kvm_pvbus_module_init(void);
+void kvm_pvbus_module_exit(void);
+int kvm_pvbus_registertype(struct kvm_pv_devtype *devtype);
+int kvm_pvbus_unregistertype(const char *name);
+int kvm_pvbus_createinstance(struct kvm *kvm, const char *name,
+                            const char *config, u64 *id);
+
+#else /* CONFIG_KVM_PVBUS_HOST */
+
+#define kvm_pvbus_init(kvm) {}
+#define kvm_pvbus_module_init() {}
+#define kvm_pvbus_module_exit() {}
+
+#endif /* CONFIG_KVM_PVBUS_HOST */
+
+#endif /* _KVM_PVBUS_HOST_H */


-------------------------------------------------------------------------
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

Reply via email to