linux中断子系统(基于imx6ul arm32分析)
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分析)相关推荐
- linux - 中断子系统分析(1) -- GICv3硬件架构
目录 1. 参考文档 2. GIC Version History 3. Interrup Types 4. Interrupt State Machine 5. Programmer's Model ...
- Linux中断子系统(二)中断控制器GIC驱动分析
Linux中断子系统(二)中断控制器GIC驱动分析 备注: 1. Kernel版本:5.4 2. 使用工具:Source Insight 4.0 3. 参考博客: Linux中断子系统(一 ...
- Linux中断子系统
首先感谢原文作者 LoyenWang 的分享,可以点击章节阅读原作者原文,或者查看本文的转载地址,再次感谢原作者分享,已经在公众号上征得作者同意. 说明: Kernel版本:4.14 ARM64处理器 ...
- linux 中断子系统
linux 中断子系统 1,异常和中断 1.1 中断引入的必要性 1.2 同步异常 1.2.1 同步异常 1.2.2 同步异常的种类 1.3 异步异常 1.3.1 异步异常 1.3.2 异步异常的种类 ...
- Linux中断子系统(一)中断控制器GIC架构
Linux中断子系统(一)中断控制器GIC架构 备注: 1. Kernel版本:5.4 2. 使用工具:Source Insight 4.0 3. 参考博客: Linux中断子系统(一)中 ...
- Linux中断子系统 - softirq
本文基于linux4.6.3内核版本代码来说明softirq机制,代码在kernel/softirq.c中,代码不算多也就近800行.在中断处理中,分上半部和下半部,有一些任务不是特别紧急的,没必要在 ...
- Linux中断子系统-通用框架处理
背景 Kernel版本:4.14 ARM64处理器,Contex-A53,双核 使用工具:Source Insight 3.5, Visio 1. 概述 <Linux中断子系统(一)-中断控制器 ...
- 浅入浅出linux中断子系统
浅入浅出linux中断子系统,如需深入,直接跳转重要参考章节. 什么是中断? 当CPU被某些信号触发,CPU暂停当前工作,转而处理信号的事件,简单的称它为中断,这个信号可以是系统外设的信号,也可能是芯 ...
- Linux中断子系统(四)之中断申请注册
Linux中断子系统(四)之中断申请注册 备注: 1. Kernel版本:5.4 2. 使用工具:Source Insight 4.0 3. 参考博客: Linux中断子系统(一)中断控制 ...
最新文章
- 【文章】本站收集与转载文章目录
- fsolve函数求解非线性方程
- 如何搭建modem编译环境
- 如何取回服务器上的文件网页设计,毕业设计(论文)-基于内容中心网络开发平台的文件分享精选.docx...
- Kubernetes基础学习(一)
- 在ATM取钱5000,查询余额却少了50000,怎么回事?
- sdram 时钟相位_零基础学FPGA (二十五)必会! 从静态时序分析到SDRAM时序收敛(下篇)...
- spring—事务控制
- javascript 点点滴滴01章 javascript的认知
- 全球顶尖科学杂志:阿里AI语音技术超越谷歌,可读懂人类潜藏意图
- 怎么创建计算机快捷方式到桌面两种方法,使用脚本主机创建Windows快捷方式 - Windows Client | Microsoft Docs...
- jsp springmvc 视图解析器_springMVC配置jsp/html视图解析器
- sqlserver 没有维护计划_制定数据库备份计划,不再为数据丢失闹心!
- kotlin函数_Kotlin函数
- jmeter 控制偏离_Jmeter 笔记(1)-安装 基本组件
- 《自然语言处理实战入门》---- 第1课:自然语言处理简介
- 7-14 然后是几点 (15 分)有时候人们用四位数字表示一个时间,比如 1106 表示 11 点零 6 分。现在,你的程序要根据起始时间和流逝的时间计算出终止时间。
- 录音转成文字的方法分享
- 用网线连接电脑传输文件
- NEC Programming Contest 2021(AtCoder Beginner Contest 229) B - Hard Calculation