@@ -71,6 +71,7 @@ config KVM_TRACE
# OK, it's a little counter-intuitive to do this, but it puts it neatly under
# the virtualization menu.
+source drivers/virt/Kconfig
source drivers/lguest/Kconfig
source drivers/virtio/Kconfig
@@ -104,6 +104,7 @@ obj-$(CONFIG_HID) += hid/
obj-$(CONFIG_PPC_PS3) += ps3/
obj-$(CONFIG_OF) += of/
obj-$(CONFIG_SSB) += ssb/
+obj-$(CONFIG_VIRT_CORE) += virt/
obj-$(CONFIG_VIRTIO) += virtio/
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
new file mode 100644
@@ -0,0 +1,5 @@
+config VIRT_CORE
+ tristate "Generic virtual device binding support"
+ ---help---
+ Core support for binding kernel drivers to virtual devices.
+ Make sure to also select any drivers you wish to use.
new file mode 100644
@@ -0,0 +1 @@
+obj-$(CONFIG_VIRT_CORE) += virt_core.o
new file mode 100644
@@ -0,0 +1,111 @@
+/*
+ * Virt core support: this is the glue that binds together drivers for virtual devices
+ * and hypervisors.
+ *
+ * Copyright (c) 2009 Red Hat Inc.
+ *
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/anon_inodes.h>
+
+#include <linux/virt.h>
+
+/* virt bus implementation */
+static LIST_HEAD(driver_list);
+static LIST_HEAD(hypervisor_list);
+static DEFINE_MUTEX(driver_lock);
+static DEFINE_MUTEX(hypervisor_lock);
+
+static int virt_close(struct inode *inode, struct file *filp)
+{
+ struct virt_dev *dev = filp->private_data;
+ dev->driver->device_remove(dev->driver, dev);
+ dev->hypervisor->put(dev->hypervisor);
+ kfree(dev);
+ return 0;
+}
+
+void virt_driver_register(struct virt_driver *driver,
+ struct file_operations *fops)
+{
+ BUG_ON(fops->release);
+ BUG_ON(fops->open);
+ driver->f_ops = fops;
+ fops->release = virt_close;
+ mutex_lock(&driver_lock);
+ list_add(&driver->list, &driver_list);
+ mutex_unlock(&driver_lock);
+}
+EXPORT_SYMBOL_GPL(virt_driver_register);
+
+void virt_driver_unregister(struct virt_driver *driver)
+{
+ mutex_lock(&driver_lock);
+ list_del(&driver->list);
+ mutex_unlock(&driver_lock);
+}
+EXPORT_SYMBOL_GPL(virt_driver_unregister);
+
+void virt_hypervisor_register(struct virt_hypervisor *hypervisor)
+{
+ mutex_lock(&hypervisor_lock);
+ list_add(&hypervisor->list, &hypervisor_list);
+ mutex_unlock(&hypervisor_lock);
+}
+EXPORT_SYMBOL_GPL(virt_hypervisor_register);
+
+void virt_hypervisor_unregister(struct virt_hypervisor *hypervisor)
+{
+ mutex_lock(&hypervisor_lock);
+ list_del(&hypervisor->list);
+ mutex_unlock(&hypervisor_lock);
+}
+EXPORT_SYMBOL_GPL(virt_hypervisor_unregister);
+
+int virt_device_create(struct virt_hypervisor *hypervisor, int flags,
+ const u8 *id, int id_len)
+{
+ struct virt_dev *dev = kmalloc(sizeof *dev, GFP_KERNEL);
+ struct virt_driver *driver;
+ const struct file_operations *fops;
+ int ret;
+ if (!dev)
+ return -ENOMEM;
+ dev->hypervisor = hypervisor;
+ hypervisor->get(hypervisor);
+
+ mutex_lock(&driver_lock);
+ ret = -ENODEV;
+ list_for_each_entry(driver, &driver_list, list) {
+ ret = driver->device_probe(driver, dev, id, id_len);
+ if (ret != -ENODEV)
+ break;
+ }
+ mutex_unlock(&driver_lock);
+ if (ret)
+ goto err_probe;
+
+ fops = fops_get(driver->f_ops);
+ if (!fops) {
+ ret = -ENOMEM;
+ goto err_file;
+ }
+
+ ret = anon_inode_getfd(driver->name, fops, dev, flags);
+ if (ret < 0)
+ goto err_file;
+ return ret;
+err_file:
+ driver->device_remove(driver, dev);
+err_probe:
+ hypervisor->put(hypervisor);
+ kfree(dev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(virt_device_create);
new file mode 100644
@@ -0,0 +1,94 @@
+#ifndef LINUX_VIRT_H
+#define LINUX_VIRT_H
+/* Virt core support.
+ * Each driver and hypervisor register with core, and then drivers can find
+ * and interfact with hypervisors through the virt_dev object.
+ */
+
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+
+struct virt_dev;
+
+struct virt_hypervisor {
+ /* Fields set by virt core. */
+ struct list_head list;
+ /* Fields set by hypervisor. */
+ /* Operations supported by this hypervisor. */
+ /* core uses reference counting to make sure hypervisor does not
+ * go away while there are devices attached to it. */
+ void (*get)(struct virt_hypervisor *);
+ void (*put)(struct virt_hypervisor *);
+ /* Optional interrupt injection support */
+ int (*set_irq)(struct virt_hypervisor *,
+ int source, int irq, int level);
+};
+
+struct virt_driver {
+ /* Fields set by virt core. */
+ struct list_head list;
+ const struct file_operations *f_ops;
+ /* Fields set by driver. */
+ const char *name;
+ /* Check whether this driver wants this device,
+ * and bind driver to device if yes.
+ * Return -ENODEV if driver does not match this device,
+ * 0 on success, any other value is an error. */
+ int (*device_probe)(struct virt_driver *, struct virt_dev *,
+ const void *id, int id_len);
+ /* Called on removal of all devices where this driver's probe
+ * returned success */
+ void (*device_remove)(struct virt_driver *, struct virt_dev *);
+};
+
+/* Device objects are allocated and freed by virt core. */
+struct virt_dev {
+ struct virt_driver *driver;
+ void *driver_ctx;
+ struct virt_hypervisor *hypervisor;
+};
+
+/* Utility function for file_operations in drivers. */
+static inline struct virt_dev *virt_dev_get(struct file *file)
+{
+ return file->private_data;
+}
+
+#ifdef CONFIG_VIRT_CORE_MODULE
+void virt_driver_register(struct virt_driver *, struct file_operations *);
+void virt_driver_unregister(struct virt_driver *);
+
+void virt_hypervisor_register(struct virt_hypervisor *);
+void virt_hypervisor_unregister(struct virt_hypervisor *);
+
+
+/* Users add devices by making a hypervisor-specific system call (e.g. ioctl)
+ * the result of which is a file descriptor controlling the device.
+ * Hypervisors implement this by calling virt_device_create to get struct file
+ * and install it in file descriptor table. Device is later released by
+ * closing the file descriptor */
+/* Associate a file descriptor with the device and return it.
+ * Returns a value < 0 on failure. */
+int virt_device_create(struct virt_hypervisor *, int flags,
+ const u8 *id, int id_len);
+#else
+static inline
+void virt_driver_register(struct virt_driver *d, struct file_operations *o) {}
+static inline
+void virt_driver_unregister(struct virt_driver *d) {}
+
+static inline
+void virt_hypervisor_register(struct virt_hypervisor *h) {}
+static inline
+void virt_hypervisor_unregister(struct virt_hypervisor *h) {}
+
+static inline
+struct file *virt_device_create(struct virt_hypervisor *h, int flags,
+ const u8 *id, int id_len)
+{
+ return ERR_PTR(-ENOTTY);
+}
+#endif
+
+#endif
This adds support for virt-core: generic glue making it possible to implement kernel-level accelerators (drivers) which are independent of a hypervisor, that is can in theory work on top of either kvm or lguest. Each driver and hypervisor registers with core, and then when user adds a virtual device, drivers can find and interfact with hypervisors through the virt_dev object. Users add devices by making a hypervisor-specific system call (e.g. ioctl) the result of which is a file descriptor controlling the device. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> --- arch/x86/kvm/Kconfig | 1 + drivers/Makefile | 1 + drivers/virt/Kconfig | 5 ++ drivers/virt/Makefile | 1 + drivers/virt/virt_core.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/virt.h | 94 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 213 insertions(+), 0 deletions(-) create mode 100644 drivers/virt/Kconfig create mode 100644 drivers/virt/Makefile create mode 100644 drivers/virt/virt_core.c create mode 100644 include/linux/virt.h