注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

FY

Johnson 's Blog

 
 
 

日志

 
 

【转】对Linux x86-64架构上硬中断的重新认识  

2017-02-28 12:42:41|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
http://www.lenky.info/archives/2013/03/2245

对于x86硬中断的概念,一直都落在理论的认识之上,直到这两天才(因某个问题)发现Linux的实现却并非如此,这里纠正一下(注意:Linux内核源码更新太快,一个说法的时效性太短,所以需注意我提到的香草内核版本,并且以x86-64架构为基准)。

以前的认识:Linux对硬中断(本文如无特殊说明,都是指普通意义上的可屏蔽硬件中断)的处理有优先级概念,高优先级硬中断可以打断低优先级硬中断。

重新认识:
1,对于x86硬件而言,在文档325462.pdf卷3章节6.9 PRIORITY AMONG SIMULTANEOUS EXCEPTIONS AND INTERRUPTS 提到一个表格,是指如果在同一时刻有多个异常或中断到达,那么CPU会按照一个指定的优先级顺序对它们进行响应和服务,而并不是我之前所想的判断是否可相互打断执行的高低级别。

2,对于Linux系统而言,硬中断之间并没有优先级的概念(虽然Intel CPU提供支持,请参考文档325462.pdf卷3章节10.8.3 Interrupt, Task, and Processor Priority),或者说优先级只有两个,全部关闭或全部开启,如下

Regardless of what the hardware might support, typical UNIX-type systems only make use of two levels: the minimum (all interrupts enabled) and the maximum (all interrupts disabled).

这意味着,如果一个硬中断处理函数正在执行,只要当前是处于开启中断的情况,那么此时发生的任何另外一个中断都可以打断当前处理函数,从而出现中断嵌套的情况。
值得注意的是,Linux提供对单个中断开启/禁止的接口(以软件实现为主,比如给对应中断描述符desc的status打上IRQ_DISABLED旗标):

1
2
de >voidde> de >disable_irq(unsigned de>de >intde> de >irq)de>
de >voidde> de >enable_irq(unsigned de>de >intde> de >irq)de>

下面来看看Linux的实际处理,其硬中断的一般处理流程(具体可见参考1、2、3以及源代码,以2.6.30.8为例):
硬件中断 –> common_interrupt –> do_IRQ –> handle_irq –> generic_handle_irq_desc
–> desc->handle_irq__do_IRQ

其中desc->handle_irq是一个回调函数,会根据不同中断类型(I/O APICMSI)有不同的指向,比如:handle_fasteoi_irq()、handle_edge_irq(),这可以参考设置函数ioapic_register_intr()和setup_msi_irq()。通过/proc/interrupts可以看到各个中断的具体类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
de >[root@localhost ~]# cat /proc/interrupts de>
de >           de>de >CPU0       CPU1       de>
de >  de>de >0:        888          0   IO-APIC-edge      timerde>
de >  de>de >1:         96        112   IO-APIC-edge      i8042de>
de >  de>de >3:          1          0   IO-APIC-edge    de>
de >  de>de >4:          1          0   IO-APIC-edge    de>
de >  de>de >7:          0          0   IO-APIC-edge      parport0de>
de >  de>de >8:          1          0   IO-APIC-edge      rtc0de>
de >  de>de >9:          0          0   IO-APIC-fasteoi   acpide>
de > de>de >12:        204          0   IO-APIC-edge      i8042de>
de > de>de >14:          0          0   IO-APIC-edge      ata_piixde>
de > de>de >15:     460641        900   IO-APIC-edge      ata_piixde>
de > de>de >16:          0          0   IO-APIC-fasteoi   Ensoniq AudioPCIde>
de > de>de >17:     118347          0   IO-APIC-fasteoi   ehci_hcd:usb1, ioc0de>
de > de>de >18:         70          0   IO-APIC-fasteoi   uhci_hcd:usb2de>
de > de>de >19:     115143          0   IO-APIC-fasteoi   eth0de>
de > de>de >24:          0          0   PCI-MSI-edge      pciehpde>
de > de>de >25:          0          0   PCI-MSI-edge      pciehpde>
de > de>de >26:          0          0   PCI-MSI-edge      pciehpde>
de > de>de >27:          0          0   PCI-MSI-edge      pciehpde>
de > de>de >28:          0          0   PCI-MSI-edge      pciehpde>
de >...de>

不管是desc->handle_irq还是__do_IRQ,它们都会调入到另外一个函数handle_IRQ_event()重点:从CPU接收到中断信号并开始处理,到这个函数为止,都是处于中断禁止状态。为什么?很简单,因为Intel开发者手册上是这么说的,在文档325462.pdf卷3章节6.8.1 Masking Maskable Hardware Interrupts提到:

When an interrupt is handled through an interrupt gate, the IF flag is automati-
cally cleared, which disables maskable hardware interrupts. (If an interrupt is
handled through a trap gate, the IF flag is not cleared.)

在CPU开始处理一个硬中断到进入函数handle_IRQ_event()为止的这段时间里,因为处于中断禁止状态,所以不会出现被其它中断打断的情况。但是,在进入到函数handle_IRQ_event()后,立马有了这么两句:

1
2
3
4
5
6
7
8
de >irqreturn_t handle_IRQ_event(unsigned de>de >intde> de >irq, de>de >structde> de >irqaction *action)de>
de >{de>
de >    de>de >irqreturn_t ret, retval = IRQ_NONE;de>
de >    de>de >unsigned de>de >intde> de >status = 0;de>
 
de >    de>de >ifde> de >(!(action->flags & IRQF_DISABLED))de>
de >        de>de >local_irq_enable_in_hardirq();de>
de >...de>

函数local_irq_enable_in_hardirq()的定义如下:

1
2
3
4
5
de >#ifdef CONFIG_LOCKDEPde>
de ># define local_irq_enable_in_hardirq()  do { } while (0)de>
de >#elsede>
de ># define local_irq_enable_in_hardirq()  local_irq_enable()de>
de >#endifde>

宏CONFIG_LOCKDEP用于表示当前是否开启内核Lockdep功能,这是一个调试功能,用于检测潜在的死锁类风险,如果开启,那么函数local_irq_enable_in_hardirq()为空,即继续保持中断禁止状态,为什么Lockdep功能需要保持中断禁止待后文再述,这里考虑一般情况,即不开启Lockdep功能,那么执行函数local_irq_enable_in_hardirq()就会开启中断。
看函数handle_IRQ_event()里的代码,如果没有带上IRQF_DISABLED旗标,那么就会执行函数local_irq_enable_in_hardirq(),从而启用中断。旗标IRQF_DISABLED可在利用函数request_irq()注册中断处理回调时设置,比如:

1
2
de >ifde> de >(request_irq(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED,de>
de >     de>de >"BFIN_UART_RX"de>de >, uart)) {de>

如果没有设置,那么到函数handle_IRQ_event()这里的代码后,因为中断已经开启,当前中断的后续处理就可能被其它中断打断,从而出现中断嵌套的情况。

3,如果新来的中断类型与当前正在执行的中断类型相同,那么会暂时挂起。主要实现代码在函数__do_IRQ()(handle_fasteoi_irq()、handle_edge_irq()类似)内:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
de >/*de>
de > de>de >* If the IRQ is disabled for whatever reason, we cannotde>
de > de>de >* use the action we have.de>
de > de>de >*/de>
de >action = NULL;de>
de >ifde> de >(likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {de>
de >    de>de >action = desc->action;de>
de >    de>de >status &= ~IRQ_PENDING; de>de >/* we commit to handling */de>
de >    de>de >status |= IRQ_INPROGRESS; de>de >/* we are handling it */de>
de >}de>
de >desc->status = status;de>
 
de >/*de>
de > de>de >* If there is no IRQ handler or it was disabled, exit early.de>
de > de>de >* Since we set PENDING, if another processor is handlingde>
de > de>de >* a different instance of this same irq, the other processorde>
de > de>de >* will take care of it.de>
de > de>de >*/de>
de >ifde> de >(unlikely(!action))de>
de >    de>de >gotode> de >out;de>

逻辑很简单,如果当前中断被禁止(IRQ_DISABLED)或正在执行(IRQ_INPROGRESS),那么goto cot,所以同种类型中断不会相互嵌套。

4,从这个补丁开始,Linux内核已经全面禁止硬中断嵌套了,即从2.6.35开始,默认就是:

run the irq handlers with interrupts disabled.

因为这个补丁,所以旗标IRQF_DISABLED没用了,mainline内核在逐步删除它

我仔细检查了一下,对于2.6.34以及以前的内核,如果要合入这个补丁,那么有略微影响的主要是两个慢速驱动,分别为rtc-twl4030和twl4030-usb,需要按照类似开启Lockdep功能一样:

1
2
3
4
5
6
7
de >#ifdef CONFIG_LOCKDEPde>
de >/* WORKAROUND for lockdep forcing IRQF_DISABLED on us, whichde>
de > de>de >* we don't want and can't tolerate.  Although it might bede>
de > de>de >* friendlier not to borrow this thread context...de>
de > de>de >*/de>
de >local_irq_enable();de>
de >#endifde>

进行主动启用中断。还有另个一个慢速驱动IDE,其驱动中调用的是函数local_irq_enable_in_hardirq(),即它在开启Lockdep功能的情况下并没有明确要求启用中断,所以它应该不受补丁合入影响。嘛,我只是理论分析研究一下,仅供参考,如有风险,请实际操作者自行承担,:)。其它请看参考4,5,6。

PS:
像我这个个性,遇到问题不搞清楚总是不爽,所以昨天周五晚弄到将近12点半,而我平常基本都是10点半睡觉的,而今天搞到现在(16:13),囧,还没去交网费了,要断网了,妹的。

参考:
1,Linux下386中断处理
2,Linux中断基础构架
3,linux源码entry_32.S中interrupt数组的分析
4,http://lwn.net/Articles/321663/
5,http://lwn.net/Articles/380931/
6,http://thread.gmane.org/gmane.linux.kernel/801267

  评论这张
 
阅读(9)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017