Skip to content

Commit

Permalink
genirq: add threaded interrupt handler support
Browse files Browse the repository at this point in the history
Add support for threaded interrupt handlers:

A device driver can request that its main interrupt handler runs in a
thread. To achive this the device driver requests the interrupt with
request_threaded_irq() and provides additionally to the handler a
thread function. The handler function is called in hard interrupt
context and needs to check whether the interrupt originated from the
device. If the interrupt originated from the device then the handler
can either return IRQ_HANDLED or IRQ_WAKE_THREAD. IRQ_HANDLED is
returned when no further action is required. IRQ_WAKE_THREAD causes
the genirq code to invoke the threaded (main) handler. When
IRQ_WAKE_THREAD is returned handler must have disabled the interrupt
on the device level. This is mandatory for shared interrupt handlers,
but we need to do it as well for obscure x86 hardware where disabling
an interrupt on the IO_APIC level redirects the interrupt to the
legacy PIC interrupt lines.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Ingo Molnar <mingo@elte.hu>
  • Loading branch information
KAGA-KOKO committed Mar 24, 2009
1 parent 80c5520 commit 3aa551c
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 17 deletions.
2 changes: 1 addition & 1 deletion include/linux/hardirq.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET
#endif

#ifdef CONFIG_SMP
#if defined(CONFIG_SMP) || defined(CONFIG_GENERIC_HARDIRQS)
extern void synchronize_irq(unsigned int irq);
#else
# define synchronize_irq(irq) barrier()
Expand Down
37 changes: 35 additions & 2 deletions include/linux/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000

/*
* Bits used by threaded handlers:
* IRQTF_RUNTHREAD - signals that the interrupt handler thread should run
* IRQTF_DIED - handler thread died
*/
enum {
IRQTF_RUNTHREAD,
IRQTF_DIED,
};

typedef irqreturn_t (*irq_handler_t)(int, void *);

/**
Expand All @@ -71,6 +81,9 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
* @next: pointer to the next irqaction for shared interrupts
* @irq: interrupt number
* @dir: pointer to the proc/irq/NN/name entry
* @thread_fn: interupt handler function for threaded interrupts
* @thread: thread pointer for threaded interrupts
* @thread_flags: flags related to @thread
*/
struct irqaction {
irq_handler_t handler;
Expand All @@ -81,11 +94,31 @@ struct irqaction {
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};

extern irqreturn_t no_action(int cpl, void *dev_id);
extern int __must_check request_irq(unsigned int, irq_handler_t handler,
unsigned long, const char *, void *);

extern int __must_check
request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn,
unsigned long flags, const char *name, void *dev);

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

#ifdef CONFIG_GENERIC_HARDIRQS
extern void exit_irq_thread(void);
#else
static inline void exit_irq_thread(void) { }
#endif

extern void free_irq(unsigned int, void *);

struct device;
Expand Down
5 changes: 5 additions & 0 deletions include/linux/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/irqreturn.h>
#include <linux/irqnr.h>
#include <linux/errno.h>
#include <linux/wait.h>

#include <asm/irq.h>
#include <asm/ptrace.h>
Expand Down Expand Up @@ -155,6 +156,8 @@ struct irq_2_iommu;
* @affinity: IRQ affinity on SMP
* @cpu: cpu index useful for balancing
* @pending_mask: pending rebalanced interrupts
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
Expand Down Expand Up @@ -186,6 +189,8 @@ struct irq_desc {
cpumask_var_t pending_mask;
#endif
#endif
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
Expand Down
2 changes: 2 additions & 0 deletions include/linux/irqreturn.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
* enum irqreturn
* @IRQ_NONE interrupt was not from this device
* @IRQ_HANDLED interrupt was handled by this device
* @IRQ_WAKE_THREAD handler requests to wake the handler thread
*/
enum irqreturn {
IRQ_NONE,
IRQ_HANDLED,
IRQ_WAKE_THREAD,
};

typedef enum irqreturn irqreturn_t;
Expand Down
5 changes: 5 additions & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,11 @@ struct task_struct {
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
spinlock_t alloc_lock;

#ifdef CONFIG_GENERIC_HARDIRQS
/* IRQ handler threads */
struct irqaction *irqaction;
#endif

/* Protection of the PI data structures: */
spinlock_t pi_lock;

Expand Down
2 changes: 2 additions & 0 deletions kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,8 @@ NORET_TYPE void do_exit(long code)
schedule();
}

exit_irq_thread();

exit_signals(tsk); /* sets PF_EXITING */
/*
* tsk->flags are checked in the futex code to protect against
Expand Down
31 changes: 30 additions & 1 deletion kernel/irq/handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,37 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)

do {
ret = action->handler(irq, action->dev_id);
if (ret == IRQ_HANDLED)

switch (ret) {
case IRQ_WAKE_THREAD:
/*
* Wake up the handler thread for this
* action. In case the thread crashed and was
* killed we just pretend that we handled the
* interrupt. The hardirq handler above has
* disabled the device interrupt, so no irq
* storm is lurking.
*/
if (likely(!test_bit(IRQTF_DIED,
&action->thread_flags))) {
set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
wake_up_process(action->thread);
}

/*
* Set it to handled so the spurious check
* does not trigger.
*/
ret = IRQ_HANDLED;
/* Fall through to add to randomness */
case IRQ_HANDLED:
status |= action->flags;
break;

default:
break;
}

retval |= ret;
action = action->next;
} while (action);
Expand Down
Loading

0 comments on commit 3aa551c

Please sign in to comment.