不同CPU体系间的中断控制器工作原理有较大差异,本文是《Linux mips64r2 PCI中断路由机制分析》的姊妹篇,主要分析Broadwell-DE X86_64 APIC中断路由原理、中断配置和处理过程,并尝试回答如下问题:

  • 为什么x86中断路由使用IO-APIC/LAPIC框架,其有什么价值?
  • pin/irq/vector的区别、作用,取值范围和分配机制?

x86_64 APIC关键概念

Pin

此处的pin特指APIC的中断输入引脚,与内外部设备的中断输入信号相连。从上图中可以看出,Pin的最大值受APIC管脚数限制,目前取值范围是[0,23]。其中[0, 15]这16个pin,基于与PIC兼容等原因考虑,有固定用途;PIRQ[A..H]这8个引脚为PCI IRQ引脚,为PCI设备提供中断路由,其中PIRQ[A..D]为纯中断引脚,PIRQ[E..H]可配置为中断引脚或GPIO引脚。

内部设备中断路由到哪个PIRQ,可以通过DxxIR(Device XX Interrupt Route Register)寄存器设置;外部设备使用哪个APIC引脚,在硬件PCB设计时即固定下来。

为什么设备中断要经过APIC再与CPU相连,而不直接与CPU相连?原因有二:1)存在大量的外部设备,但CPU的中断引脚等资源是很有限的,满足不了所有的直连需求;2)如果设备中断与CPU直接相连,连接关系随硬件固化,这样在MP系统中,中断负载均衡等需求就无法实现了。

Vector

Vector是CPU的概念,以CPU核的角度看,其以vector标识中断,详见下节中断路由原理介绍。

vector是IDT表(idt_table)的索引。

gate_desc idt_table[NR_VECTORS] __page_aligned_data = { { { { ,  } } }, };

vector的个数由硬件决定,从上图可知,Broadwell-DE X86_64支持最多256个vector。其中前32个为系统保留使用,其他由操作系统动态分配。

vector提供优先级和亲和性绑定的支持,vector的高4位为优先级,0最低,15最高。CPU只处理优先级高于LAPIC TPR值的vector中断。

为什么在irq之外,又增加个vector概念,两者是否可以合二为一?不可以,原因主要是:vector是针对每个CPU核的,描述每个CPU核对上报中断的优先级处理和亲和性关系;而irq是全局的,它维护所有CPU核上的中断处理相关信息。

IRQ

在PIC和单核时代,irq、vector、pin这个概念的确是合三为一的,irq就是PIC控制器的pin引脚,irq也暗示着中断优先级,例如IRQ0比IRQ3有着更高的优先级。当进入MP多核时代,多核CPU下中断处理带来很多问题(如如何决定哪个中断在哪个核上处理,如何保证各核上中断负载均衡等),为了解决这些问题,vector、pin等概念都从irq中剥离出来,irq不再含有特定体系架构下中断控制器的硬件属性,只是内核中对中断的一个通用的软件抽象,与特定硬件解耦,增强其通用性。在内核中,irq号做为中断的抽象表达,其功能包括:

  • 对所有cpu核上的中断进行统一编码,确保不同cpu核上相同中断的irq号不重复;
  • 作为中断相关信息的查询码,如通过irq号可以获得中断控制器对象struct mpic,可以获得中断描述符对象irq_desc,可以获得硬件中断号等等。

irq的总数由下面的方式计算得出,nr_irqs为所有cpu核支持的中断总数,NR_IRQS为nr_irqs的初始值。

当irq很大时,静态分配irq_desc表将不是个明智的决定,这时内核会使用radix tree来组织irq_desc,即irq_desc_tree

nr_irqs_gsi =
nr_cpu_ids =
X = The number of irq sources we can talk about = nr_irqs_gsi+*nr_cpu_ids+nr_irqs_gsi* =
Y = The number of irqs we can possibly service = NR_VECTORS*nr_cpu_ids =
nr_irqs = min(X, Y) = min(,) = NR_VECTORS =
NR_CPUS =
CPU_VECTOR_LIMIT = * NR_CPUS = * nr_cpu_ids =
MAX_IO_APICS =
IO_APIC_VECTOR_LIMIT = * MAX_IO_APICS =
NR_IRQS = NR_VECTORS + max(CPU_VECTOR_LIMIT,IO_APIC_VECTOR_LIMIT) =

最后,总结一下,这几个中断概念的关系和作用描述,如下图:

x86_64 PCI设备中断路由原理

如上图所示,local APIC通过 I/O APIC接受中断,I/O APIC负责把中断处理成中断消息并按一定规则转发给local APIC。

如上图所示,local APIC提供the interrupt request register (IRR) 和 in-service register (ISR) 2个寄存器,在处理一个vector的同时,缓存一个相同的vector,vector通过2个256-bit的寄存器标识,256个bit代表256个可能的vector,置1表示上报了相应的vector请求处理或者正在处理中。

local APIC以vector值作为优先级顺序来处理中断。每个vector为8-bit,高4位作为中断优先级,1最低,15最高。vector 0-31为Intel 64 and IA-32体系保留专用,所以可用的中断优先级为2-15。

另外,local APIC提供Task-Priority Register (TPR),Processor-Priority Register (PPR)来设置local APIC的task优先级和cpu优先级,但I/O APIC转发的中断vector优先级小于local APIC TPR的设置时,此中断不能打断当前CPU核上运行的task;当中断vector优先级小于local APIC PPR的设置时,此CPU核不处理此中断。操作系统通过动态设置TPR和PPR,从而实现操作系统的实时性需求和中断负载均衡需求。

中断处理过程

Broadwell-DE X86_64的中断处理过程基本和《Linux mips64r2 PCI中断路由机制分析》的类似,不同点是x86_64从ax寄存器中获取需要处理的中断vector,而不是从APIC的特定寄存器中获取。然后从vector_irq数组中根据vector查询到对于的irq号。

unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs); /* high bit used in ret_from_ code */
unsigned vector = ~regs->orig_ax;
unsigned irq; irq_enter();
exit_idle(); irq = __this_cpu_read(vector_irq[vector]); if (!handle_irq(irq, regs)) {
ack_APIC_irq(); if (printk_ratelimit())
pr_emerg("%s: %d.%d No irq handler for vector (irq %d)\n",
__func__, smp_processor_id(), vector, irq);
} irq_exit(); set_irq_regs(old_regs);
return ;
}

中断配置过程

  1. 动态分配irq,以IRQ_BITMAP_BITS (NR_IRQS + 8196) bitmap数据结构管理;
  2. 动态分配vector,为保证最大利用local APIC上vector优先级,减少具有相同vector优先级的中断,以16为间隔依次分配vector;
  3. 配置vector与irq的映射表:vector_irq表;
  4. 初始化irq_desc;
  5. 配置APIC PIRQ[A..H] pin脚属性;
  6. 配置I/O APIC pin与vector 映射表:REDIR_TBL寄存器;共有24个REDIR_TBL条目,与24个pin脚对应。除了定义相应的vector以外,还支持定义中断引脚具有如下功能:定义此引脚中断通过I/O APIC上报给指定的local APIC或local APIC组;中断触发模式;中断屏蔽等。详见下表。

附:相关数据结构:

typedef struct gate_struct64 gate_desc;

/* 16byte gate */
struct gate_struct64 {
u16 offset_low;
u16 segment;
unsigned ist : , zero0 : , type : , dpl : , p : ;
u16 offset_middle;
u32 offset_high;
u32 zero1;
} __attribute__((packed)); struct irq_desc {
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list */
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
u64 random_ip;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp; #ifndef CONFIG_SPARSE_IRQ
extern struct irq_desc irq_desc[NR_IRQS];
#endif static RADIX_TREE(irq_desc_tree, GFP_KERNEL);

--EOF--

Linux x86_64 APIC中断路由机制分析的更多相关文章

  1. Linux mips64r2 PCI中断路由机制分析

    Linux mips64r2 PCI中断路由机制分析 本文主要分析mips64r2 PCI设备中断路由原理和irq号分配实现方法,并尝试回答如下问题: PCI设备驱动中断注册(request_irq) ...

  2. Linux x86_64内核中断初始化

    Linux x86_64内核中断初始化 [TOC] 中断分类 Linux系统中,中断分为: 硬中断:由外部设备或者执行异常产生的需要快速处理的中断.如缺页中断.定时器硬件中断. 根据内部产生还是外部产 ...

  3. Linux内核中的Workqueue机制分析

    1. 什么是workqueue Linux中的workqueue(工作队列)主要是为了简化在内核创建线程而设计的.通过相应的工作队列接口,可以使开发人员只关心与特定功能相关的处理流程,而不必关心内核线 ...

  4. floodlight路由机制分析

    SDN的出现可以使得各种复杂的路由协议从原本的Device OS中剥离出来,放在SDN Controller中,Controller用一种简单的协议来和所有的Router进行通信,就可以获得网络拓扑, ...

  5. Meandering Through the Maze of MFC Message and Command Routing MFC消息路由机制分析

    Meandering Through the Maze of MFC Message and Command Routing Paul DiLascia Paul DiLascia is a free ...

  6. linux的可中断sleep_on函数分析

    void interruptible_sleep_on (struct task_struct **p)// **p是个全局变量 { struct task_struct *tmp; if (!p)# ...

  7. Linux内核NAPI机制分析

    转自:http://blog.chinaunix.net/uid-17150-id-2824051.html 简介:NAPI 是 Linux 上采用的一种提高网络处理效率的技术,它的核心概念就是不采用 ...

  8. Linux信号(signal) 机制分析

    Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...

  9. Linux内核态抢占机制分析(转)

    Linux内核态抢占机制分析  http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...

随机推荐

  1. GnuPGP介绍

    PGP(Pretty Good Privacy的首字母):PGP公司的加密.签名工具套件,使用了商业版本的IDEA算法,并集成了有商业版权的PGPdisk工具. GnuPG(GNU Privacy G ...

  2. DoNetZip类库解压和压缩文件

    using Ionic.Zip; public class ZipHelper { public static void ZipSingleFile(string zipPath) { try { u ...

  3. 史上最简单的 SpringCloud 教程

    史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka)史上最简单的SpringCloud教程 | 第二篇: 服务消费者(rest+ribbon)史上最简单的Spri ...

  4. python之字符编码

    1.以什么编码存的就以什么编码取出; 内存固定使用unicode编码; 我们可以控制的编码是往硬盘存放或者基于网络传输选择编码. 2.数据是最先产生于内存中,是unicode格式,要想传输需要转成by ...

  5. windows10的文件浏览器中无法搜索文件内容

    系统:更新到最新的win10(2018年8月23日 23:54:31) 重现步骤:git clone一个项目,然后切换到它的另一个分支:打开文件夹浏览器(explorer),在右上角里输入想要查找的字 ...

  6. 函数sigsuspend

    sigqueue函数原型: 函数作用:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用 int sigqueue(pid_t pid, int si ...

  7. error: <item> inner element must either be a resource reference or empty.

    FAQ: Android resource compilation failedOutput: /home/cmm/code/AndroidHttpCapture/app/build/intermed ...

  8. .NET C# 创建WebService服务简单的例子

    Web service是一个基于可编程的web的应用程序,用于开发分布式的互操作的应用程序,也是一种web服务 WebService的特性有以下几点: 1.使用XML(标准通用标记语言)来作为数据交互 ...

  9. Maya闪退

    电脑上装的Maya2015突然就打不开了,窗口闪一下就关闭,也没有任何提示. 将15卸载装了Maya2016还是一样. 再彻底卸载16,装了15,还不行... 将系统环境变量中PYTHONHOME和P ...

  10. You have new mail in /var/spool/mail/root消除提示的方法

    有时在进入系统的时候经常提示You have new mail in /var/spool/mail/root 你觉得烦人---解决方法: 修改系统配置文件/etc/profile,告诉系统不要去检查 ...