2009年3月17日 星期二

Linux Interrupts and Exceptions - 3

I/O Interrupt (硬體裝置的中斷) 處理函式是以 request_irq() 來向核心註冊的,並且以 free_irq() 來清除

定義於 include/linux/interrupt.h

int request_irq(
    unsigned int  irq,
    irqreturn_t   (*handler)(int, void *, struct pt_regs *),
    unsigned long  irqflags,
    const char   *devname,
    void      *dev_id
  );

void free_irq(
    unsigned int irq,
    void     *dev_id
  );

struct irqaction {
  irqreturn_t (*handler)(int, void *, struct pt_regs *);
  unsigned long flags;
  cpumask_t mask;
  const char *name;
  void *dev_id;
  struct irqaction *next;
  int irq;
  struct proc_dir_entry *dir;
};

request_irq() 函式內部則是呼叫 setup_irq() 來進行 IRQ Line 的註冊
其動作是將要求中斷處理的資訊包裝成 struct irqaction 的結構,然後記錄於 irq_desc 陣列中的 IRQ Line 的位置
若有共用 IRQ Line 的中斷,則會以鏈結串列的方式掛在同一個陣列的位置上,如下圖所示

圖中的 hw_interrupt_type 是一個描述中斷控制器的資料結構,用來抽象化不同的中斷控制硬體
例如:舊式的 8259A,或新式的 APIC,也可能是無任何中斷控制器

定義於 include/linux/irq.h

/*
* Interrupt controller descriptor. This is all we need
* to describe about the low-level hardware.
*/
struct hw_interrupt_type {
  const char * typename;
  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 (*end)(unsigned int irq);
  void (*set_affinity)(unsigned int irq, cpumask_t dest);
};

typedef struct hw_interrupt_type hw_irq_controller;

定義於 arch/i386/kernel/i8259.c

static struct hw_interrupt_type i8259A_irq_type = {
  "XT-PIC",
  startup_8259A_irq,
  shutdown_8259A_irq,
  enable_8259A_irq,
  disable_8259A_irq,
  mask_and_ack_8259A,
  end_8259A_irq,
  NULL
};

定義於 arch/i386/kernel/io_apic.c

/*
* Level and edge triggered IO-APIC interrupts need different handling,
* so we use two separate IRQ descriptors. Edge triggered IRQs can be
* handled with the level-triggered descriptor, but that one has slightly
* more overhead. Level-triggered interrupts cannot be handled with the
* edge-triggered handler, without risking IRQ storms and other ugly
* races.
*/
static struct hw_interrupt_type ioapic_edge_type = {
  .typename  = "IO-APIC-edge",
  .startup   = startup_edge_ioapic,
  .shutdown  = shutdown_edge_ioapic,
  .enable    = enable_edge_ioapic,
  .disable   = disable_edge_ioapic,
  .ack     = ack_edge_ioapic,
  .end    = end_edge_ioapic,
  .set_affinity = set_ioapic_affinity,
};

static struct hw_interrupt_type ioapic_level_type = {
  .typename   = "IO-APIC-level",
  .startup   = startup_level_ioapic,
  .shutdown   = shutdown_level_ioapic,
  .enable    = enable_level_ioapic,
  .disable   = disable_level_ioapic,
  .ack     = mask_and_ack_level_ioapic,
  .end     = end_level_ioapic,
  .set_affinity = set_ioapic_affinity,
};

定義於 kernel/irq/handle.c

struct hw_interrupt_type no_irq_type = {
  .typename = "none",
  .startup = startup_none,
  .shutdown = shutdown_none,
  .enable = enable_none,
  .disable = disable_none,
  .ack = ack_none,
  .end = end_none,
  .set_affinity = NULL
};

沒有留言: