【原创】Linux中断子系统(二)-通用框架处理
背景
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
:负责解析出设备树中描述的中断号和中断触发类型(边缘触发、电平触发等);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()
唤醒内核线程;handle_percpu_devid_irq
:处理per-CPU中断处理,在这个过程中会分别调用中断控制器的处理函数进行硬件操作,该函数调用action->handler()
来进行中断处理;
来看看中断线程化处理后的唤醒流程吧__handle_irq_event_percpu->__irq_wake_thread
:
__handle_irq_event_percpu->__irq_wake_thread
将唤醒irq_thread
中断内核线程;irq_thread
内核线程,将根据是否为强制中断线程化对函数指针handler_fn
进行初始化,以便后续进行调用;irq_thread
内核线程将while(!irq_wait_for_interrupt)
循环进行中断的处理,当满足条件时,执行handler_fn
,在该函数中最终调用action->thread_fn
,也就是完成了中断的处理;irq_wait_for_interrupt
函数,将会判断中断线程的唤醒条件,如果满足了,则将当前任务设置成TASK_RUNNING
状态,并返回0,这样就能执行中断的处理,否则就调用schedule()
进行调度,让出CPU,并将任务设置成TASK_INTERRUPTIBLE
可中断睡眠状态;
3.3 总结
中断的处理,总体来说可以分为两部分来看:
- 从上到下:围绕
irq_desc
中断描述符建立好连接关系,这个过程就包括:中断源信息的解析(设备树),硬件中断号到Linux中断号的映射关系、irq_desc
结构的分配及初始化(内部各个结构的组织关系)、中断的注册(填充irq_desc
结构,包括handler处理函数)等,总而言之,就是完成静态关系创建,为中断处理做好准备; - 从下到上,当外设触发中断信号时,中断控制器接收到信号并发送到处理器,此时处理器进行异常模式切换,并逐步从处理器架构相关代码逐级回调。如果涉及到中断线程化,则还需要进行中断内核线程的唤醒操作,最终完成中断处理函数的执行。
欢迎关注个人公众号,不定期分享Linux内核机制文章
【原创】Linux中断子系统(二)-通用框架处理的更多相关文章
- 【原创】Linux中断子系统(三)-softirq和tasklet
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- linux中断子系统:中断号的映射与维护初始化mmap过程
本文均属自己阅读源代码的点滴总结.转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2009@163.com 写在前沿: 好久好久没有静下心来整理一些东西了 ...
- Linux中断子系统:级联中断控制器驱动
Linux中断子系统 Linux中断子系统是个很大的话题,如下面的思维导图所示,包含硬件.驱动.中断上半部.中断下半部等等.本文着眼于中断控制器(PIC),特别是级联中断控制器驱动部分,对驱动的设计和 ...
- 【原创】Linux中断子系统(一)-中断控制器及驱动分析
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Linux时间子系统(二) 软件架构
一.前言 本文的主要内容是描述内核时间子系统的软件框架.首先介绍了从旧的时间子系统迁移到新的时间子系统的源由,介绍新的时间子系统的优势.第三章汇整了时间子系统的相关文件以及内核配置.最后描述各种内核配 ...
- 【原创】中断子系统-ARM GPIO中断处理流程
目录 第一部分 GIC中断控制器的注册 1. GIC驱动分析 2.GIC驱动流程分析 第二部分 device node转化为platform_device 第三部分:platform_device注册 ...
- 【原创】Linux中断子系统(四)-Workqueue
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- linux中断子系统
参考引用:http://www.wowotech.net/sort/irq_subsystem wowotech:一个很好的linux技术博客. 一.概述 目的 kernel管理硬件设备的方式:轮询. ...
- Linux usb子系统(二) _usb-skeleton.c精析
"./drivers/usb/usb-skeleton.c"是内核提供给usb设备驱动开发者的海量存储usb设备的模板程序, 程序不长, 通用性却很强,十分经典, 深入理解这个文件 ...
随机推荐
- Error: ER_BAD_FIELD_ERROR: Unknown column 'xxx' in 'where clause'
node中调用mysql模块读写时候,如果直接插入字符串: connection.query('SELECT * from users WHERE name=' + data.name , call ...
- vs2015 cppunit配置及使用
目录 第一步 第二步 第三步 编译生成lib库 使用 calculator类测试 代码部分 第一步 下载源代码 http://sourceforge.net/projects/cppunit/file ...
- NPOI导入excel为datatable (xls xlsx xlsm)
使用NPOI导入导出Excel(xls/xlsx)数据到DataTable中 http://www.cnblogs.com/songrun/p/3547738.html NPOI 2.0教程 – 自动 ...
- 机器学习决策树ID3算法,手把手教你用Python实现
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习专题的第21篇文章,我们一起来看一个新的模型--决策树. 决策树的定义 决策树是我本人非常喜欢的机器学习模型,非常直观容易理解 ...
- zoj3995 fail树
//给n个串 求两个串的(最长)公共后缀, 如果这个后缀是1个串的前缀 ,那么++ans,求ans/* 对所有串建立AC自动机,那么若前缀ii 是前缀jj 的后缀,说明ii 是Fail树上jj 的祖先 ...
- 小BUG大原理:重写WebMvcConfigurationSupport后SpringBoot自动配置失效
一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小 BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小 BUG 我了解到不少 ...
- 线程池 & 线程调度
线程池1. 第四种获取线程的方法:线程池,一个 ExecutorService,它使用可能的几个池线程之 一执行每个提交的任务, 通常使用 Executors 工厂方法配置. 2. 线程池可以解决两个 ...
- 【转】roc曲线与auc值
https://www.cnblogs.com/gatherstars/p/6084696.html ROC的全名叫做Receiver Operating Characteristic,其主要分析工具 ...
- word修改页眉使本页的页眉与别的页不一样
关键回答:双击要修改的页的页眉,word顶端的工具栏“页面设置”当中的“链接到前一个”选项不要选中,即可修改本页页眉使之与别的页不一样. 详见:https://iask.sina.com.cn/b/2 ...
- Python数据科学利器
每个工具都带有用来创造它的那种精神. -- 海森堡<物理学和哲学> Anaconda Anaconda是一个python的科学计算发行版,其附带了一大批常用的数据科学包,不用再使用pip安 ...