/* * do_IRQ handles all normal device IRQ's (the special * SMP cross-CPU interrupts have their own specific * handlers). */ asmlinkage unsigned int __noinstrument do_IRQ(int irq, struct pt_regs *regs) { /* * We ack quickly, we don't want the irq controller * thinking we're snobs just because some other CPU has * disabled global interrupts (we have already done the * INT_ACK cycles, it's too late to try to pretend to the * controller that we aren't taking the interrupt). * * 0 return value means that this irq is already being * handled by some other CPU. (or is disabled) */ int cpu = smp_processor_id(); irq_desc_t *desc = irq_desc + irq; struct irqaction * action; unsigned int status; #ifdef CONFIG_ILATENCY { extern void interrupt_overhead_start(void); interrupt_overhead_start(); } #endif /* CONFIG_ILATENCY */ preempt_disable(); TRACE_IRQ_ENTRY(irq, !user_mode(regs)); kstat.irqs[cpu][irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); /* REPLAY is when Linux resends an IRQ that was dropped earlier WAITING is used by probe to mark irqs that are being tested */ status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); status |= IRQ_PENDING; /* we _want_ to handle it */ /* * If the IRQ is disabled for whatever reason, we cannot * use the action we have. */ action = NULL; if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { action = desc->action; status &= ~IRQ_PENDING; /* we commit to handling */ status |= IRQ_INPROGRESS; /* we are handling it */ } desc->status = status; /* * If there is no IRQ handler or it was disabled, exit early. Since we set PENDING, if another processor is handling a different instance of this same irq, the other processor will take care of it. */ if (!action) goto out; /* * Edge triggered interrupts need to remember * pending events. * This applies to any hw interrupts that allow a second * instance of the same irq to arrive while we are in do_IRQ * or in the handler. But the code here only handles the _second_ * instance of the irq, not the third or fourth. So it is mostly * useful for irq hardware that does not mask cleanly in an * SMP environment. */ #ifdef CONFIG_ILATENCY { extern void interrupt_overhead_stop(void); interrupt_overhead_stop(); } #endif /* CONFIG_ILATENCY */ for (;;) { spin_unlock(&desc->lock); handle_IRQ_event(irq, regs, action); spin_lock(&desc->lock); if (!(desc->status & IRQ_PENDING)) break; desc->status &= ~IRQ_PENDING; } desc->status &= ~IRQ_INPROGRESS; out: /* * The ->end() handler has to deal with interrupts which got * disabled while the handler was running. */ desc->handler->end(irq); spin_unlock(&desc->lock); TRACE_IRQ_EXIT(); if (softirq_pending(cpu)) do_softirq(); #if defined(CONFIG_PREEMPT) for(;;) { preempt_enable_no_resched(); if (preempt_is_disabled() || !need_resched()) break; db_assert(intr_off()); db_assert(!in_interrupt()); preempt_disable(); __sti(); preempt_schedule(); __cli(); } #endif #ifdef CONFIG_ILATENCY intr_ret_from_exception(); #endif return 1; }
/* * do_IRQ handles all normal device IRQ's */ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { struct irqdesc * desc; struct irqaction * action; int cpu; #ifdef CONFIG_ILATENCY { extern void interrupt_overhead_start(void); interrupt_overhead_start(); } #endif /* CONFIG_ILATENCY */ irq = fixup_irq(irq); /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (irq >= NR_IRQS) goto bad_irq; /* this is called recursively in some cases, so... */ if (!in_irq()) preempt_lock_start(-99); desc = irq_desc + irq; TRACE_IRQ_ENTRY(irq, !(user_mode(regs))); spin_lock(&irq_controller_lock); desc->mask_ack(irq); spin_unlock(&irq_controller_lock); cpu = smp_processor_id(); irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; desc->triggered = 1; /* Return with this interrupt masked if no action */ action = desc->action; if (action) { int status = 0; if (desc->nomask) { spin_lock(&irq_controller_lock); desc->unmask(irq); spin_unlock(&irq_controller_lock); } if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); #ifdef CONFIG_ILATENCY { extern void interrupt_overhead_stop(void); interrupt_overhead_stop(); } #endif /* CONFIG_ILATENCY */ do { status |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable(); if (!desc->nomask && desc->enabled) { spin_lock(&irq_controller_lock); desc->unmask(irq); spin_unlock(&irq_controller_lock); } } /* * Debug measure - hopefully we can continue if an * IRQ lockup problem occurs... */ check_irq_lock(desc, irq, regs); irq_exit(cpu, irq); TRACE_IRQ_EXIT(); if (!in_irq()) preempt_lock_stop(); if (softirq_pending(cpu)) do_softirq(); #ifdef CONFIG_ILATENCY /* * until entry.S gets this call do it here. */ intr_ret_from_exception(); #endif /* CONFIG_ILATENCY */ return; bad_irq: irq_err_count += 1; printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq); return; }