genirq: Prevent spurious detection for unconditionally polled interrupts
authorThomas Gleixner <tglx@linutronix.de>
Wed, 6 Nov 2013 11:30:07 +0000 (12:30 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Wed, 13 Nov 2013 15:03:02 +0000 (16:03 +0100)
On a 68k platform a couple of interrupts are demultiplexed and
"polled" from a top level interrupt. Unfortunately there is no way to
determine which of the sub interrupts raised the top level interrupt,
so all of the demultiplexed interrupt handlers need to be
invoked. Given a high enough frequency this can trigger the spurious
interrupt detection mechanism, if one of the demultiplex interrupts
returns IRQ_NONE continuously. But this is a false positive as the
polling causes this behaviour and not buggy hardware/software.

Introduce IRQ_POLLED which can be set at interrupt chip setup time via
irq_set_status_flags(). The flag excludes the interrupt from the
spurious detector and from all core polling activities.

Reported-and-tested-by: Michael Schmitz <schmitzmic@gmail.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: linux-m68k@vger.kernel.org
Link: http://lkml.kernel.org/r/alpine.DEB.2.02.1311061149250.23353@ionos.tec.linutronix.de
include/linux/irq.h
kernel/irq/settings.h
kernel/irq/spurious.c

index 56bb0dc8b7d44cbab1a5a3cefb9efc04a7440995..7dc10036eff552229cdd964e88669a7759a8152f 100644 (file)
@@ -70,6 +70,9 @@ typedef       void (*irq_preflow_handler_t)(struct irq_data *data);
  * IRQ_MOVE_PCNTXT             - Interrupt can be migrated from process context
  * IRQ_NESTED_TRHEAD           - Interrupt nests into another thread
  * IRQ_PER_CPU_DEVID           - Dev_id is a per-cpu variable
+ * IRQ_IS_POLLED               - Always polled by another interrupt. Exclude
+ *                               it from the spurious interrupt detection
+ *                               mechanism and from core side polling.
  */
 enum {
        IRQ_TYPE_NONE           = 0x00000000,
@@ -94,12 +97,14 @@ enum {
        IRQ_NESTED_THREAD       = (1 << 15),
        IRQ_NOTHREAD            = (1 << 16),
        IRQ_PER_CPU_DEVID       = (1 << 17),
+       IRQ_IS_POLLED           = (1 << 18),
 };
 
 #define IRQF_MODIFY_MASK       \
        (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
         IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
-        IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID)
+        IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \
+        IRQ_IS_POLLED)
 
 #define IRQ_NO_BALANCING_MASK  (IRQ_PER_CPU | IRQ_NO_BALANCING)
 
index 1162f1030f18f9326c23522417a3de87feb18ef4..3320b84cc60f79ac09501d6f4d55acc01b0cebe6 100644 (file)
@@ -14,6 +14,7 @@ enum {
        _IRQ_NO_BALANCING       = IRQ_NO_BALANCING,
        _IRQ_NESTED_THREAD      = IRQ_NESTED_THREAD,
        _IRQ_PER_CPU_DEVID      = IRQ_PER_CPU_DEVID,
+       _IRQ_IS_POLLED          = IRQ_IS_POLLED,
        _IRQF_MODIFY_MASK       = IRQF_MODIFY_MASK,
 };
 
@@ -26,6 +27,7 @@ enum {
 #define IRQ_NOAUTOEN           GOT_YOU_MORON
 #define IRQ_NESTED_THREAD      GOT_YOU_MORON
 #define IRQ_PER_CPU_DEVID      GOT_YOU_MORON
+#define IRQ_IS_POLLED          GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK       GOT_YOU_MORON
 
@@ -147,3 +149,8 @@ static inline bool irq_settings_is_nested_thread(struct irq_desc *desc)
 {
        return desc->status_use_accessors & _IRQ_NESTED_THREAD;
 }
+
+static inline bool irq_settings_is_polled(struct irq_desc *desc)
+{
+       return desc->status_use_accessors & _IRQ_IS_POLLED;
+}
index 7b5f012bde9d73ff246652fe48d49e5b1aebc12c..a1d8cc63b56e55ddbb83741b1d20db58e5ab3b7c 100644 (file)
@@ -67,8 +67,13 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force)
 
        raw_spin_lock(&desc->lock);
 
-       /* PER_CPU and nested thread interrupts are never polled */
-       if (irq_settings_is_per_cpu(desc) || irq_settings_is_nested_thread(desc))
+       /*
+        * PER_CPU, nested thread interrupts and interrupts explicitely
+        * marked polled are excluded from polling.
+        */
+       if (irq_settings_is_per_cpu(desc) ||
+           irq_settings_is_nested_thread(desc) ||
+           irq_settings_is_polled(desc))
                goto out;
 
        /*
@@ -268,7 +273,8 @@ try_misrouted_irq(unsigned int irq, struct irq_desc *desc,
 void note_interrupt(unsigned int irq, struct irq_desc *desc,
                    irqreturn_t action_ret)
 {
-       if (desc->istate & IRQS_POLL_INPROGRESS)
+       if (desc->istate & IRQS_POLL_INPROGRESS ||
+           irq_settings_is_polled(desc))
                return;
 
        /* we get here again via the threaded handler */