Linux中断(interrupt)子系统之三:中断流控处理层【转】
转自:http://blog.csdn.net/droidphone/article/details/7489756
1. 中断流控层简介
早期的内核版本中,几乎所有的中断都是由__do_IRQ函数进行处理,但是,因为各种中断请求的电气特性会有所不同,又或者中断控制器的特性也不同,这会导致以下这些处理也会有所不同:
- 何时对中断控制器发出ack回应;
- mask_irq和unmask_irq的处理;
- 中断控制器是否需要eoi回应?
- 何时打开cpu的本地irq中断?以便允许irq的嵌套;
- 中断数据结构的同步和保护;
/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!
/*****************************************************************************************************/
为此,通用中断子系统把几种常用的流控类型进行了抽象,并为它们实现了相应的标准函数,我们只要选择相应的函数,赋值给irq所对应的irq_desc结构的handle_irq字段中即可。这些标准的回调函数都是irq_flow_handler_t类型:
- typedef void (*irq_flow_handler_t)(unsigned int irq,
- struct irq_desc *desc);
目前的通用中断子系统实现了以下这些标准流控回调函数,这些函数都定义在:kernel/irq/chip.c中,
- handle_simple_irq 用于简易流控处理;
- handle_level_irq 用于电平触发中断的流控处理;
- handle_edge_irq 用于边沿触发中断的流控处理;
- handle_fasteoi_irq 用于需要响应eoi的中断控制器;
- handle_percpu_irq 用于只在单一cpu响应的中断;
- handle_nested_irq 用于处理使用线程的嵌套中断;
驱动程序和板级代码可以通过以下几个API设置irq的流控函数:
- irq_set_handler();
- irq_set_chip_and_handler();
- irq_set_chip_and_handler_name();
以下这个序列图展示了整个通用中断子系统的中断响应过程,flow_handle一栏就是中断流控层的生命周期:

图1.1 通用中断子系统的中断响应过程
2. handle_simple_irq
该函数没有实现任何实质性的流控操作,在把irq_desc结构锁住后,直接调用handle_irq_event处理irq_desc中的action链表,它通常用于多路复用(类似于中断控制器级联)中的子中断,由父中断的流控回调中调用。或者用于无需进行硬件控制的中断中。以下是它的经过简化的代码:
- void
- handle_simple_irq(unsigned int irq, struct irq_desc *desc)
- {
- raw_spin_lock(&desc->lock);
- ......
- handle_irq_event(desc);
- out_unlock:
- raw_spin_unlock(&desc->lock);
- }
3. handle_level_irq
- void
- handle_level_irq(unsigned int irq, struct irq_desc *desc)
- {
- raw_spin_lock(&desc->lock);
- mask_ack_irq(desc);
- if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
- goto out_unlock;
- ......
- if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))
- goto out_unlock;
- handle_irq_event(desc);
- if (!irqd_irq_disabled(&desc->irq_data) && !(desc->istate & IRQS_ONESHOT))
- unmask_irq(desc);
- out_unlock:
- raw_spin_unlock(&desc->lock);
- }
虽然handle_level_irq对电平中断的流控进行了必要的处理,因为电平中断的特性:只要没有ack irq,中断线会一直有效,所以我们不会错过某次中断请求,但是驱动程序的开发人员如果对该过程理解不透彻,特别容易发生某次中断被多次处理的情况。特别是使用了中断线程(action->thread_fn)来响应中断的时候:通常mask_ack_irq只会清除中断控制器的pending状态,很多慢速设备(例如通过i2c或spi控制的设备)需要在中断线程中清除中断线的pending状态,但是未等到中断线程被调度执行的时候,handle_level_irq早就返回了,这时已经执行过unmask_irq,设备的中断线pending处于有效状态,中断控制器会再次发出中断请求,结果是设备的一次中断请求,产生了两次中断响应。要避免这种情况,最好的办法就是不要单独使用中断线程处理中断,而是要实现request_threaded_irq()的第二个参数irq_handler_t:handler,在handle回调中使用disable_irq()关闭该irq,然后在退出中断线程回调前再enable_irq()。假设action->handler没有屏蔽irq,以下这幅图展示了电平中断期间IRQ_PROGRESS标志、本地中断状态和触发其他CPU的状态:

| 状态 | 红色 | 绿色 |
|---|---|---|
| IRQ_PROGRESS | TRUE | FALSE |
| 是否允许本地cpu中断 | 禁止 | 允许 |
| 是否允许该设备再次触发中断(可能由其它cpu响应) | 禁止 | 允许 |
4. handle_edge_irq
- if (unlikely(irqd_irq_disabled(&desc->irq_data) ||
- irqd_irq_inprogress(&desc->irq_data) || !desc->action)) {
- if (!irq_check_poll(desc)) {
- desc->istate |= IRQS_PENDING;
- mask_ack_irq(desc);
- goto out_unlock;
- }
- }
- desc->irq_data.chip->irq_ack(&desc->irq_data);
从上面的分析可以知道,处理中断期间,另一次请求可能由另一个cpu响应后挂起,所以在处理完本次请求后还要判断IRQS_PENDING标志,如果被置位,当前cpu要接着处理被另一个cpu“委托”的请求。内核在这里设置了一个循环来处理这种情况,直到IRQS_PENDING标志无效为止,而且因为另一个cpu在响应并挂起irq时,会mask irq,所以在循环中要再次unmask irq,以便另一个cpu可以再次响应并挂起irq:
- do {
- ......
- if (unlikely(desc->istate & IRQS_PENDING)) {
- if (!irqd_irq_disabled(&desc->irq_data) &&
- irqd_irq_masked(&desc->irq_data))
- unmask_irq(desc);
- }
- handle_irq_event(desc);
- } while ((desc->istate & IRQS_PENDING) &&
- !irqd_irq_disabled(&desc->irq_data));
IRQS_PENDING标志会在handle_irq_event中清除。
| 状态 | 红色 | 绿色 |
| IRQ_PROGRESS | TRUE | FALSE |
| 是否允许本地cpu中断 | 禁止 | 允许 |
| 是否允许该设备再次触发中断(可能由其它cpu响应) | 禁止 | 允许 |
| 是否处于中断上下文 | 处于中断上下文 | 处于进程上下文 |
由图4.1也可以看出,在处理软件中断(softirq)期间,此时仍然处于中断上下文中,但是cpu的本地中断是处于打开状态的,这表明此时嵌套中断允许发生,不过这不要紧,因为重要的处理已经完成,被嵌套的也只是软件中断部分而已。这个也就是内核区分top和bottom两个部分的初衷吧。
5. handle_fasteoi_irq
- void
- handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
- {
- raw_spin_lock(&desc->lock);
- if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
- if (!irq_check_poll(desc))
- goto out;
- ......
- if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
- desc->istate |= IRQS_PENDING;
- mask_irq(desc);
- goto out;
- }
- if (desc->istate & IRQS_ONESHOT)
- mask_irq(desc);
- preflow_handler(desc);
- handle_irq_event(desc);
- out_eoi:
- desc->irq_data.chip->irq_eoi(&desc->irq_data);
- out_unlock:
- raw_spin_unlock(&desc->lock);
- return;
- ......
- }
此外,内核还提供了另外一个eoi版的函数:handle_edge_eoi_irq,它的处理类似于handle_edge_irq,只是无需实现mask和unmask的逻辑。
6. handle_percpu_irq
- void
- handle_percpu_irq(unsigned int irq, struct irq_desc *desc)
- {
- struct irq_chip *chip = irq_desc_get_chip(desc);
- kstat_incr_irqs_this_cpu(irq, desc);
- if (chip->irq_ack)
- chip->irq_ack(&desc->irq_data);
- handle_irq_event_percpu(desc, desc->action);
- if (chip->irq_eoi)
- chip->irq_eoi(&desc->irq_data);
- }
7. handle_nested_irq
该函数用于实现其中一种中断共享机制,当多个中断共享某一根中断线时,我们可以把这个中断线作为父中断,共享该中断的各个设备作为子中断,在父中断的中断线程中决定和分发响应哪个设备的请求,在得出真正发出请求的子设备后,调用handle_nested_irq来响应中断。所以,该函数是在进程上下文执行的,我们也无需扫描和执行irq_desc结构中的action链表。父中断在初始化时必须通过irq_set_nested_thread函数明确告知中断子系统:这些子中断属于线程嵌套中断类型,这样驱动程序在申请这些子中断时,内核不会为它们建立自己的中断线程,所有的子中断共享父中断的中断线程。
- void handle_nested_irq(unsigned int irq)
- {
- ......
- might_sleep();
- raw_spin_lock_irq(&desc->lock);
- ......
- action = desc->action;
- if (unlikely(!action || irqd_irq_disabled(&desc->irq_data)))
- goto out_unlock;
- irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
- raw_spin_unlock_irq(&desc->lock);
- action_ret = action->thread_fn(action->irq, action->dev_id);
- raw_spin_lock_irq(&desc->lock);
- irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
- out_unlock:
- raw_spin_unlock_irq(&desc->lock);
- }
Linux中断(interrupt)子系统之三:中断流控处理层【转】的更多相关文章
- Linux中断(interrupt)子系统
Linux中断(interrupt)子系统之一:中断系统基本原理 Linux中断(interrupt)子系统之二:arch相关的硬件封装层 Linux中断(interrupt)子系统之三:中断流控处理 ...
- Linux中断(interrupt)子系统之四:驱动程序接口层 & 中断通用逻辑层【转】
转自:http://blog.csdn.net/droidphone/article/details/7497787 在本系列文章的第一篇:Linux中断(interrupt)子系统之一:中断系统基本 ...
- Linux中断(interrupt)子系统之一:中断系统基本原理
这个中断系列文章主要针对移动设备中的Linux进行讨论,文中的例子基本都是基于ARM这一体系架构,其他架构的原理其实也差不多,区别只是其中的硬件抽象层.内核版本基于3.3.虽然内核的版本不断地提升,不 ...
- Linux中断(interrupt)子系统之一:中断系统基本原理【转】
转自:http://blog.csdn.net/droidphone/article/details/7445825 这个中断系列文章主要针对移动设备中的Linux进行讨论,文中的例子基本都是基于AR ...
- Linux中断(interrupt)子系统之二:arch相关的硬件封装层【转】
转自:http://blog.csdn.net/droidphone/article/details/7467436 Linux的通用中断子系统的一个设计原则就是把底层的硬件实现尽可能地隐藏起来,使得 ...
- linux中断子系统:中断号的映射与维护初始化mmap过程
本文均属自己阅读源代码的点滴总结.转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2009@163.com 写在前沿: 好久好久没有静下心来整理一些东西了 ...
- Intel 80x86 Linux Kernel Interrupt(中断)、Interrupt Priority、Interrupt nesting、Prohibit Things Whthin CPU In The Interrupt Off State
目录 . 引言 . Linux 中断的概念 . 中断处理流程 . Linux 中断相关的源代码分析 . Linux 硬件中断 . Linux 软中断 . 中断优先级 . CPU在关中断状态下编程要注意 ...
- Linux中断子系统:级联中断控制器驱动
Linux中断子系统 Linux中断子系统是个很大的话题,如下面的思维导图所示,包含硬件.驱动.中断上半部.中断下半部等等.本文着眼于中断控制器(PIC),特别是级联中断控制器驱动部分,对驱动的设计和 ...
- Linux时间子系统之三:时间的维护者:timekeeper
专题文档汇总目录 Notes: 原文地址:Linux时间子系统之三:时间的维护者:timekeeper 本系列文章的前两节讨论了用于计时的时钟源:clocksource,以及内核内部时间的一些表示方法 ...
随机推荐
- Sprint最后一天
界面流程: 数据库里的信息: 还存在的问题: 1:选择包车城市时:下面的界面没对应到包车城市类型 2:看不到个人订票信息
- 开源中文分词框架分词效果对比smartcn与IKanalyzer
一.引言: 中文分词一直是自然语言处理的一个痛处,早在08年的时候,就曾经有项目涉及到相关的应用(Lunce构建全文搜索引擎),那时的痛,没想到5年后的今天依然存在,切分效果.扩展支持.业务应用等方面 ...
- Android-TabLayout设置内容宽度以及下划线宽度
默认图: 效果图: 项目中使用到需要像今日头条那种实现顶部横向滑动标题功能,本人项目中使用TabLayout+ViewPager实现,但是,实现后默认的TabLayout间距特别大,并且下划线,文字大 ...
- Apache ActiveMQ 学习一
Apache ActiveMQ 5.8.0 Java 7 support (compiled with jdk6 and validated with jdk7) apache-activemq-5. ...
- DevOps简介
DevOps 是一个完整的面向IT运维的工作流,以 IT 自动化以及持续集成(CI).持续部署(CD)为基础,来优化程式开发.测试.系统运维等所有环节. DevOps的概念 DevOps一词的来自于D ...
- 阿里Java编码规范
详细,全面 很不错 阿里 Java编码规范
- NOIP2017逛公园(park)解题报告
park作为今年noipday1最后一道题还是相比前面几道题还是有点难度的 首先你可以思考一下,第一天dp不见了,再看一下这题,有向图,看起来就比较像一个dp,考虑dp方程,首先肯定有一维是到哪个节点 ...
- 洛谷 P4389 付公主的背包 解题报告
P4389 付公主的背包 题目背景 付公主有一个可爱的背包qwq 题目描述 这个背包最多可以装\(10^5\)大小的东西 付公主有\(n\)种商品,她要准备出摊了 每种商品体积为\(V_i\),都有\ ...
- NO.6: 若不想编译器提供自动生成的函数,就应该明确拒绝
1.为驳回编译器自动生成函数的技能,可把这些函数的声明放入private,如果是继承类型可把base class的这些函数声明private,可在编译期间得到警告
- JDK环境变量的配置1
JDK环境变量的配置 ... 1.安装完JDK后配置环境变量 计算机→属性→高级系统设置→高级→环境变量 2.系统变量→新建 JAVA_HOME 变量 .变量值填写jdk的安装目录(我的安装目录 ...