中断子系统
1 中断处理流程

2 RefLink
|
link |
comment |
|
从硬件的角度, 描述了中断控制器的架构, 以及多个中断控制器级联的情况 |
|
|
描述了如何建立一个map, 以便把HW interrupt ID转换为IRQ Number, 这个过程与dts中的interrupt-controller和interrupts+interrupt-parent属性有关系. |
|
|
描述了如何初始化irq_desc[]这个池子, 每个irq_desc描述了与这个中断相关的所有信息, 包含High level irq event handler, specific handler等 |
|
|
描述High level irq event handler的处理细节 |
|
|
描述驱动如何调用request_threaded_irq注册处理函数. |
|
|
描述从硬件产生中断信号的那一刻开始, 整个中断处理的宏观流程. 参见上图. |
|
|
描述了ARM使用的中断控制器Generic Interrupt Controller的硬件结构以及该硬件的驱动代码的详细分析. |
|
|
描述softirq的触发和处理逻辑. 介绍了各种context, 如中断上下文等.
softirq是指在用软件设定一个标记, 然后在硬件中断函数退出或softirqd这个内核线程中检测该标记, 如果发现标记被设定, 则执行相应的处理函数. 也就是说softirq并未触发任何硬件中断.
softirq一般用于中断底半部, 与tasklet相比, 它可能被多个CPU同时调度运行, 但tasklet只有被某一个CPU调度运行完毕后才可能被另一个CPU调度运行. 也就是说在SMP系统中, 可能存在某个sotfirq_handler的多个实例, 但只会存在某个tasklet_handler的一个实例.
softirq和tasklet其实都是运行在中断上下文中. 它们存在的目的是什么? 在中断的顶半部中, 我们一般会先关中断, 然后处理, 然后开中断, 如果处理过程很耗时, 那这段时间由于中断被关闭, 我们可能丢失一些硬件中断. softirq/tasklet存在的意义就是把耗时的处理过程从顶半部提取出来放到softirq_handler中, 让顶半部先开中断, 然后在中断流程退出时在调度softirq_handler.
另外, softirq_handler在某CPU上运行时, 不可能被另外一个softirq_handler抢占. 也就说某个CPU上, softirq_handler的执行是串行化的. tasklet也一样. |
|
|
tasklet是底半部的另一种机制.
它本质上是基于softirq机制构建的, 目的是为了方便驱动工程师更便捷的编写底半部代码.
但实际来看, 这种便捷性没有太大的必要. Steven Rostedt试图删除所有的tasklet, 把那些要求实时处理的放到softirq里面, 把那些不要实时处理的放入workqueue或handler threaded (http://lwn.net/Articles/239484/), 不过这个patch始终未能进入main line |
|
|
workqueue |
http://www.wowotech.net/irq_subsystem/workqueue.html http://www.wowotech.net/irq_subsystem/cmwq-intro.html http://www.wowotech.net/irq_subsystem/alloc_workqueue.html http://www.wowotech.net/irq_subsystem/queue_and_handle_work.html |
3 FAQs
3.1 Proc interface
通过/proc/interrupts 和/proc/stat 可查看中断产生的次数. 详见: 10.2.1. The /proc Interface
3.2 使能/禁止中断的API
ref : http://www.makelinux.net/ldd3/chp-10-sect-7.shtml
- enable_irq(irq_number) / disable_irq(irq_number) 可以使能/禁止某一个中断.
- local_irq_enable() / local_irq_disable() / local_irq_save(unsigned long flags) / local_irq_restore(unsigned long flags) 可使能/禁止某个CPU上的所有中断
- 但是不能禁止所有CPU上的所有中断
3.3 中断处理过程中开关中断的时机与中断抢占嵌套问题
Note : 下来描述个依据上面文章的个人总结, 并未在实际代码中验证, 后续发现错误在修正.
- interrupt controller通过IRQ assert CPU CPU读取HW interrupt ID , 然后ack controller
- 这段时间, 由于CPU还未ACK, 因此IRQ line一直处于assert状态, 也就是说CPU侧只收到了一次中断信号.
- 在CPU ACK之前, 如果有更高优先级的中断产生, 则中断控制器直接把interrupt ID修改为高优先级中断的ID, 这样当CPU读取HW interrupt ID时, 读到的就是高优先级中断的ID.
那这种情况下低优先级的那次中断是不是丢失了呢? 不会, interrupt controller会记录这个状态, 当CPU处理完高优先级的中断时, 会发送EOI给controller, controller收到EOI后, 会再次assert CPU, 这次上报的是之前那个低优先级中断的ID.
- cpuack后 顶半部执行完毕
- CPU ack后, controller会deassert. 此时如果有新的中断产生, 并且中断没有被软件禁止, controller是有能力重新assert CPU的.
- 对于电平触发的中断, CPU在ack的同时会mask这个中断, mask意味着暂时禁止了本中断(必须mask的原因是电平信号是一直有效的, 如果不mask, 一旦ack, controller马上又会assert CPU, 这显然是一次误报) . 当中断顶半部执行完毕, 会重新unmask该中断. 换句话说, 对于电平触发中断, 在整个顶半部的执行过程中, 本中断都是被禁止的.
- 对于边沿触发的中断, CPU只会ack, 这就意味着ack后, 顶半部执行过程中, 如果再次产生了同一个中断, controller会重新assert CPU. 不过由于前一个中断还没有EOI, 因此controller可能会assert 另一个CPU (例如CPU2). CPU2在处理此次中断时, 由于检测到CPU1正在处理同一个中断, 因此CPU2只是会设置一下pending标志, 然后ack & mask该中断, 注意此时mask了. CPU1在处理完第一次中断事件的顶半部后, 会检测pending标志, 如果被设置, 则CPU1会再执行一次顶半部, 等第二次顶半部执行完毕后, unmask中断并退出. 换句话说, 对于边沿触发中断, 在整个顶半部执行过程中, 中断可能且仅可能被多触发一次. 详见High level irq event handler.
- 不管是哪种触发方式, 当CPU ack后, 在顶半部的执行过程中, 都是可能被高优先级的中断抢占的.
- 底半部执行过程中
设计底半部的一个原因就是为了尽快打开中断, 以免中断丢失. 因此在底半部的处理过程中是可能被同优先级或高优先级的中断打断的.
有几个不同点是:
- 对于tasklet, 即使由于短时间内产生了多个中断导致它被调度了多次, 最终运行的时候也只有一次. 而且当它已经开始运行后, 由于中断而在此被调度时, 即使在SMP的情况下, 也只有等到第一次运行完毕后才能执行后面的调度. 也就是说tasklet在整个系统内部都是串行处理的.
- 对于softirq, 在同一个CPU上, 它是串行运行的. 但是在SMP的情况下, 它可能在多个CPU上并行运行.
- 对于workqueue和threaded handler, 被调度几次就会运行几次, 即使在同一个CPU上有可能存在多个实例并行运行.
3.4 中断处理函数(handler)的注意事项
http://www.makelinux.net/ldd3/chp-10-sect-3.shtml : 10.3. Implementing a Handler
- A handler can't transfer data to or from user space, because it doesn't execute in the context of a process
- Handlers also cannot do anything that would sleep, such as calling wait_event, allocating memory with anything other than GFP_ATOMIC, or locking a semaphore.
- Finally, handlers cannot call schedule.
3.5 中断中不能睡眠的真正原因

3.6 为什么中断执行过程越快越好
这里说的越快越好是针对顶半部.
因为在顶半部执行过程中, 电平触发类型的中断是会被mask的, 边沿触发类型的只会多接收一次, 然后也会被mask. 如果顶半部执行时间过长, 那么就可能丢失中断.
另外, 中断的优先级比任何进程的优先级都要高, 在中断执行过程中, 是不会进行进程调度的, 如果其耗时过长, 会影响调度的实时性, 进而会让用户觉得卡顿.

浙公网安备 33010602011771号