/** * preempt_schedule_context - preempt_schedule called by tracing * * The tracing infrastructure uses preempt_enable_notrace to prevent * recursion and tracing preempt enabling caused by the tracing * infrastructure itself. But as tracing can happen in areas coming * from userspace or just about to enter userspace, a preempt enable * can occur before user_exit() is called. This will cause the scheduler * to be called when the system is still in usermode. * * To prevent this, the preempt_enable_notrace will use this function * instead of preempt_schedule() to exit user context if needed before * calling the scheduler. */ asmlinkage void __sched notrace preempt_schedule_context(void) { enum ctx_state prev_ctx; if (likely(!preemptible())) return; /* * Need to disable preemption in case user_exit() is traced * and the tracer calls preempt_enable_notrace() causing * an infinite recursion. */ preempt_disable_notrace(); prev_ctx = exception_enter(); preempt_enable_no_resched_notrace(); preempt_schedule(); preempt_disable_notrace(); exception_exit(prev_ctx); preempt_enable_notrace(); }
/* * 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; }