diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2012-03-06 17:18:54 -0500 |
---|---|---|
committer | Luis Henriques <luis.henriques@canonical.com> | 2012-03-26 05:26:51 -0400 |
commit | fdb0f08a199ec80276586872d0df9e1072057732 (patch) | |
tree | f6c1eae3c3c3749a12b66db6b3cf6c5e647e1875 /kernel/irq/manage.c | |
parent | 30a7a8f897757abd8f7fbbf4aed70f61fbf8de2d (diff) |
genirq: Clear action->thread_mask if IRQ_ONESHOT is not set
BugLink: http://bugs.launchpad.net/bugs/954576
commit 52abb700e16a9aa4cbc03f3d7f80206cbbc80680 upstream.
Xommit ac5637611(genirq: Unmask oneshot irqs when thread was not woken)
fails to unmask when a !IRQ_ONESHOT threaded handler is handled by
handle_level_irq.
This happens because thread_mask is or'ed unconditionally in
irq_wake_thread(), but for !IRQ_ONESHOT interrupts never cleared. So
the check for !desc->thread_active fails and keeps the interrupt
disabled.
Keep the thread_mask zero for !IRQ_ONESHOT interrupts.
Document the thread_mask magic while at it.
Reported-and-tested-by: Sven Joachim <svenjoac@gmx.de>
Reported-and-tested-by: Stefan Lippers-Hollmann <s.l-h@gmx.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel/irq/manage.c')
-rw-r--r-- | kernel/irq/manage.c | 44 |
1 files changed, 38 insertions, 6 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index def3406fb43..e4eedb16197 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -976,6 +976,11 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
976 | 976 | ||
977 | /* add new interrupt at end of irq queue */ | 977 | /* add new interrupt at end of irq queue */ |
978 | do { | 978 | do { |
979 | /* | ||
980 | * Or all existing action->thread_mask bits, | ||
981 | * so we can find the next zero bit for this | ||
982 | * new action. | ||
983 | */ | ||
979 | thread_mask |= old->thread_mask; | 984 | thread_mask |= old->thread_mask; |
980 | old_ptr = &old->next; | 985 | old_ptr = &old->next; |
981 | old = *old_ptr; | 986 | old = *old_ptr; |
@@ -984,14 +989,41 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
984 | } | 989 | } |
985 | 990 | ||
986 | /* | 991 | /* |
987 | * Setup the thread mask for this irqaction. Unlikely to have | 992 | * Setup the thread mask for this irqaction for ONESHOT. For |
988 | * 32 resp 64 irqs sharing one line, but who knows. | 993 | * !ONESHOT irqs the thread mask is 0 so we can avoid a |
994 | * conditional in irq_wake_thread(). | ||
989 | */ | 995 | */ |
990 | if (new->flags & IRQF_ONESHOT && thread_mask == ~0UL) { | 996 | if (new->flags & IRQF_ONESHOT) { |
991 | ret = -EBUSY; | 997 | /* |
992 | goto out_mask; | 998 | * Unlikely to have 32 resp 64 irqs sharing one line, |
999 | * but who knows. | ||
1000 | */ | ||
1001 | if (thread_mask == ~0UL) { | ||
1002 | ret = -EBUSY; | ||
1003 | goto out_mask; | ||
1004 | } | ||
1005 | /* | ||
1006 | * The thread_mask for the action is or'ed to | ||
1007 | * desc->thread_active to indicate that the | ||
1008 | * IRQF_ONESHOT thread handler has been woken, but not | ||
1009 | * yet finished. The bit is cleared when a thread | ||
1010 | * completes. When all threads of a shared interrupt | ||
1011 | * line have completed desc->threads_active becomes | ||
1012 | * zero and the interrupt line is unmasked. See | ||
1013 | * handle.c:irq_wake_thread() for further information. | ||
1014 | * | ||
1015 | * If no thread is woken by primary (hard irq context) | ||
1016 | * interrupt handlers, then desc->threads_active is | ||
1017 | * also checked for zero to unmask the irq line in the | ||
1018 | * affected hard irq flow handlers | ||
1019 | * (handle_[fasteoi|level]_irq). | ||
1020 | * | ||
1021 | * The new action gets the first zero bit of | ||
1022 | * thread_mask assigned. See the loop above which or's | ||
1023 | * all existing action->thread_mask bits. | ||
1024 | */ | ||
1025 | new->thread_mask = 1 << ffz(thread_mask); | ||
993 | } | 1026 | } |
994 | new->thread_mask = 1 << ffz(thread_mask); | ||
995 | 1027 | ||
996 | if (!shared) { | 1028 | if (!shared) { |
997 | init_waitqueue_head(&desc->wait_for_threads); | 1029 | init_waitqueue_head(&desc->wait_for_threads); |