割り込みサブシステム

このページの目次

デバイスは絶えず割り込みを通知しますが、マスクしても DMA がメモリへの書き込みを続けることがあります。ここでは、通知、デバイスの状態、バックグラウンド処理をそれぞれ追跡する必要があります。

参照目標読書位置
番号付けとディスパッチ第 1~3 節
実行コンテキストと遅延処理第 4~5 節
終了と有効なアフィニティ第 6~7 節

割り込みは、デバイスが CPU にイベントを通知するためのメカニズムです。1 つの通知が複数の完了したリクエストに対応することもあり、1 つのリクエストが複数の通知に関与することもあります。したがって、ドライバは最終的にデバイスの状態、キュー、または完了記録を確認すべきであり、割り込みカウントをそのままビジネスの完了数として扱うべきではありません。

1. 3つの番号付けと4つのオブジェクト

概念責任混同しやすいオブジェクト
hwirq特定の割り込みドメイン内のハードウェア番号システム全体で一意な番号ではない
Linux IRQカーネルが管理する IRQ の論理識別子x86 CPU ベクターに等しくない
CPU ベクター / アーキテクチャ割り込み IDアーキテクチャのエントリポイントでの識別とルーティングAPIC、GIC などのメカニズムに応じて具体的な意味が異なる
irq_domainハードウェアドメインと Linux IRQ の関係をマッピング・管理階層化可能であり、単なる固定オフセットの追加ではない
irq_chipマスク、ack、EOI、アフィニティなどのコントローラ操作ドライバがデバイスのビジネスを処理するハンドラに等しくない
irq_descIRQ の状態、制御フロー、統計、action など1 つの IRQ が複数の共有 action を関連付けることができる
irqactionドライバが登録した処理関数、引数、スレッド情報必ずしも1つのエントリが1本の排他的な物理線を対応するわけではない

1つの GPIO コントローラが別の割り込みコントローラの下部として機能することができ、割り込みドメインは階層を構成する可能性があります。PCI MSI も、MSI ドメイン、リマップ、CPU ルーティングなどの多層管理を経る場合があります。ドライバは通常、サブシステムが提供する Linux IRQ を取得して処理関数を登録しますが、ハードウェア番号を推測すべきではありません。irq_domain ドキュメント を参照してください。

2. イベントのディスパッチと確認

ハードウェア通知からドライバ処理へ:各層には異なる責任がある デバイスがイベントを生成 電圧レベル、エッジ、または MSI メッセージ;必ずしも各イベントが1回の通知に対応するわけではない 1 割り込みコントローラとアーキテクチャエントリ CPU へルーティングされ、対応する例外/割り込み処理パスに入る 2 Linux IRQ 記述と flow handler マスク、ack、EOI などのコントローラ操作を調整 3 ドライバのプライマリハンドラ ソースを識別し、必要な状態を処理するか、IRQ_WAKE_THREAD を返す 4 後続処理とリソース回収 IRQ スレッド、NAPI、通常 workqueue などは要件に応じて選択 5 EOI、デバイスの状態クリア、IRQ マスク、DMA 停止は同じ動作ではありません。

アーキテクチャエントリは必要なコンテキストを保存し、カーネル処理環境を構築します。スタックの切り替えとレジスタ保存の具体的なルールは、アーキテクチャとエントリタイプによって異なります。システムコールエントリ、x86 外部割り込み、ARM 例外処理を同じ固定関数チェーンに書くことはできません。

generic IRQ レイヤは、その IRQ 用にインストールされた flow handler を呼び出し、次に irq_chip とドライバ action を呼び出します。generic_handle_irq_desc() は一般的なディスパッチ関連インターフェースであり、handle_edge_irq()、handle_fasteoi_irq() と並べて3つのトリガータイプとして描くべきではありません。

マスク、ack、EOI とデバイス状態

操作処理される状態の層
マスク通知を継続して配信するかどうか
ackコントローラの認識/応答
EOIコントローラに対して関連するサービスフェーズの終了を通知
デバイス状態のクリア、完了キューの消費ドライバがデバイスプロトコルに従って真のイベントソースを処理

コントローラの確認とデバイスのビジネス完了は別々に発生するため、一方を他方に置き換えることはできません。

レベルソースがまだ有効な場合、マスク解除後に再度通知される可能性があります。エッジソースでは、通知がラッチされているか、処理中に新しいエッジが発生するかどうかを考慮する必要があります。異なる flow handler はこれらの制約に基づいて操作を構成し、fasteoi も一律に「まずマスクし、処理し、最後にアンマスクする」として記述してはいけません。その核心は対応する EOI プロトコルであり、状態や oneshot などの条件に基づいてマスクを処理します。

コントローラへの EOI の送信は、デバイスの割り込み原因を自動的にクリアしません。IRQ のマスクも DMA を自動的に停止しません。この2層を逆にすると、割り込み嵐や解放後の DMA 書き込みの原因となる可能性があります。generic IRQ を参照してください。

デバイスソース、コントローラのサービス状態、およびマスクビットを別々に操作します。EOI のみを送信してレベルソースをクリアしない場合、再配信が発生する可能性があります。人為的に新しいデバイスイベントを作成する必要はありません。

図を準備しています
EOI でサービスは終了するが、デバイス原因はクリアされない

デバイス原因、コントローラの in-service 状態、およびマスク状態は異なるオブジェクトです。EOI はデバイス原因をクリアせず、マスクは DMA を停止したりビジネスキューをクリアしたりしません。

3. 共有 IRQ と戻り値

共有ライン上のハンドラは、イベントが自分自身のものかどうかを確認する必要があります。dev_id は対応する登録項目とプライベートコンテキストを識別し、解除時にも一致させる必要があります。ソースを識別できない状態で無条件に割り込み処理の成功を宣言してはいけません。

戻り値意味
IRQ_NONEこのハンドラは、自分自身のイベントを処理したことを確認していない
IRQ_HANDLED自分自身の割り込みイベントを処理した
IRQ_WAKE_THREAD登録された thread_fn の実行を要求

IRQ_WAKE_THREAD は戻り値であり、IRQF_WAKE_THREAD という名前の登録フラグではありません。継続的に未処理の IRQ は、カーネルの例外統計と処理をトリガーする可能性がありますが、閾値とパスは実装に依存するため、「ちょうど10万回で必ず終了する」と硬く書き込むことはできません。

MSI は通常、従来の共有ラインのソース識別問題を回避しますが、キューのマージ、割り込みの集約、および完了処理は依然として存在します。pci_alloc_irq_vectors() は実際に割り当てられた数を返し、その後 pci_irq_vector() を使用して各 IRQ を取得します。これは申請上限を固定して返すわけでも、最初の IRQ を直接返すわけでもありません。詳細はPCIe と USB ドライバモデル を参照してください。

4. プライマリハンドラとスレッド化 IRQ

request_threaded_irq() は、プライマリハンドラと thread_fn を登録できます。プライマリハンドラは、実際のハード割り込みパス内で短く、スリープしてはなりません。後続のスレッド処理が必要な場合は、IRQ_WAKE_THREAD を返します。

IRQF_ONESHOT を使用すると、コアは規約に従ってスレッド処理中に IRQ マスクを維持し、関連する処理が完了する前に再配信されるのを防ぎます。これは、1つのハードウェアイベントがちょうど1回のスレッドウェイクアップに対応することを保証するものではなく、スレッドは通常、デバイスの状態またはキューを排空する必要があります。

制約を満たす場合、デフォルトのプライマリハンドラを thread_fn と組み合わせて使用できます。共有ラインでは、特にソースの正しい識別と調整が必要です。PREEMPT_RT の強制スレッド化と例外は、実際のコンテキストを変更するため、プロセス名や API 名に基づいてすべてのハンドラがスリープ可能であると推測してはいけません。

IRQ スレッドは適切なスリープ操作を実行できますが、raw spinlock を保持している場合や、自分自身によってブロックされたパスに依存している場合に待機することはできません。スレッド化はハード割り込みセグメントの長さを短縮しますが、ロック順序やリソースの寿命を自動的に修復するものではありません。

5. softirq、tasklet、および workqueue

メカニズム実行特徴設計上の境界
softirq固定カテゴリ、CPU ごとに保留および実行、ksoftirqd によって処理される可能性あり通常の softirq コールバックは随意にスリープできず、CPU を跨ぐ並行実行が可能
taskletsoftirq ベースの古い遅延メカニズム同一の tasklet インスタンスは同時に実行されないが、すべての同種 tasklet が排他的ではない
通常 workqueueワーカープールがプロセスコンテキストを提供ルールに従ってスリープ可能であり、専用キューが必ずしも固定の排他スレッドを意味するわけではない
WQ_BH workqueueこのインターフェースをサポートするカーネル内で BH/softirq コンテキストを使用通常 workqueue のスリープ仮定をそのまま適用できない
IRQ スレッドIRQ action と関連付けられたカーネルスレッドデバイスプロトコルおよび oneshot/共有などの制約を処理

ksoftirqd の存在が、softirq コールバックを mutex を自由に使用できる通常のワーク関数にするものではありません。tasklet はまだいくつかのカーネルパスに存在しますが、新しい実装ではより適切なスレッド化、NAPI、または workqueue メカニズムを選択すべきであり、単に「カーネルから削除された」と主張してはいけません。

10回の通知で、なぜ work が10回実行されないのか

同一の work がすでに pending の場合、queue_work() は false を返す可能性があります。これは、「この work を実行する必要がある」というスケジューリング状態を保存するものであり、10個の独立したビジネスイベントではありません。

10回の IRQ がすべて同じ work をスケジュールする場合、イベントの数は、他のキュー/カウントによって保存するか、work がデバイスの照会可能な完了状態を排空することによって維持されなければなりません。これにより、スケジュール通知がマージされても、ビジネスの事実は1つだけ残ることはありません。

複数の work の並行実行、順序、およびメモリ回収コンテキストは、queue flags、pool、および設定によって決定されます。厳密な順序が必要な場合は、対応する ordered インターフェースを使用します。workqueue ドキュメント を参照してください。

6. 終了パス:新しいアクティビティを先に阻止し、古いアクティビティを待機する

終了時には、依存関係に従って段階的にアクティビティソースをシャットダウンします。

フェーズ確立すべき条件
新しい送信の停止新しいビジネスを生成しない
デバイスソースと DMA の制御ハードウェアが危険なアクセスを生成しない
IRQ の解除/同期対応するハンドラがリソースを使用しない
バックグラウンドワークのキャンセルまたは待機以前にキューに入れられたコールバックがコンテキストにアクセスしない
リソースの解放前述の使用者が退出した

実際の順序はドライバの依存関係に影響されます。特に、あるコールバックが停止したリソースを再起動しないように防止することが重要です。

disable_irq_nosync() は、実行中のハンドラを待機しません。disable_irq() や synchronize_irq() などの待機インターフェースは、ハンドラが必要とするロックを保持している状態で呼び出すことはできません。synchronize_irq() 自体が、常に後続の新しい IRQ を禁止する措置ではありません。

free_irq() は待機して対応する処理登録を取り消しますが、デバイスハードウェアを自動的に停止するわけでも、以前にキューに入れられた work を自動的にキャンセルするわけでもありません。共有 IRQ の場合、IRQ 全体を無効化して他のデバイスに影響を与えることはできません。まず、自分のデバイスソースを正しく制御することが優先されます。

7. アフィニティと読み取り専用観察

/proc/irq/N/smp_affinity_list は要求された CPU コレクションを表し、effective_affinity_list はサポートされている場合、有効なルーティングを表示します。managed IRQ、コントローラの能力、CPU hotplug、irqbalance、およびドライバポリシーが実際の結果に影響を与える可能性があります。

from pathlib import Path

for irq in sorted(Path('/proc/irq').glob('[0-9]*'), key=lambda p: int(p.name))[:12]:
    values = {}
    for field in ('smp_affinity_list', 'effective_affinity_list'):
        try:
            values[field] = (irq / field).read_text().strip()
        except OSError:
            pass
    print(irq.name, values)

/proc/interrupts には、特定のアーキテクチャの特殊イベント行も含まれており、各行がすべて通常の再割り当て可能なアフィニティを持つデバイス IRQ であるわけではありません。カウントの増加が1つの CPU に集中していることが必ずしもエラーであるとは限りません。キューマッピング、NUMA、NAPI、およびビジネス負荷と組み合わせて判断する必要があります。