ack() と end() の 8259A な例
i8259A_irq_type にデフォルトで設定されている関数は (i386 で APIC をナニしてないアレでは)
- 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 の後で判定してどうするのか。
参照した資料は主に以下の二点。
- 8259A に関する wiki
http://community.osdev.info/index.php?(PIC)8259A - intel が発行した (と思われる) マニュアル
http://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf
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 展開ってどうなるのだろうか。
ま、そりゃ良いとして何をしてるか、とゆーと
をマスタ/スレイブで切り分けて実行、って感じ。
ISR は EOI が発行されたら必ずクリアされる、との事らしいので、返却値が 0 (偽) な状態は「嘘の割り込み」って理解で良いのだろうか。でも結局割り込みな処理に戻っているんですが (goto handle_real_irq;)。
他にも
等の謎は残るがとりあえず先に。(こら
で、ようやく以下。
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() の続きを。