diff mbox

[1/3] virt-core: binding together drivers and hypervisors

Message ID 20090531185901.GB10043@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Michael S. Tsirkin May 31, 2009, 6:59 p.m. UTC
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
diff mbox

Patch

diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index a58504e..693b807 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -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
 
diff --git a/drivers/Makefile b/drivers/Makefile
index 1266ead..9f14b08 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -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/
diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
new file mode 100644
index 0000000..ace7b2e
--- /dev/null
+++ b/drivers/virt/Kconfig
@@ -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.
diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile
new file mode 100644
index 0000000..7a77047
--- /dev/null
+++ b/drivers/virt/Makefile
@@ -0,0 +1 @@ 
+obj-$(CONFIG_VIRT_CORE) += virt_core.o
diff --git a/drivers/virt/virt_core.c b/drivers/virt/virt_core.c
new file mode 100644
index 0000000..89e1f53
--- /dev/null
+++ b/drivers/virt/virt_core.c
@@ -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);
diff --git a/include/linux/virt.h b/include/linux/virt.h
new file mode 100644
index 0000000..ecc0aef
--- /dev/null
+++ b/include/linux/virt.h
@@ -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