点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
3.5.1 发送并处理IPIPE_CRITICAL_IPI
__ipipe_init()最核心的就是__ipipe_enable_pipeline(),接下来对其展开分析!__ipipe_enable_pipeline()又可以分成两大部分来分析。本小节分析第一部分:ipipe_critical_enter和ipipe_critical_exit。
第一步:ipipe_send_ipi(IPIPE_CRITICAL_IPI, allbutself);
传入的ipi号是IPIPE_CRITICAL_IPI,它的定义是IPIPE_IPI_BASE + NR_IPI,它的值是virtual interrupt编号1031。在函数ipipe_send_ipi中减掉IPIPE_IPI_BASE得到7,存入ipinr并传递给函数smp_cross_call。
传入的cpumask是allbutself,它在ipipe_critical_enter中被初始化,除了bootup的CPU core 0,其它所有online CPU cores对应的bit位被置位1.
第二步:smp_cross_call(&cpumask, ipinr);
如果传入的ipinr小于7即0~6,说明是Linux的原生IPI,需要复用SGI0,变量sgi等于0。此时需要把ipinr记录到per cpu变量ipi_messages,方便其它CPU Core接收到SGI0中断时,从per cpu变量ipi_messages读出正确的ipinr的数值。
在当前的例子中,传入的ipinr等于7,所以在第862行sgi等于1。这就印证了《3.4.1.2 IPIPE对Linux中断号的改造》里面的分析,3个OOB IPI使用SGI1/2/3.
第三步:__smp_cross_call(target, sgi);
函数指针__smp_cross_call是在gic_init_bases-> gic_smp_init中初始化的,指向函数gic_raise_softirq。
drivers/irqchip/irq-gic-v3.c:
static void gic_smp_init(void)
{
set_smp_cross_call(gic_raise_softirq);
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
"irqchip/arm/gicv3:starting",
gic_starting_cpu, NULL);
}
arch/arm64/kernel/smp.c:
void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
{
__smp_cross_call = fn;
}
第四步:set_smp_cross_call(gic_raise_softirq);
GIC V3中断控制器,最终通过写SYS_ICC_SGI1R_EL1寄存器像其它CPU Cores发生SGI 1中断。
gic_raise_softirq
->gic_send_sgi
->gic_write_sgi1r
->write_sysreg_s(val, SYS_ICC_SGI1R_EL1)
第五步:gic_handle_irq
SGI 1中断发生后,根据《2.3.5 irq_handler》的分析,GIC V3中断控制器调用gic_handle_irq来处理中断。因为是IPI中断,所以调用ipipe_handle_multi_ipi来处理。
第六步:ipipe_handle_multi_ipi(irqnr, regs)
只调用了一个函数__ipipe_grab_ipi(irq, regs)。
第七步:__ipipe_grab_ipi(irq, regs);
如果sgi为非0,则说明是OOB IPI。在当前分析的上下文中,因为sgi传入的是1,所以irq最终等于1031,就是IPIPE_CRITICAL_IPI。所以,在这一步已经还原出需要响应的virtual interrupt编号。
如果sgi编号为0,则说明是Linux原生IPI,从per cpu变量ipi_messages还原出Linux原生IPI编号0~6,再加上IPIPE_IPI_BASE,变成1024~1030。对照第二步,就搞清楚了Linux原生IPI是如何复用SGI0了。
第八步:__ipipe_dispatch_irq
实时内核的注册是在start_kernel->rest_init中,是在_ipipe_init()之后才调用的。所以此时实时内核并没有注册,即ipipe_head_domain其实就等于ipipe_root_domain。在此背景下,virtual interrupt编号1031就会被__ipipe_set_irq_pending记录到interrupt log。
最后层层调用__ipipe_sync_pipeline ->__ipipe_do_sync_pipeline ->__ipipe_sync_stage->__ipipe_do_sync_stage,通过__ipipe_next_irq进行interrupt log回放。找到virtual interrupt编号1031,执行它的中断处理程序。
根据《3.4.3 __ipipe_init_early之初始化root domain》,IPIPE_CRITICAL_IPI(1031)对应的中断处理程序初始化为__ipipe_do_critical_sync()。也就是说,除了CPU Core 0,其它online的CPU cores都开始执行__ipipe_do_critical_sync()。
第九步:_ipipe_do_critical_sync()
除了CPU Core 0,其它online的CPU cores都开始执行__ipipe_do_critical_sync()。这些CPU core都把自身的cpu编号在__ipipe_cpu_sync_map中进行置位,以此来通知CPU Core 0:老大,我已顺利收到IPIPE_CRITICAL_IPI(1031),并开始执行__ipipe_do_critical_sync()。
而CPU Core 0在ipipe_critical_enter中,不断检查__ipipe_cpu_sync_map的值,只要所有CPU cores对应的bit已经置位了,则说明:兄弟们都收到通知并开始执行了。
第十步,ipipe_critical_exit
其它online的CPU cores执行完__ipipe_do_critical_sync(),会最终把自身的cpu编号在__ipipe_cpu_sync_map中进行清空,以此来通知CPU Core 0:老大,我已顺利完成任务。
而CPU Core 0在ipipe_critical_exit中,不断检查__ipipe_cpu_sync_map,确定兄弟们都完成了任务,则最终退出循环,代表整个IPIPE_CRITICAL_IPI处理完美结束。
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
标签:__,CRITICAL,IPI,sync,3.5,gic,ipipe,CPU From: https://blog.csdn.net/aspirestro/article/details/142427010