/* * We can use these to temporarily drop into * "lazy TLB" mode and back. */ struct mm_struct * start_lazy_tlb(void) { struct mm_struct *mm = current->mm; #ifdef CONFIG_PREEMPT if (preempt_is_disabled() == 0) BUG(); #endif current->mm = NULL; /* active_mm is still 'mm' */ atomic_inc(&mm->mm_count); enter_lazy_tlb(mm, current, smp_processor_id()); return mm; }
void end_lazy_tlb(struct mm_struct *mm) { struct mm_struct *active_mm = current->active_mm; #ifdef CONFIG_PREEMPT if (preempt_is_disabled() == 0) BUG(); #endif current->mm = mm; if (mm != active_mm) { current->active_mm = mm; activate_mm(active_mm, mm); } mmdrop(active_mm); }
/* * 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; }