Read the fucking source code! --By 鲁迅
A picture is worth a thousand words. --By 高尔基
说明:
Kernel版本:4.14
ARM64处理器,Contex-A53,双核
使用工具:Source Insight 3.5, Visio
1. 概述【原创】Linux中断子系统(一)-中断控制器及驱动分析讲到了底层硬件GIC驱动,以及Arch-Specific的中断代码,本文将研究下通用的中断处理的过程,属于硬件无关层。当然,我还是建议你看一下上篇文章。
这篇文章会解答两个问题:
用户是怎么使用中断的(中断注册)?
外设触发中断信号时,最终是怎么调用到中断handler的(中断处理)?
2. 数据结构分析先来看一下总的数据结构,核心是围绕着struct irq_desc来展开:
Linux内核的中断处理,围绕着中断描述符结构struct irq_desc展开,内核提供了两种中断描述符组织形式:
打开CONFIG_SPARSE_IRQ宏(中断编号不连续),中断描述符以radix-tree来组织,用户在初始化时进行动态分配,然后再插入radix-tree中;
关闭CONFIG_SPARSE_IRQ宏(中断编号连续),中断描述符以数组的形式组织,并且已经分配好;
不管哪种形式,最终都可以通过linux irq号来找到对应的中断描述符;
图的左侧灰色部分,主要在中断控制器驱动中进行初始化设置,包括各个结构中函数指针的指向等,其中struct irq_chip用于对中断控制器的硬件操作,struct irq_domain与中断控制器对应,完成的工作是硬件中断号到Linux irq的映射;
图的上侧灰色部分,中断描述符的创建(这里指CONFIG_SPARSE_IRQ),主要在获取设备中断信息的过程中完成的,从而让设备树中的中断能与具体的中断描述符irq_desc匹配;
图中剩余部分,在设备申请注册中断的过程中进行设置,比如struct irqaction中handler的设置,这个用于指向我们设备驱动程序中的中断处理函数了;
中断的处理主要有以下几个功能模块:
硬件中断号到Linux irq中断号的映射,并创建好irq_desc中断描述符;
中断注册时,先获取设备的中断号,根据中断号找到对应的irq_desc,并将设备的中断处理函数添加到irq_desc中;
设备触发中断信号时,根据硬件中断号得到Linux irq中断号,找到对应的irq_desc,最终调用到设备的中断处理函数;
上述的描述比较简单,更详细的过程,往下看吧。
3. 流程分析 3.1 中断注册这一次,让我们以问题的方式来展开:
先来让我们回答第一个问题:用户是怎么使用中断的?
熟悉设备驱动的同学应该都清楚,经常会在驱动程序中调用request_irq()接口或者request_threaded_irq()接口来注册设备的中断处理函数;
request_irq()/request_threaded_irq接口中,都需要用到irq,也就是中断号,那么这个中断号是从哪里来的呢?它是Linux irq,它又是如何映射到具体的硬件设备的中断号的呢?
先来看第二个问题:设备硬件中断号到Linux irq中断号的映射
硬件设备的中断信息都在设备树device tree中进行了描述,在系统启动过程中,这些信息都已经加载到内存中并得到了解析;
驱动中通常会使用platform_get_irq或irq_of_parse_and_map接口,去根据设备树的信息去创建映射关系(硬件中断号到linux irq中断号映射);
【原创】Linux中断子系统(一)-中断控制器及驱动分析提到过struct irq_domain用于完成映射工作,因此在irq_create_fwspec_mapping接口中,会先去找到匹配的irq domain,再去回调该irq domain中的函数集,通常irq domain都是在中断控制器驱动中初始化的,以ARM GICv2为例,最终回调到gic_irq_domain_hierarchy_ops中的函数;
如果已经创建好了映射,那么可以直接进行返回linux irq中断号了,否则的话需要irq_domain_alloc_irqs来创建映射关系;
irq_domain_alloc_irqs完成两个工作:
针对linux irq中断号创建一个irq_desc中断描述符;
调用domain->ops->alloc函数来完成映射,在ARM GICv2驱动中对应gic_irq_domain_alloc函数,这个函数很关键,所以下文介绍一下;
gic_irq_domain_alloc函数如下:
gic_irq_domain_translate:负责解析出设备树中描述的中断号和中断触发类型(边缘触发、电平触发等);