在gicv2/gicv3中,SGI中断(中断号0-15)是software generate interrupt,用户核间中断。
我们一般将0-7划分给linux中使用,8-15给TEE使用。在smp.c定义了linux中使用的SGI中断。
kernel/arch/arm/kernel/smp.c
enum ipi_msg_type {
IPI_WAKEUP,
IPI_TIMER,
IPI_RESCHEDULE,
IPI_CALL_FUNC,
IPI_CPU_STOP,
IPI_IRQ_WORK,
IPI_COMPLETION,
/*
* CPU_BACKTRACE is special and not included in NR_IPI
* or tracable with trace_ipi_*
/
IPI_CPU_BACKTRACE,
#ifdef CONFIG_TRUSTY
IPI_CUSTOM_FIRST,
IPI_CUSTOM_LAST = 15,
#endif
/
* SGI8-15 can be reserved by secure firmware, and thus may
* not be usable by the kernel. Please keep the above limited
* to at most 8 entries.
*/
};
当ARM core收到IRQ后,会触发cpu的irq异常,会跳转到linux kernel的irq异常向量表,在该向量表中,会调用gicv2/gicv3的gic_handle_irq函数,在kernel/drivers/irqchip/irq-gic-v3.c中
static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { u32 irqnr; do { irqnr = gic_read_iar(); if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) { int err; if (static_key_true(&supports_deactivate)) gic_write_eoir(irqnr); err = handle_domain_irq(gic_data.domain, irqnr, regs); if (err) { WARN_ONCE(true, "Unexpected interrupt received!\n"); if (static_key_true(&supports_deactivate)) { if (irqnr < 8192) gic_write_dir(irqnr); } else { gic_write_eoir(irqnr); } } continue; } if (irqnr < 16) { gic_write_eoir(irqnr); if (static_key_true(&supports_deactivate)) gic_write_dir(irqnr); #ifdef CONFIG_SMP /* * Unlike GICv2, we don't need an smp_rmb() here. * The control dependency from gic_read_iar to * the ISB in gic_write_eoir is enough to ensure * that any shared data read by handle_IPI will * be read after the ACK. */ handle_IPI(irqnr, regs); #else WARN_ONCE(true, "Unexpected SGI received!\n"); #endif continue; } } while (irqnr != ICC_IAR1_EL1_SPURIOUS); }
void handle_IPI(int ipinr, struct pt_regs *regs) { ...... case IPI_RESCHEDULE: scheduler_ipi(); break; ...... }
void scheduler_ipi(void) { int cpu = smp_processor_id(); /* * Fold TIF_NEED_RESCHED into the preempt_count; anybody setting * TIF_NEED_RESCHED remotely (for the first time) will also send * this IPI. */ preempt_fold_need_resched(); if (llist_empty(&this_rq()->wake_list) && !got_nohz_idle_kick()) { #ifdef CONFIG_MTK_SCHED_MONITOR mt_trace_IPI_start(IPI_RESCHEDULE); mt_trace_IPI_end(IPI_RESCHEDULE); #endif return; } /* * Not all reschedule IPI handlers call irq_enter/irq_exit, since * traditionally all their work was done from the interrupt return * path. Now that we actually do some work, we need to make sure * we do call them. * * Some archs already do call them, luckily irq_enter/exit nest * properly. * * Arguably we should visit all archs and update all handlers, * however a fair share of IPIs are still resched only so this would * somewhat pessimize the simple resched case. */ irq_enter(); #ifdef CONFIG_MTK_SCHED_MONITOR mt_trace_IPI_start(IPI_RESCHEDULE); #endif sched_ttwu_pending(); /* * Check if someone kicked us for doing the nohz idle load balance. */ if (unlikely(got_nohz_idle_kick()) && !cpu_isolated(cpu)) { this_rq()->idle_balance = 1; raise_softirq_irqoff(SCHED_SOFTIRQ); } #ifdef CONFIG_MTK_SCHED_MONITOR mt_trace_IPI_end(IPI_RESCHEDULE); #endif irq_exit(); }