Пример #1
0
/*
 * 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;
}
Пример #2
0
asmlinkage unsigned int 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();

	preempt_lock_start(-99);

	kstat.irqs[cpu][irq]++;
	spin_lock(&desc->lock);
	desc->handler->ack(irq);
	latency_cause(-99,~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);

	preempt_lock_stop();

	if (softirq_pending(cpu))
		do_softirq();

#if defined(CONFIG_PREEMPT)
	while (--current->preempt_count == 0) {
		db_assert(intr_off());
		db_assert(!in_interrupt());

		if (current->need_resched == 0) {
			break;
		}

		current->preempt_count ++;
		sti();
		if (user_mode(regs)) {
			schedule();
		} else {
			preempt_schedule();
		}
		cli();
	}
#endif

#ifdef CONFIG_ILATENCY
	intr_ret_from_exception();
#endif
	return 1;
}