linux stm32mp157 GIC-V2 中断处理过程分析

/* 【】 gic_handle_irq 函数
** tip1:
** 经证明,中断触发时,调用的 handle_arch_irq 入口地址。
** 因为此时,挂接的就是 gic_handle_irq 函数!gic_handle_irq 是个全局函数指针,
** static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
** 它是Linux内核中处理GIC中断的核心函数,响应来自GIC的中断请求,完成中断状态的读取、
** 确认(ACK)、处理(ISR调用)和结束(EOI)的完整生命周期。函数通过循环处理可能存在
** 的多个挂起中断。
** 形参 regs 是 long 类型的数组指针,用于保存CPU切换上下文时刻的CPU寄存器现场数据。
**
** tip2:
** ARM的GIC-V2架构手册内,对中断ID号段的类型划分如下:
**  0-15    软中断(SGI),核间私有;Software-generated interrupt (SGI) 。
**  16-31   外设私有中断(PPI),核间私有;Private Peripheral Interrupt (PPI) 。
**  32-1019 外设共享中断(SPI),核间共享;Shared Peripheral Interrupt (SPI) 。
**  1020-1023 当前仍被保留的中断编号。
**
** tip3:
** SGI是GIC(通用中断控制器)提供的一种硬件中断类型,物理上由 GIC 直接支持,通过写
** GIC 的寄存器(如 `GICD_SGIR`)触发。属于ARM的GIC硬件模块里的中断编号域(0~15),
** SoftIRQ(软中断)是Linux内核实现的延迟处理机制,完全由软件实现,没有直接的硬件支持。
** 所以,从中断控制器的角度上看ISR:
**      中断上半部就是真正的ISR,而中断下半部不是ISR,只是被OS内核优先安排的机制。
** 但是,从linux的角度看ISR:
**      中断上下半部都是ISR的完整体不可或缺的一部分!只是出于效率,才将下半部安排到
**      内核线程中执行的!这俩货少了谁,这ISR都是会出严重错误的!!!
*/
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
	u32 irqstat, irqnr;
	/* gic_data 是 struct gic_chip_data 类型的全局数组 */
	struct gic_chip_data *gic = &gic_data[0];
	void __iomem *cpu_base = gic_data_cpu_base(gic);

	/*
	** 多中断处理​:当多个中断同时到达时,GIC会通过优先级排序依次处理,直到无挂起中断。
	** 抢占限制​:Linux默认不启用中断抢占,当前中断处理完成后才会响应新中断。
	** RTOS几乎都是中断抢占的!
	*/
	do {
		/* 读取CPU接口的GICC_IAR(Interrupt Acknowledge Register),
		该操作会返回当前最高优先级的中断ID,并将该中断状态从Pending转为Active​。 */
		irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
		/* 获得硬中断ID:GICC_IAR_INT_ID_MASK用于过滤保留位,仅保留有效中断号(0-1019) */
		irqnr = irqstat & GICC_IAR_INT_ID_MASK;

		/* ​外设中断处理(PPI和SPI:16-1019)​ */
		if (likely(irqnr > 15 && irqnr < 1020)) {
			if (static_branch_likely(&supports_deactivate_key))
				/* EOI操作​:向GICC_EOIR寄存器写入中断ID,
				   通知GIC中断处理完成,状态从Active转为Inactive */
				writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
			isb();
			/* 路由​对应的ISR:handle_domain_irq通过中断描述符表
			   (gic->domain)找到对应的中断处理函数(ISR)。 */
			handle_domain_irq(gic->domain, irqnr, regs);
			continue;
		}
		if (irqnr < 16) { /* SGI的中断处理(SGI:0-15)​专门处理CPUs的核间通信 */
			writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
			if (static_branch_likely(&supports_deactivate_key))
				writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP  /* 若支持多核CPU的协同处理 */
			smp_rmb();
			handle_IPI(irqnr, regs); //核间通信
#endif
			continue;
		}
		break;
	} while (1);
}

两条路线的对比

这两条路线,最终都执行到了__handle_domain_irq 函数这里来,但是他们传入的参数不同。

实际采纳的路线 gic_handle_irq:

默认路线:

所以得出结论,不支持domain路由机制的旧版本的 linux generic interrupt layer 正如2.6.25版本说的,这种legacy,只是为了向前兼容,会慢慢过度,直到被废弃。

GIC-V3 重要的新特性 Affinity routing

这个比较重要,硬件层面新增了许多实用的特性来支持linux这种操作系统里中断管理层的工作,互相配合,提升性能。 Affinity 有关于实时性的支持。 GIC-V2 不支持 Affinity ,是V3版本后引入的。

在嵌入式linux中,IRQ编号是对机器上不同中断源的枚举。通常情况下,枚举的是系统中所有中断控制器的输入引脚。IRQ是一个虚拟的中断ID,与硬件无关。 linux IRQ号始终与 irq_desc 数据结构绑定,该数据结构代表了 IRQ 号。

irq_domain里关联着一些成对的硬件中断号 hwirq 与 逻辑中断号 virq 的路由关系。

某个层级的domain里的这些成对绑定的irq仅仅针对本层级有意义。

hwirqx 是从本层级硬件控制器的相关中断寄存器内读取的数值;对应的 virqx 身后的 virqx_desc 则是对 hwirqx 信号的响应,这种相应要么流控到下一级别的控制器,要么流控到 ISR ,当然前提是这些响应需要下一级提前注册好。

irq 与 irq_desc 的绑定关系存在于哪里?

在 Linux 5.4.31 内核中,当启用 CONFIG_SPARSE_IRQ(稀疏 IRQ 模式)时,所有动态分配的 irq_desc 实例确实存储在基数树 irq_desc_tree 中:

  • 存储位置:\linux-5.4.31\kernel\irq\irqdesc.c (CONFIG_SPARSE_IRQ=y)

    全局声明: static RADIX_TREE(irq_desc_tree, GFP_KERNEL); 作用域:全局。

    IRQ 号与 irq_desc 的绑定关系通过 基数树(Radix Tree)或 XArray 存储在内核内存中。

  • 访问方式

    • 通过 __irq_alloc_descs 函数的派生形式,如 irq_alloc 函数,分配并初始化一组中断描述符;

    • 通过 irq_to_desc(irq) 函数查找指定irq的描述符。

    • 通过 delete_irq_desc 函数,删除指定irq的描述符;

  • 设计优势:动态分配、高效查找、支持多级中断控制器的复杂映射。

  • 基数树角色:作为全局数据库,动态管理稀疏的 irq_desc,优化内存使用。

  • 操作效率:通过 O(k) 复杂度实现快速查找和动态扩展。

  • 演进方向:XArray 逐步替代基数树,提供更高效的内存管理。

irq 与 hwirq 的绑定关系存在于哪里?

  • 存储位置:

    • hwirq → irq 的映射存储在 irq_domain.revmap_tree(基数树)。

    • irq → hwirq 的映射存储在 irq_data.hwirq

  • 级联支持:多级中断控制器通过父子 irq_domain 的协作传递绑定关系。

  • 关键数据结构irq_domainirq_data 共同维护中断号的双向映射。

在linux中断子系统内,关于中断域irq_domain:

  • 每个独立的中断域对应一个中断控制器;

  • 每个独立的中断域负责对应控制器在中断系统内的路由;

  • 中断的路由(级联流控)过程中,需要:

    • 中断域提供中断号间映射服务,同时也需要,

    • 中断描述符提供其本层级的个性化路由流控服务(handle_riq);

  • 每个独立的中断域内存放有独立的映射过程需要的数据,具体存放在域内的成员:

    • 线性映射(Linear):linear_revmap[] ;

    • 树形映射(Tree):revmap_tree (基于 Radix Tree 基数树实现);

    • 其他类型:如 simple(固定映射)、hierarchy(层级映射)等。

    • 注意:这些元数据是仅维护本层级中断号间映射的数据集合,是独立的。

  • 每个独立的中断域,共享统一的映射算法:

    • 线性映射:使用数组 linear_revmap[],复杂度 O(1)。

    • 树形映射:使用基数树 revmap_tree,复杂度 O(log n)。

    • 简单映射:直接预定义 HW IRQ → VIRQ 的偏移(如 virq = hwirq + offset)。

  • 级联中断的路由是跨 irq_domain 的协作过程,需父域和子域共同完成。 当父控制器检测到子控制器的中断时,父域与子域的协作

    1. 父控制器通过 irq_domain 解析出子控制器的 virq

    2. 调用 generic_handle_irq(child_virq),触发子控制器的流控逻辑。

    3. 子控制器的 irq_desc->handle_irq 处理具体中断(如 handle_edge_irq)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值