Commit 2d860ad7 authored by Linus Torvalds's avatar Linus Torvalds Committed by Thomas Gleixner

genirq: prevent wakeup of freed irq thread

free_irq() can remove an irqaction while the corresponding interrupt
is in progress, but free_irq() sets action->thread to NULL
unconditionally, which might lead to a NULL pointer dereference in
handle_IRQ_event() when the hard interrupt context tries to wake up
the handler thread.

Prevent this by moving the thread stop after synchronize_irq(). No
need to set action->thread to NULL either as action is going to be
freed anyway.

This fixes a boot crash reported against preempt-rt which uses the
mainline irq threads code to implement full irq threading.

[ tglx: removed local irqthread variable ]
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 3493e84d
...@@ -761,7 +761,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) ...@@ -761,7 +761,6 @@ 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);
...@@ -809,9 +808,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) ...@@ -809,9 +808,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
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);
...@@ -819,12 +815,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) ...@@ -819,12 +815,6 @@ 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
...@@ -840,6 +830,13 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) ...@@ -840,6 +830,13 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
local_irq_restore(flags); local_irq_restore(flags);
} }
#endif #endif
if (action->thread) {
if (!test_bit(IRQTF_DIED, &action->thread_flags))
kthread_stop(action->thread);
put_task_struct(action->thread);
}
return action; return action;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment