zoukankan      html  css  js  c++  java
  • Linux x86_64 APIC中断路由机制分析

    不同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 = { { { { 0, 0 } } }, };

    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 = 64
    nr_cpu_ids = 16 
    X = The number of irq sources we can talk about = nr_irqs_gsi+8*nr_cpu_ids+nr_irqs_gsi*16 = 1216
    Y = The number of irqs we can possibly service = NR_VECTORS*nr_cpu_ids = 4096
    nr_irqs = min(X, Y) = min(1216,4096) = 1216
    
    NR_VECTORS = 256
    NR_CPUS = 64
    CPU_VECTOR_LIMIT = 64 * NR_CPUS = 256 * nr_cpu_ids = 4096
    MAX_IO_APICS = 128
    IO_APIC_VECTOR_LIMIT = 32 * MAX_IO_APICS = 4096
    NR_IRQS = NR_VECTORS + max(CPU_VECTOR_LIMIT,IO_APIC_VECTOR_LIMIT) = 4352

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

    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)
    ",
                    __func__, smp_processor_id(), vector, irq);
        }
    
        irq_exit();
    
        set_irq_regs(old_regs);
        return 1;
    }

    中断配置过程

    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 : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
        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--

  • 相关阅读:
    编译原理-第二章 一个简单的语法指导编译器-2.4 语法制导翻译
    编译原理-第二章 一个简单的语法指导编译器-2.3 语法定义
    编译原理-第二章 一个简单的语法指导编译器-2.2 词法分析
    LeetCode 1347. Minimum Number of Steps to Make Two Strings Anagram
    LeetCode 1348. Tweet Counts Per Frequency
    1349. Maximum Students Taking Exam(DP,状态压缩)
    LeetCode 1345. Jump Game IV(BFS)
    LeetCode 212. Word Search II
    LeetCode 188. Best Time to Buy and Sell Stock IV (动态规划)
    LeetCode 187. Repeated DNA Sequences(位运算,hash)
  • 原文地址:https://www.cnblogs.com/wahaha02/p/6341095.html
Copyright © 2011-2022 走看看