【原创】Linux中断子系统(二)-通用框架处理

背景

  • Read the fucking source code! –By 鲁迅
  • A picture is worth a thousand words. –By 高尔基

说明:

  1. Kernel版本:4.14
  2. ARM64处理器,Contex-A53,双核
  3. 使用工具:Source Insight 3.5, Visio

  4. 概述

【原创】Linux中断子系统(一)-中断控制器及驱动分析讲到了底层硬件GIC驱动,以及Arch-Specific的中断代码,本文将研究下通用的中断处理的过程,属于硬件无关层。当然,我还是建议你看一下上篇文章。

这篇文章会解答两个问题:

  1. 用户是怎么使用中断的( 中断注册)?
  2. 外设触发中断信号时,最终是怎么调用到中断handler的( 中断处理)?

  3. 数据结构分析

先来看一下总的数据结构,核心是围绕着 struct irq_desc来展开:

【原创】Linux中断子系统(二)-通用框架处理
  • 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 irqactionhandler的设置,这个用于指向我们设备驱动程序中的中断处理函数了;

中断的处理主要有以下几个功能模块:

  1. 硬件中断号到 Linux irq中断号的映射,并创建好 irq_desc中断描述符;
  2. 中断注册时,先获取设备的中断号,根据中断号找到对应的 irq_desc,并将设备的中断处理函数添加到 irq_desc中;
  3. 设备触发中断信号时,根据硬件中断号得到 Linux irq中断号,找到对应的 irq_desc,最终调用到设备的中断处理函数;

上述的描述比较简单,更详细的过程,往下看吧。

  1. 流程分析

3.1 中断注册

这一次,让我们以问题的方式来展开:
先来让我们回答第一个问题:用户是怎么使用中断的?

  1. 熟悉设备驱动的同学应该都清楚,经常会在驱动程序中调用 request_irq()接口或者 request_threaded_irq()接口来注册设备的中断处理函数;
  2. request_irq()/request_threaded_irq接口中,都需要用到 irq,也就是中断号,那么这个中断号是从哪里来的呢?它是 Linux irq,它又是如何映射到具体的硬件设备的中断号的呢?

先来看第二个问题:设备硬件中断号到 Linux irq中断号的映射

【原创】Linux中断子系统(二)-通用框架处理
  • 硬件设备的中断信息都在设备树 device tree中进行了描述,在系统启动过程中,这些信息都已经加载到内存中并得到了解析;
  • 驱动中通常会使用 platform_get_irqirq_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函数如下:

【原创】Linux中断子系统(二)-通用框架处理
  • 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

上述函数执行完成后,完成了两大工作:

  1. 硬件中断号与Linux中断号完成映射,并为Linux中断号创建了 irq_desc中断描述符;
  2. 数据结构的绑定及初始化,关键的地方是设置了中断处理往上执行的入口;

再看第一个问题:中断是怎么来注册的?

设备驱动中,获取到了 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的用户共享中断时,执行本设备的中断处理函数

【原创】Linux中断子系统(二)-通用框架处理
  • 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处理流程:

【原创】Linux中断子系统(二)-通用框架处理
  • 中断收到之后,首先会跳转到异常向量表的入口处,进而逐级进行回调处理,最终调用到 generic_handle_irq来进行中断处理。

generic_handle_irq处理如下图:

【原创】Linux中断子系统(二)-通用框架处理
  • generic_handle_irq函数最终会调用到 desc->handle_irq(),这个也就是对应到上文中在建立映射关系的过程中,调用 irq_domain_set_info函数,设置好了函数指针,也就是 handle_fasteoi_irqhandle_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

【原创】Linux中断子系统(二)-通用框架处理
  • __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 总结

中断的处理,总体来说可以分为两部分来看:

  1. 从上到下:围绕 irq_desc中断描述符建立好连接关系,这个过程就包括:中断源信息的解析(设备树),硬件中断号到Linux中断号的映射关系、 irq_desc结构的分配及初始化(内部各个结构的组织关系)、中断的注册(填充 irq_desc结构,包括handler处理函数)等,总而言之,就是完成静态关系创建,为中断处理做好准备;
  2. 从下到上,当外设触发中断信号时,中断控制器接收到信号并发送到处理器,此时处理器进行异常模式切换,并逐步从处理器架构相关代码逐级回调。如果涉及到中断线程化,则还需要进行中断内核线程的唤醒操作,最终完成中断处理函数的执行。

欢迎关注个人公众号,不定期分享Linux内核机制文章

【原创】Linux中断子系统(二)-通用框架处理

Original: https://www.cnblogs.com/LoyenWang/p/13052677.html
Author: LoyenWang
Title: 【原创】Linux中断子系统(二)-通用框架处理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585967/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • JavaScript json&ajax

    本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。 博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。 博主…

    Linux 2023年6月13日
    090
  • 什么是视频编码?编解码器和压缩技术

    想知道什么是视频编码,为什么它很重要? 在本文中,我们将研究编码、编解码器和压缩技术的过程。这包括什么使得一个推荐的编解码器,虽然是取决于情况。它还涵盖了为什么某些伪影,与压缩有关…

    Linux 2023年6月7日
    0112
  • 常见题目

    这几天有朋友反映给小编说让多发点关于面试的文章,小编深知从事IT行业的难处,跳槽多,加班多,薪资不乐观,大多数朋友都想找新的工作,进入一个好的公司,今天小编就给大家带来了C语言面试…

    Linux 2023年6月13日
    093
  • 华为IPv6 GRE隧道

    IPv6 over IPv4 GRE封装隧道 实验目标: 该实验参考了华为官网案例配置https://support.huawei.com/enterprise/zh/doc/ED…

    Linux 2023年6月7日
    078
  • webshell查杀的方法

    从您反馈的情况看,是您的网站被植入了webshel后门文件导致的。您可以先对当前的服务器做下快照备份,然后将您的网站代码拷贝到本地进行下webshell查杀:https://www…

    Linux 2023年5月28日
    0107
  • Canal-1.1.5部署安装

    canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议 MySQL master 收到 dum…

    Linux 2023年6月13日
    088
  • USB转双串口产品设计-RS485串口

    基于USB转2路串口芯片CH342,可以为各类主机扩展出2个独立的串口。CH342芯片支持使用操作系统内置的CDC串口驱动,也支持使用厂商提供的VCP串口驱动程序,可支持Windo…

    Linux 2023年6月7日
    0105
  • MySQL安装和配置

    一、关闭防火墙并安装epel源 1、关闭selinux ①修改selinux的配置文件 [root@localhost ~]# vim /etc/selinux/config SE…

    Linux 2023年6月7日
    087
  • ​Linux知识点总结(内附思维导图,建议收藏)

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    0102
  • IDEA生成带参数和返回值注释

    步骤说明 打开IDEA进入点击左上角 – 文件 – 设置 – 编辑器 – 活动模板 新建活动模板 填写模板文本 编辑变量 添加变量表…

    Linux 2023年6月6日
    0105
  • 【实测】Python 和 C++ 下字符串查找的速度对比

    最近在备战一场算法竞赛,语言误选了 Python ,无奈只能着手对常见场景进行语言迁移。而字符串查找的场景在算法竞赛中时有出现。本文即对此场景在 Python 和竞赛常用语言 C+…

    Linux 2023年6月13日
    0107
  • redis 安装和命令

    转自:https://blog.csdn.net/hzlarm/article/details/99432240 在线安装: 查看使用的默认端口: 查看redis服务器的状态: 重…

    Linux 2023年5月28日
    087
  • linux学习之shell脚本

    【实验目的】‍ ‌ 通过本实验练习,使学生了解常用SHELL的编程特点,掌握SHELL 程序设计的基础知识。对SHELL程序流程控制、SHELL程序的运行方式、bash程序的调试方…

    Linux 2023年5月27日
    0126
  • 使用Kotlin协程配合Retrofit发送请求

    Retrofit2.6开始增加了对Kotlin协程的支持,可以通过suspend函数进行异步调用。本文简单介绍一下使用Kotlin协程配合Retrofit使用,发起网络请求。 ap…

    Linux 2023年6月8日
    0105
  • Spring Boot中异步调用的正确使用姿势(详解)【转】

    介绍:异步请求的处理。除了异步请求,一般上我们用的比较多的应该是异步调用。通常在开发过程中,会遇到一个方法是和实际业务无关的,没有紧密性的。比如记录日志信息等业务。这个时候正常就是…

    Linux 2023年6月8日
    0101
  • 【小记】腾讯云 Linux 虚拟机如何正确修改 hosts 文件

    如果直接修改 /etc/hosts 文件,重启后设置会丢失还原,原因是腾讯云虚拟机默认使用了 Cloud-Init 进行初始化操作。 参见:https://cloud.tencen…

    Linux 2023年6月13日
    089
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球