genirq: Warn when IRQ_NOAUTOEN is used with shared interrupts
authorThomas Gleixner <tglx@linutronix.de>
Wed, 31 May 2017 09:58:33 +0000 (11:58 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Sun, 4 Jun 2017 12:38:41 +0000 (14:38 +0200)
Shared interrupts do not go well with disabling auto enable:

1) The sharing interrupt might request it while it's still disabled and
   then wait for interrupts forever.

2) The interrupt might have been requested by the driver sharing the line
   before IRQ_NOAUTOEN has been set. So the driver which expects that
   disabled state after calling request_irq() will not get what it wants.
   Even worse, when it calls enable_irq() later, it will trigger the
   unbalanced enable_irq() warning.

Reported-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: dianders@chromium.org
Cc: jeffy <jeffy.chen@rock-chips.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: tfiga@chromium.org
Link: http://lkml.kernel.org/r/20170531100212.210682135@linutronix.de
kernel/irq/chip.c
kernel/irq/manage.c

index e0051d58c909fe2f77ebb8783eac53252f919ed6..bc1331f84fb5f11d5845ec191f89fe6fd62ad82c 100644 (file)
@@ -935,6 +935,13 @@ void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
 
        if (!desc)
                return;
+
+       /*
+        * Warn when a driver sets the no autoenable flag on an already
+        * active interrupt.
+        */
+       WARN_ON_ONCE(!desc->depth && (set & _IRQ_NOAUTOEN));
+
        irq_settings_clr_and_set(desc, clr, set);
 
        irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU |
index 57056109f1760431a9230c0fdfeea6bf9c1d5603..49c37f1e71c09bb3d6f70465ae8f05f3036f6ed9 100644 (file)
@@ -1334,11 +1334,19 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                if (new->flags & IRQF_ONESHOT)
                        desc->istate |= IRQS_ONESHOT;
 
-               if (irq_settings_can_autoenable(desc))
+               if (irq_settings_can_autoenable(desc)) {
                        irq_startup(desc, true);
-               else
+               } else {
+                       /*
+                        * Shared interrupts do not go well with disabling
+                        * auto enable. The sharing interrupt might request
+                        * it while it's still disabled and then wait for
+                        * interrupts forever.
+                        */
+                       WARN_ON_ONCE(new->flags & IRQF_SHARED);
                        /* Undo nested disables: */
                        desc->depth = 1;
+               }
 
                /* Exclude IRQ from balancing if requested */
                if (new->flags & IRQF_NOBALANCING) {