public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* User mode drivers: part 1, interrupt handling (patch for 2.6.11)
@ 2005-03-11  3:36 Peter Chubb
  2005-03-11 10:29 ` Pavel Machek
                   ` (3 more replies)
  0 siblings, 4 replies; 29+ messages in thread
From: Peter Chubb @ 2005-03-11  3:36 UTC (permalink / raw)
  To: linux-kernel


As many of you will be aware, we've been working on infrastructure for
user-mode PCI and other drivers.  The first step is to be able to
handle interrupts from user space. Subsequent patches add
infrastructure for setting up DMA for PCI devices.

The user-level interrupt code doesn't depend on the other patches, and
is probably the most mature of this patchset.


This patch adds a new file to /proc/irq/<nnn>/ called irq.  Suitably 
privileged processes can open this file.  Reading the file returns the 
number of interrupts (if any) that have occurred since the last read.
If the file is opened in blocking mode, reading it blocks until 
an interrupt occurs.  poll(2) and select(2) work as one would expect, to 
allow interrupts to be one of many events to wait for.
(If you didn't like the file, one could have a special system call to
return the file descriptor).

Interrupts are usually masked; while a thread is in poll(2) or read(2) on the 
file they are unmasked.  

All architectures that use CONFIG_GENERIC_HARDIRQ are supported by
this patch.

A low latency user level interrupt handler would do something like
this, on a CONFIG_PREEMPT kernel:

  int irqfd;
  int n_ints;
  struct sched_param sched_param;

  irqfd = open("/proc/irq/513/irq", O_RDONLY);
  mlockall()
  sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 10;
  sched_setscheduler(0, SCHED_FIFO, &sched_param);

  while(read(irqfd, n_ints, sizeof n_ints) == sizeof nints) {
       ... talk to device to handle interrupt
  }

If you don't care about latency, then forget about the mlockall() and
setting the priority, and you don't need CONFIG_PREEMPT.

Signed-off-by: Peter Chubb <peterc@gelato.unsw.edu.au>

 kernel/irq/proc.c |  163 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 153 insertions(+), 10 deletions(-)

Index: linux-2.6.11-usrdrivers/kernel/irq/proc.c
===================================================================
--- linux-2.6.11-usrdrivers.orig/kernel/irq/proc.c	2005-03-11 10:30:57.875619102 +1100
+++ linux-2.6.11-usrdrivers/kernel/irq/proc.c	2005-03-11 10:45:07.146928168 +1100
@@ -9,6 +9,8 @@
 #include <linux/irq.h>
 #include <linux/proc_fs.h>
 #include <linux/interrupt.h>
+#include <linux/poll.h>
+#include "internals.h"
 
 static struct proc_dir_entry *root_irq_dir, *irq_dir[NR_IRQS];
 
@@ -90,27 +92,168 @@
 	action->dir = proc_mkdir(name, irq_dir[irq]);
 }
 
+struct irq_proc {
+ 	unsigned long irq;
+ 	wait_queue_head_t q;
+ 	atomic_t count;
+ 	char devname[TASK_COMM_LEN];
+};
+ 
+static irqreturn_t irq_proc_irq_handler(int irq, void *vidp, struct pt_regs *regs)
+{
+ 	struct irq_proc *idp = (struct irq_proc *)vidp;
+ 
+ 	BUG_ON(idp->irq != irq);
+ 	disable_irq_nosync(irq);
+ 	atomic_inc(&idp->count);
+ 	wake_up(&idp->q);
+ 	return IRQ_HANDLED;
+}
+ 
+
+/*
+ * Signal to userspace an interrupt has occured.
+ */
+static ssize_t irq_proc_read(struct file *filp, char  __user *bufp, size_t len, loff_t *ppos)
+{
+ 	struct irq_proc *ip = (struct irq_proc *)filp->private_data;
+ 	irq_desc_t *idp = irq_desc + ip->irq;
+ 	int pending;
+	
+ 	DEFINE_WAIT(wait);
+	
+ 	if (len < sizeof(int))
+ 		return -EINVAL;
+	
+	pending = atomic_read(&ip->count);
+ 	if (pending == 0) {
+ 		if (idp->status & IRQ_DISABLED)
+ 			enable_irq(ip->irq);
+ 		if (filp->f_flags & O_NONBLOCK)
+ 			return -EWOULDBLOCK;
+ 	}
+	
+ 	while (pending == 0) {
+ 		prepare_to_wait(&ip->q, &wait, TASK_INTERRUPTIBLE);
+		pending = atomic_read(&ip->count);
+		if (pending == 0)
+ 			schedule();
+ 		finish_wait(&ip->q, &wait);
+ 		if (signal_pending(current))
+ 			return -ERESTARTSYS;
+ 	}
+	
+ 	if (copy_to_user(bufp, &pending, sizeof pending))
+ 		return -EFAULT;
+
+ 	*ppos += sizeof pending;
+	
+ 	atomic_sub(pending, &ip->count);
+ 	return sizeof pending;
+}
+
+
+static int irq_proc_open(struct inode *inop, struct file *filp)
+{
+ 	struct irq_proc *ip;
+ 	struct proc_dir_entry *ent = PDE(inop);
+ 	int error;
+
+ 	ip = kmalloc(sizeof *ip, GFP_KERNEL);
+ 	if (ip == NULL)
+ 		return -ENOMEM;
+	
+ 	memset(ip, 0, sizeof(*ip));
+ 	strcpy(ip->devname, current->comm);
+ 	init_waitqueue_head(&ip->q);
+ 	atomic_set(&ip->count, 0);
+ 	ip->irq = (unsigned long)ent->data;
+	
+ 	error = request_irq(ip->irq,
+			    irq_proc_irq_handler,
+			    SA_INTERRUPT,
+			    ip->devname,
+			    ip);
+	if (error < 0) {
+		kfree(ip);
+		return error;
+	}
+ 	filp->private_data = (void *)ip;
+
+ 	return 0;
+}
+
+static int irq_proc_release(struct inode *inop, struct file *filp)
+{
+ 	struct irq_proc *ip = (struct irq_proc *)filp->private_data;
+ 	(void)inop;
+ 	free_irq(ip->irq, ip);
+ 	filp->private_data = NULL;
+ 	kfree(ip);
+ 	return 0;
+}
+
+static unsigned int irq_proc_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ 	struct irq_proc *ip = (struct irq_proc *)filp->private_data;
+ 	irq_desc_t *idp = irq_desc + ip->irq;
+	
+ 	if (atomic_read(&ip->count) > 0)
+ 		return POLLIN | POLLRDNORM; /* readable */
+
+ 	/* if interrupts disabled and we don't have one to process... */
+ 	if (idp->status & IRQ_DISABLED)
+ 		enable_irq(ip->irq);
+
+ 	poll_wait(filp, &ip->q, wait);
+
+ 	if (atomic_read(&ip->count) > 0)
+ 		return POLLIN | POLLRDNORM; /* readable */
+
+ 	return 0;
+}
+
+static struct file_operations irq_proc_file_operations = {
+ 	.read = irq_proc_read,
+ 	.open = irq_proc_open,
+ 	.release = irq_proc_release,
+ 	.poll = irq_proc_poll,
+};
+ 
 #undef MAX_NAMELEN
 
 #define MAX_NAMELEN 10
 
 void register_irq_proc(unsigned int irq)
 {
+	struct proc_dir_entry *entry;
 	char name [MAX_NAMELEN];
 
-	if (!root_irq_dir ||
-		(irq_desc[irq].handler == &no_irq_type) ||
-			irq_dir[irq])
+	if (!root_irq_dir)
 		return;
-
-	memset(name, 0, MAX_NAMELEN);
-	sprintf(name, "%d", irq);
-
-	/* create /proc/irq/1234 */
-	irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+	
+	if (!irq_dir[irq]) {
+		memset(name, 0, MAX_NAMELEN);
+		sprintf(name, "%d", irq);
+
+		/* create /proc/irq/1234 */
+		irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+
+		/* 
+		 * Create handles for user-mode interrupt handlers
+		 * if the kernel hasn't already grabbed the IRQ
+		 */
+ 		entry = create_proc_entry("irq", 0600, irq_dir[irq]);
+ 		if (entry) {
+ 			entry->data = (void *)(unsigned long)irq;
+ 			entry->read_proc = NULL;
+ 			entry->write_proc = NULL;
+ 			entry->proc_fops = &irq_proc_file_operations;
+ 		}
+	}
 
 #ifdef CONFIG_SMP
-	{
+	if (!smp_affinity_entry[irq]) {
 		struct proc_dir_entry *entry;
 
 		/* create /proc/irq/<irq>/smp_affinity */

^ permalink raw reply	[flat|nested] 29+ messages in thread
* RE: User mode drivers: part 1, interrupt handling (patch for 2.6.11)
@ 2005-03-15 19:20 Stephen Warren
  0 siblings, 0 replies; 29+ messages in thread
From: Stephen Warren @ 2005-03-15 19:20 UTC (permalink / raw)
  To: Peter Chubb, Jon Smirl; +Cc: linux-kernel

From: linux-kernel-owner@vger.kernel.org
[mailto:linux-kernel-owner@vger.kernel.org] On Behalf Of Peter Chubb
> >>>>> "Jon" == Jon Smirl <jonsmirl@gmail.com> writes:
> 
> >>  The scenario I'm thinking about with these patches are things like
> >> low-latency user-level networking between nodes in a cluster, where
> >> for good performance even with a kernel driver you don't want to
> >> share your interrupt line with anything else.
> 
> Jon> The code needs to refuse to install if the IRQ line is shared.
> 
> It does.  The request_irq() call explicitly does not include SA_SHARED
> in its flags, so if the line is shared, it'll return an error to user
> space when the driver tries to open the file representing the
interrupt. 

I actually have a simple case of user-mode handling of shared IRQs
working, based off the Gelato work from maybe 6 months ago.

What I did was add a new IOCTL to tell the kernel code how to clear the
interrupt status (or disable the interrupt output on the card, as
appropriate).

This basically took the form of telling the kernel to map a specified
subsection of a specified BAR into kernel space for register access,
then execute a small configurable read-modify-write sequence for the
actual interrupt disable (configurable read/write address, optionally no
read, configurable and/or/shift of the read value (or initial constant)
before write etc.)

This would probably cover most situations.

Once the interrupt is disabled/cleared, user-space can handle it just
like a non-shared IRQ, and finally re-enable it if the kernel mode
twiddling was a disable rather than a status clear.

This is probably pretty generic, and at least will open the code up to
handling a lot more devices, even if not everthing.

Also, the kernel detects a release on the file-handle, and can execute a
different configurable register access sequence to permanently disable
interrupts and/or reset the device, for safety once the user-space app
quits.

-- 
Stephen Warren, Software Engineer, NVIDIA, Fort Collins, CO
swarren@nvidia.com        http://www.nvidia.com/
swarren@wwwdotorg.org     http://www.wwwdotorg.org/pgp.html

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2005-03-15 19:25 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-11  3:36 User mode drivers: part 1, interrupt handling (patch for 2.6.11) Peter Chubb
2005-03-11 10:29 ` Pavel Machek
2005-03-12 16:27   ` Jon Smirl
2005-03-12 18:55     ` Jon Smirl
2005-03-14  0:39     ` Peter Chubb
2005-03-14  1:24       ` Jon Smirl
2005-03-11 13:50 ` Michael Raymond
2005-03-11 17:25   ` Greg KH
2005-03-11 17:31     ` Michael Raymond
2005-03-11 19:14 ` Alan Cox
2005-03-13  2:03   ` Jon Smirl
2005-03-15  4:32     ` Lee Revell
2005-03-15 13:28       ` Alan Cox
2005-03-14  0:02   ` Peter Chubb
2005-03-14 13:33     ` Alan Cox
2005-03-15  3:15       ` Jon Smirl
2005-03-12 15:55 ` Jon Smirl
2005-03-12 17:11   ` Zwane Mwaikambo
2005-03-14  1:55     ` Jon Smirl
2005-03-14  3:04       ` Peter Chubb
2005-03-14  0:36   ` Peter Chubb
     [not found]     ` <9e47339105031317193c28cbcf@mail.gmail.com>
2005-03-14  1:42       ` Peter Chubb
2005-03-14  1:52         ` Jon Smirl
2005-03-14  3:06           ` Peter Chubb
2005-03-15  3:19         ` Jon Smirl
2005-03-15  3:47           ` Peter Chubb
2005-03-15  3:50             ` Jon Smirl
2005-03-15  4:11               ` Peter Chubb
  -- strict thread matches above, loose matches on Subject: below --
2005-03-15 19:20 Stephen Warren

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox