genirq: Prevent resend to interrupts marked IRQ_NESTED_THREAD
authorThomas Gleixner <tglx@linutronix.de>
Thu, 16 Jul 2015 12:10:17 +0000 (14:10 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 17 Jul 2015 09:29:34 +0000 (11:29 +0200)
The resend mechanism happily calls the interrupt handler of interrupts
which are marked IRQ_NESTED_THREAD from softirq context. This can
result in crashes because the interrupt handler is not the proper way
to invoke the device handlers. They must be invoked via
handle_nested_irq.

Prevent the resend even if the interrupt has no valid parent irq
set. Its better to have a lost interrupt than a crashing machine.

Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
kernel/irq/resend.c

index 9065107f083e90e3fbd3c38f2fbc164d11ff2b3c..7a5237a1bce5b5626d74cdb377f8e5a900d06f7a 100644 (file)
@@ -75,13 +75,21 @@ void check_irq_resend(struct irq_desc *desc, unsigned int irq)
                    !desc->irq_data.chip->irq_retrigger(&desc->irq_data)) {
 #ifdef CONFIG_HARDIRQS_SW_RESEND
                        /*
-                        * If the interrupt has a parent irq and runs
-                        * in the thread context of the parent irq,
-                        * retrigger the parent.
+                        * If the interrupt is running in the thread
+                        * context of the parent irq we need to be
+                        * careful, because we cannot trigger it
+                        * directly.
                         */
-                       if (desc->parent_irq &&
-                           irq_settings_is_nested_thread(desc))
+                       if (irq_settings_is_nested_thread(desc)) {
+                               /*
+                                * If the parent_irq is valid, we
+                                * retrigger the parent, otherwise we
+                                * do nothing.
+                                */
+                               if (!desc->parent_irq)
+                                       return;
                                irq = desc->parent_irq;
+                       }
                        /* Set it pending and activate the softirq: */
                        set_bit(irq, irqs_resend);
                        tasklet_schedule(&resend_tasklet);