このページの目次
割込みサブシステム
カバー範囲: irq_desc/irq_chip/irq_domain → 割込み処理フロー (トップハーフ/ボトムハーフ) → softirq/tasklet/workqueue → スレッド化された割込み (threaded IRQ) → MSI/MSI-X → APIC/IOMMU による割込みリマップ カーネルバージョン: 2.6 ~ 6.x
概要
割込みサブシステムは、カーネルにおいてハードウェアと最も密接に結合している部分です。ネットワークカードがパケットを受信したり、ディスクがDMAを完了したり、タイマーが期限切れになったりすると、ハードウェアは割込みコントローラ (APIC/GIC) を介してCPUに割込み信号を送信します。カーネルの割込みサブシステムは、以下の役割を担います:割込みを正しいCPUへルーティングする、対応するデバイスドライバのハンドラへ分散する、そして時間のかかる処理をボトムハーフ (下半部) へ延期する。
現代の割込みサブシステムの中心的な抽象化は3層で構成されます:
- irq_chip: ハードウェア割込みコントローラ (IO-APIC, GIC など)
- irq_domain: ハードウェア割込み番号からLinux IRQ番号へのマッピング
- irq_desc: 各Linux IRQのディスクリプタ (ハンドラ、統計情報、アフィニティ)
ハードウェア割込み vs Linux IRQ
ハードウェア割込み番号 (hwirq):
割込みコントローラ内部の割込みラインの番号
異なるコントローラで同じhwirqが存在し得る
Linux IRQ番号 (virq):
カーネルが統一する仮想割込み番号
irq_domainを介してマッピングされる: virq = irq_domain->map(hwirq)
/proc/interrupts で表示されるのはLinux IRQ番号
irq_desc
// include/linux/irqdesc.h
;
irqaction: ドライバが登録したハンドラ
// include/linux/interrupt.h
;
割込み処理フロー
ハードウェアからカーネルへ
handle_irq_desc() パス
共有割込み
IRQF_SHARED: 複数のデバイスが同じ割込みラインを共有
→ 登録された全ハンドラが順に呼び出される
→ 各ハンドラは「自分のデバイスからの割込みかどうか」を確認する
(デバイスレジスタを読み取り、一致しない場合は → IRQ_NONEを返す)
→ 全ハンドラがIRQ_NONEを返す → 誤割込み (spurious interrupt)
+ spuriousカウントを増加 → 100k回継続 → この割込みを無効化
ボトムハーフ (下半部)
割込みハンドラは最も緊急の作業のみを行う (割込みの確認、デバイスのマスク、データのコピー)。時間のかかる処理 (プロトコルスタックの処理、キャッシュへの書き戻し) はボトムハーフに委ねる。
softirq
// kernel/softirq.c
// 事前に定義されたsoftirqの種類 (コンパイル時に決定):
;
// softirqは割込みコンテキストで実行される (ただし割込み有効)
// irq_exit() によってチェックがトリガされる:
// if (in_interrupt()) return; // ネストしたハードウェア割込み中でない場合
// do_softirq() → __do_softirq() → softirqビットマップを走査
// ksoftirqd: per-CPUカーネルスレッド
// softirqの処理が多すぎる場合 (MAX_SOFTIRQ_RESTART) → ksoftirqdへ委譲
// → CPUをユーザープロセスへ譲渡
tasklet (廃止済み、threaded IRQに置き換え)
// kernel/softirq.c
// taskletはsoftirq (HI_SOFTIRQ または TASKLET_SOFTIRQ) をベースにする
// 同種のtaskletは同時に実行されない (同期を簡素化)
// ⚠️ 2022以降のカーネルでは廃止: 新コードではthreaded IRQを使用
;
Threaded IRQ: 現在推奨される方法
// kernel/irq/manage.c
// 各割込みごとに独立したカーネルスレッドを持つことができる
// ハンドラが IRQ_WAKE_THREAD を返すと、カーネルは threaded_fn の実行をスケジューリングする
;
// handler: ハードウェア割込みコンテキストで実行 (最小限の作業のみ)
// thread_fn: 独立したカーネルスレッド内で実行 (スリープ可能、mutex取得可能)
//
// 利点:
// - 割込み無効時間の削減
// - 下半部でスリープ可能 (mutex取得、IO実行)
// - リアルタイムスケジューリングポリシーで管理可能
Workqueue: より重い下半部
// kernel/workqueue.c
// workqueueはプロセスコンテキスト (カーネルスレッド kworker) で実行される
// 適している用途: スリープが必要、長時間実行される作業
// tasklet/softirqとの主な違い:
// workqueue: プロセスコンテキスト、スリープ可能
// tasklet/softirq: 割込みコンテキスト、スリープ不可
; // システムworkqueue
; // 専用workqueue
MSI / MSI-X
// kernel/irq/msi.c
// メッセージシグナルド割込み (Message Signaled Interrupts)
// デバイスが直接メモリアドレスへ書き込み → 割込みコントローラ (割込みラインを経由しない)
// 利点:
// - 共有不要 (各MSIに一意のアドレスがある)
// - より高性能 (PCIeのposted write vs INTxの低速なレベル信号)
// - MSI-Xではデバイスが複数の割込みベクタを持てる (例: NVMeはキューごとに1つ)
// PCIデバイスでMSIを有効化:
;
→ デバイスが16個のMSIベクタを割り当て
→ Linux
割込みアフィニティと負荷分散
# 割込みの分布を確認
# 割込みアフィニティの設定 (どのCPUがどの割込みを処理するか)
# irqbalanceデーモンが自動でバランスを取る
# 割込みカウントとCPU負荷に基づいて判断
# 特定の割込み (例: NVMe) はNUMAローカルCPUに直接バインドされることがある
参考と拡張
- カーネルドキュメント:
Documentation/core-api/irq/,Documentation/PCI/msi-howto.rst - LWN: "The design of the interrupt subsystem", "Threaded interrupt handlers"
- ソースコード:
kernel/irq/— 割込みコア (handle, manage, chip, domain, msi)kernel/softirq.c— softirqとtaskletkernel/workqueue.c— workqueuearch/x86/kernel/irq.c— x86割込みエントリarch/x86/kernel/apic/— APICドライバ
キーワード: irq_desc, irq_chip, irq_domain, softirq, tasklet, threaded IRQ, workqueue, MSI/MSI-X, interrupt affinity