IA64 Linux 外部中断处理机制(转)

IA64 Linux 外部中断处理机制

developerWorks
文档选项
将打印机的版面设置成横向打印模式

打印本页

将此页作为电子邮件发送

将此页作为电子邮件发送


本文分析和介绍了 Intel IA64 体系结构中新型的中断处理方式 ―― SAPIC(Streamlined Advanced Programmable Interrupt Controller),通过分析 IA64 Linux 2.4.21 内核的外部中断处理过程,着重讨论了 IA64 Linux 对 SAPIC 的支持,为 IA64 Linux 系统开发人员实现 SAPIC 的中断处理提供技术参考。

一、IA64 中断处理架构


Intel 公司的 IA64 体系结构采用了一种全新的中断处理架构 SAPIC(Streamlined Advanced Programmable Interrupt Controller),它与传统的 8259 和 APIC(Advanced Programmable Interrupt Controller)中断架构不同,具有良好的可扩展性和高效的中断处理性能。与传统的 8259 相比,SAPIC 架构将基于引腿的中断机制转变为基于消息的中断机制。采用基于引腿的方式,当需要增加中断源时,就必须增加引腿数目,中断架构的可扩展性较差;而采用基于消息的中断机制,总线上中断源的数目是没有限制的。SAPIC 用系统总线中断发送机制比 APIC 用专用串行总线的实现更高效,中断处理能力可以随着处理器系统总线的速度扩展,外部 I/O 中断通过 I/O 总线发送,并可快速发送到系统总线上。此外,SAPIC 在运行中断服务程序时可一次性处理同一优先级中的所有等待中断,减少了现场切换次数,提高了系统性能。

SAPIC 通常是指整个中断架构,具体实现时将它分为两部分:本地 CPU 部分和 IO 部分,前者简称为 LSAPIC,后者简称为 IOSAPIC。如图所示。

图1典型 Intel 安腾多处理器平台中断架构





回页首


CPU 内部中断


IA64 中断架构由一个 8 位的中断向量构成,用于标识中断源,每个 CPU 最多可以支持 256 种不同的中断源。前 16 个向量(0-15)是预留的,有特殊用途,如:非屏蔽中断的中断向量号为 2,野中断的中断向量号为 15。剩下的 240 个向量没有固定的含义,操作系统可以根据自己的需要定义和使用,中断向量的优先级是由中断向量号决定的,中断向量号越大中断优先级越高,如 IA64 Linux 中时钟中断的中断向量号为 0xef(include/asm-ia64/hw_irq.h 中定义的)。

IA64 中断架构中,除了基于优先级的特点外,还可以通过屏蔽处理器状态寄存器 psr.i 位和控制寄存器 tpr(任务优先级寄存器)进行控制。如 psr.i 清 0,则将屏蔽所有的外部中断(包括非屏蔽中断)。tpr 可以提供细粒度的中断屏蔽,该寄存器的格式如图所示。

图2控制寄存器 tpr 的布局

如果 mmi 置 1,则屏蔽除非屏蔽中断外的所有外部中断,如果 mmi 置 0,则 mic 域控制中断向量的屏蔽。256 个中断被分成 16 组,每组 16 个向量,向量 16-31 为第 1 组,32-47 为第 2 组,依次类推。向 mic 域写值n,则屏蔽从 1 到 n 组的所有中断,第 0 组(向量 0-15)不受 mic 值的影响。因此,如果 mic 置 0,则允许所有的外部中断;如 mic 置 15,则屏蔽除非屏蔽中断外的所有外部中断。

每个 IA64 CPU 自身有三个外部中断源:定时器、可修复故障检测和性能监控中断。此外,还提供外部设备直接连接 CPU 的中断引腿 LINT0 和 LINT1,但一般不使用。CPU 内部的每个中断源都有自己的控制寄存器用于存储 8 位中断向量,当定时器中断发生时,itv 寄存器存储定时器中断向量号,当发生一个可修复故障时,cmcv 寄存器存储可修复故障中断向量号,当发生性能监控中断时,pmv 寄存器存储性能监控中断向量号。





回页首


平台中断


I/O 设备通常采用两种方式触发中断:电平或边缘触发方式,在现代计算机中大多采用电平方式,它具有多个 I/O 设备共享中断和能降低信号干扰等优势。所有外部的 I/O 引腿中断都需要通过 IOSAPIC 进行路由,IOSAPIC 负责将设备的中断请求转化为中断事务,通过其内部的 I/O RTE 表将一个输入中断信号转化为一个中断请求消息,然后该消息以一个内存写事务通过 I/O 总线。

系统桥控制器的中断发送实现是可选的。如果桥控制器中没有实现 XTP 寄存器,它将中断事务没有修改地发送到系统总线上,桥控制器不检查中断事务。如果实现了 XTP 寄存器,则系统总线桥控制器接收到中断事务时,它首先检测重定向提示信息位是否设置,然后将该中断事务发送到系统总线中最低优先级的处理器。桥控制器用芯片组中的 XTP 寄存器确定中断发送到哪个处理器,系统总线上的每个处理器都会对应一个 XTP 寄存器,用于反映最低优先级处理器,这些寄存器放置在平台中断发送的路径上,并用于将一个中断事务重定向到一个最低优先级的处理器。

在系统总线上,一个中断事务如同一个正常的读或写事务,除了发送的数据编码为中断向量号而不是内存地址。在多处理机系统中,由于多个 CPU 连接到系统总线,因此向某个 CPU 发送中断时需要在中断事务中标识该 CPU,中断事务中的 ID 和 EID 域可以唯一标识平台中的任意一个 CPU。在系统启动时,firmware 为每个 CPU 分配一个唯一的本地 ID,并将该 ID 写入一个特殊的 CPU 控制寄存器 lid,在进行中断事务处理时,目标 CPU 的 lid 与中断向量号一起进行编码。





回页首


处理器间中断


在多处理器系统中,操作系统需要在多个处理器间协调操作,这通常是通过机间中断(IPI)实现的。IA64 的 IPI 机制是以 LSAPIC 的处理器中断块形式实现的,该中断块在物理内存地址空间中占据 2MB,缺省情况下,它的基址为 0xfee00000,该中断块主要具有两个功能:(1)将中断消息发送到系统中的其它处理器,(2)可以设置本地 CPU 的 XTP 值。

与普通的设备中断一样,IPI 采用基于事务的中断方式。如果一个处理器向另一个处理器发送中断,则查询目标处理器的本地 ID,将该值与需要发送的中断向量号结合写入到处理器的中断块中。LSAPIC 在系统总线上产生一个中断消息,目标处理器以通常的方式处理到来的机间中断。

一个处理器可以发送 IPI 到任意一个处理器,包括它自己,这对延迟一个中断处理是非常有用的。如果操作系统在不合适时机接收到一个中断,则操作系统只简单记录发生的中断向量,继续其它处理。当操作系统可以处理该中断时,则通过 IPI 向自己发送先前被忽略的中断向量实现中断的接力。

不明确区分 IPI 与设备中断是非常有用的,例如,当一个处理器接收到一个中断时,如果发现另一个处理器处理该中断更加合理,则可以通过 IPI 机制将该中断传递到其它的处理器,可以实现处理器的负载平衡。





回页首


二、IA64 Linux 外部中断处理流程


IA64 Linux 外部中断处理机制(转)_第1张图片

图3 IA64 Linux 中断处理流程

IA64 Linux 的中断处理从 arch/ia64/kernel/ivt.S 开始。该文件定义了 CPU 使用的中断向量表,并不是一个中断向量表表项对应一个中断,可能有多个中断共用一个中断向量表表项。该文件共定义了 68 个表项,其中前 20 个表项中每个表项可以包含 64 条指令束(由于 IA64 采用 EPIC 架构,每条指令束可以同时有 3 条指令),因此可以将较短的中断处理函数嵌入在该表项中,这对需要快速中断处理是十分有效的,如与 TLB 失效相关的中断处理函数就嵌入在中断向量表中。后 48 个表项中每个表项只包含 16 条指令束,因此一般不能满足中断处理函数嵌入的需求,通常采用跳转函数。外部中断处理在中断向量表中位于 0x3000,是第 12 个表项,具体内容如下,

// 0x3000 Entry 12 (size 64 bundles) External Interrupt (4)
ENTRY(interrupt)
 DBG_FAULT(12)
 mov r31=pr  // prepare to save predicates
 ;;
 SAVE_MIN_WITH_COVER // uses r31; defines r2 and r3
 ssm psr.ic | PSR_DEFAULT_BITS
 ;;
 adds r3=8,r2  // set up second base pointer for SAVE_REST
 srlz.i   // ensure everybody knows psr.ic is back on
 ;;
 SAVE_REST
 ;;
 alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group
 mov out0=cr.ivr  // pass cr.ivr as first arg
 add out1=16,sp  // pass pointer to pt_regs as second arg
 ;;
 srlz.d   // make  sure we see the effect of cr.ivr
 movl r14=ia64_leave_kernel
 ;;
 mov rp=r14
 br.call.sptk.many b6=ia64_handle_irq
END(interrupt)
                

从中断控制寄存器 ivr 中读取中断向量号,然后调用外部中断处理函数 ia64_handle_irq(),跳转到 C 函数写的处理程序中。该函数在 arch/ia64/kernel/irq_ia64.c 中定义,主要过程如下,

saved_tpr = ia64_get_tpr();
ia64_srlz_d();
while (vector != IA64_SPURIOUS_INT_VECTOR) {
  if (!IS_RESCHEDULE(vector)) {
   ia64_set_tpr(vector);
   ia64_srlz_d();
   do_IRQ(local_vector_to_irq(vector), regs);
   /*
    * Disable interrupts and send EOI:
    */
   local_irq_disable();
   ia64_set_tpr(saved_tpr);
  } else {
   kstat.irqs[smp_processor_id()][IA64_IPI_RESCHEDULE]++;
  }
  ia64_eoi();
  vector = ia64_get_ivr();
 }
 if (local_softirq_pending())
  do_softirq();
                

它利用 local_vector_to_irq() 将中断向量号映射到相应的 irq 号,并调用 do_IRQ 函数激活相应的设备中断处理函数(该设备中断处理函数在系统初始化时已注册到 irq 描述符的动作列表中)。当处理完该中断,内核须通过写 eoi 寄存器通知 CPU 当前中断已完成。中断处理函数重新读取 ivr,判断是否还有未处理的中断,如果没有,ivr 返回 15,则退出死循环;如果有,则返回新中断向量,重新处理该中断,处理流程与前面的一样。这种等待中断的批处理方式可以减少进入内核和退出内核的次数,从而提供效率。软中断的处理在 ia64_eoi()之后,因为只有内核在调用 ia64_eoi()之后才会产生新的中断。

IA64 外部中断是根据中断向量号排优先级的,中断向量号越大优先级越高。由于有 256 个中断向量,因此在同一个内核栈中有可能产生上百个嵌套中断,假设处理每个中断平均需要 1KB 的栈空间,则该内核栈需要 0.25 兆空间才能避免栈溢出。由于每个任务都需要有自己的内核栈,因此这么大的内核栈是不切合实际的。为了避免这个问题,Linux/ia64 用 tpr 寄存器控制嵌套深度。处理过程如上述程序所示,它将 tpr 的当前值保留在 saved_tpr 局部变量中,然后将当前中断的向量号写入 tpr。该写操作会屏蔽该组或低于该组的所有中断,在设备中断处理完成后,恢复原来的 tpr 值。由于只有全系统只有 16 个优先级组,因此 32KB 的内核栈就可以保证避免栈溢出。如果在 tpr 写入新值或在旧值恢复后发生另一个中断,则会导致刚才的嵌套深度机制失效,因此这时内核必须关闭处理器状态寄存器中的中断位(psr.i),在调用外部中断处理函数时该位必须关闭。在恢复 tpr 前,通过 local_irq_disable() 再次清除该位。这样确保内核的进入和退出路径都在屏蔽中断发送情况下进行的。有个例外,如果有等待的软中断,在调用 do_softirq() 时该函数会调用 local_irq_enable() 重新赋能中断发送,因此 do_softirq() 在执行前先检查当前嵌套中断的深度,如果深度超过 1,则立即返回。软中断只在深度为 1 的时候处理。

IA64 Linux 的中断处理过程充分利用 SAPIC 中断架构的特点,通过中断向量表中嵌入中断处理函数、中断优先级控制、中断屏蔽、等待中断的批处理等方式实现高效的中断处理。随着 SAPIC 中断架构的日趋成熟和壮大,IA64 Linux 的中断处理过程将具有重要的参考价值。


你可能感兴趣的:(IA64 Linux 外部中断处理机制(转))