割り込みコードの同期

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

  • デバイスが割り込むたびに、その情報は次のデバイスが割り込んだときに上書きされる可能性があるため、揮発性の割り込み固有の情報が提供されます。

  • 比較的高い IRCL とその割り込みサービス ルーチン (ISR) でデバイスが割り込まれると、他のドライバー コードの実行が中断される可能性があります。

  • DIRQL 割り込みの場合、ISR はドライバーが提供するスピン ロックを保持しながら DIRQL で実行する必要があります。これにより、ISR は揮発性情報を保存しながら追加の割り込みを防ぐことができます。 DIRQL は現在のプロセッサによる中断を防ぎ、スピン ロックは別のプロセッサによる中断を防ぎます。

  • ISR の実行中にデバイスを中断できないため、ISR を迅速に実行する必要があります。 ISR の実行時間が長いと、システムが遅くなったり、データが失われる可能性があります。

  • ISR ルーチンと遅延プロシージャ コール (DPC) ルーチンは、通常、ISR がデバイスの揮発性データを格納する記憶域にアクセスする必要があります。 これらのルーチンは、同時に記憶域にアクセスしないように、相互に同期する必要があります。

これらすべての要因により、割り込みを処理するドライバー コードを記述するときは、次の規則を使用する必要があります。

  • EvtInterruptIsr コールバック関数のみが、割り込み情報を含むデバイス レジスタなどの揮発性割り込みデータにアクセスします。

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

    ドライバーが割り込みオブジェクトの EvtInterruptDpc または EvtInterruptWorkItem コールバック関数を提供する場合、割り込みデータを格納する最適な場所は、割り込みオブジェクトのコンテキスト空間です。 割り込みオブジェクトのコールバック関数は、受信したオブジェクト ハンドルを使用して、オブジェクトのコンテキスト空間にアクセスできます。

    ドライバーが EvtInterruptIsr コールバック関数ごとに複数の EvtDpcFunc コールバック関数を提供する場合は、各 DPC オブジェクトのコンテキスト空間に割り込みデータを格納できます。

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

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

    EvtInterruptIsr コールバック関数を除く、割り込みデータ バッファーにアクセスするすべてのルーチンは、次のいずれかを実行する必要があります。

    どちらの手法でも、EvtInterruptDpc 関数または EvtDpcFunc 関数は、割り込みのスピン ロックを保持しながら DIRQL の割り込みデータにアクセスできます。 DIRQL は現在のプロセッサによる中断を防ぎ、スピン ロックは別のプロセッサによる中断を防ぎます。

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

    パッシブレベルの割り込みオブジェクトの場合、フレームワークは、IRQL = PASSIVE_LEVEL でドライバーの EvtInterruptIsr コールバック関数を呼び出す前に、パッシブレベルの割り込みロックを取得します。 その結果、バッファーにアクセスするすべてのルーチンは、割り込みロックを取得するか、バッファー アクセスを内部的に同期する必要があります。 通常、割り込みの EvtInterruptWorkItem コールバック関数は、バッファーにアクセスする唯一の別のルーチンです。 EvtInterruptWorkItem コールバック関数から割り込みロックを取得する方法については、同ページの「解説」セクションを参照してください。

    また、1 つの待機ロックを複数のパッシブレベルの割り込みオブジェクトに割り当てることで、複数の割り込みベクトルのドライバーの処理を同期することもできます。

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

    または、KMDF バージョン 1.11 以降では、ドライバーは WdfInterruptQueueWorkItemForIsr を呼び出すことによって割り込み作業項目を要求できます。 (ドライバーの EvtInterruptIsr コールバック関数は、WdfInterruptQueueWorkItemForIsr または WdfInterruptQueueDpcForIsr を呼び出すことができますが、両方を呼び出すわけではありません)。

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

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