Chinaunix首页 | 论坛 | 博客
  • 博客访问: 376839
  • 博文数量: 44
  • 博客积分: 2060
  • 博客等级: 上尉
  • 技术积分: 528
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-17 20:50
文章分类
文章存档

2011年(1)

2010年(28)

2008年(15)

分类: LINUX

2008-04-28 21:19:32

的初始化:

smpboot_setup_io_apic()函数会调用setup_IO_APIC()完成所有的工作。setup_IO_APIC()流程如下:

1、      调用enable_IO_APIC()对初始化一些基本数据结构,这些结构是:

u     irq_2_pin全局数组:用于记录IRQpin的对应关系,以IRQ为索引,可以得到对应pin的信息。Pin用结构体struct irq_pin_list表示,它有三个字段:

字段

描述

Apic

pin从属的IOAPIC,该值用于所以mp_ioapics数组

Pin

管脚号

Next

下一个pin结构体

2-12 struct irq_pin_list结构体

目前Linuxirq_2_pin元素个数为2 * NR_IRQSNR_IRQS是平台最大IRQ数量,对于IOAPIC,该值为224。对于PIC,该值为16

u     pirq_entries全局数组:若在内核启动参数中配置了PIRQ参数,其值记录在pirq_entries数组中。目前内核允许最多配置8PIRQ

u     nr_ioapic_registers全局数组:用于记录每个IOAPIC的管脚数,目前内核最多支持64IOAPIC,故该数组长度为64

u     ioapic_i8259全局变量:记录PIC所在的IOAPIC与管脚。有两个字段,apic表示IOAPIC(同struct irq_pin_listapic字段),pin代表管脚号。

笔者:关于PIRQ内核参数的详细信息,请参考内核文档Documentation/i386/IO-apic.txt。呵呵,不要被名字迷惑以为内容很多,它除了讲PIRQ什么都没有。此外,如果看了该文档仍不明白PCI中断线和PIRQ的换算关系,可以参考PCI spec2.2.6节,里面有一个INTA~DPIRQ的换算公式。这里就不多做介绍了。PIRQ内核参数在ioapic_pirq_setup()函数中解析。

            enable_IO_APIC()首先将irq_2_pinpirq_entires初始化到无效状态,并读取每个IOAPIC的管脚数存入nr_ioapic_registers数组。接着探测是否有PIC接到IOAPIC上,这通过读取每个IOAPICPRT表,寻找是否有RTE被设置成了ExtINT模式实现。如果找到,把apicpin信息记录在ioapic_i8259中。最后通过mp_irqs数组,验证MP table报告的PIC信息和自己读取到的是否相同,如果不同,信任MP table并更改ioapic_i8259相应字段。最后将所有IOAPICPRTmask掉,RTESMI类型除外。

2、        设置一个bit map ——unsigned long io_apic_irqs。该bit map每一bit对应一个IRQ,该bit1,表示该IRQIOAPIC,否则接PIC。如果系统处于ACPI模式,io_apic_irqs = ~0UL;否则,io_apic_irqs = ~(1<<2)

笔者:如果系统遵循MP specIRQ2用于接第二片i8259,故系统中不会有IRQ2存在。对于ACPI模式,没找资料说PIC的接法。

3、        如果系统不处于ACPI模式,调用setup_ioapic_ids_from_mpc()。该函数根据从MP table得到的IOAPIC ID,设置各IOAPICAPIC ID寄存器。并查找是否有冲突,如有冲突则重新分配,并更新mp_irqs中对应中断的IOAPIC ID字段。

笔者:还记得我们前面有一个关于APIC ID的题外话吗?我们说对于Pentium4Xeon系列,IOAPIC ID只用于区分多个IOAPIC,即使LAPIC ID冲突也无所谓。在setup_ioapic_ids_from_mpc()中,如果系统的APICxAPIC,不用检查IOAPIC ID直接返回,因为在没有APIC BUS的情况下,它们毫无意义。什么是xAPICPentium4Xeon系列用的APIC就叫xAPIC。内核佐证了我们前面的说法。

4、        调用sync_Arb_IDs(),如果当前系统APIC使用APIC BUS通讯,该函数将广播一个Level触发、类型为INITIPIInter Processor Interrupt,处理器间中断),将各LAPICArb同步为其自身LAPIC ID

LAPIC总线竞争

ArbArbitration Register,仲裁寄存器。该寄存器用4bit表示0~1516个优先级(15为最高优先级),用于确定LAPIC竞争APIC BUS的优先级。系统RESET后,各LAPICArb被初始化为其LAPIC ID。总线竞争时,Arb值最大的LAPIC赢得总线,同时将自身的Arb清零,并将其它LAPICArb加一。由此可见,Arb仲裁是一个轮询机制。Level触发的INIT IPI可以将各LAPICArb同步回当前的LAPIC ID

对于Pentium4Xeon系列,总线竞争由前端总线协议决定。

者:IOAPIC是否有Arb用于总线竞争?我猜想是有的。但x86 spec没说,在看APIC系统实现时,关于总线竞争的部分又看的太模糊了,没大看懂。想着以后不用这东西,看着就没动力了,有兴趣的朋友可以深究一下。

关于IPI的内容,《Interrupt in Linux(软件篇)》中会介绍(如果我写了^_^

5、              调用setup_IO_APIC_irqs()。该函数是IOAPIC初始化中的重中之重,几乎所有工作都是由它完成的,下面我们看看其流程:

a、  初始化各IOAPICPRT表。内核用struct IO_APIC_route_entry结构表示RTE,下表描述了内核初始化RTE的格式:

字段

对应RTE字段

初始值

Vector

Vector

见后面内容

Delivery_mode

Delivery Mode

Lowest Priority001

Dest_mode

Destination Mode

Logical1

Polarity

Polarity

irq_polarity()函数根据mp_irqs中的信息,或总线类型决定

Trigger

Trigger Mode

irq_trigger()函数根据mp_irqs中的信息,或总线类型决定

Mask

Mask

Enable(0)

logical_dest

Destination Field

所有可用的CPU

2-13 内核初始RTE结构值

从表中可以看出,内核将RTE配置成了logical模式,并且中断消息的目的地是所有CPUDelivery modelowest priority。这样IOAPIC的中断消息由优先级最低的CPU接收。

b、  调用pin_2_irq()pin转换成IRQ。对于ISA中断,pin对应的IRQmp_irqs数组得到。对于PCI中断,使用GSI方式把pin转换成唯一的IRQ,其代码如下:

                  i = irq = 0;

                     while (i < apic)

                            irq += nr_ioapic_registers[i++];

                     irq += pin;

笔者:可以看出,内核使用了GSI的思想,同时我们也可以看出IRQGSI是个可以互换的概念。

c、  调用add_pin_to_irq()IRQpin的对应关系存入irq_2_pin数组。

d、  如果是该IRQIOAPIC管脚(用前面提到的io_apic_irqs bit map判断),调用assign_irq_vector()为该IRQ分配一个vector,然后调用ioapic_register_intr()给该IRQ注册一个handler。分配的vector会存入一个irq_vector全局数组,用IRQ号为索引,可得到对应的vector

Vector分配策略

__ assign_irq_vector()写的比较晦涩,较为难懂。我算法烂,就不画流程图论述过程了。画了一副图,展示vector分配的策略:

图中有3IOAPIC。根据Linux分配IRQ的策略,IOAPIC0 24个管脚对应IRQ0~23pin0对应IRQ0IOAPIC1pin0对应IRQ24 …… 依此类推。由于vector本身是代表优先级的,为了公平,Linux将所有vector平均的分配给3IOAPICIRQ0分配到vector49IRQ24分配到vector50IRQ48分配到vector51 …… 依次类推。Linux中设备可用的第一个vector0x31,也就是vector49,最大可用vector0xef。但实际上__ assign_irq_vector()的实现将最大可用vector限制到了238,最多支持8IOAPIC(每个IOAPIC 24个管脚,且最后一个IOAPIC3个管脚分配不到vector)。在分配的过程中,避开了vector 0x80

这个函数看的我有点头疼,为什么限制到238?为什么从49开始分配?没用的vector留给谁的?暂时不去想了,大家可以深究一下。

笔者:上面例子中的假设,IOAPIC0pin0对应IRQ0,实际上不是这样的。前面已经讲了ISA中断的IRQMP table得到,IRQ0实际对应pin2。这样假设只是为了论述方便。

ioapic_register_intr()注册的handler,只是通用的处理函数(如处理level中断的,处理edge中断的),它会具体再调用设备的中断处理函数。ULK3上的中断处理路径__do_IRQ()已经过时了,内核已引入generic IRQ layer

如果我写了《软件篇》,会介绍该处理机制。内核文档Documentation/DocBook/genericirq.tmpl详细讲解了generic IRQ layer,很简单的,大家可以自己看看。

    Vector是值越大优先级越高,PIT对应IRQ0,按Linux的分配策略,它的优先级最低。是我错了?还是PITLinux中本身就不重要?

e、  对于ISA中断,调用disable_8259A_irq()PIC上对应的管脚mask掉(进入APIC模式后,PIC要被mask掉)。

f、   至此,我们已经配置好一个RTE的全部信息,调用__ioapic_write_entry()将该RTE写到IOAPIC中。

6、  调用init_IO_APIC_traps()irq_vector数组中未分配到vectorIRQ设置默认的处理函数(通常这些IRQ不会发生,但Linux尽可能的保证安全。这个属于generic IRQ layer的内容,就不多讲了)。

好了,搞定。IOAPIC已经设置好了,有些内容我们没提到,例如check_timer(),有机会在说吧。

阅读(3364) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~