Linux之中断子系统-中断控制器的中断函数gic_handle_irq分析(5)
1.概述
在前面文章中,已经分析汇编函数, 最终调用的是handle_arch_irq或arch_irq_handler_default函数。以zynq7k为例,其采用的是GIC pl390中断控制器,gic初始化的时候调用set_handle_irq函数,将handle_arch_irq函数指针指向了 gic_handle_irq 。因此这里以zynq7为例,介绍Linux内核对uart0的中断处理过程。
2.GIC中断入口函数
底层irq_handler调用gic_handle_irq处理中断,从函数名可以看出,此时的中断处理和具体的中断控制器有关,arm平台上中断控制器一般都为gic。传入的参数为struct pt_regs *regs,指向了保存中断发生时刻的寄存器信息地址。首先读取中断响应寄存器ICCIAR,获取硬件中断号及产生SGI中断的CPU ID,根据硬件中断号,判断中断类型。当读取了ICCIAR寄存器后,表示此CPU响应了中断,在此cpu处理完该中断之前,gic中断控制器不会再向此cpu发送任何中断。PPI中断和SPI中断调用handle_domain_irq处理,SGI中断调用handle_IPI处理。当然,SGI中断在SMP系统中才有效,在UP系统中无效,不做处理。 从下面的代码可以看出,整个中断处理逻辑是在一个死循环中,只要能从ICCIAR寄存器中读出有效的硬件中断号,就会一直进行中断处理,也就是说,只要中断一直产生,则中断服务函数gic_handle_irq会一直运行,不会退出。这正是Linux内核设计巧妙的地方,进入中断处理程序后,会处理所有pending或者active and pending状态的中断,直到没有中断产生,中断处理函数才退出,避免频繁进入和退出中断处理程序导致的性能下降。
[drivers/irqchip/irq-gic.c]static struct gic_chip_data gic_data[CONFIG_ARM_GIC_MAX_NR] __read_mostly;gic_handle_irqstruct gic_chip_data *gic = &gic_data[0] // 获取gic设备结构体指针->gic_data_cpu_base // 获取GIC CPU interface的基地址空间do {// 读取中断响应寄存器ICCIAR,读取后就清除了gic中的中断,gic中对应的中断状态由pending变为acting// ICCIAR的[bit9-0]为硬件中断ID,SPI和PPI中断用到,[bit12-10]产生中断的CPU的ID,SGI中断用到irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);irqnr = irqstat & GICC_IAR_INT_ID_MASK; // 获取ICCIAR的[bit9-0],即硬件中断号if (likely(irqnr > 15 && irqnr < 1020)) { // PPI和SPI中断走此流程// 如果支持priority drop(降低优先级)and deactivation operations(中断状态变化)分离,// 则先写ICCEOIR寄存器,中断完成后再写ICCDIR寄存器,表示中断处理完成,zynq7不支持if (static_key_true(&supports_deactivate))writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);handle_domain_irq(gic->domain, irqnr, regs);->__handle_domain_irqcontinue;}if (irqnr < 16) { // SGI中断走此流程writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);if (static_key_true(&supports_deactivate))writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);#ifdef CONFIG_SMP // SMP系统中,SGI中断才真正有意义/** Ensure any shared data written by the CPU sending* the IPI is read after we've read the ACK register* on the GIC.** Pairs with the write barrier in gic_raise_softirq*/smp_rmb();handle_IPI(irqnr, regs); // SGI中断处理函数,内核中将软件产生的中断叫IPI中断#endifcontinue;}break; // 直到没有处于pending或者active and pending状态的中断,才跳出循坏,退出中断处理程序} while (1)
gic_handle_irq函数调用handle_domain_irq处理PPI和SPI中断,首先根据硬件中断号找到软件中断号,根据软件中断号找到对应的中断描述符irq_desc,调用中断描述符中的通用中断处理函数handle_irq,zynq7k平台的通用中断处理函数handle_irq指向了handle_fasteoi_irq函数。__handle_domain_irq开始调用了irq_enter,用来增加preempt_count成员里HARDIRQ域的值,HARDIRQ域的值大于0,则表示此进程处于硬件中断上下文中,否则表示此进程不处于硬件中断上下文,__handle_domain_irq退出时调用irq_enter,递减preempt_count成员里HARDIRQ域的值,并判断当前上下文是否处于硬件中断和是否有软中断pending,如当前上下文不处于硬件中断并且有软中断pending,则处理软件中断。
handle_domain_irq->__handle_domain_irq->irq_enter // 进入中断上下文->__irq_enter// 增加当前进程struct thread_info中的preempt_count成员里HARDIRQ域的值// HARDIRQ域用于判断当前进程是否处于硬件中断上下文->preempt_count_add(HARDIRQ_OFFSET)->irq_find_mapping // 根据中断控制的irq_domain和硬件中断号,获取软件中断号// 如果硬件中断号小于直接映射的最大中断号,则直接比较dom