mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 11:46:19 +00:00
Merge branch 'irq/threaded' of git://git.kernel.org/pub/scm/linux/kernel/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
This commit is contained in:
commit
c61b79b6ef
9 changed files with 320 additions and 26 deletions
|
@ -116,7 +116,7 @@
|
||||||
# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET
|
# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
#if defined(CONFIG_SMP) || defined(CONFIG_GENERIC_HARDIRQS)
|
||||||
extern void synchronize_irq(unsigned int irq);
|
extern void synchronize_irq(unsigned int irq);
|
||||||
#else
|
#else
|
||||||
# define synchronize_irq(irq) barrier()
|
# define synchronize_irq(irq) barrier()
|
||||||
|
|
|
@ -59,6 +59,18 @@
|
||||||
#define IRQF_NOBALANCING 0x00000800
|
#define IRQF_NOBALANCING 0x00000800
|
||||||
#define IRQF_IRQPOLL 0x00001000
|
#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 *);
|
typedef irqreturn_t (*irq_handler_t)(int, void *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,6 +83,9 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
|
||||||
* @next: pointer to the next irqaction for shared interrupts
|
* @next: pointer to the next irqaction for shared interrupts
|
||||||
* @irq: interrupt number
|
* @irq: interrupt number
|
||||||
* @dir: pointer to the proc/irq/NN/name entry
|
* @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 {
|
struct irqaction {
|
||||||
irq_handler_t handler;
|
irq_handler_t handler;
|
||||||
|
@ -81,18 +96,68 @@ struct irqaction {
|
||||||
struct irqaction *next;
|
struct irqaction *next;
|
||||||
int irq;
|
int irq;
|
||||||
struct proc_dir_entry *dir;
|
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 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 *);
|
extern void free_irq(unsigned int, void *);
|
||||||
|
|
||||||
struct device;
|
struct device;
|
||||||
|
|
||||||
extern int __must_check devm_request_irq(struct device *dev, unsigned int irq,
|
extern int __must_check
|
||||||
irq_handler_t handler, unsigned long irqflags,
|
devm_request_threaded_irq(struct device *dev, unsigned int irq,
|
||||||
const char *devname, void *dev_id);
|
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);
|
extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <linux/irqnr.h>
|
#include <linux/irqnr.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/topology.h>
|
#include <linux/topology.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
|
@ -158,6 +159,8 @@ struct irq_2_iommu;
|
||||||
* @affinity: IRQ affinity on SMP
|
* @affinity: IRQ affinity on SMP
|
||||||
* @cpu: cpu index useful for balancing
|
* @cpu: cpu index useful for balancing
|
||||||
* @pending_mask: pending rebalanced interrupts
|
* @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
|
* @dir: /proc/irq/ procfs entry
|
||||||
* @name: flow handler name for /proc/interrupts output
|
* @name: flow handler name for /proc/interrupts output
|
||||||
*/
|
*/
|
||||||
|
@ -189,6 +192,8 @@ struct irq_desc {
|
||||||
cpumask_var_t pending_mask;
|
cpumask_var_t pending_mask;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
atomic_t threads_active;
|
||||||
|
wait_queue_head_t wait_for_threads;
|
||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
struct proc_dir_entry *dir;
|
struct proc_dir_entry *dir;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,10 +5,12 @@
|
||||||
* enum irqreturn
|
* enum irqreturn
|
||||||
* @IRQ_NONE interrupt was not from this device
|
* @IRQ_NONE interrupt was not from this device
|
||||||
* @IRQ_HANDLED interrupt was handled by this device
|
* @IRQ_HANDLED interrupt was handled by this device
|
||||||
|
* @IRQ_WAKE_THREAD handler requests to wake the handler thread
|
||||||
*/
|
*/
|
||||||
enum irqreturn {
|
enum irqreturn {
|
||||||
IRQ_NONE,
|
IRQ_NONE,
|
||||||
IRQ_HANDLED,
|
IRQ_HANDLED,
|
||||||
|
IRQ_WAKE_THREAD,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum irqreturn irqreturn_t;
|
typedef enum irqreturn irqreturn_t;
|
||||||
|
|
|
@ -1294,6 +1294,11 @@ struct task_struct {
|
||||||
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
|
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
|
||||||
spinlock_t alloc_lock;
|
spinlock_t alloc_lock;
|
||||||
|
|
||||||
|
#ifdef CONFIG_GENERIC_HARDIRQS
|
||||||
|
/* IRQ handler threads */
|
||||||
|
struct irqaction *irqaction;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Protection of the PI data structures: */
|
/* Protection of the PI data structures: */
|
||||||
spinlock_t pi_lock;
|
spinlock_t pi_lock;
|
||||||
|
|
||||||
|
|
|
@ -923,6 +923,8 @@ NORET_TYPE void do_exit(long code)
|
||||||
schedule();
|
schedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit_irq_thread();
|
||||||
|
|
||||||
exit_signals(tsk); /* sets PF_EXITING */
|
exit_signals(tsk); /* sets PF_EXITING */
|
||||||
/*
|
/*
|
||||||
* tsk->flags are checked in the futex code to protect against
|
* tsk->flags are checked in the futex code to protect against
|
||||||
|
|
|
@ -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
|
* @dev: device to request interrupt for
|
||||||
* @irq: Interrupt line to allocate
|
* @irq: Interrupt line to allocate
|
||||||
* @handler: Function to be called when the IRQ occurs
|
* @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
|
* @irqflags: Interrupt type flags
|
||||||
* @devname: An ascii name for the claiming device
|
* @devname: An ascii name for the claiming device
|
||||||
* @dev_id: A cookie passed back to the handler function
|
* @dev_id: A cookie passed back to the handler function
|
||||||
|
@ -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
|
* If an IRQ allocated with this function needs to be freed
|
||||||
* separately, dev_free_irq() must be used.
|
* separately, dev_free_irq() must be used.
|
||||||
*/
|
*/
|
||||||
int devm_request_irq(struct device *dev, unsigned int irq,
|
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
|
||||||
irq_handler_t handler, unsigned long irqflags,
|
irq_handler_t handler, irq_handler_t thread_fn,
|
||||||
const char *devname, void *dev_id)
|
unsigned long irqflags, const char *devname,
|
||||||
|
void *dev_id)
|
||||||
{
|
{
|
||||||
struct irq_devres *dr;
|
struct irq_devres *dr;
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -54,7 +57,8 @@ int devm_request_irq(struct device *dev, unsigned int irq,
|
||||||
if (!dr)
|
if (!dr)
|
||||||
return -ENOMEM;
|
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) {
|
if (rc) {
|
||||||
devres_free(dr);
|
devres_free(dr);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -66,7 +70,7 @@ int devm_request_irq(struct device *dev, unsigned int irq,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(devm_request_irq);
|
EXPORT_SYMBOL(devm_request_threaded_irq);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* devm_free_irq - free an interrupt
|
* devm_free_irq - free an interrupt
|
||||||
|
|
|
@ -339,6 +339,15 @@ irqreturn_t no_action(int cpl, void *dev_id)
|
||||||
return IRQ_NONE;
|
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_entry);
|
||||||
DEFINE_TRACE(irq_handler_exit);
|
DEFINE_TRACE(irq_handler_exit);
|
||||||
|
|
||||||
|
@ -363,8 +372,47 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
|
||||||
trace_irq_handler_entry(irq, action);
|
trace_irq_handler_entry(irq, action);
|
||||||
ret = action->handler(irq, action->dev_id);
|
ret = action->handler(irq, action->dev_id);
|
||||||
trace_irq_handler_exit(irq, action, ret);
|
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;
|
status |= action->flags;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
retval |= ret;
|
retval |= ret;
|
||||||
action = action->next;
|
action = action->next;
|
||||||
} while (action);
|
} while (action);
|
||||||
|
|
|
@ -8,16 +8,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
#include <linux/kthread.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
|
||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
|
|
||||||
#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
|
|
||||||
cpumask_var_t irq_default_affinity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
|
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
|
||||||
* @irq: interrupt number to wait for
|
* @irq: interrupt number to wait for
|
||||||
|
@ -53,9 +52,18 @@ void synchronize_irq(unsigned int irq)
|
||||||
|
|
||||||
/* Oops, that failed? */
|
/* Oops, that failed? */
|
||||||
} while (status & IRQ_INPROGRESS);
|
} while (status & IRQ_INPROGRESS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We made sure that no hardirq handler is running. Now verify
|
||||||
|
* that no threaded handlers are active.
|
||||||
|
*/
|
||||||
|
wait_event(desc->wait_for_threads, !atomic_read(&desc->threads_active));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(synchronize_irq);
|
EXPORT_SYMBOL(synchronize_irq);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
cpumask_var_t irq_default_affinity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* irq_can_set_affinity - Check if the affinity of a given irq can be set
|
* irq_can_set_affinity - Check if the affinity of a given irq can be set
|
||||||
* @irq: Interrupt to check
|
* @irq: Interrupt to check
|
||||||
|
@ -72,6 +80,18 @@ int irq_can_set_affinity(unsigned int irq)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
irq_set_thread_affinity(struct irq_desc *desc, const struct cpumask *cpumask)
|
||||||
|
{
|
||||||
|
struct irqaction *action = desc->action;
|
||||||
|
|
||||||
|
while (action) {
|
||||||
|
if (action->thread)
|
||||||
|
set_cpus_allowed_ptr(action->thread, cpumask);
|
||||||
|
action = action->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* irq_set_affinity - Set the irq affinity of a given irq
|
* irq_set_affinity - Set the irq affinity of a given irq
|
||||||
* @irq: Interrupt to set affinity
|
* @irq: Interrupt to set affinity
|
||||||
|
@ -100,6 +120,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
|
||||||
cpumask_copy(desc->affinity, cpumask);
|
cpumask_copy(desc->affinity, cpumask);
|
||||||
desc->chip->set_affinity(irq, cpumask);
|
desc->chip->set_affinity(irq, cpumask);
|
||||||
#endif
|
#endif
|
||||||
|
irq_set_thread_affinity(desc, cpumask);
|
||||||
desc->status |= IRQ_AFFINITY_SET;
|
desc->status |= IRQ_AFFINITY_SET;
|
||||||
spin_unlock_irqrestore(&desc->lock, flags);
|
spin_unlock_irqrestore(&desc->lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -150,6 +171,8 @@ int irq_select_affinity_usr(unsigned int irq)
|
||||||
|
|
||||||
spin_lock_irqsave(&desc->lock, flags);
|
spin_lock_irqsave(&desc->lock, flags);
|
||||||
ret = setup_affinity(irq, desc);
|
ret = setup_affinity(irq, desc);
|
||||||
|
if (!ret)
|
||||||
|
irq_set_thread_affinity(desc, desc->affinity);
|
||||||
spin_unlock_irqrestore(&desc->lock, flags);
|
spin_unlock_irqrestore(&desc->lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -401,6 +424,90 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int irq_wait_for_interrupt(struct irqaction *action)
|
||||||
|
{
|
||||||
|
while (!kthread_should_stop()) {
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
|
||||||
|
if (test_and_clear_bit(IRQTF_RUNTHREAD,
|
||||||
|
&action->thread_flags)) {
|
||||||
|
__set_current_state(TASK_RUNNING);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
schedule();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt handler thread
|
||||||
|
*/
|
||||||
|
static int irq_thread(void *data)
|
||||||
|
{
|
||||||
|
struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, };
|
||||||
|
struct irqaction *action = data;
|
||||||
|
struct irq_desc *desc = irq_to_desc(action->irq);
|
||||||
|
int wake;
|
||||||
|
|
||||||
|
sched_setscheduler(current, SCHED_FIFO, ¶m);
|
||||||
|
current->irqaction = action;
|
||||||
|
|
||||||
|
while (!irq_wait_for_interrupt(action)) {
|
||||||
|
|
||||||
|
atomic_inc(&desc->threads_active);
|
||||||
|
|
||||||
|
spin_lock_irq(&desc->lock);
|
||||||
|
if (unlikely(desc->status & IRQ_DISABLED)) {
|
||||||
|
/*
|
||||||
|
* CHECKME: We might need a dedicated
|
||||||
|
* IRQ_THREAD_PENDING flag here, which
|
||||||
|
* retriggers the thread in check_irq_resend()
|
||||||
|
* but AFAICT IRQ_PENDING should be fine as it
|
||||||
|
* retriggers the interrupt itself --- tglx
|
||||||
|
*/
|
||||||
|
desc->status |= IRQ_PENDING;
|
||||||
|
spin_unlock_irq(&desc->lock);
|
||||||
|
} else {
|
||||||
|
spin_unlock_irq(&desc->lock);
|
||||||
|
|
||||||
|
action->thread_fn(action->irq, action->dev_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
wake = atomic_dec_and_test(&desc->threads_active);
|
||||||
|
|
||||||
|
if (wake && waitqueue_active(&desc->wait_for_threads))
|
||||||
|
wake_up(&desc->wait_for_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear irqaction. Otherwise exit_irq_thread() would make
|
||||||
|
* fuzz about an active irq thread going into nirvana.
|
||||||
|
*/
|
||||||
|
current->irqaction = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called from do_exit()
|
||||||
|
*/
|
||||||
|
void exit_irq_thread(void)
|
||||||
|
{
|
||||||
|
struct task_struct *tsk = current;
|
||||||
|
|
||||||
|
if (!tsk->irqaction)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printk(KERN_ERR
|
||||||
|
"exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
|
||||||
|
tsk->comm ? tsk->comm : "", tsk->pid, tsk->irqaction->irq);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the THREAD DIED flag to prevent further wakeups of the
|
||||||
|
* soon to be gone threaded handler.
|
||||||
|
*/
|
||||||
|
set_bit(IRQTF_DIED, &tsk->irqaction->flags);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internal function to register an irqaction - typically used to
|
* Internal function to register an irqaction - typically used to
|
||||||
* allocate special interrupts that are part of the architecture.
|
* allocate special interrupts that are part of the architecture.
|
||||||
|
@ -436,6 +543,26 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
||||||
rand_initialize_irq(irq);
|
rand_initialize_irq(irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Threaded handler ?
|
||||||
|
*/
|
||||||
|
if (new->thread_fn) {
|
||||||
|
struct task_struct *t;
|
||||||
|
|
||||||
|
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
|
||||||
|
new->name);
|
||||||
|
if (IS_ERR(t))
|
||||||
|
return PTR_ERR(t);
|
||||||
|
/*
|
||||||
|
* We keep the reference to the task struct even if
|
||||||
|
* the thread dies to avoid that the interrupt code
|
||||||
|
* references an already freed task_struct.
|
||||||
|
*/
|
||||||
|
get_task_struct(t);
|
||||||
|
new->thread = t;
|
||||||
|
wake_up_process(t);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following block of code has to be executed atomically
|
* The following block of code has to be executed atomically
|
||||||
*/
|
*/
|
||||||
|
@ -473,15 +600,15 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
||||||
if (!shared) {
|
if (!shared) {
|
||||||
irq_chip_set_defaults(desc->chip);
|
irq_chip_set_defaults(desc->chip);
|
||||||
|
|
||||||
|
init_waitqueue_head(&desc->wait_for_threads);
|
||||||
|
|
||||||
/* Setup the type (level, edge polarity) if configured: */
|
/* Setup the type (level, edge polarity) if configured: */
|
||||||
if (new->flags & IRQF_TRIGGER_MASK) {
|
if (new->flags & IRQF_TRIGGER_MASK) {
|
||||||
ret = __irq_set_trigger(desc, irq,
|
ret = __irq_set_trigger(desc, irq,
|
||||||
new->flags & IRQF_TRIGGER_MASK);
|
new->flags & IRQF_TRIGGER_MASK);
|
||||||
|
|
||||||
if (ret) {
|
if (ret)
|
||||||
spin_unlock_irqrestore(&desc->lock, flags);
|
goto out_thread;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
} else
|
} else
|
||||||
compat_irq_chip_set_default_handler(desc);
|
compat_irq_chip_set_default_handler(desc);
|
||||||
#if defined(CONFIG_IRQ_PER_CPU)
|
#if defined(CONFIG_IRQ_PER_CPU)
|
||||||
|
@ -549,8 +676,19 @@ mismatch:
|
||||||
dump_stack();
|
dump_stack();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
ret = -EBUSY;
|
||||||
|
|
||||||
|
out_thread:
|
||||||
spin_unlock_irqrestore(&desc->lock, flags);
|
spin_unlock_irqrestore(&desc->lock, flags);
|
||||||
return -EBUSY;
|
if (new->thread) {
|
||||||
|
struct task_struct *t = new->thread;
|
||||||
|
|
||||||
|
new->thread = NULL;
|
||||||
|
if (likely(!test_bit(IRQTF_DIED, &new->thread_flags)))
|
||||||
|
kthread_stop(t);
|
||||||
|
put_task_struct(t);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -576,6 +714,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct irq_desc *desc = irq_to_desc(irq);
|
struct irq_desc *desc = irq_to_desc(irq);
|
||||||
struct irqaction *action, **action_ptr;
|
struct irqaction *action, **action_ptr;
|
||||||
|
struct task_struct *irqthread;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
|
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
|
||||||
|
@ -622,6 +761,10 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
|
||||||
else
|
else
|
||||||
desc->chip->disable(irq);
|
desc->chip->disable(irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irqthread = action->thread;
|
||||||
|
action->thread = NULL;
|
||||||
|
|
||||||
spin_unlock_irqrestore(&desc->lock, flags);
|
spin_unlock_irqrestore(&desc->lock, flags);
|
||||||
|
|
||||||
unregister_handler_proc(irq, action);
|
unregister_handler_proc(irq, action);
|
||||||
|
@ -629,6 +772,12 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
|
||||||
/* Make sure it's not being used on another CPU: */
|
/* Make sure it's not being used on another CPU: */
|
||||||
synchronize_irq(irq);
|
synchronize_irq(irq);
|
||||||
|
|
||||||
|
if (irqthread) {
|
||||||
|
if (!test_bit(IRQTF_DIED, &action->thread_flags))
|
||||||
|
kthread_stop(irqthread);
|
||||||
|
put_task_struct(irqthread);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_SHIRQ
|
#ifdef CONFIG_DEBUG_SHIRQ
|
||||||
/*
|
/*
|
||||||
* It's a shared IRQ -- the driver ought to be prepared for an IRQ
|
* It's a shared IRQ -- the driver ought to be prepared for an IRQ
|
||||||
|
@ -681,9 +830,12 @@ void free_irq(unsigned int irq, void *dev_id)
|
||||||
EXPORT_SYMBOL(free_irq);
|
EXPORT_SYMBOL(free_irq);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* request_irq - allocate an interrupt line
|
* request_threaded_irq - allocate an interrupt line
|
||||||
* @irq: Interrupt line to allocate
|
* @irq: Interrupt line to allocate
|
||||||
* @handler: Function to be called when the IRQ occurs
|
* @handler: Function to be called when the IRQ occurs.
|
||||||
|
* Primary handler for threaded interrupts
|
||||||
|
* @thread_fn: Function called from the irq handler thread
|
||||||
|
* If NULL, no irq thread is created
|
||||||
* @irqflags: Interrupt type flags
|
* @irqflags: Interrupt type flags
|
||||||
* @devname: An ascii name for the claiming device
|
* @devname: An ascii name for the claiming device
|
||||||
* @dev_id: A cookie passed back to the handler function
|
* @dev_id: A cookie passed back to the handler function
|
||||||
|
@ -695,6 +847,15 @@ EXPORT_SYMBOL(free_irq);
|
||||||
* raises, you must take care both to initialise your hardware
|
* raises, you must take care both to initialise your hardware
|
||||||
* and to set up the interrupt handler in the right order.
|
* and to set up the interrupt handler in the right order.
|
||||||
*
|
*
|
||||||
|
* If you want to set up a threaded irq handler for your device
|
||||||
|
* then you need to supply @handler and @thread_fn. @handler ist
|
||||||
|
* still called in hard interrupt context and has to check
|
||||||
|
* whether the interrupt originates from the device. If yes it
|
||||||
|
* needs to disable the interrupt on the device and return
|
||||||
|
* IRQ_THREAD_WAKE which will wake up the handler thread and run
|
||||||
|
* @thread_fn. This split handler design is necessary to support
|
||||||
|
* shared interrupts.
|
||||||
|
*
|
||||||
* Dev_id must be globally unique. Normally the address of the
|
* Dev_id must be globally unique. Normally the address of the
|
||||||
* device data structure is used as the cookie. Since the handler
|
* device data structure is used as the cookie. Since the handler
|
||||||
* receives this value it makes sense to use it.
|
* receives this value it makes sense to use it.
|
||||||
|
@ -710,8 +871,9 @@ EXPORT_SYMBOL(free_irq);
|
||||||
* IRQF_TRIGGER_* Specify active edge(s) or level
|
* IRQF_TRIGGER_* Specify active edge(s) or level
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int request_irq(unsigned int irq, irq_handler_t handler,
|
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
|
||||||
unsigned long irqflags, const char *devname, void *dev_id)
|
irq_handler_t thread_fn, unsigned long irqflags,
|
||||||
|
const char *devname, void *dev_id)
|
||||||
{
|
{
|
||||||
struct irqaction *action;
|
struct irqaction *action;
|
||||||
struct irq_desc *desc;
|
struct irq_desc *desc;
|
||||||
|
@ -759,6 +921,7 @@ int request_irq(unsigned int irq, irq_handler_t handler,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
action->handler = handler;
|
action->handler = handler;
|
||||||
|
action->thread_fn = thread_fn;
|
||||||
action->flags = irqflags;
|
action->flags = irqflags;
|
||||||
action->name = devname;
|
action->name = devname;
|
||||||
action->dev_id = dev_id;
|
action->dev_id = dev_id;
|
||||||
|
@ -788,4 +951,4 @@ int request_irq(unsigned int irq, irq_handler_t handler,
|
||||||
#endif
|
#endif
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(request_irq);
|
EXPORT_SYMBOL(request_threaded_irq);
|
||||||
|
|
Loading…
Reference in a new issue