diff mbox

[3/3] virt_irq: virtual device for injecting interrupts

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

Commit Message

Michael S. Tsirkin May 31, 2009, 6:59 p.m. UTC
virt_irq is an alternative to irqfd interface, based
on the virt core infrastructure, which also serves
as an example of virt core usage.

The main advantage here compared to irqfd is the use of fd
created by the virt core, which avoids any possibility of
deadlock issues with eventfd and kvm file descriptors
referencing each other.

As a minor positive side effect, we don't need an
extra lock and don't need to schedule work to inject
the interrupt.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 drivers/virt/Kconfig     |    6 +++
 drivers/virt/Makefile    |    1 +
 drivers/virt/virt_irq.c  |   78 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/virt_irq.h |   19 +++++++++++
 4 files changed, 104 insertions(+), 0 deletions(-)
 create mode 100644 drivers/virt/virt_irq.c
 create mode 100644 include/linux/virt_irq.h
diff mbox

Patch

diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
index ace7b2e..060c8da 100644
--- a/drivers/virt/Kconfig
+++ b/drivers/virt/Kconfig
@@ -3,3 +3,9 @@  config VIRT_CORE
 	---help---
 	  Core support for binding kernel drivers to virtual devices.
 	  Make sure to also select any drivers you wish to use.
+
+config VIRT_IRQ
+	tristate "Virtual device for injecting interrupts from userspace"
+	depends on VIRT_CORE
+	---help---
+	  Simple virtual device that supports injecting interrupts.
diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile
index 7a77047..0072530 100644
--- a/drivers/virt/Makefile
+++ b/drivers/virt/Makefile
@@ -1 +1,2 @@ 
 obj-$(CONFIG_VIRT_CORE) += virt_core.o
+obj-$(CONFIG_VIRT_IRQ) += virt_irq.o
diff --git a/drivers/virt/virt_irq.c b/drivers/virt/virt_irq.c
new file mode 100644
index 0000000..c10087e
--- /dev/null
+++ b/drivers/virt/virt_irq.c
@@ -0,0 +1,78 @@ 
+/*
+ * Virt irq device: simple virtual device for interrupt injection.
+ *
+ * Copyright (c) 2009 Red Hat Inc.
+ *
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/virt.h>
+#include <linux/virt_irq.h>
+
+struct virt_irq_dev {
+	int irq;
+};
+
+static ssize_t virt_irq_write(struct file *f, const char __user *p, size_t s,
+			      loff_t *o)
+{
+	struct virt_dev *dev = virt_dev_get(f);
+	struct virt_irq_dev *vdev = dev->driver_ctx;
+	int r = dev->hypervisor->set_irq(dev->hypervisor, vdev->irq, 0, 1);
+	dev->hypervisor->set_irq(dev->hypervisor, vdev->irq, 0, 0);
+	return r < 0 ? r : 0;
+}
+
+int virt_irq_probe(struct virt_driver *driver, struct virt_dev *dev,
+		    const void *id, int id_len)
+{
+	struct virt_irq_dev *vdev;
+	const struct virt_irq_id *irq_id;
+	if (!dev->hypervisor->set_irq)
+		return -ENODEV;
+	if (id_len != sizeof id)
+		return -ENODEV;
+	irq_id = id;
+	if (memcmp(irq_id->name, "irq", sizeof irq_id->name))
+		return -ENODEV;
+	
+	vdev = kmalloc(sizeof *vdev, GFP_KERNEL);
+	if (!vdev)
+		return -ENOMEM;
+	vdev->irq = irq_id->irq;
+	dev->driver_ctx = vdev;
+	return 0;
+}
+
+void virt_irq_remove(struct virt_driver *driver, struct virt_dev *dev)
+{
+	kfree(dev->driver_ctx);
+}
+
+static struct file_operations virt_irq_fops = {
+	.owner	 = THIS_MODULE,
+	.write   = virt_irq_write,
+};
+
+static struct virt_driver virt_irq_driver = {
+	.name = "virt_irq",
+	.device_probe = virt_irq_probe,
+	.device_remove = virt_irq_remove,
+};
+
+static int __init virt_irq_init(void)
+{
+	virt_driver_register(&virt_irq_driver, &virt_irq_fops);
+	return 0;
+}
+
+static void __exit virt_irq_cleanup(void)
+{
+	virt_driver_unregister(&virt_irq_driver);
+}
+
+module_init(virt_irq_init);
+module_exit(virt_irq_cleanup);
diff --git a/include/linux/virt_irq.h b/include/linux/virt_irq.h
new file mode 100644
index 0000000..6b3421d
--- /dev/null
+++ b/include/linux/virt_irq.h
@@ -0,0 +1,19 @@ 
+#ifndef LINUX_VIRT_IRQ_H
+#define LINUX_VIRT_IRQ_H
+
+#include <linux/types.h>
+
+/* Format for IRQ device id */
+
+struct virt_irq_id {
+	__u8 name[4]; /* Must be "irq\0" */
+	__u32 irq;
+};
+
+static inline void virt_irq_id_init(struct virt_irq_id *id, int irq)
+{
+	memcpy(id->name, "irq", sizeof id->name);
+	id->irq = irq;
+}
+
+#endif