全部博文(44)
分类: LINUX
2008-04-28 21:12:58
Linux的中断子系统
从这章开始,我们来看看Linux中断子系统的实现。
、Pin、GSI、Vector
这几个概念经常把人搅晕掉,下面的内容经常要用到它们,还是先说清楚为妙。
IRQ是PIC时代的产物,由于ISA设备通常是连接到固定的PIC管脚,所以说一个设备的IRQ实际是指它连接的PIC管脚号。IRQ暗示着中断优先级,例如IRQ0比IRQ3有着更高的优先级。当前进到APIC时代后,或许是出于习惯,人们仍习惯用IRQ表示一个设备的中断号,但对于16以下的IRQ,它们可能不再与IOAPIC的管脚对应,例如PIT此时接的是2号管脚。
Pin是管脚号,通常它表示IOAPIC的管脚(前面说了,PIC时代我们用IRQ)。Pin的最大值受IOAPIC管脚数限制,目前取值范围是[0,23]。
GSI是ACPI引入的概念,全称是Global System Interrupt。它为系统中每个中断源指定一个唯一的中断号。下图展示了GSI的思想:
图2-1
图2-1中有3个IOAPIC:IOAPIC0~2。IOAPIC0有24个管脚,其GSI base为0,每个管脚的GSI=GSI base + pin,故IOAPIC0的GSI范围为[0~23]。IOAPIC2有16个管脚,GSI base为24,GSI范围为[24,39],依次类推。ACPI要求ISA的16个IRQ应该被identify map到GSI的[0,15]。
IRQ和GSI在APIC系统中常常被混用,实际上对15以上的IRQ,它和GSI相等。我们在谈到IRQ时,一定要注意它所处的语境。
Vector是CPU的概念,是中断在IDT表中的索引。每个IRQ(或GSI)都对应一个Vector。在PIC模式下,IRQ对应的vector=start vector + IRQ;在APIC模式下,IRQ/GSI的vector由操作系统分配。
中断探测
操作系统如何知道平台上硬件中断系统的情况?BIOS告诉它的。表,是BIOS报告系统资源的通常方式。MP spec定义了MP table,ACPI规范定义了MADT(Multiple APIC Description Table)。虽然构建方式不一样,但从OS看来两者大同小异,都支持LAPIC entry、IOAPIC entry,以及其它几个与它们相关的entry。这些entry描述了平台APIC硬件的情况。下面根据Linux两条不同的中断探测路径,介绍如何根据MP table或MADT构建中断子系统的主要数据结构。
关键数据结构
Linux最初实现的是MP spec相关规范,故定义一组符合MP spec的数据结构来管理平台硬件。ACPI被引入后,虽然汇报硬件资源的方式变了,但仍可以使用MP spec的数据结构来管理。Linux的ACPI解析路径,实际上是用ACPI的方式读表,填充MP spec的数据结构。由此看来,我们只要理解MP spec数据结构就可以了,和中断相关的有如下几个:
笔者:这些结构体和MP spec规定的各个entry结构是相同的,下面表格将两者列在一起说明,以后就不单独画MP spec各entry的结构表了。
struct mpc_config_ioapic:对应MP spec的IOAPIC entry ,各字段如下:
结构体成员 |
MP spec字段 |
长度(byte) |
描述 |
mpc_type |
TYPE |
1 |
类型,2(IOAPIC ENTRY) |
mpc_apicid |
IOAPIC ID |
1 |
IOAPIC ID |
mpc_apicver |
IOAPIC Version |
1 |
IOAPIC版本 |
mpc_flags |
IOAPIC Flags:EN |
1 |
最低bit有效,其余bit预留 0:IOAPIC disabled 1:IOAPIC enabled |
mpc_apicaddr |
IOAPIC Address |
4 |
该IOAPIC基地址 |
表2-1 IOAPIC entry和struct mpc_config_ioapic
内核用mp_ioapics全局数组存放系统中所有IOAPIC对应的struct mpc_config_ioapic,nr_ioapics为IOAPIC数量。目前Linux最多支持64个IOAPIC,当数量超过时可以配置MAX_IO_APICS宏扩大限制。
struct mpc_config_intsrc:对应MP spec的IO interrupt entry,代表各个中断源(一个IOAPIC管脚连接一个中断源)各字段如下:
结构体成员 |
MP spec字段 |
长度(byte) |
描述 |
mpc_type |
TYPE |
1 |
3(IOAPIC interrupt) |
mpc_irqtype |
INTERRUPT TYPE |
1 |
INT:APIC模式中断 NMI:不可屏蔽中断 SMI:系统管理中断 ExtINT:vector由PIC提供。如果8259用作外部PIC,即PIC INTR接APIC的一个管脚,vector由8259提供 |
mpc_irqflag |
PO:位于2byte 0bit EL:位于2byte 1bit |
2 |
PO:中断管脚极性 EL:触发模式 |
mpc_srcbus |
SOURCE BUS ID |
1 |
产生中断的总线 |
mpc_srcbusirq |
SOURCE BUS IRQ |
1 |
相对于产生中断的总线,该中断管脚代表的中断号。例如0,相对于ISA总线即IRQ0 |
mpc_dstapic |
DESTINATION IOAPIC ID |
1 |
该中断连接的IOAPIC。0xff标识连接到所有的IOAPIC |
mpc_dstirq |
DESTINATION IOAPIC INTN# |
1 |
连接到IOAPIC的管脚号 |
表2-2 IO interrupt entry和mpc_config_intsrc
不同总线中断共享情况:如果两个IO interrupt entry的destination(IOAPIC ID、INTN#)相同,则共享。例如IPCI-device1/INTA#和ISA-IRQ2如对应相同的destination,则两者共享,接同样的IOAPIC和INTN#。
中断共享和中断丢失
我们不讨论Level触发的情况,因为意义不大,它可以共享且不会丢失中断。
Edge触发,问题就比较多了。它通常对应ISA设备,一个常见的说法是“ISA设备不能共享中断”,但也有人说这是个“superstition”(内核邮件列表曾有关于它的争论)。
从能找到的资料来看,标准的ISA设备是不支持中断共享的,但问题是有很多非标准实现的存在。从操作系统的角度看,支持ISA中断共享并非难事,我们完全可以使用level触发同样的方式去处理edge触发(例如Linux的实现)。Edge共享的一个难题是,当一个设备在另一个设备的中断正在被处理时发起中断,该中断会丢失(或者说要等到另一个设备再发起中断时才会处理),因为此时该中断管脚通常是被屏蔽的。为了解决这个问题,一些非标准的实现会用“re-trigger while in service”、“sequencing of interrupt-generating”加以保证。前者会在一个中断服务完后再触发一次中断;后者会保证中断事件顺序产生,即处理完一个再产生一个。无论怎样,我们尽量在操作系统中支持ISA中断共享,但应该认识到edge中断是不应该共享的。
从理论上分析,edge中断会丢失,Edge中断丢失出现在中断管脚被mask,设备又发起中断时。对于PIC,通过IMR屏蔽中断管脚时,到来的中断仍然会pending在IRR中,unmask中断会提交给CPU。对于APIC,操作系统在进行中断处理时不mask中断(至少Linux如此),尽力保证不丢中断。
内核用mp_irqs全局数组记录系统中所有的mpc_config_intsrc。目前支持的最大数——MAX_IRQ_SOURCES为256。
Struct mpc_config_lintsrc:定义和mpc_config_intsrc一样。只是mpc_dstapic代表LAPIC,且mpc_dstirq只能取值0或1。