IoCompletion ルーチンの実装

入力時、IoCompletion ルーチンはコンテキスト ポインターを受け取ります。 ディスパッチ ルーチンが IoSetCompletionRoutine を呼び出すと、コンテキスト ポインターを指定できます。 このポインターは、IoCompletion ルーチンが IRP を処理するために必要な、ドライバーによって決定されたコンテキスト情報を参照できます。 IoCompletion ルーチンは IRQL = DISPATCH_LEVEL で呼び出すことができるため、コンテキスト領域をページングできない点に注意してください。

IoCompletion ルーチンの実装ガイドラインを次に示します。

  • IoCompletion ルーチンは、IRP の I/O ステータス ブロックをチェックして、I/O 操作の結果を判断できます。

  • 入力 IRP が IoAllocateIrp または IoBuildAsynchronousFsdRequest を使用してディスパッチ ルーチンによって割り当てられた場合、IoCompletion ルーチンは、可能であれば元の IRP を完了する前に、IoFreeIrp を呼び出してその IRP を解放する必要があります。

    • IoCompletion ルーチンは、可能であれば対応する IRP を解放する前に、ドライバー割り当て IRP に割り当てられたディスパッチ ルーチンを IRP ごとのリソースを解放する必要があります。

      たとえば、ディスパッチ ルーチンが IoAllocateMdl によって MDL を割り当て、割り当てられる部分転送 IRP に対して IoBuildPartialMdl を呼び出す場合、IoCompletion ルーチンは IoFreeMdl で MDL を解放する必要があります。 元の IRP に関する状態を保持するリソースを割り当てる場合、可能であれば元の IRP によって IoCompleteRequest を呼び出す前、かつ必ずコントロールを返す前に、それらのリソースを解放する必要があります。

      一般に、IRP を解放または完了する前に、IoCompletion ルーチンはディスパッチ ルーチンによって割り当てられた IRP ごとのリソースを解放する必要があります。 それ以外の場合、ドライバーはその IoCompletion ルーチンが元の要求の完了からコントロールを返す前に、解放されるリソースに関する状態を保持する必要があります。

    • IoCompletion ルーチンは、STATUS_SUCCESS を使用して元の IRP を完了できない場合、元の IRP の I/O ステータス ブロックを、IoCompletion ルーチンが元の要求に失敗する原因となったドライバー割り当て IRP で返された値に設定する必要があります。

    • IoCompletion ルーチンは、STATUS_PENDING を使用して元の要求を完了する場合、IoCompleteRequest を呼び出す前に、元の IRP で IoMarkIrpPending を呼び出す必要があります。

    • IoCompletion ルーチンがエラー STATUS_XXX で元の IRP を失敗させる必要がある場合、エラーをログに記録できます。 ただし、発生したデバイス I/O エラーをログに記録するのは、基になるデバイス ドライバーの役割であるため、IoCompletion ルーチンは通常エラーを記録しません。

    • IoCompletion ルーチンが処理され、ドライバー割り当て IRP を解放すると、ルーチンは、STATUS_MORE_PROCESSING_REQUIRED でコントロールを返す必要があります。

      IoCompletion ルーチンから STATUS_MORE_PROCESSING_REQUIRED を返すことにより、ドライバー割り当ての解放された IRP に対する I/O マネージャーの完了処理が事前に防止されます。 IoCompleteRequest を 2 回目に呼び出すと、I/O マネージャーは IRP の完了ルーチンの呼び出しを再開します。これは、STATUS_MORE_PROCESSING_REQUIRED を返したルーチンのすぐ上の完了ルーチンから始まります。

  • IoCompletion ルーチンが受信 IRP を再利用して下位ドライバーに 1 つ以上の要求を送信した場合、またはルーチンが失敗した操作を再試行した場合、IRP が再利用または再試行されるたびに IoCompletion ルーチンが維持するコンテキストをすべて更新する必要があります。 次に、次の下位ドライバーの I/O スタックの場所をもう一度設定し、独自のエントリ ポイントで IoSetCompletionRoutine を呼び出して、IRP の IoCallDriver を呼び出すことができます。

    • IoCompletion ルーチンは、IRP が再利用または再試行されるたびに IoMarkIrpPending を呼び出さないでください。

      ディスパッチ ルーチンが、元の IRP を既に保留中としてマークしています。 チェーン内のすべてのドライバーが IoCompleteRequest を使用して元の IRP を完了するまで、保留中のままです。

    • 要求を再試行する前に、IoCompletion ルーチンは、可能であれば返されたエラー情報を保存した後、ステータスが STATUS_SUCCESS、情報が 0 の I/O ステータス ブロックをリセットする必要があります。

      IoCompletion ルーチンは通常、再試行されるたびに、ディスパッチ ルーチンによって設定された再試行回数を減らします。 通常、IoCompletion ルーチンは IoCompleteRequest を呼び出し、制限された再試行回数が失敗した場合に IRP を失敗させる必要があります。

    • IoCompletion ルーチンは、IRP を再利用または再試行して IoSetCompletionRoutineIoCallDriver を呼び出した後、STATUS_MORE_PROCESSING_REQUIRED を返す必要があります。

      IoCompletion ルーチン から STATUS_MORE_PROCESSING_REQUIRED を返すと、再利用または再試行された IRP の I/O マネージャーの完了処理を事前に防止します。

    • IoCompletion ルーチンは、STATUS_SUCCESS を使用して元の IRP を完了できない場合、I/O ステータス ブロックを、IoCompletion ルーチンが IRP を失敗させる再利用または再試行操作のために下位ドライバーによって返された状態のままにします。

    • IoCompletion ルーチンは、STATUS_PENDING を使用して元の要求を完了する場合、IoCompleteRequest を呼び出す前に、元の IRP で IoMarkIrpPending を呼び出す必要があります。

    • IoCompletion ルーチンがエラー STATUS_XXX で元の IRP を失敗させる必要がある場合、エラーをログに記録できます。 ただし、発生したデバイス I/O エラーをログに記録するのは、基になるデバイス ドライバーの役割であるため、IoCompletion ルーチンは通常エラーを記録しません。

  • IRP で IoCompletion ルーチンを設定し、下位ドライバーに IRP を渡すドライバーはすべて、IoCompletion ルーチンで IRP->PendingReturned フラグをチェックする必要があります。 フラグが設定されている場合、IoCompletion ルーチンは、IRP で IoMarkIrpPending を呼び出す必要があります。 ただし、IRP を渡し、イベントを待機するドライバーは、IRP を保留中にマークすべきではない点に注意してください。 代わりに、その IoCompletion ルーチンはイベントを通知し、STATUS_MORE_PROCESSING_REQUIRED を返す必要があります。

  • IoCompletion ルーチンは、可能であれば IoCompletion ルーチンが元の IRP で IoCompleteRequest を呼び出す前、かつ必ず IoCompletion ルーチンが元の IRP の完了からコントロールを返す前に、元の IRP を処理するために割り当てられたディスパッチ ルーチンのすべてのリソースを解放する必要があります。

上位レベルのドライバーが元の IRP で IoCompletion ルーチンを設定している場合、そのドライバーの IoCompletion ルーチンは、下位レベルのすべてのドライバーの IoCompletion ルーチンが呼び出されるまで呼び出されません。

IoCompleteRequest の呼び出しでの Priority Boost の提供

最下位レベルのデバイス ドライバーがディスパッチ ルーチンで IRP を完了できる場合、IO_NO_INCREMENT の PriorityBoost を使用して IoCompleteRequest を呼び出します。 元の要求元がその I/O 操作の完了を待たなかったとドライバーが想定できるため、実行時の優先度引き上げは必要ありません。

それ以外の場合、最下位レベルのドライバーは、要求元がそのデバイス I/O 要求で待機した時間を補うため、要求元の実行時優先度を引き上げるシステム定義およびデバイスの種類固有の値を提供します。 引き上げ値については、Wdm.h または Ntddk.h をご覧ください。

上位レベルのドライバーは、IoCompleteRequest を呼び出すとき、それぞれの基になるデバイス ドライバーと同じ PriorityBoost を適用します。

IoCompleteRequest の呼び出しの効果

ドライバーが IoCompleteRequest を呼び出すと、I/O マネージャーは、IRP に対して呼び出される IoCompletion ルーチンを設定している次の上位レベルのドライバー (ある場合) を呼び出す前、そのドライバーの I/O スタックの場所をゼロで埋めます。

上位レベルのドライバーの IoCompletion ルーチンは、IRP の I/O ステータス ブロックのみをチェックし、下位ドライバーすべてが要求をどのように処理したかを判断できます。

IoCompleteRequest の呼び出し元は、完了したばかりの IRP へのアクセスを試みてはなりません。 そのような試行は、システム クラッシュの原因となるプログラミング エラーです。