全部博文(44)
分类: LINUX
2008-04-28 21:08:54
收到来自IOAPIC的中断消息后,LAPIC会将该中断交由CPU处理。和IOAPIC比较,LAPIC具有更多的寄存器以及更复杂的机制。但对于处理来自IOAPIC的中断消息,最重要的寄存器还是IRR、ISR以及EOI。
图1-4显示了x86平台上,IRR和ISR的格式:
图1-4 IRR、ISR构成
与PIC中的IRR、ISR不同的是,LAPIC的ISR、IRR均为256bit寄存器,对应x86平台上的256个中断vector,其中0~15为架构预留。
u IRR:功能和PIC的类似,代表LAPIC已接收中断,但还未交CPU处理。
u ISR:功能和PIC类似,代表CPU已开始处理中断,但还未完成。与PIC有所不同的是,当CPU正在处理某中断时,同类型中断如果发生,相应的IRR bit会再次置一(PIC模式下,同类型的中断被屏蔽);如果某中断被pending在IRR中,同类型的中断发生,则ISR中相应的bit被置一。这说明在APIC系统中,同一类型中断最多可以被计数两次(想不通什么意思?想不通就联想一下Linux可信信号)。超过两次时,不同架构处理不一样。对于Pentium系列CPU和P6架构,中断消息被LAPIC拒绝;对于Pentium4和Xeon系列,新来的中断和IRR中对应的bit重叠。
笔者:上图还有个TMR寄存器,即Trigger Mode Register,用于表示当前正在处理中断的触发模式。1为level,0为edge。对于level触发的中断,当软件写EOI时,会被广播到所有IOAPIC,消息中含有中断的vector,IOAPIC收到后检查自己的PRT表,把相应RTE的Remote IRR位清零。
对于IRR,x86 spec说:“当CPU准备处理中断时,IRR中最高优先级的bit被清零,ISR中对应bit被置一”。这样说不能算错,但至少不准确。根据《Multi-Processor Computer System With Interrupt Controllers Providing Remote Reading》一文中APIC的实现,只有对于edge触发的中断,ISR对应bit置一时IRR相应 bit才清零。对于level触发,IRR中的bit保留到软件写EOI,LAPIC收到IOAPIC发出的Level-deassert消息后才清零。
如果你仔细看了前面关于Remote IRR的介绍,此时一定会产生一个疑问。这里说同类型中断发生两次,会同时Pending在ISR和IRR的对应bit中。问题是,前面Remote IRR的异或逻辑保证了在写EOI前,新的中断不会发过来啊?呵呵,或许你想到答案了,这种pending两次的机制,只对edge触发中断有效。
与PIC一样,LAPIC同样需要软件写EOI来知会中断处理的完成,不同的是,LAPIC中的EOI是一个32bit寄存器,如下图:
图1-5 EOI寄存器格式
X86架构中,对EOI写0表示中断处理完成。由上述几个寄存器相互配合,一个典型的LAPIC中断处理流程是:
对于Pentium4、Xeon系列:
1. 通过中断消息的destination field字段,确定该中断是否是发送给自己的。
2. 如果该中断的delivery mode为NMI、SMI、INIT、ExtINT、SIPI,直接交由CPU处理。
3. 如果不为2)中所列的中断,置一IRR中相应的bit。
4. 当中断被pending到IRR或ISR中后,根据TPR和PPR寄存器,判断当前最高优先级的中断是否能发送给CPU处理。
5. 软件写EOI通知中断处理完成。如果中断为level触发,该EOI广播到所有IOAPIC(见前)。NMI、SMI、INIT、ExtINT、SIPI类型中断无需写EOI。
对于Pentium系列和P6架构:
1. 确定该中断是否由自己接收。如果是一个IPI,且delivery mode为lowest priority,LAPIC与其它LAPIC一起仲裁该IPI由谁接收。
2. 若该中断由自己接收,且类型为NMI、SMI、INIT、ExtINIT、INIT-deassert、或MP协议中的IPI中断(BIPI、FIPI、SIPI),直接交由CPU处理。
3. 将中断pending到IRR或ISR,若该已有相同的的中断pending到IRR和ISR上,拒绝该中断消息,并通知IOAPIC “retry”。
4. 同Pentium4、Xeon系列流程。
5. 同Pentium4、Xeon系列流程。
发出的中断消息是如何找到LAPIC的?
上面两个流程的第一步都是确定是否由自己接收中断。前面我们提到,RTE中的Destination Field用于指定由哪个APIC接收,并且分为Physical和Logical两种模式。对于LAPIC,两种模式有着不同的意义。
Physical模式:在该模式下,RTE中的Destination Field表示的是LAPIC ID。对于LAPIC来说,系统在RESET后,都会分配一个唯一的ID用作标识。在X86平台下,我们可以通过LAPIC ID寄存器得到它。图1-6为其格式:
图1-6 Local APIC ID
操作系统或BIOS,通常会使用LAPIC ID唯一的标识一个CPU。在Pentium系列和P6架构中,由于APIC BUS最多只支持15个LAPIC ID,即一个MP平台最多只能有15个CPU,RTE中的destination field表示LAPIC ID时只用了4bit,LAPIC ID寄存器也只有4bit可用。对于Pentium4和Xeon系列,APIC ID被扩展至8bit,最多支持255个LAPIC。系统RESET后,可以用CPUID指令(EAX写参数1,EBX的24~31即为返回的ID)获得默认的LAPIC ID。某些CPU允许软件更改默认的ID号,但通常来说,软件应该避免这样的行为。无论何时,CPUID指令返回的都是系统RESET后默认分配的LAPIC ID,即使当前的LAPIC ID寄存器已经被软件更改过。
关于APIC ID
APIC ID分为LAPIC ID和IOAPIC ID。前者唯一的标识系统中某个LAPIC,后者唯一标识某个IOAPIC。LAPIC ID前面已经介绍了,根据MP spec(Multiple Processor Specification,多处理器规范)规定,LAPIC ID必须唯一,但可以不连续。与LAPIC ID不同,IOAPIC ID在系统RESET后统一清零,操作系统或BIOS负责验证IOAPIC ID是否唯一,如果有冲突检测到,由操作系统或BIOS重新分配。重分配的原则是从系统中所有LAPIC ID后最小的数字开始分配。例如当前系统有两个LAPIC,ID为0、1,则分配IOAPIC ID应该从2开始。
需要注意的是,MP spec对APIC ID的分配规则只适用于系统用APIC BUS的情况。X86 spec说4bit的LAPIC ID可以表示15个CPU,还有一个哪儿去了?我想是留给IOAPIC了。
笔者:LAPIC ID是从0开始分配的,IOAPIC ID在系统RESET后自动清0。MP spec说“操作系统在探测到IOAPIC ID冲突时,有义务为它分配一个新的ID”。那相同的LAPIC ID和IOAPIC ID算不算冲突呢?算,但是对于Pentium4和Xeon系列使用前端总线通讯的系统,”It’s not an issue”。在此种系统中,LAPIC是要用ID参与前端总线竞争的,IOAPIC却不用,因为是北桥代理它竞争总线。IOAPIC ID只用于区分多个IOAPIC,它和LAPIC ID不在一个上下文。
APIC BUS下,IOAPIC ID要用于竞争总线,不能和LAPIC ID冲突,分配规则“题外话”中已说明。
当中断消息通过Physical模式发送时,LAPIC通过LAPIC ID来判断该中断是否由自己接收。
Logical模式:在该模式下,中断消息中的Destination Field包含的不是LAPIC ID,而是被称为MDA(Message Destination Address,消息目的地地址)的信息。此时,LAPIC需要两个额外的寄存器来判断自己是否为中断消息的目的地。它们是LDR和DFR。
LDR的格式如图1-7所示:
图1-7 LDR格式
LDR全称是Logical Destination Register,逻辑目的地寄存器。该寄存器包含一个8bit的逻辑APIC ID(注意区分,它和LAPIC ID不是一个东西),在Logical模式下用于和MDA匹配。LDR的格式由DFR指定,DFR如图8所示:
图1-8 DFR格式
DFR,Destination Format Register,目的地格式寄存器。该寄存器包含一个4bit的mode字段,用于指定LDR中的Logical APIC ID用何种方式与MDA匹配。通过这两个寄存器的配合,Logical模式又被分为了Flat与Cluster两种模式。
u Flat模式:DFR的model值为1111b,此时,LAPIC将MDA与LDR的logical APIC ID做位与,如结果不为0则接收中断。Logical APIC ID中每个bit代表一个LAPIC,故8bit最多代表8个CPU。
u Cluster模式:DFR的model值为0000b。我不得不说我可能会把你搞晕了,但实际上确实如此,x86太TMD复杂了。Cluster模式又分为两种模式:Flat Cluster模式和Hierarchical Cluster模式。
l Flat Cluster模式:该模式只支持P6架构和Pentium系列CPU,并假定所有APIC通过APIC BUS通讯。该模式将MDA编码为两个部分,高4bit为簇号,低4bit标识LAPIC在该簇内的ID(每个bit代表一个LAPIC,故一个簇最多有4个LAPIC)。与之对应,LDR的logical APIC ID也被编码成同样两个部分。
工作在该模式时,LAPIC先将MDA的高4bit和Logical APIC ID的高4bit比较,以确定自己是否是中断的目的簇。若是,将MDA的低4bit与Logical APIC ID的低4bit位与,若值不为0则接收中断。否则拒绝。
通过这种方法,高4bit的簇号可以表示15个簇,低4bit的ID可以代表簇内的4个CPU,最多可以支持60个CPU。但由于APIC BUS的限制,具体的说是APIC Arb ID(APIC仲裁ID)的限制,该模式最多只支持15个CPU。
l Hierarchical Cluster模式:支持P6架构和Pentium系列,以及Xeon、Pentium4系列。该模式通过为每个簇引入一个“簇管理器”,将Flat Cluster模式中平等的簇构成一个具有等级结构的分级网络,并最多支持60个CPU。这个话题太远了,相关spec没有更多资料,就不多做介绍了。
笔者:不要问我Hierarchical Cluster模式下,P6架构和Pentium系列如何突破APIC BUS对于15个CPU的限制的,我真的不知道。
“通过这种方法,高4bit的簇号可以表示15个簇”,实际上应该是0~15共16个簇,但MDA全1时为广播到所有LAPIC,笔者认为簇号1111b被预留给广播模式了。
对于Flat Cluster模式,我们举个例子吧(此例中,中断为Fix delivery mode。关于Lowest Prority的例子见后面内容)。假设有三个CPU的logical模式配置为(此时DFR的model值为0000b):CPU1的LDR值为0000 0001b,CPU2的LDR值为0001 0010b,CPU3的LDR值为0000 0100b,则CPU1、CPU3为一簇,CPU2为另一簇。IOAPIC发出一条中断消息,其Destination Mode为1,destination field值为0000 00001b。三个LAPIC收到该消息后,CPU1、CPU3通过destination field的高4bit判断出该消息目的地为本簇,再将自身Logical APIC ID的低4bit与destination field低4bit位与,最终CPU1接收该中断消息,CPU2、CPU3丢弃。
图1-9总结了中断消息的Destination Field字段用于寻址LAPIC的几种模式:
图1-9 Destination Field模式
、PPR、APR和Lowest priority —— 中断发给Whom?
RTE的delivery mode有一中模式为lowest priority,即最低优先级,它是Linux配置RTE时使用的模式。这里的最低优先级不是指中断的优先级,而是指将中断发送给destination field列出的CPU中,优先级最低的一个。如何决定一个CPU的优先级呢?x86平台依靠TPR寄存器和PPR寄存器。
TPR,task priority register,任务优先级寄存器,它确定当前CPU可处理什么优先级别范围内的中断。具有如下的格式:
图1-10 TPR寄存器
TPR寄存器接收0~15共16个值,对应16个CPU规定的中断优先级级别,值越大优先级越高。CPU只处理比TPR中值优先级别更高的中断。例如TPR中值为8,则级别小于等于8的中断被屏蔽(注意,屏蔽不代表拒绝,LAPIC接收它们,把它们pending到IRR中,但不交CPU处理。见前面LAPIC中断处理流程)。值15表示屏蔽所有中断;值0表示接收所有中断,噢,这也是Linux为TPR设置的默认值。注意,TPR是由软件读/写的,硬件不更改它。
的中断优先级级别
我们知道每个中断都有一个vector与之对应,x86平台共有256个vector,除去架构预留和被异常等占去的0~31号vector,可供外部中断使用的还有224个(噢,实际上只有223个,还要除去一个INT 80)。中断的优先级别由下列公式:
优先级别 = vector / 16
这里“/”是c语言的除,不是数学里的除号,所以我们是没有小数的。16~255号vector构成了1~15共15个优先级别,中断拥有2~15级别。对于同一个级别的中断,vector号越大的优先级越高。例如vector33、34都属于级别2,34的优先级就比33高。所以,对于8bit的vector,又可以划分成两部分,高4bit表示中断优先级别,低4bit表示该中断在这一级别中的位置。
噢,应该讲清楚了,记住,TPR的值增加1,将会屏蔽16个vector对应的中断。NMI、SMI、ExtINT、INIT、start-up delivery的中断不受TPR约束。
笔者:TPR,任务优先级寄存器 ,很容易的就让人和进程的优先级联想到一起。实际上,x86的spec也强调这里的“Task”代表操作系统中的进程、线程、任务、程序 ……不过,Linux的进程优先级和它没有关系。如果Linux的中断处理例程线程化了,如果Linux具有更强的实时性,如果Linux跟着Windows、Solaris学习,如果 …… Linux或许会把它和进程优先级挂上勾。现在嘛,Linux没有那样做。不过,APIC设计TPR的目的,真是给Task用的,期望进程切换时也会更新TPR值,这是APIC的历史文档告诉我们的。
TPR为8bit组成,从图中我们可以看到,0~15这16个值应该写到高4bit去。如果我们同时也写了低4bit会怎么样?从关于TPR的论述来看,CPU会忽略低4bit的值。
PPR,Processor priority register,处理器优先级寄存器。该寄存器决定当前CPU正在处理的中断的优先级级别,以确定一个Pending在IRR上的中断是否发送给CPU。与TPR不同,它的值由CPU写而不是软件写。PPR取值范围为[0,15],计算方式由下列伪代码描述:
If TPR[7:4] >= ISR[7:4] THEN
PPR[7:0] = TPR[7:0]
ELSE
PPR[7:4] = ISRV[7:4]
PPR[3:0] = 0
这里,ISRV[7:4]标识当前ISR中,最高优先级中断对应vector的高4bit,如前面所说,这代表了该中断的优先级级别。简而言之,取TPR和正在服务的最高优先级中断中,优先级级别高的。好了,都知道了,IRR中pending的中断,优先级级别必须高于PPR中值才会被发送给CPU处理,否则,继续等 ……
谁是Lowest Priority?
有了前面两个寄存器的论述,现在可以看看Lowest Priority是怎么决定的了。先来从Pentium4和Xeon系列说吧,它们要简单点(实际上更复杂,但因为资料少,我可以说的简单点。具体信息?去看mindshare那本讲前端总线的书吧,2000多页的英文读本)
对于这个系列,LAPIC和IOAPIC通过前端总线通讯。X86 spec对这个描述是:“芯片组负责从destination filed列出的CPU中,选出一个优先级最低的接收中断。如果是Pentium4,CPU会用一个特殊的总线周期(bus cycle)将每个CPU当前的任务优先级提交到总线,芯片组记录它们,并作为挑选最低优先级CPU的依据”。完了,够简单吧^_^
笔者:从spec来看,无论是Xeon还是Pentium4,都是由芯片组选最低优先级CPU。依据是什么?spec讲的不清楚。但对于pentium4,提到了“用一个特殊的bus cycle将当前各CPU的task priority提交到总线”。这个task priority具体指什么?spec也没说。但在TPR相关章节中有这么一句话:“注意,task priority用于决定CPU的仲裁优先级(见8.6.2.4,Lowest Priority Delivery Mode)”。虽然没有明说,但我们有理由认为,这里的task priority就是TPR的值。
P6架构的系统,依靠一个APR寄存器(Arbitration Priority Register,优先级仲裁寄存器)决定CPU优先级,其格式如图1-11:
图1-11 APR寄存器
APR值可由下列伪代码计算:
If ( (TPR[7:4] >= IRRV[7:4]) && (TPR[7:4] > ISRV[7:4]) )
APR[7:0] = TPR[7:0]
Else
{
APR[7:4] = max(TPR[7:4],IRRV[7:4],ISRV[7:4])
APR[3:0] = 0
}
上述代码表述了这么一个意思:当TRP的值大于IRR中最高优先级中断的优先级级别,并大于ISR中最高优先级中断的优先级级别时,APR等于TPR。否则,APR高4bit取TPR、IRRV、ISRV三者中优先级级别最高的;低4bit取0
对于P6架构和Pentium系列CPU,spec提到了一个Focus Processor的概念。如果一个中断A正在被某CPU1处理,或者pending在CPU1的IRR上,则称CPU1为中断A的Focus Processor。当系统中有Focus Processor存在时,Focus Processor可能(may)接收该中断,不管自身的优先级是多少。Xeon系列没有Focus Processor的概念。
笔者:关于Focus Processor是尽量按spec的原话翻译,当然,E文水平有限,翻译的不好。不过大概就是这个意思。Focus Processor在哪儿设定?伪中断寄存器(见后面的伪中断相关内容)。spec说:“If a focus processor exists, it may accept the interrupt, regardless of its priority”,spec用了一个may。这种不清不楚的用词太万恶了,让人完全搞不清楚它什么时候会may,什么时候又may not。不管怎样,Focus Processor给了我们这样一种暗示,即当系统中大量产生同一种中断时,该中断会被发送给同一个CPU处理。这样的好处是可以避免cache颠簸,缺点也十分明显:运行在该CPU上的进程可能饿死、中断处理过慢、系统中断负载不平衡 …… 我们又注意到,Xeon系列取消了这个功能,应该是顺应民心吧。既然已经有了PRT表,CPU就不应该再提供这种重定向中断的功能。
发了一堆牢骚,其实情况没那么糟糕。Focus Processor只对edge触发中断有效。而目前平台上大量使用的PCI是level触发的。为什么?想想Focus Processor的定义,再去看看前面关于同类型中断pending两次的说明吧。
我们来举一个Pentium4和Xeon系列优先级仲裁的例子,说明谁是Lowest Priority。假设有CPU1、CPU2、CPU3三个CPU,相应的TPR值为:TPR1=5、TPR2=6、TPR3=10,IOAPIC以lowest priority模式发送一条中断消息,该中断对应的优先级级别为3。则CPU1具有最低优先级,接收该中断。此时,该中断被pending到IRR中,但不会交给CPU处理,因为其优先级级别低于TPR值。
至此,IOAPIC和LAPIC相互配合的中断机制已经大体介绍完了,其中提到了一部分寄存器和它们的功用。APIC的其它内容在后面分析Linux实现时补充,一次把硬件内容写完,你看着烦,我写着也累,而且逻辑容易乱。