[PATCH 09/10] KVM: Add PVBUS support to the KVM host

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

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

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

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

diff --git a/drivers/kvm/Kconfig b/drivers/kvm/Kconfig
index d9def33..9f2ef22 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..cc506f4
--- /dev/null
+++ b/drivers/kvm/pvbus_host.c
@@ -0,0 +1,636 @@
+/*
+ * 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/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/highmem.h>
+#include <linux/workqueue.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);
+
+ struct mutex lock;
+ struct rb_root root;
+ size_t count;
+};
+
+struct _pv_devtype {
+ struct kvm_pv_devtype *item;
+ struct rb_node node;
+};
+
+struct _pv_device {
+ struct kvm_pv_device *item;
+ struct rb_node node;
+ struct _pv_devtype *parent;
+ int synced;
+};
+
+static struct pvbus_map pvbus_typemap;
+
+struct kvm_pvbus_eventq {
+ struct mutex lock;
+ struct ioq *ioq;
+
+};
+
+struct kvm_pvbus {
+ struct mutex lock;
+ struct kvm *kvm;
+ struct pvbus_map devmap;
+ struct kvm_pvbus_eventq eventq;
+};
+
+/*
+ * ------------------
+ * generic rb map management
+ * ------------------
+ */
+
+static void pvbus_map_init(struct pvbus_map *map)
+{
+ mutex_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;
+
+ mutex_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);
+ map->count++;
+ }
+
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+
+static struct rb_node* pvbus_map_find(struct pvbus_map *map, const void *key)
+{
+ struct rb_node *node;
+
+ mutex_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;
+ }
+ }
+
+ mutex_unlock(&map->lock);
+
+ return node;
+}
+
+static void pvbus_map_erase(struct pvbus_map *map, struct rb_node *node)
+{
+ mutex_lock(&map->lock);
+ rb_erase(node, &map->root);
+ map->count--;
+ mutex_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);
+}
+
+/*
+ * ------------------
+ * event-inject code
+ * ------------------
+ */
+static void kvm_pvbus_inject_event(struct kvm_pvbus *pvbus, u32 eventid,
+ void *data, size_t len)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct kvm_pvbus_eventq *eventq = &pvbus->eventq;
+ struct ioq_iterator iter;
+ struct pvbus_event *entry;
+ int ret;
+
+ add_wait_queue(&eventq->ioq->wq, &wait);
+
+ mutex_lock(&eventq->lock);
+
+ /* We want to iterate on the head of the in-use index */
+ ret = ioq_iter_init(eventq->ioq, &iter,
+ ioq_idxtype_inuse, IOQ_ITER_AUTOUPDATE);
+ BUG_ON(ret < 0);
+
+ ret = ioq_iter_seek(&iter, ioq_seek_head, 0, 0);
+ BUG_ON(ret < 0);
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ while (!iter.desc->sown)
+ schedule();
+
+ set_current_state(TASK_RUNNING);
+
+ entry = (struct pvbus_event*)gpa_to_hva(pvbus->kvm, iter.desc->ptr);
+
+ entry->eventid = eventid;
+ memcpy(&entry->data, data, len);
+
+ mb();
+ iter.desc->sown = 0;
+ mb();
+
+ /*
+ * This will push the index AND signal the guest since AUTOUPDATE is
+ * enabled
+ */
+ ret = ioq_iter_push(&iter, 0);
+ BUG_ON(ret < 0);
+
+ /* FIXME: Unmap the entry */
+
+ mutex_unlock(&eventq->lock);
+}
+
+static void kvm_pvbus_inject_add(struct kvm_pvbus *pvbus,
+ const char *name, u64 id)
+{
+ struct pvbus_add_event data = {
+ .id = id,
+ };
+
+ strncpy(data.name, name, PVBUS_MAX_NAME);
+
+ kvm_pvbus_inject_event(pvbus, KVM_PVBUS_EVENT_ADD,
+ &data, sizeof(data));
+}
+
+/*
+ * ------------------
+ * add-event code
+ * ------------------
+ */
+
+struct deferred_add {
+ struct kvm_pvbus *pvbus;
+ struct work_struct work;
+ size_t count;
+ struct pvbus_add_event data[1];
+};
+
+static void kvm_pvbus_deferred_resync(struct work_struct *work)
+{
+ struct deferred_add *event = container_of(work,
+ struct deferred_add,
+ work);
+ int i;
+
+
+ for (i = 0; i < event->count; i++) {
+ struct pvbus_add_event *entry = &event->data[i];
+
+ kvm_pvbus_inject_add(event->pvbus, entry->name, entry->id);
+ }
+
+ kfree(event);
+}
+
+#define for_each_rbnode(node, root) \
+ for (node = rb_first(root); node != NULL; node = rb_next(node))
+
+/*
+ * This function will build a list of all currently registered devices and
+ * send it to a work-queue to be placed on the ioq. We do this as a two
+ * step operation because the work-queues can queue infinitely deep
+ * (assuming enough memory) whereas IOQ is only queue as deep as the
+ * guest's allocation and then we must sleep. Since we cannot sleep during
+ * registration we have no real choice but to defer things here
+ */
+static int kvm_pvbus_resync(struct kvm_pvbus *pvbus)
+{
+ struct pvbus_map *map = &pvbus->devmap;
+ struct deferred_add *event;
+ struct rb_node *node;
+ size_t len;
+ int i = 0;
+ int ret = 0;
+
+ mutex_lock(&map->lock);
+
+ if (!map->count)
+ /* There are no items current registered so just exit */
+ goto out;
+
+ /*
+ * First allocate a structure large enough to hold our map->count
+ * number of entries that are pending
+ */
+
+ /* we subtract 1 because of item already in struct */
+ len = sizeof(struct pvbus_add_event) * (map->count - 1);
+ event = kzalloc(sizeof(*event) + len, GFP_KERNEL);
+ if (!event) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ event->pvbus = pvbus;
+ event->count = map->count;
+ INIT_WORK(&event->work, kvm_pvbus_deferred_resync);
+
+ /*
+ * Then cycle through the map and load each node discovered into
+ * the event
+ */
+ for_each_rbnode(node, &map->root) {
+ struct pvbus_add_event *entry = &event->data[i++];
+ struct _pv_device *dev = container_of(node,
+ struct _pv_device,
+ node);
+
+ strncpy(entry->name, dev->parent->item->name, PVBUS_MAX_NAME);
+ entry->id = dev->item->id;
+ }
+
+ /* Finally, fire off the work */
+ schedule_work(&event->work);
+
+ out:
+ mutex_unlock(&map->lock);
+
+ return 0;
+}
+
+
+/*
+ * ------------------
+ * hypercall implementation
+ * ------------------
+ */
+
+/*
+ * This function is invoked when the guest wants to start getting hotplug
+ * events from us to publish on the pvbus
+ */
+static int kvm_pvbus_register(struct kvm_pvbus *pvbus, ioq_id_t id)
+{
+ struct ioq_mgr *ioqmgr = pvbus->kvm->ioqmgr;
+ int ret = 0;
+
+ mutex_lock(&pvbus->lock);
+
+ /*
+ * Trying to register while someone else is already registered
+ * is just plain illegal
+ */
+ if (pvbus->eventq.ioq) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Open the IOQ channel back to the guest so we can deliver hotplug
+ * events as devices are registered
+ */
+ ret = ioqmgr->connect(ioqmgr, id, &pvbus->eventq.ioq, 0);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Enable interrupts on the queue
+ */
+ ioq_start(pvbus->eventq.ioq, 0);
+
+ /*
+ * Now we need to backfill the guest by sending any of our currently
+ * registered devices up as hotplug events as if they just happened
+ */
+ ret = kvm_pvbus_resync(pvbus);
+
+ out:
+ mutex_unlock(&pvbus->lock);
+
+ return ret;
+}
+
+/*
+ * This function is invoked whenever a driver calls pvbus_device->call()
+ */
+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_REGISTER: {
+ struct pvbus_register_params *params;
+
+ params = (struct pvbus_register_params*)vdata;
+
+ ret = kvm_pvbus_register(pvbus, params->qid);
+ }
+ case KVM_PVBUS_OP_CALL: {
+ struct pvbus_call_params *params;
+ void *data;
+
+ params = (struct pvbus_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;
+
+ return pvbus_map_register(&pvbus_typemap, &_devtype->node);
+}
+EXPORT_SYMBOL_GPL(kvm_pvbus_registertype);
+
+int kvm_pvbus_unregistertype(const char *name)
+{
+ /* FIXME: */
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(kvm_pvbus_unregistertype);
+
+/*
+ * This function is invoked by an administrative operation which wants to
+ * instantiate a registered type into a device associated with a specific VM.
+ *
+ * For instance, QEMU may one day issue an ioctl that says
+ *
+ * createinstance("ioqnet", "mac = 00:30:cc:00:20:10");
+ *
+ * This would cause the system to search for any registered types called
+ * "ioqnet". If found, it would instantiate the device with a config string
+ * set to give it a specific MAC. Obviously the name and config string are
+ * specific to a particular driver type.
+ */
+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 = NULL;
+ struct kvm_pv_device *dev;
+ u64 _id;
+ int ret = 0;
+
+ mutex_lock(&pvbus->lock);
+
+ _devtype = devtype_map_find(&pvbus_typemap, name);
+ if (!_devtype) {
+ ret = -ENOENT;
+ goto out_err;
+ }
+
+ devtype = _devtype->item;
+
+ _dev = kzalloc(sizeof(*_dev), GFP_KERNEL);
+ if (!_dev) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ /* 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;
+ _dev->parent = _devtype;
+
+ pvbus_map_register(&pvbus->devmap, &_dev->node);
+
+ mutex_unlock(&pvbus->lock);
+
+ *id = _id;
+
+ kvm_pvbus_inject_add(pvbus, name, _id);
+
+ return 0;
+
+ out_err:
+ mutex_unlock(&pvbus->lock);
+
+ kfree(_dev);
+
+ return ret;
+}
+
+int kvm_pvbus_init(struct kvm *kvm)
+{
+ struct kvm_pvbus *pvbus = kzalloc(sizeof(*pvbus), GFP_KERNEL);
+ if (!pvbus)
+ return -ENOMEM;
+
+ mutex_init(&pvbus->lock);
+ pvbus->kvm = kvm;
+ pv_device_map_init(&pvbus->devmap);
+ mutex_init(&pvbus->eventq.lock);
+
+ 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 <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_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 */