免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2465 | 回复: 0
打印 上一主题 下一主题

Interrupt in Linux(硬件篇)—— Linux5 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-04-28 21:19 |只看该作者 |倒序浏览

2.1.3.1 IOAPIC的初始化:
smpboot_setup_io_apic()函数会调用setup_IO_APIC()完成所有的工作。setup_IO_APIC()流程如下:
1、      调用enable_IO_APIC()对初始化一些基本数据结构,这些结构是:
u     irq_2_pin全局数组:用于记录IRQ和pin的对应关系,以IRQ为索引,可以得到对应pin的信息。Pin用结构体struct irq_pin_list表示,它有三个字段:
字段
描述
Apic
该pin从属的IOAPIC,该值用于所以mp_ioapics数组
Pin
管脚号
Next
下一个pin结构体
表2-12 struct irq_pin_list结构体
目前Linux中irq_2_pin元素个数为2 * NR_IRQS。NR_IRQS是平台最大IRQ数量,对于IOAPIC,该值为224。对于PIC,该值为16。
u     pirq_entries全局数组:若在内核启动参数中配置了PIRQ参数,其值记录在pirq_entries数组中。目前内核允许最多配置8个PIRQ。
u     nr_ioapic_registers全局数组:用于记录每个IOAPIC的管脚数,目前内核最多支持64个IOAPIC,故该数组长度为64。
u     ioapic_i8259全局变量:记录PIC所在的IOAPIC与管脚。有两个字段,apic表示IOAPIC(同struct irq_pin_list的apic字段),pin代表管脚号。
笔者:关于PIRQ内核参数的详细信息,请参考内核文档Documentation/i386/IO-apic.txt。呵呵,不要被名字迷惑以为内容很多,它除了讲PIRQ什么都没有。此外,如果看了该文档仍不明白PCI中断线和PIRQ的换算关系,可以参考PCI spec的2.2.6节,里面有一个INTA~D到PIRQ的换算公式。这里就不多做介绍了。PIRQ内核参数在ioapic_pirq_setup()函数中解析。
            enable_IO_APIC()首先将irq_2_pin和pirq_entires初始化到无效状态,并读取每个IOAPIC的管脚数存入nr_ioapic_registers数组。接着探测是否有PIC接到IOAPIC上,这通过读取每个IOAPIC的PRT表,寻找是否有RTE被设置成了ExtINT模式实现。如果找到,把apic和pin信息记录在ioapic_i8259中。最后通过mp_irqs数组,验证MP table报告的PIC信息和自己读取到的是否相同,如果不同,信任MP table并更改ioapic_i8259相应字段。最后将所有IOAPIC的PRT表mask掉,RTE为SMI类型除外。
2、        设置一个bit map ——unsigned long io_apic_irqs。该bit map每一bit对应一个IRQ,该bit为1,表示该IRQ接IOAPIC,否则接PIC。如果系统处于ACPI模式,io_apic_irqs = ~0UL;否则,io_apic_irqs = ~(1。
笔者:如果系统遵循MP spec,IRQ2用于接第二片i8259,故系统中不会有IRQ2存在。对于ACPI模式,没找资料说PIC的接法。
3、        如果系统不处于ACPI模式,调用setup_ioapic_ids_from_mpc()。该函数根据从MP table得到的IOAPIC ID,设置各IOAPIC的APIC ID寄存器。并查找是否有冲突,如有冲突则重新分配,并更新mp_irqs中对应中断的IOAPIC ID字段。
笔者:还记得我们前面有一个关于APIC ID的题外话吗?我们说对于Pentium4和Xeon系列,IOAPIC ID只用于区分多个IOAPIC,即使LAPIC ID冲突也无所谓。在setup_ioapic_ids_from_mpc()中,如果系统的APIC是xAPIC,不用检查IOAPIC ID直接返回,因为在没有APIC BUS的情况下,它们毫无意义。什么是xAPIC?Pentium4和Xeon系列用的APIC就叫xAPIC。内核佐证了我们前面的说法。
4、        调用sync_Arb_IDs(),如果当前系统APIC使用APIC BUS通讯,该函数将广播一个Level触发、类型为INIT的IPI(Inter Processor Interrupt,处理器间中断),将各LAPIC的Arb同步为其自身LAPIC ID。
题外话 —— Arb和LAPIC总线竞争
Arb,Arbitration Register,仲裁寄存器。该寄存器用4个bit表示0~15共16个优先级(15为最高优先级),用于确定LAPIC竞争APIC BUS的优先级。系统RESET后,各LAPIC的Arb被初始化为其LAPIC ID。总线竞争时,Arb值最大的LAPIC赢得总线,同时将自身的Arb清零,并将其它LAPIC的Arb加一。由此可见,Arb仲裁是一个轮询机制。Level触发的INIT IPI可以将各LAPIC的Arb同步回当前的LAPIC ID。
对于Pentium4和Xeon系列,总线竞争由前端总线协议决定。
笔者:IOAPIC是否有Arb用于总线竞争?我猜想是有的。但x86 spec没说,在看APIC系统实现时,关于总线竞争的部分又看的太模糊了,没大看懂。想着以后不用这东西,看着就没动力了,有兴趣的朋友可以深究一下。
关于IPI的内容,《Interrupt in Linux(软件篇)》中会介绍(如果我写了^_^)
5、              调用setup_IO_APIC_irqs()。该函数是IOAPIC初始化中的重中之重,几乎所有工作都是由它完成的,下面我们看看其流程:
a、  初始化各IOAPIC的PRT表。内核用struct IO_APIC_route_entry结构表示RTE,下表描述了内核初始化RTE的格式:
字段
对应RTE字段
初始值
Vector
Vector
见后面内容
Delivery_mode
Delivery Mode
Lowest Priority(001)
Dest_mode
Destination Mode
Logical(1)
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模式,并且中断消息的目的地是所有CPU,Delivery mode为lowest priority。这样IOAPIC的中断消息由优先级最低的CPU接收。
b、  调用pin_2_irq()将pin转换成IRQ。对于ISA中断,pin对应的IRQ由mp_irqs数组得到。对于PCI中断,使用GSI方式把pin转换成唯一的IRQ,其代码如下:
                  i = irq = 0;
                     while (i
                            irq += nr_ioapic_registers[i++];
                     irq += pin;
笔者:可以看出,内核使用了GSI的思想,同时我们也可以看出IRQ和GSI是个可以互换的概念。
c、  调用add_pin_to_irq()将IRQ和pin的对应关系存入irq_2_pin数组。
d、  如果是该IRQ接IOAPIC管脚(用前面提到的io_apic_irqs bit map判断),调用assign_irq_vector()为该IRQ分配一个vector,然后调用ioapic_register_intr()给该IRQ注册一个handler。分配的vector会存入一个irq_vector全局数组,用IRQ号为索引,可得到对应的vector。
题外话 —— Linux的Vector分配策略
__ assign_irq_vector()写的比较晦涩,较为难懂。我算法烂,就不画流程图论述过程了。画了一副图,展示vector分配的策略:

图中有3个IOAPIC。根据Linux分配IRQ的策略,IOAPIC0 24个管脚对应IRQ0~23,pin0对应IRQ0。IOAPIC1的pin0对应IRQ24 …… 依此类推。由于vector本身是代表优先级的,为了公平,Linux将所有vector平均的分配给3个IOAPIC。IRQ0分配到vector49,IRQ24分配到vector50,IRQ48分配到vector51 …… 依次类推。Linux中设备可用的第一个vector是0x31,也就是vector49,最大可用vector为0xef。但实际上__ assign_irq_vector()的实现将最大可用vector限制到了238,最多支持8个IOAPIC(每个IOAPIC 24个管脚,且最后一个IOAPIC有3个管脚分配不到vector)。在分配的过程中,避开了vector 0x80。
这个函数看的我有点头疼,为什么限制到238?为什么从49开始分配?没用的vector留给谁的?暂时不去想了,大家可以深究一下。
笔者:上面例子中的假设,IOAPIC0的pin0对应IRQ0,实际上不是这样的。前面已经讲了ISA中断的IRQ从MP 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的分配策略,它的优先级最低。是我错了?还是PIT在Linux中本身就不重要?
e、  对于ISA中断,调用disable_8259A_irq()将PIC上对应的管脚mask掉(进入APIC模式后,PIC要被mask掉)。
f、   至此,我们已经配置好一个RTE的全部信息,调用__ioapic_write_entry()将该RTE写到IOAPIC中。
6、  调用init_IO_APIC_traps()为irq_vector数组中未分配到vector的IRQ设置默认的处理函数(通常这些IRQ不会发生,但Linux尽可能的保证安全。这个属于generic IRQ layer的内容,就不多讲了)。
好了,搞定。IOAPIC已经设置好了,有些内容我们没提到,例如check_timer(),有机会在说吧。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/66786/showart_629904.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP