Skip to content

Commit 3aa551c

Browse files
committed
genirq: add threaded interrupt handler support
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 <[email protected]> Reviewed-by: Ingo Molnar <[email protected]>
1 parent 80c5520 commit 3aa551c

File tree

8 files changed

+259
-17
lines changed

8 files changed

+259
-17
lines changed

include/linux/hardirq.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET
106106
#endif
107107

108-
#ifdef CONFIG_SMP
108+
#if defined(CONFIG_SMP) || defined(CONFIG_GENERIC_HARDIRQS)
109109
extern void synchronize_irq(unsigned int irq);
110110
#else
111111
# define synchronize_irq(irq) barrier()

include/linux/interrupt.h

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@
5959
#define IRQF_NOBALANCING 0x00000800
6060
#define IRQF_IRQPOLL 0x00001000
6161

62+
/*
63+
* Bits used by threaded handlers:
64+
* IRQTF_RUNTHREAD - signals that the interrupt handler thread should run
65+
* IRQTF_DIED - handler thread died
66+
*/
67+
enum {
68+
IRQTF_RUNTHREAD,
69+
IRQTF_DIED,
70+
};
71+
6272
typedef irqreturn_t (*irq_handler_t)(int, void *);
6373

6474
/**
@@ -71,6 +81,9 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
7181
* @next: pointer to the next irqaction for shared interrupts
7282
* @irq: interrupt number
7383
* @dir: pointer to the proc/irq/NN/name entry
84+
* @thread_fn: interupt handler function for threaded interrupts
85+
* @thread: thread pointer for threaded interrupts
86+
* @thread_flags: flags related to @thread
7487
*/
7588
struct irqaction {
7689
irq_handler_t handler;
@@ -81,11 +94,31 @@ struct irqaction {
8194
struct irqaction *next;
8295
int irq;
8396
struct proc_dir_entry *dir;
97+
irq_handler_t thread_fn;
98+
struct task_struct *thread;
99+
unsigned long thread_flags;
84100
};
85101

86102
extern irqreturn_t no_action(int cpl, void *dev_id);
87-
extern int __must_check request_irq(unsigned int, irq_handler_t handler,
88-
unsigned long, const char *, void *);
103+
104+
extern int __must_check
105+
request_threaded_irq(unsigned int irq, irq_handler_t handler,
106+
irq_handler_t thread_fn,
107+
unsigned long flags, const char *name, void *dev);
108+
109+
static inline int __must_check
110+
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
111+
const char *name, void *dev)
112+
{
113+
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
114+
}
115+
116+
#ifdef CONFIG_GENERIC_HARDIRQS
117+
extern void exit_irq_thread(void);
118+
#else
119+
static inline void exit_irq_thread(void) { }
120+
#endif
121+
89122
extern void free_irq(unsigned int, void *);
90123

91124
struct device;

include/linux/irq.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/irqreturn.h>
2121
#include <linux/irqnr.h>
2222
#include <linux/errno.h>
23+
#include <linux/wait.h>
2324

2425
#include <asm/irq.h>
2526
#include <asm/ptrace.h>
@@ -155,6 +156,8 @@ struct irq_2_iommu;
155156
* @affinity: IRQ affinity on SMP
156157
* @cpu: cpu index useful for balancing
157158
* @pending_mask: pending rebalanced interrupts
159+
* @threads_active: number of irqaction threads currently running
160+
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
158161
* @dir: /proc/irq/ procfs entry
159162
* @name: flow handler name for /proc/interrupts output
160163
*/
@@ -186,6 +189,8 @@ struct irq_desc {
186189
cpumask_var_t pending_mask;
187190
#endif
188191
#endif
192+
atomic_t threads_active;
193+
wait_queue_head_t wait_for_threads;
189194
#ifdef CONFIG_PROC_FS
190195
struct proc_dir_entry *dir;
191196
#endif

include/linux/irqreturn.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
* enum irqreturn
66
* @IRQ_NONE interrupt was not from this device
77
* @IRQ_HANDLED interrupt was handled by this device
8+
* @IRQ_WAKE_THREAD handler requests to wake the handler thread
89
*/
910
enum irqreturn {
1011
IRQ_NONE,
1112
IRQ_HANDLED,
13+
IRQ_WAKE_THREAD,
1214
};
1315

1416
typedef enum irqreturn irqreturn_t;

include/linux/sched.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,11 @@ struct task_struct {
12921292
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
12931293
spinlock_t alloc_lock;
12941294

1295+
#ifdef CONFIG_GENERIC_HARDIRQS
1296+
/* IRQ handler threads */
1297+
struct irqaction *irqaction;
1298+
#endif
1299+
12951300
/* Protection of the PI data structures: */
12961301
spinlock_t pi_lock;
12971302

kernel/exit.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,8 @@ NORET_TYPE void do_exit(long code)
10371037
schedule();
10381038
}
10391039

1040+
exit_irq_thread();
1041+
10401042
exit_signals(tsk); /* sets PF_EXITING */
10411043
/*
10421044
* tsk->flags are checked in the futex code to protect against

kernel/irq/handle.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,37 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
357357

358358
do {
359359
ret = action->handler(irq, action->dev_id);
360-
if (ret == IRQ_HANDLED)
360+
361+
switch (ret) {
362+
case IRQ_WAKE_THREAD:
363+
/*
364+
* Wake up the handler thread for this
365+
* action. In case the thread crashed and was
366+
* killed we just pretend that we handled the
367+
* interrupt. The hardirq handler above has
368+
* disabled the device interrupt, so no irq
369+
* storm is lurking.
370+
*/
371+
if (likely(!test_bit(IRQTF_DIED,
372+
&action->thread_flags))) {
373+
set_bit(IRQTF_RUNTHREAD, &action->thread_flags);
374+
wake_up_process(action->thread);
375+
}
376+
377+
/*
378+
* Set it to handled so the spurious check
379+
* does not trigger.
380+
*/
381+
ret = IRQ_HANDLED;
382+
/* Fall through to add to randomness */
383+
case IRQ_HANDLED:
361384
status |= action->flags;
385+
break;
386+
387+
default:
388+
break;
389+
}
390+
362391
retval |= ret;
363392
action = action->next;
364393
} while (action);

0 commit comments

Comments
 (0)