1、SGI中断(核间通信中断)
在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.
*/
};
2、SGI中断的调用流程
(1)、CPU收到IRQ后的处理
当ARM core收到IRQ后,会触发cpu的irq异常,会跳转到linux kernel的irq异常向量表,在该向量表中,会调用gicv2/gicv3的gic_handle_irq函数,在kernel/drivers/irqchip/irq-gic-v3.c中
- irqnr = gic_read_iar() 读gic寄存器获取硬件中断号
- likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192 这这个条件下处理PPI中断和SPI中断
- if (irqnr < 16) 在这个条件下调用handle_IPI(irqnr, regs)来处理SGI中断
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);
}
(2)、处理SGI中断(也叫核间通信中断)
void handle_IPI(int ipinr, struct pt_regs *regs)
{
......
case IPI_RESCHEDULE:
scheduler_ipi();
break;
......
}
(3)、处理IPI_RESCHEDULE中断的操作
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();
}