点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
3.4.3 __ipipe_init_early之初始化root domain
如下图所示,红框里面的函数当前都是空的,本章还是分析蓝框中的代码片段。
第295行,变量ipd指向了ipipe_root即ipd代表root domain。
第305行,root domain的名字被命名为“Linux”。
第306行,初始化ipd->context_offset变量,详细分析见《3.4.1.1 IPIPE基础数据结构》。
第307行,调用init_stage(ipd)对root domain进行初始化。此函数只要3行,直接在代码里面进行注释如下:
linux/kernel/ipipe/core.c:
static void init_stage(struct ipipe_domain *ipd)
{
//对ipipe_irqdesc irqs[IPIPE_NR_IRQS]数组进行清零操作。
//此数据结构的意义具体见《3.4.1.2 IPIPE对Linux中断号的改造》
memset(&ipd->irqs, 0, sizeof(ipd->irqs));
//对互斥量进行初始化
mutex_init(&ipd->mutex);
//(1) 调用__ipipe_ipis_alloc()完成所有IPI中断的分配
//(2) 调用hook_internal_ipi设置IPIPE_CRITICAL_IPI
__ipipe_hook_critical_ipi(ipd);
}
arch/arm64/kernel/ipipe.c:
void __ipipe_hook_critical_ipi(struct ipipe_domain *ipd)
{
__ipipe_ipis_alloc();
hook_internal_ipi(ipd, IPIPE_CRITICAL_IPI, __ipipe_do_critical_sync);
}
init_stage最后调用的函数__ipipe_hook_critical_ipi(ipd),里面做了两件事情,需要展开说一下。
- 调用__ipipe_ipis_alloc()完成所有IPI中断的分配
arch/arm64/kernel/smp.c:
void __ipipe_ipis_alloc(void)
{
unsigned int virq, ipi;
static bool done;
if (done)
return;
/*
* We have to get virtual IRQs in the range
* [ IPIPE_IPI_BASE..IPIPE_IPI_BASE + NR_IPI + IPIPE_OOB_IPI_NR - 1 ],
* otherwise something is wrong (likely someone would have
* allocated virqs before we do, and this would break our
* fixed numbering scheme for IPIs).
*/
for (ipi = 0; ipi < NR_IPI + IPIPE_OOB_IPI_NR; ipi++) {
virq = ipipe_alloc_virq();
WARN_ON_ONCE(virq != IPIPE_IPI_BASE + ipi);
}
done = true;
}
根据《3.4.1.2 IPIPE对Linux中断号的改造》的分析,NR_IPI等于7,IPIPE_OOB_IPI_NR等于3,所以for循环调用ipipe_alloc_virq()函数10次,共申请10个virtual interrupt编号。关于virtual interrupt,之前已经总结过了,它的总数量就是IPIPE_NR_VIRQS(64)。为了管理virtual interrupt,IPIPE定义了全局变量 __ipipe_virtual_irq_map,通过位图来标记是否virtual interrupt是否被占用。
ipipe_alloc_virq()函数的本质,就是在变量__ipipe_virtual_irq_map的64个bit中,找到第一个为0的bit位(ffz就是find first zero)即空闲的bit位,然后把此bit位设置为1即占用此bit位。从__ipipe_virtual_irq_map中找到的bit位存到变量ipos,必须再加上IPIPE_VIRQ_BASE(1024),才能得到最终的virtual interrupt的编号。
回到__ipipe_ipis_alloc,它for循环调用ipipe_alloc_virq()函数10次,得到的10个virq是1024~1033. 注意这个范围哦,下一步的IPIPE_CRITICAL_IPI是1031,就在这个范围内!
linux/kernel/ipipe/core.c:
// 共64 bit位,通过位图来标记是否VIRQ是否被占用
static unsigned long __ipipe_virtual_irq_map;
unsigned int ipipe_alloc_virq(void)
{
unsigned long flags, irq = 0;
int ipos;
raw_spin_lock_irqsave(&__ipipe_lock, flags);
if (__ipipe_virtual_irq_map != ~0) {
ipos = ffz(__ipipe_virtual_irq_map);
set_bit(ipos, &__ipipe_virtual_irq_map);
irq = ipos + IPIPE_VIRQ_BASE;
}
raw_spin_unlock_irqrestore(&__ipipe_lock, flags);
return irq;
}
EXPORT_SYMBOL_GPL(ipipe_alloc_virq);
- 调用hook_internal_ipi设置IPIPE_CRITICAL_IPI
IPIPE_CRITICAL_IPI是OOB IPI之一,它对应的virq编号是1031。上一步已经把1031分配出来了,hook_internal_ipi就是来设置IPIPE_CRITICAL_IPI对应的中断处理程序为__ipipe_do_critical_sync()!具体这个函数的用处,后面的章节再分析。
arch/arm64/kernel/ipipe.c:
static inline void
hook_internal_ipi(struct ipipe_domain *ipd, int virq,
void (*handler)(unsigned int irq, void *cookie))
{
ipd->irqs[virq].ackfn = NULL;
ipd->irqs[virq].handler = handler;
ipd->irqs[virq].cookie = NULL;
/* Immediately handle in the current domain but *never* pass */
ipd->irqs[virq].control = IPIPE_HANDLE_MASK|IPIPE_STICKY_MASK;
}
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!