/* * linux/kernel/irq/spurious.c * * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar * * This file contains spurious interrupt handling. */ #include #include #include #include #include #include #include #include "internals.h" static int irqfixup __read_mostly; #define POLL_SPURIOUS_IRQ_INTERVAL (HZ/10) static void poll_spurious_irqs(unsigned long dummy); static DEFINE_TIMER(poll_spurious_irq_timer, poll_spurious_irqs, 0, 0); static int irq_poll_cpu; static atomic_t irq_poll_active; /* * We wait here for a poller to finish. * * If the poll runs on this CPU, then we yell loudly and return * false. That will leave the interrupt line disabled in the worst * case, but it should never happen. * * We wait until the poller is done and then recheck disabled and * action (about to be disabled). Only if it's still active, we return * true and let the handler run. */ bool irq_wait_for_poll(struct irq_desc *desc) { if (WARN_ONCE(irq_poll_cpu == smp_processor_id(), "irq poll in progress on cpu %d for irq %d\n", smp_processor_id(), desc->irq_data.irq)) return false; #ifdef CONFIG_SMP do { raw_spin_unlock(&desc->lock); while (irqd_irq_inprogress(&desc->irq_data)) cpu_relax(); raw_spin_lock(&desc->lock); } while (irqd_irq_inprogress(&desc->irq_data)); /* Might have been disabled in meantime */ return !irqd_irq_disabled(&desc->irq_data) && desc->action; #else return false; #endif } /* * Recovery handler for misrouted interrupts. */ static int try_one_irq(int irq, struct irq_desc *desc, bool force) { irqreturn_t ret = IRQ_NONE; struct irqaction *action; raw_spin_lock(&desc->lock); /* * 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; /* * Do not poll disabled interrupts unless the spurious * disabled poller asks explicitely. */ if (irqd_irq_disabled(&desc->irq_data) && !force) goto out; /* * All handlers must agree on IRQF_SHARED, so we test just the * first. */ action = desc->action; if (!action || !(action->flags & IRQF_SHARED) || (action->flags & __IRQF_TIMER)) goto out; /* Already running on another processor */ if (irqd_irq_inprogress(&desc->irq_data)) { /* * Already running: If it is shared get the other * CPU to go looking for our mystery interrupt too */ desc->istate |= IRQS_PENDING; goto out; } /* Mark it poll in progress */ desc->istate |= IRQS_POLL_INPROGRESS; do { if (handle_irq_event(desc) == IRQ_HANDLED) ret = IRQ_HANDLED; /* Make sure that there is still a valid action */ action = desc->action; } while ((desc->istate & IRQS_PENDING) && action); desc->istate &= ~IRQS_POLL_INPROGRESS; out: raw_spin_unlock(&desc->lock); return ret == IRQ_HANDLED; } static int misrouted_irq(int irq) { struct irq_desc *desc; int i, ok = 0; if (atomic_inc_return(&irq_poll_active) != 1) goto out; irq_poll_cpu = smp_processor_id(); for_each_irq_desc(i, desc) { if (!i) continue; if (i == irq) /* Already tried */ continue; if (try_one_irq(i, desc, false)) ok = 1; } out: atomic_dec(&irq_poll_active); /* So the caller can adjust the irq error counts */ return ok; } static void poll_spurious_irqs(unsigned long dummy) { struct irq_desc *desc; int i; if (atomic_inc_return(&irq_poll_active) != 1) goto out; irq_poll_cpu = smp_processor_id(); for_each_irq_desc(i, desc) { unsigned int state; if (!i) continue; /* Racy but it doesn't matter */ state = desc->istate; barrier(); if (!(state & IRQS_SPURIOUS_DISABLED)) continue; local_irq_disable(); try_one_irq(i, desc, true); local_irq_enable(); } out: atomic_dec(&irq_poll_active); mod_timer(&poll_spurious_irq_timer, jiffies + POLL_SPURIOUS_IRQ_INTERVAL); } static inline int bad_action_ret(irqreturn_t action_ret) { if (likely(action_ret <= (IRQ_HANDLED | IRQ_WAKE_THREAD))) return 0; return 1; } /* * If 99,900 of