点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-优快云博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
4.7 chained中断如何流动
4.7.1 Linux chained中断处理流程
4.7.2 IPIPE chained中断处理流程
接着上一个章节分析,三个中断控制器INTC_A、INTC_B、INTC_C,形成了多级级联的chained中断。
根据上文的分析,Linux想要完成chained中断的处理,需要在Linux中断子系统中多次嵌套调用generic_handle_irq,每次都传入正确的virq。这对于IPIPE是不可接受的,因为Linux中断子系统的处理延迟是不可控的。
IPIPE的实现方式,是把多次嵌套调用generic_handle_irq,改成了多次嵌套调用__ipipe_dispatch_irq。

第一步,当中断INTC_C_hwirq32发生时,依次触发INTC_B_hwirq32、INTC_A_hwirq32,最终触发ARM64 IRQ。
第二步,参考《4.4 timer中断从Xenomai流向Linux》,IPIPE会根据接管中断管理,根据INTC_A_hwirq32找到对应的virq 10,会第一次调用__ipipe_dispatch_irq(irq=10, flags=0)。
(1)__ipipe_dispatch_irq(irq=10, flags=0)首先初始化desc和chained_irq。通过irq_to_desc函数获取中断描述符desc,并判断该中断是否为chained_irq。

chained_irq的值,取决于desc->handle_irq是否等于__ipipe_chained_irq,如果是,则chained_irq 为1,否则chained_irq 为0。
在当前的例子中,chained_irq 为1。在什么时候会把什么中断的desc->handle_irq初始化成__ipipe_chained_irq?
举一个实际的例子,在GIC中断控制器(irq-gic.c)初始化过程中,如果存在父,则把父中断的irq_desc.handle_irq设置为gic_handle_cascade_irq。
gic_of_init
-> gic_cascade_irq
-> irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, &gic_data[gic_nr]);
-> __irq_do_set_handler(desc, handle, 1, NULL) //其中第3个参数即is_chained=1
-> handle = __fixup_irq_handler(desc, handle, is_chained)//第3个参数is_chained=1
-> desc->handle = handle //此处handle是__fixup_irq_handler修正过的
在Linux的__irq_do_set_handler中,IPIPE增加了__fixup_irq_handler的调用。

第1047行,把原来的中断流控函数gic_handle_cascade_irq传递给了ipipe_ack。
第1049行,设置handle为__ipipe_chained_irq。而这个__ipipe_chained_irq函数是空函数,所以它的作用就是用于记录chained_irq的信息。
(2)__ipipe_dispatch_irq(irq=10, flags=0)继续执行ackfn。如《4.4.2 timer中断的应答(__ipipe_dispatch_irq上半部)》分析,IPIPE引入了ack和end的概念,在irq_desc和ipipe_irqdesc中都定义了ack函数,默认情况下,irq_desc->ipipe_ack、root域和head域的ipipe_irqdesc->ackfn都是相同的。

在当前的例子中,ackfn就是gic_handle_cascade_irq,执行后会转到第三步。当从ackfn返回后,因为chained_irq是1,flags是0,所以一定会执行goto sync,跳转到__ipipe_sync_pipeline函数进行interrupt log回放。
第三步,执行gic_handle_cascade_irq。这个函数被IPIPIE改造了,在INTC_B找到hwirq 32,再找出virq 12后,调用ipipe_handle_demuxed_irq(cascade_irq=12)-> __ipipe_dispatch_irq(irq=12, flags=IPIPE_IRQF_NOSYNC),进入第四步。

第四步,执行__ipipe_dispatch_irq(irq=12, flags=IPIPE_IRQF_NOSYNC)。

在当前的例子中,ackfn就是gic_handle_cascade_irq,执行后会转到第五步。当从ackfn返回后,因为chained_irq是1,flags是IPIPE_IRQF_NOSYNC,所以会直接return,不会执行任何SYNC操作。
第五步,执行gic_handle_cascade_irq。这个函数被IPIPIE改造了,在INTC_C找到hwirq 32,再找出virq 14后,调用ipipe_handle_demuxed_irq(cascade_irq=14)-> __ipipe_dispatch_irq(irq=14, flags=IPIPE_IRQF_NOSYNC),进入第六步。

第六步,执行__ipipe_dispatch_irq(irq=14, flags=IPIPE_IRQF_NOSYNC)。

在当前的例子中,ackfn就是handle_fasteoi_irq,执行之后,在第1518行的判断不成立,所以既不会sync,也不会return,会继续向下执行。
继续向下之后,无论此中断(virq 14)在head domain注册了中断处理程序,还是在root domain注册了中断处理程序,都是把中断记录到interrupt log,然后根据flags=IPIPE_IRQF_NOSYNC直接return。

在第六步return后,相当于返回了第四步中的__ipipe_dispatch_irq(irq=12, flags=IPIPE_IRQF_NOSYNC)。第四步中也会return,相当于返回了第二步中的__ipipe_dispatch_irq(irq=10, flags=0),在第二步中会继续走到sync:跳转到__ipipe_sync_pipeline函数对第六步中记录的interrupt log进行回放。
小结一下:在处理chained中断过程中,IPIPE把Linux多次嵌套调用generic_handle_irq,改成了多次嵌套调用__ipipe_dispatch_irq,在最后一次调用中,把中断记录到interrupt log中。在每一层的嵌套调用中,总是利用flags=IPIPE_IRQF_NOSYNC直接return,最终回到第一次调用__ipipe_dispatch_irq,实现对interrupt log的回放。
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-优快云博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
945

被折叠的 条评论
为什么被折叠?



