gic_irq_domain_map:将硬件中断号和linux中断号绑定到一个结构中,也就完成了映射,此外还绑定了irq_desc结构中的其他字段,最重要的是设置了irq_desc->handle_irq的函数指针,这个最终是中断响应时往上执行的入口,这个是关键,下文讲述中断处理过程时还会提到;
根据硬件中断号的范围设置irq_desc->handle_irq的指针,共享中断入口为handle_fasteoi_irq,私有中断入口为handle_percpu_devid_irq;
上述函数执行完成后,完成了两大工作:
硬件中断号与Linux中断号完成映射,并为Linux中断号创建了irq_desc中断描述符;
数据结构的绑定及初始化,关键的地方是设置了中断处理往上执行的入口;
再看第一个问题:中断是怎么来注册的?
设备驱动中,获取到了irq中断号后,通常就会采用request_irq/request_threaded_irq来注册中断,其中request_irq用于注册普通处理的中断,request_threaded_irq用于注册线程化处理的中断;
在讲具体的注册流程前,先看一下主要的中断标志位:
#define IRQF_SHARED 0x00000080 //多个设备共享一个中断号,需要外设硬件支持 #define IRQF_PROBE_SHARED 0x00000100 //中断处理程序允许sharing mismatch发生 #define __IRQF_TIMER 0x00000200 //时钟中断 #define IRQF_PERCPU 0x00000400 //属于特定CPU的中断 #define IRQF_NOBALANCING 0x00000800 //禁止在CPU之间进行中断均衡处理 #define IRQF_IRQPOLL 0x00001000 //中断被用作轮训 #define IRQF_ONESHOT 0x00002000 //一次性触发的中断,不能嵌套,1)在硬件中断处理完成后才能打开中断;2)在中断线程化中保持关闭状态,直到该中断源上的所有thread_fn函数都执行完 #define IRQF_NO_SUSPEND 0x00004000 //系统休眠唤醒操作中,不关闭该中断 #define IRQF_FORCE_RESUME 0x00008000 //系统唤醒过程中必须强制打开该中断 #define IRQF_NO_THREAD 0x00010000 //禁止中断线程化 #define IRQF_EARLY_RESUME 0x00020000 //系统唤醒过程中在syscore阶段resume,而不用等到设备resume阶段 #define IRQF_COND_SUSPEND 0x00040000 //与NO_SUSPEND的用户共享中断时,执行本设备的中断处理函数request_irq也是调用request_threaded_irq,只是在传参的时候,线程处理函数thread_fn函数设置成NULL;
由于在硬件中断号和Linux中断号完成映射后,irq_desc已经创建好,可以通过irq_to_desc接口去获取对应的irq_desc;
创建irqaction,并初始化该结构体中的各个字段,其中包括传入的中断处理函数赋值给对应的字段;
__setup_irq用于完成中断的相关设置,包括中断线程化的处理:
中断线程化用于减少系统关中断的时间,增强系统的实时性;
ARM64默认开启了CONFIG_IRQ_FORCED_THREADING,引导参数传入threadirqs时,则除了IRQF_NO_THREAD外的中断,其他的都将强制线程化处理;
中断线程化会为每个中断都创建一个内核线程,如果中断进行共享,对应irqaction将连接成链表,每个irqaction都有thread_mask位图字段,当所有共享中断都处理完成后才能unmask中断,解除中断屏蔽;
3.2 中断处理当完成中断的注册后,所有结构的组织关系都已经建立好,剩下的工作就是当信号来临时,进行中断的处理工作。
来回顾一下【原创】Linux中断子系统(一)-中断控制器及驱动分析中的Arch-specific处理流程:
中断收到之后,首先会跳转到异常向量表的入口处,进而逐级进行回调处理,最终调用到generic_handle_irq来进行中断处理。
generic_handle_irq处理如下图:
generic_handle_irq函数最终会调用到desc->handle_irq(),这个也就是对应到上文中在建立映射关系的过程中,调用irq_domain_set_info函数,设置好了函数指针,也就是handle_fasteoi_irq和handle_percpu_devid_irq;
handle_fasteoi_irq:处理共享中断,并且遍历irqaction链表,逐个调用action->handler()函数,这个函数正是设备驱动程序调用request_irq/request_threaded_irq接口注册的中断处理函数,此外如果中断线程化处理的话,还会调用__irq_wake_thread()唤醒内核线程;