0、说明

本文主要针对linux内核中断整个框架进行梳理,针对的是armv7架构,硬件平台是imx6ul,基于arm GIC控制器来分析。

GIC是arm公司设计使用的中断控制器,全称Global Interrupt Controller,对于arm32来说,中断发生后,被GIC捕获,之后上报CPU,CPU调用GIC中断处理函数,完成事件处理。此文主要弄清楚,内核启动后从中断向量的构建,到中断初始化完成以及中断处理的整个流程。

开始中断分析前,我们先列出以下中断基础认识。

中断的基础认识

  • 中断发生后,会终止当前应用的进行,因此需要保存应用现场,保存现场通过栈来实现
  • linux中断不支持嵌套
  • 中断处理函数越快越好,不然应用等太久,可能会卡顿
  • 中断可分为上半部/下半部,上半部不可打断处理必要事情,下半部处理耗时操作
  • 下半部通过软中断work内核线程(仅1个)来实现处理,下半部可参与内核调度
  • 线程化中断技术:对每个中断创建一个线程去处理下半部,在多核上效果明显
  • 中断流程:中断源->中断控制器->cpu
  • 中断分配器可指定某个中断给哪个核去处理

分析中断自然是分析中断控制器入手,arm使用gic中断控制器,下面开始由gic开始,首先来认识gic控制器。

1、GIC基础

参考文档-gic手册

IHI0048B_b_gic_architecture_specification.pdf:下载地址

Documentation – Arm Developer

GIC控制器组成

  • 分发器:Distributor

分配器集中所有中断源,确定每个中断的优先级,并为每个CPU接口将具有最高优先级的中断转发给接口,用于优先级屏蔽和抢占处理。

  • 处理器接口:CPU interfaces

每个CPU接口块为连接到GIC的处理器提供接口:

•启用向处理器发送中断请求的信号•确认中断 •指示中断处理完成 •为处理器设置中断优先级掩码 •定义处理器的抢占策略 •确定进程的最高优先级挂起中断

中断类别

  • SPIs(Shared Peripheral Interrupt):共享中断,多核共享,从32号硬件中断ID开始编号
  • PPIs(Private Peripheral Interrupt):单CPU私有中断,16-31
  • SGIs(Software-generated interrupt): 软件触发中断,CPU间通信,0-15

2、中断处理流程及相关数据结构

中断初始化流程

内核启动阶段会初始化IRQ,自然重点是初始化GIC,有GIC中断控制器驱动提供初始化函数,解析设备树获取GIC分发器和CPU接口寄存器基址,初始化中断总数,设置控制器domian,设置中断入口函数,初始化分发器、CPU接口寄存器,完成GIC初始化和函数设置。

中断发生的时候,set_handle_irq(gic_handle_irq)设置的gic_handle_irq会被调用,是中断处理的一个入口函数。

中断处理流程

中断产生后,从中断向量表开始,一直执行到该中断所对应的中断描述符的handle_irq函数。因此,描述一个中断的irq_desc描述符的初始化尤为重要。

  • 中断发生:调到中断向量表处执行,是所有中断总的入口
  • 保存现场,调用中断处理函数,最后恢复现场
  • 中断处理函数由gic提供,读取gic控制器获取发生的是哪个硬件中断 HW ID
  • 根据HW ID在irq_domain中找到 irq num,由此找到irq_desc,该中断描述符
  • irq_desc中存在中断处理函数,执行具体中断的处理

中断相关数据结构

中断描述符 irq_desc

描述一个中断,这个结构体存在的意义很清晰,中断描述符,用来描述一个中断,每一个虚拟中断号都对应一项irq_desc,可以根据virq找到这个irq_desc ,可以猜测里面一定有中断处理函数。irq_desc中重要的几个成员:

irq_data:中断的一些数据

handle_irq:发生这个中断的时候,最先调用的函数

action:处理链表,里面存放该中断最终调用的用户注册的处理函数

struct irq_desc {                                                                  struct irq_data     irq_data;                                                  unsigned int __percpu   *kstat_irqs;                                           irq_flow_handler_t  handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI                                                  irq_preflow_handler_t   preflow_handler;
#endif                                                                             struct irqaction    *action;    /* IRQ action list */                          unsigned int        status_use_accessors;                                      unsigned int        core_internal_state__do_not_mess_with_it;                  unsigned int        depth;      /* nested irq disables */                      unsigned int        wake_depth; /* nested wake enables */                      unsigned int        irq_count;  /* For detecting broken IRQs */                unsigned long       last_unhandled; /* Aging timer for unhandled count */      unsigned int        irqs_unhandled;                                            atomic_t        threads_handled;                                               int         threads_handled_last;                                              raw_spinlock_t      lock;                                                      struct cpumask      *percpu_enabled;
#ifdef CONFIG_SMP                                                                  const struct cpumask    *affinity_hint;                                        struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ                                                  cpumask_var_t       pending_mask;
#endif
#endif                                                                             unsigned long       threads_oneshot;                                           atomic_t        threads_active;                                                wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PM_SLEEP                                                             unsigned int        nr_actions;                                                unsigned int        no_suspend_depth;                                          unsigned int        cond_suspend_depth;                                        unsigned int        force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS                                                              struct proc_dir_entry   *dir;
#endif                                                                             int         parent_irq;                                                        struct module       *owner;                                                    const char      *name;
} ____cacheline_internodealigned_in_smp;   

中断数据 irq_data

一个irq_desc对应的中断信息。重要成员:

irq:中断虚拟中断号,IRQ number

hwirq:HW interrupt ID

irq_chip:操作控制器的处理函数

irq_domain:所属中断控制器的域

struct irq_data {                             u32         mask;                         unsigned int        irq;                  unsigned long       hwirq;                unsigned int        node;                 unsigned int        state_use_accessors;  struct irq_chip     *chip;                struct irq_domain   *domain;
#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY           struct irq_data     *parent_data;
#endif                                        void            *handler_data;            void            *chip_data;               struct msi_desc     *msi_desc;            cpumask_var_t       affinity;
};  

irq_chip

中断控制器操作函数抽象。重要成员:

struct irq_chip {                                                                                             const char  *name;                                                                                        unsigned int    (*irq_startup)(struct irq_data *data);                                                    void        (*irq_shutdown)(struct irq_data *data);                                                       void        (*irq_enable)(struct irq_data *data);                                                         void        (*irq_disable)(struct irq_data *data);                                                        void        (*irq_ack)(struct irq_data *data);                                                            void        (*irq_mask)(struct irq_data *data);                                                           void        (*irq_mask_ack)(struct irq_data *data);                                                       void        (*irq_unmask)(struct irq_data *data);                                                         void        (*irq_eoi)(struct irq_data *data);                                                            int     (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);               int     (*irq_retrigger)(struct irq_data *data);                                                          int     (*irq_set_type)(struct irq_data *data, unsigned int flow_type);                                   int     (*irq_set_wake)(struct irq_data *data, unsigned int on);                                          void        (*irq_bus_lock)(struct irq_data *data);                                                       void        (*irq_bus_sync_unlock)(struct irq_data *data);                                                void        (*irq_cpu_online)(struct irq_data *data);                                                     void        (*irq_cpu_offline)(struct irq_data *data);                                                    void        (*irq_suspend)(struct irq_data *data);                                                        void        (*irq_resume)(struct irq_data *data);                                                         void        (*irq_pm_shutdown)(struct irq_data *data);                                                    void        (*irq_calc_mask)(struct irq_data *data);                                                      void        (*irq_print_chip)(struct irq_data *data, struct seq_file *p);                                 int     (*irq_request_resources)(struct irq_data *data);                                                  void        (*irq_release_resources)(struct irq_data *data);                                              void        (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);                           void        (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);                             int     (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);       int     (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);        unsigned long   flags;
};     

irq_domain

描述一个中断控制器的域。

irq_domain_ops:提供设备树解析、中断号翻译能力

linear_revmap:中断号对应信息

struct irq_domain {                                                              struct list_head link;                                                       const char *name;                                                            const struct irq_domain_ops *ops;                                            void *host_data;                                                             unsigned int flags;                                                          /* Optional data */                                                          struct device_node *of_node;                                                 struct irq_domain_chip_generic *gc;
#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY                                              struct irq_domain *parent;
#endif                                                                           /* reverse map data. The linear map gets appended to the irq_domain */       irq_hw_number_t hwirq_max;                                                   unsigned int revmap_direct_max_irq;                                          unsigned int revmap_size;                                                    struct radix_tree_root revmap_tree;                                          unsigned int linear_revmap[];
};    

3、中断控制器层叠架构

硬件架构

硬件上可以包含很多中断控制器,形成一个上下级的结构。GIC作为一个顶层,与CPU进行交互并提供CPU中断接口。在GIC中断控制器之下存在其他控制器,GPIO就是一个典型的二级中断控制器。

我们知道在设备树中通过“interrupt-controller”修饰来说明自身是一个中断控制器。在设备树中搜索这个关键字,看看有哪些中断控制器:

//顶层GIC中断控制器
intc: interrupt-controller@00a01000 {compatible = "arm,cortex-a7-gic";#interrupt-cells = <3>;interrupt-controller;reg = <0x00a01000 0x1000>,<0x00a02000 0x100>;
};//gpc中断控制器,父亲是GIC中断控制器gpc: gpc@020dc000 {compatible = "fsl,imx6ul-gpc", "fsl,imx6q-gpc";reg = <0x020dc000 0x4000>;interrupt-controller;#interrupt-cells = <3>;interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;interrupt-parent = <&intc>;fsl,mf-mix-wakeup-irq = <0xfc00000 0x7d00 0x0 0x1400640>;};//GPIO中断控制器,父亲是gpc中断控制器gpio1: gpio@0209c000 {compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";reg = <0x0209c000 0x4000>;interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;gpio-controller;#gpio-cells = <2>;interrupt-controller;#interrupt-cells = <2>;interrupt-parent = <&gpc>;};//GPIO中断控制器,父亲是gpc中断控制器gpio2: gpio@020a0000 {compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";reg = <0x020a0000 0x4000>;interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;gpio-controller;#gpio-cells = <2>;interrupt-controller;#interrupt-cells = <2>;interrupt-parent = <&gpc>;};

驱动架构

硬件上既然分出了层叠的中断控制器,自然在驱动上也存在具有层叠关系的中断控制器,在数据结构一节我们知道每个中断控制器都有一个irq_domain来描述该层控制器的能力。在驱动上也将创建出多个中断控制器的irq_domain,并提供该控制器的能力。

以下三个中断控制器相关驱动程序,自然会创建出3个irq_domain.

drivers/irqchip/irq-gic.c
arch/arm/mach-imx/gpc.c
drivers/gpio/gpio-mxc.c

中断对上结构及内核API

中断对上结构

  • 一个中断源对应一个中断ID:一对一
  • 多个中断源对应一个中断ID:多对一

内核api

GPC的下一级产生中断对应GPC的N个中断,对于GPC来说,对下是1对1的关系。多个GPIO产生的中断对应GPC的1个中断,对于GPIO来说是一个多对1。

一级GIC:

二级GPC:

三级GPIO:

4、中断控制器驱动

4.1 设备树描述

GIC描述:

    intc: interrupt-controller@00a01000 {compatible = "arm,cortex-a7-gic";#interrupt-cells = <3>;interrupt-controller;reg = <0x00a01000 0x1000>,<0x00a02000 0x100>;};

imx6ul中加入了GPC用于电源管理,在中断章节也说明了所有常规中断都连接到了GPC中断控制器上,因此,设备树有如下GPC描述:

            gpc: gpc@020dc000 {compatible = "fsl,imx6ul-gpc", "fsl,imx6q-gpc";reg = <0x020dc000 0x4000>;interrupt-controller;#interrupt-cells = <3>;interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;interrupt-parent = <&intc>;fsl,mf-mix-wakeup-irq = <0xfc00000 0x7d00 0x0 0x1400640>;};

网卡

            fec1: ethernet@02188000 {compatible = "fsl,imx6ul-fec", "fsl,imx6q-fec";reg = <0x02188000 0x4000>;interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_ENET>,<&clks IMX6UL_CLK_ENET_AHB>,<&clks IMX6UL_CLK_ENET_PTP>,<&clks IMX6UL_CLK_ENET_REF>,<&clks IMX6UL_CLK_ENET_REF>;clock-names = "ipg", "ahb", "ptp","enet_clk_ref", "enet_out";stop-mode = <&gpr 0x10 3>;fsl,num-tx-queues=<1>;fsl,num-rx-queues=<1>;fsl,magic-packet;fsl,wakeup_irq = <0>;status = "disabled";};

3.2 内核中断向量表的初始化

中断向量表如下,和裸机相似。

arch/arm/kernel/entry-armv.S

    .section .vectors, "ax", %progbits
__vectors_start:W(b)    vector_rstW(b)    vector_undW(ldr)  pc, __vectors_start + 0x1000W(b)    vector_pabtW(b)    vector_dabtW(b)    vector_addrexcptnW(b)    vector_irqW(b)    vector_fiq         

内核启动阶段,从vector段拷贝向量表所有内容到分配的物理地址,并完成向量表到虚拟地址空间的映射关系,映射到虚拟地址0xffff0000位置。

start_kernel // init\main.csetup_arch(&command_line); // arch\arm\kernel\setup.cpaging_init(mdesc);    // arch\arm\mm\mmu.cdevicemaps_init(mdesc); // arch\arm\mm\mmu.cvectors = early_alloc(PAGE_SIZE * 2); // 1.分配新向量表early_trap_init(vectors);             // 2.从代码把向量表复制到新向量表// 3. 映射新向量表到虚拟地址0xffff0000/** Create a mapping for the machine vectors at the high-vectors* location (0xffff0000).  If we aren't using high-vectors, also* create a mapping at the low-vectors virtual address.*/map.pfn = __phys_to_pfn(virt_to_phys(vectors));map.virtual = 0xffff0000;map.length = PAGE_SIZE;#ifdef CONFIG_KUSER_HELPERSmap.type = MT_HIGH_VECTORS;#elsemap.type = MT_LOW_VECTORS;#endifcreate_mapping(&map);

gic中断处理

调用handle_irq函数处理中断流程,最终调用用户注册的中断函数。

handle_fasteoi_irqstruct irq_chip *chip = desc->irq_data.chip;mask_irq(desc);handle_irq_event(desc);//调用desc中的action函数,也就是request_irq时的函数cond_unmask_eoi_irq(desc, chip);

总结

中断你能想到什么?

第1:中断向量表,中断发生的时候最先跳转的地方,完成现场保护和恢复,并调用用户注册的中断处理函数。

第2:用户中断处理函数,这个是发生中断的时候要执行的目标内容,比如是接收串口数据,还是处理网卡收发,中断发生时候要做的具体内容。

第3:中断的初始化,完成前面两部的基础是完成中断的初始化,设置好中断向量表的位置,设置好中断的寄存器,比如使能中断。

第4:内核如何找到中断处理函数,根据HW ID到VIRQ NUM,再到irq_desc。

总结,因此中断无非就是维护好irq_desc,他描述了一个中断的信息,发生中断后,找到它,调用处理函数即可。

参考资料:

Linux中断子系统(一)-中断控制器及驱动分析

linux中断子系统(基于imx6ul arm32分析)相关推荐

  1. linux - 中断子系统分析(1) -- GICv3硬件架构

    目录 1. 参考文档 2. GIC Version History 3. Interrup Types 4. Interrupt State Machine 5. Programmer's Model ...

  2. Linux中断子系统(二)中断控制器GIC驱动分析

    Linux中断子系统(二)中断控制器GIC驱动分析 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一 ...

  3. Linux中断子系统

    首先感谢原文作者 LoyenWang 的分享,可以点击章节阅读原作者原文,或者查看本文的转载地址,再次感谢原作者分享,已经在公众号上征得作者同意. 说明: Kernel版本:4.14 ARM64处理器 ...

  4. linux 中断子系统

    linux 中断子系统 1,异常和中断 1.1 中断引入的必要性 1.2 同步异常 1.2.1 同步异常 1.2.2 同步异常的种类 1.3 异步异常 1.3.1 异步异常 1.3.2 异步异常的种类 ...

  5. Linux中断子系统(一)中断控制器GIC架构

    Linux中断子系统(一)中断控制器GIC架构 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一)中 ...

  6. Linux中断子系统 - softirq

    本文基于linux4.6.3内核版本代码来说明softirq机制,代码在kernel/softirq.c中,代码不算多也就近800行.在中断处理中,分上半部和下半部,有一些任务不是特别紧急的,没必要在 ...

  7. Linux中断子系统-通用框架处理

    背景 Kernel版本:4.14 ARM64处理器,Contex-A53,双核 使用工具:Source Insight 3.5, Visio 1. 概述 <Linux中断子系统(一)-中断控制器 ...

  8. 浅入浅出linux中断子系统

    浅入浅出linux中断子系统,如需深入,直接跳转重要参考章节. 什么是中断? 当CPU被某些信号触发,CPU暂停当前工作,转而处理信号的事件,简单的称它为中断,这个信号可以是系统外设的信号,也可能是芯 ...

  9. Linux中断子系统(四)之中断申请注册

    Linux中断子系统(四)之中断申请注册 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一)中断控制 ...

最新文章

  1. 【文章】本站收集与转载文章目录
  2. fsolve函数求解非线性方程
  3. 如何搭建modem编译环境
  4. 如何取回服务器上的文件网页设计,毕业设计(论文)-基于内容中心网络开发平台的文件分享精选.docx...
  5. Kubernetes基础学习(一)
  6. 在ATM取钱5000,查询余额却少了50000,怎么回事?
  7. sdram 时钟相位_零基础学FPGA (二十五)必会! 从静态时序分析到SDRAM时序收敛(下篇)...
  8. spring—事务控制
  9. javascript 点点滴滴01章 javascript的认知
  10. 全球顶尖科学杂志:阿里AI语音技术超越谷歌,可读懂人类潜藏意图
  11. 怎么创建计算机快捷方式到桌面两种方法,使用脚本主机创建Windows快捷方式 - Windows Client | Microsoft Docs...
  12. jsp springmvc 视图解析器_springMVC配置jsp/html视图解析器
  13. sqlserver 没有维护计划_制定数据库备份计划,不再为数据丢失闹心!
  14. kotlin函数_Kotlin函数
  15. jmeter 控制偏离_Jmeter 笔记(1)-安装 基本组件
  16. 《自然语言处理实战入门》---- 第1课:自然语言处理简介
  17. 7-14 然后是几点 (15 分)有时候人们用四位数字表示一个时间,比如 1106 表示 11 点零 6 分。现在,你的程序要根据起始时间和流逝的时间计算出终止时间。
  18. 录音转成文字的方法分享
  19. 用网线连接电脑传输文件
  20. NEC Programming Contest 2021(AtCoder Beginner Contest 229) B - Hard Calculation

热门文章

  1. Vue数据更新但页面没有更新的多种情况
  2. 屏幕的单位如何计算机,如何查看您的计算机显示器有多少英寸
  3. 防控青光眼的3大武器
  4. 幸福三月丨盐城北大青鸟女神节快乐!
  5. 顺序表前m和后n元素交换位置
  6. 不忘历史、维护中国海权
  7. 蓝/绿部署 VS 金丝雀部署
  8. 欧拉品牌升级:坚持女性品牌是最正确的决策
  9. 国科大首期“一生一芯”计划初见成效——本科生带着自己设计的处理器芯片毕业
  10. windowns下VS缺少头文件“unistd.h“的解决方案