0%

Linux内核里IPI的基本逻辑

基本逻辑

CPU直接可以通过核间中断(IPI)相互之间发中断,ARM上使用SGI支持核间中断。其中SGI的
基本逻辑可以参考这里,ARM KVM的vSGI的基本逻辑可以参考这里

Linux内核里,基于IPI封装的接口在include/linux/smp.h。基本的接口如下:

1
2
3
smp_call_function(func, info, wait) 
smp_call_function_many(mask, func, info, wait)
...

如上函数的语意是,在所有online CPU或者特定CPU上执行对应的函数,wait为true表示调
用者同步等待相关函数执行完。

代码分析

一个IPI中断应用的例子。

1
2
3
4
5
6
7
8
9
10
11
12
wake_up_process
+-> try_to_wake_up(p, TASK_NORMAL, 0)
+-> ttwu_queue
+-> ttwu_queue_wakelist
+-> __ttwu_queue_wakelist
+-> __smp_call_single_queue <--- IPI的封装接口
+-> send_call_function_single_ipi
+-> arch_send_call_function_single_ipi // arch/arm64/kernel/smp.c
/* smp_cross_call给一个core发IPI可以带ipinr,硬件层面是中断号。*/
+-> smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC)
+-> __ipi_send_mask(ipi_desc[ipinr], target)
+-> chip->ipi_send_mask(data, dest) // gic_ipi_send_mask

GICv3的IPI回调函数:

1
2
3
4
5
/* drivers/irqchip/irq-gic-v3.c */
gic_ipi_send_mask
+-> gic_send_sgi
+-> gic_write_sgi1r
+-> write_sysreg(val, ICC_SGI1R)

注册SGI中断的入口函数:

1
2
3
4
5
6
/* drivers/irqchip/irq-gic-v3.c */
gic_smp_init
+-> irq_domain_alloc_irqs
+-> set_smp_ipi_range
/* arch/arm64/kernel/smp.c,为每个CPU注册相关IPI */
+-> request_percpu_irq(ipi_base + i, ipi_handler, "IPI", ...)

所以,收到IPI中断的core的处理逻辑是:

1
2
3
4
5
6
7
8
9
10
11
12
ipi_handler
+-> do_handle_IPI(irq - ipi_irq_base)
/* IPI的中断号区分不同种类的IPI */
+-> switch (ipinr)
+-> IPI_RESCHEDULE
IPI_CALL_FUNC
IPI_CPU_STOP
IPI_CPU_CRASH_STOP
IPI_TIMER
IPI_IRQ_WORK
IPI_CPU_BACKTRACE
IPI_KGDB_ROUNDUP

IPI测试工具

社区有一个IPI的benchmark工具ipi_benchmark,不过这个小工具还没有在Linux内核主线。
这个工具反复使用IPI在特定CPU上执行函数,并记录发起IPI和函数开始执行之间的时间差值。
可以利用这个工具测试系统IPI的延时性能,特别是可以测试虚拟化场景下IPI的延时性能。