1. 程式人生 > >Linux中斷體系結構

Linux中斷體系結構

depend machine lin extend process start eal 相關 開發

1.中斷處理體系結構

Linux內核將所有中斷統一編號,使用一個irq_desc結構數組來描述這些中斷。

數組聲明在/linux/kernel/irq/handle.c中,其中#define NR_IRQS 128,定義在/linux/include/asm/irq.h

 1 /*
 2  * Linux has a controller-independent interrupt architecture.
 3  * Every controller has a ‘controller-template‘, that is used
 4  * by the main code to do the right thing. Each driver-visible
5 * interrupt source is transparently wired to the appropriate 6 * controller. Thus drivers need not be aware of the 7 * interrupt-controller. 8 * 9 * The code is designed to be easily extended with new/different 10 * interrupt controllers, without having to do assembly magic or 11 * having to touch the generic code.
12 * 13 * Controller mappings for all interrupt sources: 14 */ 15 struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { 16 [0 ... NR_IRQS-1] = { 17 .status = IRQ_DISABLED, 18 .chip = &no_irq_chip, 19 .handle_irq = handle_bad_irq, 20 .depth = 1, 21 .lock
= __SPIN_LOCK_UNLOCKED(irq_desc->lock), 22 #ifdef CONFIG_SMP 23 .affinity = CPU_MASK_ALL 24 #endif 25 } 26 };
irq_desc結構的數據類型在/linuxinclude/linux/irq.h中定義,
 1 struct irq_desc {
 2     irq_flow_handler_t    handle_irq;
 3     struct irq_chip        *chip;
 4     struct msi_desc        *msi_desc;
 5     void            *handler_data;
 6     void            *chip_data;
 7     struct irqaction    *action;    /* IRQ action list */
 8     unsigned int        status;        /* IRQ status */
 9 
10     unsigned int        depth;        /* nested irq disables */
11     unsigned int        wake_depth;    /* nested wake enables */
12     unsigned int        irq_count;    /* For detecting broken IRQs */
13     unsigned int        irqs_unhandled;
14     spinlock_t        lock;
15 #ifdef CONFIG_SMP
16     cpumask_t        affinity;
17     unsigned int        cpu;
18 #endif
19 #if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
20     cpumask_t        pending_mask;
21 #endif
22 #ifdef CONFIG_PROC_FS
23     struct proc_dir_entry    *dir;
24 #endif
25     const char        *name;
26 } ____cacheline_internodealigned_in_smp;

handle_irq是這個或這組中斷的處理函數入口。發生中斷時,總入口函數asm_do_IRQ將根據中斷號調用相應irq_desc數組項中handle_irq.

typedef    void fastcall (*irq_flow_handler_t)(unsigned int irq,                struct irq_desc *desc);

handle_irq使用chip結構中的函數清除、屏蔽或者重新使能中斷,還要調用用戶在action鏈表中註冊的中斷處理函數。irq_chip結構類型也是在include/linux/irq.h中定義,其中的成員大多用於操作底層硬件,比如設置寄存器以屏蔽中斷,使能中斷,清除中斷等。註意這裏的成員name會出現在/proc/interrupts中。

struct irq_chip {
    const char    *name;
    unsigned int    (*startup)(unsigned int irq);
    void        (*shutdown)(unsigned int irq);
    void        (*enable)(unsigned int irq);
    void        (*disable)(unsigned int irq);

    void        (*ack)(unsigned int irq);
    void        (*mask)(unsigned int irq);
    void        (*mask_ack)(unsigned int irq);
    void        (*unmask)(unsigned int irq);
    void        (*eoi)(unsigned int irq);

    void        (*end)(unsigned int irq);
    void        (*set_affinity)(unsigned int irq, cpumask_t dest);
    int        (*retrigger)(unsigned int irq);
    int        (*set_type)(unsigned int irq, unsigned int flow_type);
    int        (*set_wake)(unsigned int irq, unsigned int on);

    /* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
    void        (*release)(unsigned int irq, void *dev_id);
#endif
    /*
     * For compatibility, ->typename is copied into ->name.
     * Will disappear.
     */
    const char    *typename;
};

irq_desc結構中的irqaction結構類型在include/linux/iterrupt.h中定義。用戶註冊的每個中斷處理函數用一個irqaction結構來表示,一個中斷比如共享中斷可以有多個處理函數,它們的irqaction結構鏈接成一個鏈表,以action為表頭。irqation結構在linux/include/linux/interrupt.h中定義如下:

typedef irqreturn_t (*irq_handler_t)(int, void *);

struct irqaction {
    irq_handler_t handler;
    unsigned long flags;
    cpumask_t mask;
    const char *name;
    void *dev_id;
    struct irqaction *next;
    int irq;
    struct proc_dir_entry *dir;
};

irq_desc結構數組、它的成員“struct irq_chip *chip” "struct irqaction *action",這3種數據結構構成了中斷處理體系的框架。下圖中描述了Linxu中斷處理體系結構的關系圖:

技術分享 中斷處理流程如下
(1)發生中斷時,CPU執行異常向量vector_irq的代碼
(2)在vector_irq裏面,最終會調用中斷處理的總入口函數asm_do_IRQ
(3)asm_do_IRQ根據中斷號調用irq_desc數組項中的handle_irq。
(4)handle_irq會使用chip成員中的函數來設置硬件,比如清除中斷、禁止中斷、重新使能中斷等
(5)handle_irq逐個調用用戶在aciton鏈表中註冊的處理函數
中斷體系結構的初始化就是構造這些數據結構,比如irq_desc數組項中的handle_irq、chip等成員;用戶註冊中斷時就是構造action鏈表;用戶卸載中斷時就是從action鏈表中去除不需要的項。
2.中斷處理體系結構的初始化 init_IRQ函數被用來初始化中斷處理體系結構,代碼在arch/arm/kernel/irq.c中
void __init init_IRQ(void)
{
    int irq;

    for (irq = 0; irq < NR_IRQS; irq++)
        irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;

#ifdef CONFIG_SMP
    bad_irq_desc.affinity = CPU_MASK_ALL;
    bad_irq_desc.cpu = smp_processor_id();
#endif
    init_arch_irq();
}

下面簡單分析下init_arch_irq();的獲取過程及調用順序

1 /*
2 init_arch_irq()的由來
3 定義一個空函數指針void (*init_arch_irq)(void) __initdata = NULL;
4 */
5 asmlinkage void __init start_kernel(void)
6     -->setup_arch(&command_line);
7         -->init_arch_irq = mdesc->init_irq;
8     -->init_IRQ();    
9         -->init_arch_irq()//即mdesc->init_irq=s3c24xx_init_irq

上述machine_desc結構在/linux/arch/arm/mach-s3c2410/mach-bast.c如下宏中獲得,後面會分析machine_desc結構。

MACHINE_START(BAST, "Simtec-BAST")
    //Maintainer: Ben Dooks <[email protected]> 
    .phys_io    = S3C2410_PA_UART,
    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    .boot_params    = S3C2410_SDRAM_PA + 0x100,
    .map_io        = bast_map_io,
    .init_irq    = s3c24xx_init_irq,
    .init_machine    = bast_init,
    .timer        = &s3c24xx_timer,
MACHINE_END

對於S3C2440開發板,這個函數就是s3c24xx_init_irq,移植machine_desc結構中的init_irq成員就指向這個函數s3c24xx_init_irq函數在arch/arm/plat-s3c24xx/irq.c中定義,它為所有中斷設置了芯片相關的數據結構(irq_desc[irq].chip),設置了處理函數入口(irq_desc[irq].handle_irq)。

Linux中斷體系結構