Skip to content

Commit

Permalink
Merge branch 'irq/threaded' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/tip/linux-2.6-tip

* 'irq/threaded' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  genirq: fix devres.o build for GENERIC_HARDIRQS=n
  genirq: provide old request_irq() for CONFIG_GENERIC_HARDIRQ=n
  genirq: threaded irq handlers review fixups
  genirq: add support for threaded interrupts to devres
  genirq: add threaded interrupt handler support
  • Loading branch information
torvalds committed Apr 7, 2009
2 parents 2b2ec75 + 9efe21c commit c61b79b
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 26 deletions.
2 changes: 1 addition & 1 deletion include/linux/hardirq.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,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
75 changes: 70 additions & 5 deletions include/linux/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@
#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
* IRQTF_WARNED - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed
*/
enum {
IRQTF_RUNTHREAD,
IRQTF_DIED,
IRQTF_WARNED,
};

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

/**
Expand All @@ -71,6 +83,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,18 +96,68 @@ 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 *);

#ifdef CONFIG_GENERIC_HARDIRQS
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);
}

extern void exit_irq_thread(void);
#else

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

/*
* Special function to avoid ifdeffery in kernel/irq/devres.c which
* gets magically built by GENERIC_HARDIRQS=n architectures (sparc,
* m68k). I really love these $@%#!* obvious Makefile references:
* ../../../kernel/irq/devres.o
*/
static inline 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)
{
return request_irq(irq, handler, flags, name, dev);
}

static inline void exit_irq_thread(void) { }
#endif

extern void free_irq(unsigned int, void *);

struct device;

extern int __must_check devm_request_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id);
extern int __must_check
devm_request_threaded_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, irq_handler_t thread_fn,
unsigned long irqflags, const char *devname,
void *dev_id);

static inline int __must_check
devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,
devname, dev_id);
}

extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id);

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

#include <asm/irq.h>
#include <asm/ptrace.h>
Expand Down Expand Up @@ -158,6 +159,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 @@ -189,6 +192,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 @@ -1294,6 +1294,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 @@ -923,6 +923,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
16 changes: 10 additions & 6 deletions kernel/irq/devres.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ static int devm_irq_match(struct device *dev, void *res, void *data)
}

/**
* devm_request_irq - allocate an interrupt line for a managed device
* devm_request_threaded_irq - allocate an interrupt line for a managed device
* @dev: device to request interrupt for
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs
* @thread_fn: function to be called in a threaded interrupt context. NULL
* for devices which handle everything in @handler
* @irqflags: Interrupt type flags
* @devname: An ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
Expand All @@ -42,9 +44,10 @@ static int devm_irq_match(struct device *dev, void *res, void *data)
* If an IRQ allocated with this function needs to be freed
* separately, dev_free_irq() must be used.
*/
int devm_request_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id)
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, irq_handler_t thread_fn,
unsigned long irqflags, const char *devname,
void *dev_id)
{
struct irq_devres *dr;
int rc;
Expand All @@ -54,7 +57,8 @@ int devm_request_irq(struct device *dev, unsigned int irq,
if (!dr)
return -ENOMEM;

rc = request_irq(irq, handler, irqflags, devname, dev_id);
rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname,
dev_id);
if (rc) {
devres_free(dr);
return rc;
Expand All @@ -66,7 +70,7 @@ int devm_request_irq(struct device *dev, unsigned int irq,

return 0;
}
EXPORT_SYMBOL(devm_request_irq);
EXPORT_SYMBOL(devm_request_threaded_irq);

/**
* devm_free_irq - free an interrupt
Expand Down
50 changes: 49 additions & 1 deletion kernel/irq/handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,15 @@ irqreturn_t no_action(int cpl, void *dev_id)
return IRQ_NONE;
}

static void warn_no_thread(unsigned int irq, struct irqaction *action)
{
if (test_and_set_bit(IRQTF_WARNED, &action->thread_flags))
return;

printk(KERN_WARNING "IRQ %d device %s returned IRQ_WAKE_THREAD "
"but no thread function available.", irq, action->name);
}

DEFINE_TRACE(irq_handler_entry);
DEFINE_TRACE(irq_handler_exit);

Expand All @@ -363,8 +372,47 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
trace_irq_handler_entry(irq, action);
ret = action->handler(irq, action->dev_id);
trace_irq_handler_exit(irq, action, ret);
if (ret == IRQ_HANDLED)

switch (ret) {
case IRQ_WAKE_THREAD:
/*
* Set result to handled so the spurious check
* does not trigger.
*/
ret = IRQ_HANDLED;

/*
* Catch drivers which return WAKE_THREAD but
* did not set up a thread function
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}

/*
* 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);
}

/* 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 c61b79b

Please sign in to comment.