中断控制器介绍-软件篇(linux)
软件框架
CPU arch layer:具体cpu架构相关的中断处理部分,比如arm的异常向量表机制
IRQ chip driver:具体的中断控制器驱动,比如arm gicv3驱动,与gic的寄存器交互
IRQ subsystem:kernel的中断子系统,主要包括cpu、中断控制器无关的核心处理逻辑,包括中断处理流程,中断号映射管理,中断线程化以及各类中断的申请与释放
Device driver:通用外设驱动,使用irq subsystem提供的api来使用中断相关功能
数据结构
irq_domain
hwirq是中断控制器自己内部的编号,一个中断控制器一般一个中断域,也可以多个
swirq是linux系统全局唯一的软件编号,可以映射为irq domain + hwirq的组合
irq_desc
irq_desc 与 irq 一一对应,包含了描述信息,有个全局的radix tree描述这个信息
irq_chip
irq_data
irqaction
这里的irq是linux内部的全局软件中断号,void * dev_id可以自定义传参
连接关系
其中irq_domain、irq_domain_ops、irq_chip为per irq domain 的数据,其他的都是per irq的数据
allocated_irqs作为软件中断号为索引的全局数组来表示中断号的分配情况
irq_desc有两种组织方式
CONFIG_SPARSE_IRQ = y
稀疏分配模式
irq_desc
不是全局大数组,而是动态按需分配,放在(irq_desc_tree)
里。空间占用更小,特别适合有 上万个潜在中断号 的系统(如 ARM64 GICv3/ITS,PCIe MSI/MSI-X)。
mtree
= multi-order radix tree,是内核中 lib/maple_tree.c
提供的数据结构。
它比 radix tree 更高效,支持批量分配/释放,后来也被应用在 vma
管理上(取代 rbtree)。
在 IRQ 子系统里,sparse_irqs
就是一个 maple tree 根节点,用来存放稀疏分配的 irq_desc
。
!CONFIG_SPARSE_IRQ
(即没有开启这个选项)
紧凑分配模式
内核在启动时直接分配一个
irq_desc[NR_IRQS]
全局数组。每个中断号都对应一个固定的 slot。
查找快(直接数组索引),但浪费内存。
中断注册
整体流程就是将中断相关数据结构关联完成
DTS node中的 interrupts 属性,例如
interrupts = <GIC_SPI 637 IRQ_TYPE_LEVEL_HIGH 0>;
GICv3 驱动初始化,创建了 GIC 的
irqdomain
软件驱动中调用
platform_get_irq
:从 allocated_irqs bitmap 中申请 irq,然后申请 irq_desc,将两者关联并且插入到 irq_desc_tree
调用 gic irq domain 的 alloc 函数,将 hwirq 和 irq_data 插入到 irq_domain 的 revmap_tree,完成 hwirq 到 irq_desc 关联(通过 irq_data 外包结构体 irq_desc 找到)
软件驱动中调用
request_irq/request_threaded_irq
:将软件关心的 handler/thread_fn 更新到 irqaction (irq_desc的子模块)
中断处理
外设发生中断,报到 GIC 后,满足上报条件,则 GIC 上报 IRQ 给 CPU;
CPU 进入异常处理,执行 IRQ 异常向量表:
上下文保存
调用中断处理器注册的处理函数
GICv3 初始化时设置为了 gic_handle_irq
这就是中断处理的 C 语言接口
读取 GIC IAR 寄存器获取 hwirq;
通过 GIC irq domain 找到该 hwirq 对应的 irqdata
通过 container_of 找到 irqdata 外包的 irq_desc
调用 irq_desc 中记录的 irq_action 中的 handle 与 thread_fn