割り込みコードの同期

マルチプロセッサ システムでのハードウェア割り込みを処理するドライバーのコードが複雑になる要因には、次のようなものがあります。

  • デバイスが割り込みを行うたびに、そのデバイスで揮発性の (デバイスが次に割り込みを行うと上書きされる) 割り込み固有の情報が発生する。

  • デバイスの割り込みは比較的高い IRQL で実行されるため、割り込みサービス ルーチン (ISR) が他のドライバー コードの実行を阻害する可能性がある。

  • ISR は、揮発性情報の保存中に別の割り込みが行われないよう、ドライバーから提供されるスピン ロックを保持したまま DIRQL で実行する必要がある。DIRQL を使用すると現在のプロセッサから割り込みが行われなくなり、スピン ロックを使用すると他のプロセッサから割り込みが行われなくなります。

  • ISR の実行中はデバイスが割り込みを行うことができないため、ISR は迅速に実行する必要がある。ISR の実行時間が長くなると、システムの処理速度が低下することがあり、データ損失が発生する可能性もあります。

  • 多くの場合、ISR と遅延プロシージャ呼び出し (DPC) ルーチンの両方が、ISR によってデバイスの揮発性情報が格納されるストレージ領域にアクセスする必要がある。これらのルーチンは、ストレージ領域に同時にアクセスすることがないよう、互いに同期する必要があります。

これらすべての要因を考慮して、割り込みを処理するドライバー コードの記述時には、次の規則に従う必要があります。

  • 揮発性の割り込みデータ (割り込み情報を格納するデバイス レジスタなど) には、EvtInterruptIsr コールバック関数からのみアクセスするようにします。

    EvtInterruptIsr コールバック関数では、ドライバーの EvtInterruptDpc コールバック関数または複数の EvtDpcFunc コールバック関数からのアクセスが可能な、ドライバーで定義された割り込みデータ バッファーに揮発性データを移動する必要があります。

    ドライバーで割り込みオブジェクト対して EvtInterruptIsrEvtInterruptDpc の各コールバック関数を指定する場合、割り込みデータの保存に最も適した場所は、割り込みオブジェクトのコンテキスト領域です。割り込みオブジェクトのコールバック関数は、受け取ったオブジェクト ハンドルを使用して、オブジェクトのコンテキスト領域にアクセスできます。

    ドライバーで各 EvtInterruptIsr コールバック関数に対して複数の EvtDpcFunc コールバック関数を指定する場合、割り込みデータを各 DPC オブジェクトのコンテキスト領域に保存できます。

  • 割り込みデータ バッファーにアクセスするすべてのドライバー コードを同期させて、一度に 1 つのルーチンのみがデータにアクセスするようにする必要があります。

    EvtInterruptIsr コールバック関数は、ドライバーが提供した割り込みオブジェクトのスピン ロックを保持したまま、このデータ バッファーに IRQL = DIRQL でアクセスします。このため、バッファーにアクセスするすべてのルーチンは、スピン ロックを保持したまま DIRQL で実行する必要があります (通常、バッファーにアクセスする必要があるその他のルーチンは、割り込みの EvtInterruptDpc コールバック関数または EvtDpcFunc コールバック関数だけです)。割り込みデータ バッファーにアクセスするすべてのルーチン (EvtInterruptIsr コールバック関数を除く) では、次の処理を行う必要があります。

    どちらの技法を使用しても、EvtInterruptDpc コールバック関数または EvtDpcFunc コールバック関数が、割り込みのスピン ロックを保持したまま DIRQL で割り込みデータにアクセスできるようになります。DIRQL を使用すると現在のプロセッサから割り込みが行われなくなり、スピン ロックを使用すると他のプロセッサから割り込みが行われなくなります。

    デバイスで複数の割り込みベクターまたはメッセージをサポートする場合に、これらの割り込みに対するドライバーの処理を同期するには、すべての割り込みオブジェクトに 1 つのスピン ロックを割り当てます。この場合、フレームワークが一連の割り込みの中で最も高い DIRQL を特定し、その他の割り込みベクターやメッセージによって同期対象のコードが中断されることのないよう、その割り込みが必ずその DIRQL でスピン ロックを取得します。

  • 割り込みを処理するコードの一部を IRQL = PASSIVE_LEVEL で実行する必要がある場合は、EvtInterruptDpc コールバック関数または EvtDpcFunc コールバック関数で 1 つ以上の作業項目を作成して、そのコードが EvtWorkItem コールバック関数として実行されるようにする必要があります。

  • ドライバーの EvtInterruptDpcEvtDpcFunc の各コールバック関数を互いに同期し、デバイスに関連付けられた他のコールバック関数とも同期する必要がある場合、ドライバーで割り込みの WDF_INTERRUPT_CONFIG 構造体と DPC オブジェクトの WDF_DPC_CONFIG 構造体の AutomaticSerialization メンバーを TRUE に設定します。また、ドライバーでフレームワークのスピン ロックを使用する方法もあります (AutomaticSerialization メンバーを TRUE に設定しても、EvtInterruptIsr コールバック関数と他のコールバック関数を同期することはできません。EvtInterruptIsr コールバック関数を同期するには、このトピックで前述したように WdfInterruptSynchronize または WdfInterruptAcquireLock を使用します)。

ドライバー ルーチンを同期化する方法の詳細については、「フレームワークベースのドライバーの同期技法」を参照してください。