目录
1.中断路由
2.中断优先级
3.中断平衡
4.Linux内核中重要的数据结构
5.中断标识
承前文,本文从中断路由、优先级、数据结构和标识意义等方面对Linux内核中断进行一步的解析。
1.中断路由
Aset affinity flow
GIC文中有提到SPI类型中断的路由控制器寄存器为GICD_IROUTER,设置该寄存器即配置了中断的路由方式。内核中irq_set_affinity_hint、__irq_set_affinity、irq_set_affinity_locked等都可以设置对应的中断的路由即affinity,以irq_set_affinity_hint为例来说明affinity设置的过程,对应代码流程中如上图。
irq_set_affinity_hint 最终会调用到GIC的中的gic_set_affinity函数,通过该函数将系统需要设置的中断的affinity值设置到该中断对应的GICD_IROUTER寄存器;当该中断触发时GIC会根据该中断对应的GICD_IROUTER寄存器值,将该中断路由到指定的PE去处理。
affinify值即CPU对应的MPIDR_EL1(关于MPIDR_EL1寄存器解释,看一查看另一文章 CSDARM处理器 -- ARM64 MPIDR_EL1寄存器-CSDN博客CSD),Linux系统中有多处记录该中断affinify值对应的cpu number,如上图的三处:
irq_desc->affinity_hint 系统要设置的该中断可路由到的CPU,可以是某个特定的
CPU或者是CPU的集合,该CPU可以是unpluged state。
irq_data->irq_common_data->effective_affinity 最终设置到该中断
GICD_IROUTER寄存器的affnity值对应的CPU,特定某个CPU,该CPU
是系统要设置的该中断可以路由的CPU集合的中的一个。
irq_data->irq_common_data->affinity 同irq_desc->affinity_hint,系统要设
置的该中断可路由到的CPU。
gic_set_affinity函数负责中断对应的GICD_IROUTER寄存器的最终设置。其中有两种设置方式:强制和非强制方式。强制方式时,会取系统要设置的可路由的CPU集合中第一个(有地位到高位)为1的bit所对应CPU的MPIDR_EL1值;非强制时,会取系统要设置的可路由的CPU集合中任意一个online CPU的MPIDR_EL1值。
2.中断优先级
kernel-5.10内核,除虚拟化相关逻辑外,仅对NMI中断的处理优先级进行了配置,其他如SPI类型的中断都没有设置对应的中断优先级,即这些中断的优先级为GIC寄存器中的默认值0,这些SPI的中断具有相同的优先级,所以这些中断不存在中断竞态问题(关于中断竞态问题查看ARM处理器 -- 中断控制器GICv3_interrupt_routing_mode-CSDN博客 的3.4 运行优先级和竞态)。
ARM GICD_IPRIORITYn寄存器
3.中断平衡
中断平衡服务运行在用户态,非内核态,目前高通方案中有此服务。对此不做详细介绍,感兴趣可参考该服务代码:
https://github.com/Irqbalance/irqbalance
4.Linux内核中重要的数据结构
如上图Linux内核中有两个重要的irq相关数据 :
4.1 struct irq_desc irq_desc[NR_IRQS] 用于存放系统所有irq的中断描述符,该数据在early_irq_init()时被初始化为默认值,在中断控制器和中断在注册时被进一步被设置为中断相关的属性和数据。irq_desc数组的index为linux系统对应的中断号(即软件中断号),struct irq_desc是在表述中断的自身的系统属性。而其irq_desc->irq_data 即struct irq_data更多的是在说明该中断硬件属性(如其对应的硬件中断、其所属的硬件中断控制器等)。irq_data->irq 即为irq_desc数组的index、也就是系统软件中断号;irq_data->hwirq该中断所属的中断控制器的硬件中断号。irq_data->irq 与 irq_data->hwirq在系统是一一对应的、能够相互指正,这种对应关系是中断控制器在注册时通过irq_create_mapping()函数完成的。
irq_desc中还有一个重要的元素是action,对应的结构是struct irqaction,该j结构中的数据会通过request_threaded_irq()等类似的中断注册函数进行填充。如图中所以基本与request irq函数中的入参相对应。
4.2 irq_domain_list是内核中另一个中断相关的重要数据,是一个指向struct irq_domian结构的链表。irq_damain是在中断控制器注册时通过irq_domain_create_tree函数创建,其与系统中注册的中断控制器相对应,故irq_data->domain 与irq_data->chip相对应,irq_data->domain为中断控制器的系统属性,irq_data->chip为中断控制器的硬件特性。
系统中正式因为有irq_desc数组,所以可以通过系统中断号遍历到该该中断对对对应的所有特性,如对应的中断控制器、硬件中断号、中断处理函数、中断状态等等。
【从数据的角度看,初始类的函数是在为了完成数据结构的填充,其中数据代表状态;过程函数是在修改结构数据,也是在改变状态。】
5.中断标识
中断注册时可设置的中断flag,这些flag影响中断chip及该中断irq_desc的属性。
中断注册标识 | 意义 |
IRQF_SHARED | 多个外设共享一个中断信号;注册共享中断时,1.必须要传入注册设备的信息、否则无法设备哪个设备触发的中断; 2. 所有共享中断的中断触发类型必须相同; 3.共享同一硬件中断,如果有中断设置IRQF_ONESHOT/IRQF_PERCPU则共享该中断的其他中断也需要设置IRQF_ONESHOT/IRQF_PERCPU属性。 4.共享中断最好具有自动使能功能(即该中断不具备__IRQ_NOAUTOEN属性),否则该中断的使能可能不平衡;5.共享中断不能通过try_one_irq函数从秘书rounted状态恢复IRQF_COND_SUSPEND 仅对共享中断有意义,另IRQF_NO_SUSPEND 和 IRQF_COND_SUSPEND时不能同时存在的。 |
IRQF_PROBE_SHARED | 用于在IRQF_SHARED注册失败时打印异常的中断注册信息 |
__IRQF_TIMER | 用于标记为时钟中断 |
IRQF_PERCPU | 表示该中断可路由到任意CPU, 该标识的中断不能被强制设置为thread处理形式 |
IRQF_NOBALANCING | 表示该中断不支持 irq balancing |
IRQF_ONESHOT | 用于支持thread的中断,当该中断的handler处理完成是不能使能的,直到该中断的thread_fn运行。 |
IRQF_NO_SUSPEND | 系统suspend时不会关闭此中断,但是并不保证此中断可以唤醒系统 |
IRQF_FORCE_RESUME | 当系统唤醒时,强制使能该中断; |
中断描述符的状态irq_desc -> status_use_accessors表示该该中的状态属性。
中断描述符的状态 | 意义 |
IRQ_PER_CPU | 与注册中断时IRQF_PERCPU flag相对应,表示该中断可路由到任意CPU |
IRQ_NOPROBE | 表示该中断不能被autoprobing,指设备自动探测中断号 |
IRQ_NOREQUEST | 标识该中断不能被 requst_irq() 申请 |
IRQ_NOAUTOEN | 表示该中断不具有自动使能能力,自动使能中断在完成注册后主动调用irq_startup(IRQ_RESEND)处理中断;IRQF_SHARED 共享中断要支持自动使能 |
IRQ_NO_BALANCING | 与注册中断时IRQF_NOBALANCINGflag对应,表示该中断不支持中断平衡、不能通过irq_set_affinity函数设置其亲和性。支持平衡也有另一个前提即支持 per cpu |
IRQ_MOVE_PCNTXT | 支持migrated中断处理器上下文到其他处理器 |
IRQ_NESTED_THREAD | 表示该中断支持嵌套处理,当前Linux内核不支持中断嵌套 |
IRQ_NOTHREAD | 该中断不能通过thread处理 |
IRQ_PER_CPU_DEVID | 为PPI类型中断标识,具有该标识的中断有没有CPU独立变量percpu_dev_id,该类型中断通过request_precpu_irq函数进行注册 |
IRQ_IS_POLLED | 有此标识的中断会从suprious中断检测机制&core polling检测中剔除 |
IRQ_DISABLE_UNLAZY | 此标识时表示该中断控制器可能没有实现irq_disable函数,通过标识该中断desc中的中断状态来表示该中断disable |
中断要设置的或者反应中断对应的irqchip状态标识。
中断irqchip状态标识 | 意义 |
IRQD_ACTIVATED | 表示该中断为active状态,代表该中断的irqchip链路已经OK;是 IRQD_IRQ_STARTED 的前一个状态。 |
IRQD_WAKEUP_STATE | 表示该中断具有唤醒系统能力 |
IRQD_IRQ_DISABLED | 与IRQD_IRQ_MASKED表示一致:代表该中断被关闭或者掩住;对应的函数为irq_disable/irq_enable |
IRQD_IRQ_MASKED | 与IRQD_IRQ_DISABLED表示一致:代表该中断被关闭或者掩住;对应的函数为irq_mask/irq_unmask |
IRQD_IRQ_INPROGRESS | 该中断正在被处理,即正执行该中断的处理函数 |
IRQD_WAKEUP_ARMED | 支持IRQD_WAKEUP_STATE属性的中断在系统待机时,该中中断会被IRQD_WAKEUP_ARMED标记表示该中不可休眠、该中断可以唤醒系统, |
IRQD_AFFINITY_MANAGED | affinity 是由kernel自动管理 |
IRQD_IRQ_STARTED | 表示该中断是started |
至此,对内核中断的学习、介绍就告一段落。