4.4.2 timer中断的应答(__ipipe_dispatch_irq上半部)

点击查看系列文章 =》 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博客

原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值