Linux内核笔记 -- IRQ

来源:互联网 发布:网站快速排名软件 编辑:程序博客网 时间:2024/05/29 15:44

IRQ

众所周知,CPU分为CU、ALU和MMU三个重要的部分,通常情况下CU控制MMU读取指令和数据到相应的寄存器和缓存中来,然后在时钟周期中调用ALU完成计算.然而实际使用过程中处理器并不只做计算,尤其是在多核心的处理器上,控制也是处理器的主要功能之一. 当你按下按键的时候机器要能够响应你而不是自顾自的执行,在各种内部和外部的事件通告的时候都需要停下来,及时做出处理.这就是处理器的中断过程.计算机系统通常使用中断控制器来控制中断(包括最早的8259芯片和的lapic+io-apic高级可编程中断控制器等),这些控制器记录各个中断源的中断,按照一定的仲裁规则将中断推送到各个处理器上,处理器相应的进入中断处理过程,处理相应的中断,接下来通常设置相应的寄存器告诉中断控制器中断处理完毕。有些中断处理过程可能比较复杂,这时候在中断上下文中只记录下中断发生的状态就退出中断上下文,在相应的内核线程处理完中断之后再打开中断。

对于中断控制器而言,最基本的而言就是要根据一定的规则触发或分发中断到指定的处理器上.如8259的仲裁机制,在同一时刻根据一定优先级来提交中断.另外还需要实现中断的Mask和Unmask,在指定的时刻能够打开和关闭指定的中断.在多CPU系统上对外部中断进行分发时通常还需要考虑各个CPU的情况,设置中断的亲核性.8259控制器使用IO端口来进行控制,给指定端口发送IO指令可以实现相应的功能.APIC通过MMIO来进行控制器的管理,通过在MMU上指定一块用来与APIC交互的内存,然后读写内存实现对APIC的编程. 这块内存映射了了中断控制器中的一块存储空间,控制着控制器的的重定向、触发方式等。

struct IO_APIC_route_entry {    __u32   vector      :  8,        delivery_mode   :  3,   /* 000: FIXED                     * 001: lowest prio                     * 111: ExtINT                     */        dest_mode   :  1,   /* 0: physical, 1: logical */        delivery_status :  1,        polarity    :  1,        irr     :  1,        trigger     :  1,   /* 0: edge, 1: level */        mask        :  1,   /* 0: enabled, 1: disabled */        __reserved_2    : 15;    __u32   __reserved_3    : 24,        dest        :  8;} __attribute__ ((packed));struct IR_IO_APIC_route_entry {    __u64   vector      : 8,        zero        : 3,        index2      : 1,        delivery_status : 1,        polarity    : 1,        irr     : 1,        trigger     : 1,        mask        : 1,        reserved    : 31,        format      : 1,        index       : 15;} __attribute__ ((packed));

[:/linux4.13.12/arch/x86/include/asm/io_apic.h]
由于各种中断控制器的实现存在差异,在使用过程中分配和管理的方式都是存在差异的,这时就需要irq_domian进行管理。

enum irq_alloc_type {    X86_IRQ_ALLOC_TYPE_IOAPIC = 1,    X86_IRQ_ALLOC_TYPE_HPET,    X86_IRQ_ALLOC_TYPE_MSI,    X86_IRQ_ALLOC_TYPE_MSIX,    X86_IRQ_ALLOC_TYPE_DMAR,    X86_IRQ_ALLOC_TYPE_UV,};

[:/linux4.13.12/arch/x86/include/asm/hw_irq.h]
在系统初始化的时候,会对系统的各个中断向量进行初始化,X86平台上的中断除了系统默认的中断之外,其他都交给common_interrupt来转到do_IRQ来处理,可以作为资源进行分配。

/* * Build the entry stubs with some assembler magic. * We pack 1 stub into every 8-byte block. */    .align 8ENTRY(irq_entries_start)    vector=FIRST_EXTERNAL_VECTOR    .rept (FIRST_SYSTEM_VECTOR - FIRST_EXTERNAL_VECTOR)    pushl   $(~vector+0x80)           /* Note: always in signed byte range */    vector=vector+1    jmp common_interrupt    .align  8    .endrEND(irq_entries_start)

[:/linux4.13.12/arch/x86/entry/entry_32.S]

void __init native_init_IRQ(void){    int i;    /* Execute any quirks before the call gates are initialised: */    x86_init.irqs.pre_vector_init();    apic_intr_init();    /*     * Cover the whole vector space, no vector can escape     * us. (some of these will be overridden and become     * 'special' SMP interrupts)     */    i = FIRST_EXTERNAL_VECTOR;#ifndef CONFIG_X86_LOCAL_APIC#define first_system_vector NR_VECTORS#endif    for_each_clear_bit_from(i, used_vectors, first_system_vector) {        /* IA32_SYSCALL_VECTOR could be used in trap_init already. */        set_intr_gate(i, irq_entries_start +                8 * (i - FIRST_EXTERNAL_VECTOR));    }#ifdef CONFIG_X86_LOCAL_APIC    for_each_clear_bit_from(i, used_vectors, NR_VECTORS)        set_intr_gate(i, spurious_interrupt);#endif    if (!acpi_ioapic && !of_ioapic && nr_legacy_irqs())        setup_irq(2, &irq2);    irq_ctx_init(smp_processor_id());}

[:/linux4.13.12/arch/x86/kernel/irqinit.c]

对于X86而言,它直接根据IRQ的编号索引到desc,调用相应的handle来处理,可以看出,linux基本上处理所有问题都是这么解决的,上层维护数据结构,下层参考数据结构控制流程

    X86 IRQ 处理内核路径:        do_IRQ              ->  handle_irq                -> static inline void generic_handle_irq_desc                    -> 执行IRQ对应的handle

IRQ映射的处理过程

(1)首先找到root interrupt controller对应的irq domain。(2)根据HW 寄存器信息和irq domain信息获取HW interrupt ID(3)调用irq_find_mapping找到HW interrupt ID对应的irq number(4)调用handle_IRQ(对于ARM平台)来处理该irq number

IRQ domain是为了解决多中断控制器的时候各个控制器对应的IRQ的分配和管理,贴一段原文在这里。

The current design of the Linux kernel uses a single large numberspace where each separate IRQ source is assigned a different number.This is simple when there is only one interrupt controller, but insystems with multiple interrupt controllers the kernel must ensurethat each one gets assigned non-overlapping allocations of LinuxIRQ numbers.The number of interrupt controllers registered as unique irqchipsshow a rising tendency: for example subdrivers of different kindssuch as GPIO controllers avoid reimplementing identical callbackmechanisms as the IRQ core system by modelling their interrupthandlers as irqchips, i.e. in effect cascading interrupt controllers.Here the interrupt number loose all kind of correspondence tohardware interrupt numbers: whereas in the past, IRQ numbers couldbe chosen so they matched the hardware IRQ line into the rootinterrupt controller (i.e. the component actually fireing theinterrupt line to the CPU) nowadays this number is just a number.For this reason we need a mechanism to separate controller-localinterrupt numbers, called hardware irq's, from Linux IRQ numbers.The irq_alloc_desc*() and irq_free_desc*() APIs provide allocation ofirq numbers, but they don't provide any support for reverse mapping ofthe controller-local IRQ (hwirq) number into the Linux IRQ numberspace.The irq_domain library adds mapping between hwirq and IRQ numbers ontop of the irq_alloc_desc*() API.  An irq_domain to manage mapping ispreferred over interrupt controller drivers open coding their ownreverse mapping scheme.irq_domain also implements translation from an abstract irq_fwspecstructure to hwirq numbers (Device Tree and ACPI GSI so far), and canbe easily extended to support other IRQ topology data sources.

IPI中断

在实现了irq_chip之后,中断控制器就有一定程度的抽象意义,不一定是真实存在的中断控制器. 通过在IPI 的irq_domain中分配核心相关IRQ中断线,然后在处理器上给中断控制器发送请求,在指定核心上产生中断。IPI中断可以用在核心间通信和数据传输,也能在核心休眠之后唤醒指定的处理器核心。

spurious interrupt

我可能触发了一个假中断。。。

  SPU -- a spurious interrupt is some interrupt that was raised then lowered  by some IO device before it could be fully processed by the APIC.  Hence  the APIC sees the interrupt but does not know what device it came from.  For this case the APIC will generate the interrupt with a IRQ vector  of 0xff. This might also be generated by chipset bugs.

[:/linux4.13.12/Documentation/filesystems/proc.txt]

原创粉丝点击