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: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
3493e84de6
commit
2d860ad76f
|
@ -761,7 +761,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
|
|||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irqaction *action, **action_ptr;
|
||||
struct task_struct *irqthread;
|
||||
unsigned long flags;
|
||||
|
||||
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)
|
|||
desc->chip->disable(irq);
|
||||
}
|
||||
|
||||
irqthread = action->thread;
|
||||
action->thread = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
|
||||
unregister_handler_proc(irq, action);
|
||||
|
@ -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: */
|
||||
synchronize_irq(irq);
|
||||
|
||||
if (irqthread) {
|
||||
if (!test_bit(IRQTF_DIED, &action->thread_flags))
|
||||
kthread_stop(irqthread);
|
||||
put_task_struct(irqthread);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_SHIRQ
|
||||
/*
|
||||
* 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)
|
|||
local_irq_restore(flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (action->thread) {
|
||||
if (!test_bit(IRQTF_DIED, &action->thread_flags))
|
||||
kthread_stop(action->thread);
|
||||
put_task_struct(action->thread);
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue