点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
4.4.2 timer中断的应答(__ipipe_dispatch_irq上半部)

先上图,一图抵千言!
基于《4.3 Linux的中断处理流程》,针对timer中断,加上IPIPE对Linux中断流程的改造以及如何从Xenomai传递到Linux的过程。同时,在图中用黄色高亮了3个中断处理程序。
IPIPE改变了物理中断的响应顺序,参考《2.3.5.3 handle_arch_irq_pipelined函数》,最终回到了gic_handle_irq:
irq_handler->handle_arch_irq_pipelined->handle_arch_irq->gic_handle_irq。
在gic_handle_irq中,IPIPE将gic_handle_irq->handle_domain_irq变成了gic_handle_irq->ipipe_handle_domain_irq,调用irq_find_mapping,从hwirq 27反向映射找出virq 3,传递给__ipipe_grab_irq。

进入__ipipe_grab_irq后,调用__ipipe_dispatch_irq,来到了IPIPE中断分发的核心函数,如下图函数调用堆栈所示。

第1506行,通过virq找出irq_desc。
第1507行,chained_irq等于0。
第1512行,ipd指针指向head域ipipe_head_domain。
第1514行,在head domain中,调用ipipe_request_irq注册XIRQ 3的中断处理程序时,会设置对应的ipd->irqs[3].control为IPIPE_HANDLE_MASK。所以此处判断为假,ipd变量仍然指向head域ipipe_head_domain。
第1517行,调用ipd->irqs[3].ackfn(desc),传入的参数是irq_desc。ackfn回调函数,指向__ipipe_ack_hrtimer_irq,它是什么时候设置的?
(1)__fixup_irq_handler函数初始化desc->ipipe_ack
如《4.2.4 根据DTS完成timer初始化》,在timer初始化过程中,会设置中断流控函数。如下图调用堆栈所示,IPIPE增加一个调用__fixup_irq_handler。在这个函数里,会根据已经注册的中断流控函数的类型,来设置irq_desc结构体中新增的两个成员:ipipe_ack和ipipe_end函数指针。
在__fixup_irq_handler的末尾,会把virq在root域的ackfn设为与desc->ipipe_ack相同。
注意,virq在head域的ackfn并没有设置,在哪里初始化呢?

(2)ipipe_request_irq设置ackfn
ipipe_request_irq向root域或head域注册中断处理程序的时候,还可以注册ackfn函数。如果没有指定ackfn,那么root域保持上述__fixup_irq_handler函数初始化的ackfn;而head域ackfn则和root域保持一致。
对于timer中断来说,在root域注册中断处理函数__ipipe_do_IRQ时,传入的ackfn为NULL,所以ipipe_root_domain->irqs[3].ackfn和__fixup_irq_handler初始化一致,是__ipipe_ack_fasteoi_irq。
而在head域注册中断处理函数xnintr_core_clock_handler时,指定了ackfn为__ipipe_ack_hrtimer_irq。

这个函数里面又回调了desc->ipipe_ack和ipipe_end,最终还是得搞清楚__fixup_irq_handler函数初始化的desc->ipipe_ack和ipipe_end。
(3)ackfn到底干啥了?

__ipipe_ack_hrtimer_irq这个函数里面又回调了desc->ipipe_ack和ipipe_end,它们是在__fixup_irq_handler函数初始化的,分别是__ipipe_ack_fasteoi_irq和__ipipe_end_fasteoi_irq。
void __ipipe_ack_fasteoi_irq(struct irq_desc *desc)
{
desc->irq_data.chip->irq_hold(&desc->irq_data);
}
void __ipipe_end_fasteoi_irq(struct irq_desc *desc)
{
if (desc->irq_data.chip->irq_release)
desc->irq_data.chip->irq_release(&desc->irq_data);
}
__ipipe_ack_fasteoi_irq和__ipipe_end_fasteoi_irq分别调用了irq_hold和irq_release,这两个函数指针是IPIPE在irq_chip中新增的成员。在irq-gic-v3.c驱动中,进行了初始化。
static struct irq_chip gic_chip = {
.name = "GICv3",
.irq_mask = gic_mask_irq,
.irq_unmask = gic_unmask_irq,
.irq_eoi = gic_eoi_irq,
.irq_set_type = gic_set_type,
#ifdef CONFIG_IPIPE
.irq_hold = gic_hold_irq,
.irq_release = gic_release_irq,
#endif
.irq_set_affinity = gic_set_affinity,
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_PIPELINE_SAFE |
IRQCHIP_MASK_ON_SUSPEND,
};
gic_hold_irq和gic_release_irq做了什么?

第315行,gic_hold_irq调用gic_poke_irq写GICD_ICENABLER(GIC Distributor Interrupt Clear-Enable Register)寄存器,屏蔽timer中断hwirq 27.
第322行,gic_hold_irq调用gic_eoi_irq,写SYS_ICC_EOIR1_EL1寄存器,代表中断已处理完成。System Interrupt Controller End of Interrupt Register 1 at Exception Level 1,用于在 EOI 模式 0 下发送 End of Interrupt (EOI) 信号。这个寄存器允许 CPU 通知 GIC 当前中断已经处理完毕,从而使 GIC 可以释放资源并允许其他中断被处理。
第327行,gic_release_irq调用gic_poke_irq写GICD_ISENABLER(GIC Distributor Interrupt Set-Enable Register)寄存器,打开timer中断hwirq 27。
参考Documentation/ipipe.rst,指出了irq_hold和irq_release的实现原则。

从实际实现来看,timer中断属于percpu中断,,符合percpu->mask[+ack][eoi]的原则。上述第315行和第322行代码,分别对应mask和eoi,是对GIC V3中断控制器的操作。目前还缺少ack,对应是什么?
再次回到__ipipe_ack_hrtimer_irq中,第386行有个timer->ack,最终走到arch_timer_ack函数。

arch_timer_ack函数用于检查timer定时器控制寄存器中的中断状态位,并在必要时清除中断状态位以确认中断已被处理。
所以说,ack代表了对timer设备自身的中断相关寄存器的操作。
static int arch_timer_ack(const int access, struct clock_event_device *evt)
{
unsigned long ctrl;
// 从定时器控制寄存器中读取当前的控制值
ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, evt);
// 检查定时器控制寄存器中的中断状态位
if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {
// 如果中断状态位被设置,将中断状态位清除
ctrl |= ARCH_TIMER_CTRL_IT_MASK;
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, evt);
return 1; // 返回 1,表示中断已被确认
}
// 如果中断状态位没有被设置,返回 0,表示没有中断需要确认
return 0;
}
小结一下:
IPIPE引入了ack和end的概念,在irq_desc和ipipe_irqdesc中都定义了ack函数,默认情况下,irq_desc->ipipe_ack、root域和head域的ipipe_irqdesc->ackfn都是相同的,但是ipipe_irqdesc->ackfn又可以通过ipipe_request_irq单独指定。
在__ipipe_dispatch_irq中,无论中断没有在head域注册中断处理程序,则会调用root域ipd->irqs[irq].ackfn,对中断控制器进行响应。
补充一点:
什么时候调用irq_desc.ipipe_end?
1) head domain的中断流控函数,xnintr_irq_handler,如果处理完毕,不再PROPAGATE给root domain,则会调用ipipe_end_irq函数,回调irq_desc->ipipe_end。这个地方有点奇怪,因为在__ipipe_ack_hrtimer_irq 已经调用过一次了irq_desc->ipipe_end。
2) 假设中断到达了root domain
a. handle_percpu_devid_irq/handle_percpu_irq会显示的调用desc->ipipe_end(desc);
b. handle_fasteoi_irq: 调用cond_release_fasteoi_irq达到了desc->ipipe_end(desc)相同的效果。
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
5278

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



