1. IPI中断介绍
IPI中断,即处理器核间中断(Inter-Processor Interrupt),其实就是ARM GIC架构里定义的SGI中断(Software Generated Interrupt)。在GICv3架构中,共有16个SGI中断(不包括extension的),中断号是从0到15,如下图。
核间中断就是一个CPU核(PE)向系统中的目标CPU核(target PE)发送中断信号,以使目标CPU执行特定的操作。IPI中断的基本流程为: PE触发 -> GICR -> GICD -> target GICR -> target CPU interface -> target PE。
IPI中断的触发:PE通过写系统寄存器ICC_SGI0R_EL1、ICC_SGI1R_EL1或ICC_ASGI1R_EL1来触发。
IPI中断有两种routing模式(通过ICC_SGI0R_EL1.IRM/ ICC_SGI1R_EL1.IRM设置):
IRM bit为1,SGI会广播给系统中所有PE(除了产生SGI的PE)。
IRM bit为0,SGI会发给指定的一些PE,通过Aff3.Aff2.Aff1.指定。(可以包括产生SGI的PE)。
ICC_SGI0R_EL1/ICC_SGI1R_EL1寄存器
2. linux里的IPI中断类型
在Linux kernel代码中,默认定义了8种IPI中断(SGI0 - SGI7),具体如下(linux/arch/arm64/kernel/smp.c):
enum?ipi_msg_type {?IPI_RESCHEDULE,?IPI_CALL_FUNC,?IPI_CPU_STOP,?IPI_CPU_CRASH_STOP,?IPI_TIMER,?IPI_IRQ_WORK,?IPI_WAKEUP,?NR_IPI};
IPI_RESCHEDULE
0号中断,重新调度进程scheduler_ipi()。
一般是先为进程设置TIF_NEED_RESCHED标志(表示需要调度该进程),如果该进程没有在当前CPU上,会通过smp_send_reschedule接口触发IPI_RESCHEDULE中断给目标CPU,目标CPU最终进入hadle_IPI的scheduler_ipi中。
IPI_CALL_FUNC
1号中断,调用generic_smp_call_function_interrupt(),远程cpu执行回调函数。
如果想在某个cpu(不是本cpu)上调用一个函数时,会触发IPI_CALL_FUNC中断,常用smp_call_function接口触发target cpu的IPI_CALL_FUNC软中断。
IPI_CPU_STOP
2号中断,调用local_cpu_stop()函数使当前CPU停止工作,进入wfi/wfe的低功耗状态。
当某个cpu想让其他cpu停下来时会发送此IPI,主要接口是machine_halt, machine_power_off, machine_restart。这些接口都会调用smp_send_stop来触发IPI_CPU_STOP中断,target cpu收到中断后会调用local_cpu_stop()函数,如下:
??static?void?local_cpu_stop(void) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??set_cpu_online(smp_processor_id(),?false);/*把当前cpu offline*/? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??local_daif_mask();?/*设置pstate的DAIF状态位为1,关闭本cpu的系统调试(D),系统错误SError(A),IRQ中断(I),FIQ中断(F)*/? ? ? ? ??sdei_mask_local_cpu();?? ? ? ? ??cpu_park_loop();?/*通过wfe和wfi指令,让当前cpu进入低功耗standby状态*/? ? ? ? ? ? ? ? ? ?? }
IPI_CPU_CRASH_STOP
3号中断,调用ipi_cpu_crash_stop(),使处理器停止。
在系统crash时,发生crash的cpu给其他cpu发送该中断。在使能了KEXEC的系统中,做出以下响应,保存寄存器信息并传递给第二内核。KEXEC常用于系统crash时在不重启的情况下快速进入第二内核。第二内核的目的是把当前ddr内存镜像保存下来,方便之后通过crash tool分析系统crash问题。
IPI_TIMER :
4号中断,调用tick_receive_broadcast(), 广播时钟事件。
当某个cpu调用tick_broadcast(const struct cpumask *mask)时,即调用IPI_TIMER中断给相应cpu(通过mask指定cpu)发送timer的广播中断,target cpu执行tick_receive_broadcast()函数进行响应。
IPI_IRQ_WORK :
5号中断,调用irq_work_run(),在中断上下文中执行回调函数。
IPI_WAKEUP :
6号中断,调用acpi_parking_protocol_valid(cpu), 唤醒CPU。当CPU核收到该IPI中断,即从parked的状态(wfi/wfe的低功耗状态)唤醒过来。
NR_IPI :
7号中断,没有使用。
3. IPI中断源码
负责处理IPI中断的函数主要是handle_IPI,源代码如下:
<arch/arm64/kernel/smp.c> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????void?handle_IPI(int?ipinr,?struct?pt_regs *regs)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??unsigned?int?cpu =?smp_processor_id();?/*获得当前cpu id*/?? ? ? ? ??struct?pt_regs?*old_regs =?set_irq_regs(regs);?/*pt_regs结构体包含当前的寄存器信息*/? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??if?((unsigned)ipinr < NR_IPI) { ?/*当前有效的IPI中断为7个*/?? ? ? ? ? ? ? ? ??trace_ipi_entry_rcuidle(ipi_types[ipinr]);?/* ftrace记录进入ipi中断,用于debug */? ? ? ? ? ? ? ? ? __inc_irq_stat(cpu, ipi_irqs[ipinr]);?/* 统计各cpu不同类型的ipi中断数量*/? ? ? ?? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??switch?(ipinr) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ??case?IPI_RESCHEDULE: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??scheduler_ipi(); ?/* 触发重调度 */? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??case?IPI_CALL_FUNC: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??generic_smp_call_function_interrupt();?/*执行本cpu所有function回调 */? ?? ? ? ? ? ? ? ? ??irq_exit(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??case?IPI_CPU_STOP: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??local_cpu_stop(); ?/*将本cpu停下来,进入低功耗状态*/? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??irq_exit(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??case?IPI_CPU_CRASH_STOP: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??if?(IS_ENABLED(CONFIG_KEXEC_CORE)) {?/*如果配置了KEXEC,在系统panic时会进入第二内核*/? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ??ipi_cpu_crash_stop(cpu, regs); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ??unreachable(); ? ? ? ??? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????#ifdef?CONFIG_GENERIC_CLOCKEVENTS_BROADCAST ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??case?IPI_TIMER: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??tick_receive_broadcast(); ?/* 接收timer广播,执行timer的中断回调 */? ??? ? ? ? ? ? ? ? ??irq_exit(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???#endif? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????#ifdef?CONFIG_IRQ_WORK ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ??case?IPI_IRQ_WORK: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??irq_work_run();?/*本cpu执行irq_work */? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??irq_exit(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???#endif? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????#ifdef?CONFIG_ARM64_ACPI_PARKING_PROTOCOL ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??case?IPI_WAKEUP: ? ? ? ?/* 从低功耗状态中唤醒本cpu */? ? ? ? ? ? ? ? ??WARN_ONCE(!acpi_parking_protocol_valid(cpu), ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ??"CPU%u: Wake-up IPI outside the ACPI parking protocoln", ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???#endif? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??default: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??pr_crit("CPU%u: Unknown IPI message 0x%xn", cpu, ipinr); ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??if?((unsigned)ipinr < NR_IPI) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ??trace_ipi_exit_rcuidle(ipi_types[ipinr]); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ??set_irq_regs(old_regs); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? }
								
								
								
4623