1. IPI中斷介紹
IPI中斷,即處理器核間中斷(Inter-Processor Interrupt),其實(shí)就是ARM GIC架構(gòu)里定義的SGI中斷(Software Generated Interrupt)。在GICv3架構(gòu)中,共有16個(gè)SGI中斷(不包括extension的),中斷號(hào)是從0到15,如下圖。
核間中斷就是一個(gè)CPU核(PE)向系統(tǒng)中的目標(biāo)CPU核(target PE)發(fā)送中斷信號(hào),以使目標(biāo)CPU執(zhí)行特定的操作。IPI中斷的基本流程為: PE觸發(fā) -> GICR -> GICD -> target GICR -> target CPU interface -> target PE。
IPI中斷的觸發(fā):PE通過寫系統(tǒng)寄存器ICC_SGI0R_EL1、ICC_SGI1R_EL1或ICC_ASGI1R_EL1來觸發(fā)。
IPI中斷有兩種routing模式(通過ICC_SGI0R_EL1.IRM/ ICC_SGI1R_EL1.IRM設(shè)置):
IRM bit為1,SGI會(huì)廣播給系統(tǒng)中所有PE(除了產(chǎn)生SGI的PE)。
IRM bit為0,SGI會(huì)發(fā)給指定的一些PE,通過Aff3.Aff2.Aff1.指定。(可以包括產(chǎn)生SGI的PE)。
ICC_SGI0R_EL1/ICC_SGI1R_EL1寄存器
2. linux里的IPI中斷類型
在Linux kernel代碼中,默認(rèn)定義了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號(hào)中斷,重新調(diào)度進(jìn)程scheduler_ipi()。
一般是先為進(jìn)程設(shè)置TIF_NEED_RESCHED標(biāo)志(表示需要調(diào)度該進(jìn)程),如果該進(jìn)程沒有在當(dāng)前CPU上,會(huì)通過smp_send_reschedule接口觸發(fā)IPI_RESCHEDULE中斷給目標(biāo)CPU,目標(biāo)CPU最終進(jìn)入hadle_IPI的scheduler_ipi中。
IPI_CALL_FUNC
1號(hào)中斷,調(diào)用generic_smp_call_function_interrupt(),遠(yuǎn)程cpu執(zhí)行回調(diào)函數(shù)。
如果想在某個(gè)cpu(不是本cpu)上調(diào)用一個(gè)函數(shù)時(shí),會(huì)觸發(fā)IPI_CALL_FUNC中斷,常用smp_call_function接口觸發(fā)target cpu的IPI_CALL_FUNC軟中斷。
IPI_CPU_STOP
2號(hào)中斷,調(diào)用local_cpu_stop()函數(shù)使當(dāng)前CPU停止工作,進(jìn)入wfi/wfe的低功耗狀態(tài)。
當(dāng)某個(gè)cpu想讓其他cpu停下來時(shí)會(huì)發(fā)送此IPI,主要接口是machine_halt, machine_power_off, machine_restart。這些接口都會(huì)調(diào)用smp_send_stop來觸發(fā)IPI_CPU_STOP中斷,target cpu收到中斷后會(huì)調(diào)用local_cpu_stop()函數(shù),如下:
??static?void?local_cpu_stop(void) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??set_cpu_online(smp_processor_id(),?false);/*把當(dāng)前cpu offline*/
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??local_daif_mask();?/*設(shè)置pstate的DAIF狀態(tài)位為1,關(guān)閉本cpu的系統(tǒng)調(diào)試(D),系統(tǒng)錯(cuò)誤SError(A),IRQ中斷(I),F(xiàn)IQ中斷(F)*/
? ? ? ? ??sdei_mask_local_cpu();?
? ? ? ? ??cpu_park_loop();?/*通過wfe和wfi指令,讓當(dāng)前cpu進(jìn)入低功耗standby狀態(tài)*/? ? ? ? ? ? ? ? ? ?
? }
IPI_CPU_CRASH_STOP
3號(hào)中斷,調(diào)用ipi_cpu_crash_stop(),使處理器停止。
在系統(tǒng)crash時(shí),發(fā)生crash的cpu給其他cpu發(fā)送該中斷。在使能了KEXEC的系統(tǒng)中,做出以下響應(yīng),保存寄存器信息并傳遞給第二內(nèi)核。KEXEC常用于系統(tǒng)crash時(shí)在不重啟的情況下快速進(jìn)入第二內(nèi)核。第二內(nèi)核的目的是把當(dāng)前ddr內(nèi)存鏡像保存下來,方便之后通過crash tool分析系統(tǒng)crash問題。
IPI_TIMER :
4號(hào)中斷,調(diào)用tick_receive_broadcast(), 廣播時(shí)鐘事件。
當(dāng)某個(gè)cpu調(diào)用tick_broadcast(const struct cpumask *mask)時(shí),即調(diào)用IPI_TIMER中斷給相應(yīng)cpu(通過mask指定cpu)發(fā)送timer的廣播中斷,target cpu執(zhí)行tick_receive_broadcast()函數(shù)進(jìn)行響應(yīng)。
IPI_IRQ_WORK :
5號(hào)中斷,調(diào)用irq_work_run(),在中斷上下文中執(zhí)行回調(diào)函數(shù)。
IPI_WAKEUP :
6號(hào)中斷,調(diào)用acpi_parking_protocol_valid(cpu), 喚醒CPU。當(dāng)CPU核收到該IPI中斷,即從parked的狀態(tài)(wfi/wfe的低功耗狀態(tài))喚醒過來。
NR_IPI :
7號(hào)中斷,沒有使用。
3. IPI中斷源碼
負(fù)責(zé)處理IPI中斷的函數(shù)主要是handle_IPI,源代碼如下:
<arch/arm64/kernel/smp.c> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
??void?handle_IPI(int?ipinr,?struct?pt_regs *regs)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??unsigned?int?cpu =?smp_processor_id();?/*獲得當(dāng)前cpu id*/?
? ? ? ? ??struct?pt_regs?*old_regs =?set_irq_regs(regs);?/*pt_regs結(jié)構(gòu)體包含當(dāng)前的寄存器信息*/? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??if?((unsigned)ipinr < NR_IPI) { ?/*當(dāng)前有效的IPI中斷為7個(gè)*/?
? ? ? ? ? ? ? ? ??trace_ipi_entry_rcuidle(ipi_types[ipinr]);?/* ftrace記錄進(jìn)入ipi中斷,用于debug */
? ? ? ? ? ? ? ? ? __inc_irq_stat(cpu, ipi_irqs[ipinr]);?/* 統(tǒng)計(jì)各cpu不同類型的ipi中斷數(shù)量*/? ? ? ?
? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??switch?(ipinr) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ??case?IPI_RESCHEDULE: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ??scheduler_ipi(); ?/* 觸發(fā)重調(diào)度 */
? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??case?IPI_CALL_FUNC: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ??generic_smp_call_function_interrupt();?/*執(zhí)行本cpu所有function回調(diào) */? ?
? ? ? ? ? ? ? ? ??irq_exit(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??case?IPI_CPU_STOP: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ??local_cpu_stop(); ?/*將本cpu停下來,進(jìn)入低功耗狀態(tài)*/? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??irq_exit(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??case?IPI_CPU_CRASH_STOP: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ??if?(IS_ENABLED(CONFIG_KEXEC_CORE)) {?/*如果配置了KEXEC,在系統(tǒng)panic時(shí)會(huì)進(jìn)入第二內(nèi)核*/? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ??ipi_cpu_crash_stop(cpu, regs); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ??unreachable(); ? ? ? ??
? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
??#ifdef?CONFIG_GENERIC_CLOCKEVENTS_BROADCAST ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??case?IPI_TIMER: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ??tick_receive_broadcast(); ?/* 接收timer廣播,執(zhí)行timer的中斷回調(diào) */? ??
? ? ? ? ? ? ? ? ??irq_exit(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
??#endif? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
??#ifdef?CONFIG_IRQ_WORK ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ??case?IPI_IRQ_WORK: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ??irq_enter(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ??irq_work_run();?/*本cpu執(zhí)行irq_work */? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??irq_exit(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ??break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
??#endif? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
??#ifdef?CONFIG_ARM64_ACPI_PARKING_PROTOCOL ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ??case?IPI_WAKEUP: ? ? ? ?/* 從低功耗狀態(tài)中喚醒本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); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? }