ack() と end() の 8259A な例

i8259A_irq_type にデフォルトで設定されている関数は (i386APIC をナニしてないアレでは)

  • ack には mask_and_ack_8259A()#arch/i386/kernel/i8259.c
  • end には end_8259A_irq()#arch/i386/kernel/i8259.c

となっております。(arch/i386/kernel/i8259.c による)

見てみます。
まず、mask_and_ack_8259A() から。これも分けて解析。コメントは削除。

    unsigned int irqmask = 1 << irq;
    unsigned long flags;

    spin_lock_irqsave(&i8259A_lock, flags);

    if (cached_irq_mask & irqmask)
        goto spurious_8259A_irq;

IRQ 番号なビットマスクを設定して spin_lock した後が amazon:Linux カーネル解析入門 曰く「spurious(偽の)IRQ対策」との事。if な条件判定時点で mask されているハズの IRQ から割り込みが発行された、という意味に見える。本当かどうかは goto の後で判定してどうするのか。

参照した資料は主に以下の二点。

goto した後どうなるのか、というと以下。(コメントは削除してますのでソース参照した方が良いです)

spurious_8259A_irq:
    if (i8259A_irq_real(irq))
        goto handle_real_irq;

    {
        static int spurious_irq_mask;
        if (!(spurious_irq_mask & irqmask)) {
            printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq);
            spurious_irq_mask |= irqmask;
        }
        atomic_inc(&irq_err_count);
        goto handle_real_irq;
    }

結局のところ、最終的に割り込み処理は実行するらしい。
まず最初に呼び出している i8259A_irq_real() で何をしているのか。TAG を引っ張ってみると同じファイルに記述されている模様。定義は以下。

static inline int i8259A_irq_real(unsigned int irq)
{
    int value;
    int irqmask = 1<<irq;

    if (irq < 8) {
        outb(0x0B,PIC_MASTER_CMD);    /* ISR register */
        value = inb(PIC_MASTER_CMD) & irqmask;
        outb(0x0A,PIC_MASTER_CMD);    /* back to the IRR register */
        return value;
    }
    outb(0x0B,PIC_SLAVE_CMD);    /* ISR register */
    value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
    outb(0x0A,PIC_SLAVE_CMD);    /* back to the IRR register */
    return value;
}

if 文の条件式で inline 展開ってどうなるのだろうか。
ま、そりゃ良いとして何をしてるか、とゆーと

  • OCW3 発行して読み込むレジスタを ISR に変更
  • ISR の読み込み
  • OCW3 発行して読み込むレジスタを IRR に戻す
  • irqmask と ISR のビット論理積を取って返却

をマスタ/スレイブで切り分けて実行、って感じ。

ISR は EOI が発行されたら必ずクリアされる、との事らしいので、返却値が 0 (偽) な状態は「嘘の割り込み」って理解で良いのだろうか。でも結局割り込みな処理に戻っているんですが (goto handle_real_irq;)。
他にも

  • spurious_irq_mask って何だよ
  • irq_err_count を何故に atomic_inc()#include/asm-i386/atomic.h で増分なのだ

等の謎は残るがとりあえず先に。(こら

で、ようやく以下。

handle_real_irq:
    if (irq & 8) {
        inb(PIC_SLAVE_IMR);    /* DUMMY - (do we need this?) */
        outb(cached_slave_mask, PIC_SLAVE_IMR);
        outb(0x60+(irq&7),PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
        outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
    } else {
        inb(PIC_MASTER_IMR);    /* DUMMY - (do we need this?) */
        outb(cached_master_mask, PIC_MASTER_IMR);
        outb(0x60+irq,PIC_MASTER_CMD);    /* 'Specific EOI to master */
    }
    spin_unlock_irqrestore(&i8259A_lock, flags);
    return;

やってる事はマスタ/スレイブの判断後に、

  • IMR の読み込み (コメントには DUMMY とある)
  • cached_{master,slave}_mask を IMR に出力
  • EOI の発行 (OCW2)

の処理を順次実行。スレイブの場合、EOI をマスタの 2番にも発行しているのがわかる。(仕様)


一連の処理を見るに、割り込み信号を優先、って事なんだろうか。(推測)

次は end_8259A_irq() です。これは短くってシンプル。

static void end_8259A_irq (unsigned int irq)
{
    if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) &&
                            irq_desc[irq].action)
        enable_8259A_irq(irq);
}

IRQ の状態が disable でも割り込み実行中でもなく、struct irqaction 型へのポインタである action がNULL でなければ enable_8259A_irq() を実行、と。

その enable_8259A_irq() を以下に。

void enable_8259A_irq(unsigned int irq)
{
    unsigned int mask = ~(1 << irq);
    unsigned long flags;

    spin_lock_irqsave(&i8259A_lock, flags);
    cached_irq_mask &= mask;
    if (irq & 8)
        outb(cached_slave_mask, PIC_SLAVE_IMR);
    else
        outb(cached_master_mask, PIC_MASTER_IMR);
    spin_unlock_irqrestore(&i8259A_lock, flags);
}

処理としては、割り込みが発生した IRQ 番号のフラグを cached_irq_mask から落として、IMR にもそれを反映させている。

寄り道が結構なボリュームになってしまいました。以降は __do_IRQ() の続きを。