I/O 操作の進行の保証
システムのページング デバイス用のストレージ ドライバーなど、一部のドライバーは、重要なシステム データが失われないようにするために、それらがサポートする I/O 操作の少なくとも一部を障害なしで実行する必要があります。ドライバー障害の考えられる原因として、メモリ不足の状況が挙げられます。フレームワークまたはドライバーは、I/O 要求を処理するための十分なメモリを割り当てることができない場合、エラー状態の値を伴ってその I/O 要求を完了することで、I/O 要求を失敗として処理しなければならないことがあります。
Version 1.9 より古いバージョンの KMDF では、I/O マネージャーがドライバーに送信した I/O 要求パケット (IRP) に対して、フレームワークがフレームワーク要求オブジェクトを割り当てることができない場合、フレームワークは常にその I/O 要求を失敗として処理します。メモリ不足の状況時にドライバーが I/O 要求を処理できるようにするために、Version 1.9 以降のフレームワークには、I/O キュー対応の保証進行機能が用意されています。**
この機能により、フレームワークおよびドライバーは、一連の要求オブジェクトに対して、および要求関連のドライバー コンテキスト バッファーに対して、それぞれメモリを事前に割り当てることができます。フレームワークおよびドライバーは、システム メモリの量が少ない場合に限り、この事前に割り当てられたメモリを使用します。
保証進行の機能
ドライバーは、フレームワークの I/O キュー対応の保証進行機能を使用することで、以下を実行できます。
メモリ不足の状況時に特定の I/O キューで使用する一連の要求オブジェクトを事前に割り当てるようにフレームワークに要求する。
要求固有のリソースを事前に割り当てるコールバック関数を提供する。ドライバーは、メモリ不足の状況時に、事前に割り当てられた要求オブジェクトをフレームワークから受けった場合に、これらのリソースを使用できます。
メモリ不足の状況が検出されなかった場合に、I/O 要求にドライバー固有のリソースを割り当てる別のコールバック関数を提供する。**これにより、メモリ不足が原因でこのコールバック関数の割り当てが失敗した場合に、事前に割り当てられた要求オブジェクトのいずれかをフレームワークで使用する必要があるかどうかを示せます。
事前に割り当てられた要求オブジェクトを、どの I/O 要求が使用する必要があるかを指定する。オプションには、すべての IRP に対してオブジェクトを使用する、ページング I/O 操作が進行中の場合に限り事前に割り当てられたオブジェクトを使用する、追加のドライバー コールバック関数で各 IRP を検査して事前に割り当てられたオブジェクトを使用するかどうかを判断する、などがあります。
ドライバーが 1 つ以上の I/O キューに対して保証進行機能を実装している場合、ドライバーは、メモリ不足時により適切に I/O 要求を処理できます。保証進行機能は、デバイスの既定の I/O キュー、およびドライバーが WdfDeviceConfigureRequestDispatching を呼び出すことにより構成する I/O キューに対して実装できます。
フレームワークの保証進行機能がドライバーに対して動作するのは、ドライバーとドライバーの I/O ターゲットの両方が保証進行機能を実装している場合に限ります。つまり、ドライバーでデバイス用の保証進行機能を実装する場合、デバイスのドライバー スタック内のすべての低レベル ドライバーでも保証進行機能を実装する必要があります。
I/O キューに対する保証進行機能の有効化
I/O キューに対して保証進行機能を有効にするには、ドライバーで WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY 構造体を初期化し、その後 WdfIoQueueAssignForwardProgressPolicy メソッドを呼び出します。ドライバーで WdfDeviceConfigureRequestDispatching を呼び出して I/O キューを構成する場合、WdfIoQueueAssignForwardProgressPolicy を呼び出す前に行う必要があります。
ドライバーで WdfIoQueueAssignForwardProgressPolicy を呼び出す場合、次の 3 つのイベント コールバック関数を指定できます。これらはすべてオプションです。
EvtIoAllocateResourcesForReservedRequest
ドライバーの EvtIoAllocateResourcesForReservedRequest コールバック関数は、フレームワークがメモリ不足の状況に備えて予約した要求オブジェクトに対して、要求固有のリソースを割り当てて保存します。フレームワークは、予約済みの要求オブジェクトを作成するたびに、このコールバック関数を呼び出します。ドライバーでは、通常、予約済み要求オブジェクトのコンテキスト領域を使用して、要求固有のリソースを 1 つの I/O 要求に割り当てる必要があります。
EvtIoAllocateRequestResources
ドライバーの EvtIoAllocateRequestResources コールバック関数は、要求固有のリソースを割り当てて即座に使用できるようにします。このコールバック関数は、フレームワークが IRP を受け取り、IRP 用の要求オブジェクトを作成した直後に呼び出されます。コールバック関数のリソース割り当て試行が失敗した場合、コールバック関数はエラー状態値を返します。その後、フレームワークは、新しく作成された要求オブジェクトを削除し、予約済み要求オブジェクトのいずれかを使用します。次に、EvtIoAllocateRequestResources コールバック関数が事前に割り当てた要求固有のリソースを、ドライバーの要求ハンドラーが使用します。
EvtIoWdmIrpForForwardProgress
ドライバーの EvtIoWdmIrpForForwardProgress コールバック関数は、IRP を検査し、IRP に対して予約済み要求オブジェクトを使用するようにフレームワークに通知するか、またはエラー状態値を使用して I/O 要求を完了することで I/O 要求を失敗として処理するようにフレームワークに通知します。フレームワークは、フレームワークが新しい要求オブジェクトを作成できず、かつ (ドライバーの WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY 構造体でフラグを設定することにより) メモリ不足時にドライバーで IRP を検査するように指定した場合に限り、このコールバック関数を呼び出します。つまり、ドライバーは各 IRP にアクセスして、メモリ不足時でもその IRP を処理する必要があるかどうかを決定できます。
ドライバーは、WdfIoQueueAssignForwardProgressPolicy を呼び出す際に、フレームワークがメモリ不足の状況に備えて事前に割り当てる予約済み要求オブジェクトの数も指定します。要求オブジェクトの数は、デバイスおよびドライバーに応じて選択できます。パフォーマンスが低下するのを防ぐには、通常、ドライバーおよびデバイスが並列処理できる I/O 要求の数に近い数をドライバーで指定する必要があります。
ただし、ドライバーの WdfIoQueueAssignForwardProgressPolicy の呼び出し、および EvtIoAllocateResourcesForReservedRequest コールバック関数によって、非常に多くの予約済み要求オブジェクトや要求固有のリソース メモリが事前に割り当てられる場合、ドライバーがメモリ不足の原因になる可能性があります。最適な数を選択するためには、ドライバーおよびデバイスのパフォーマンスをテストし、低メモリ シミュレーションを追加する必要があります。
フレームワークは、WdfIoQueueAssignForwardProgressPolicy が戻る前に、ドライバーにより指定された数の要求オブジェクトを作成して予約します。フレームワークは、要求オブジェクトを予約するたびに、直ちにドライバーの EvtIoAllocateResourcesForReservedRequest コールバック関数を呼び出します。これは、フレームワークが実際に予約済みの要求オブジェクトを使用する場合に、ドライバーが要求固有のリソースを割り当てて保存できるようにするためです。
ドライバーの要求ハンドラーの 1 つが I/O キューから I/O 要求を受け取った場合、その要求ハンドラーは WdfRequestIsReserved メソッドを呼び出すことで、フレームワークがメモリ不足の状況に備えて事前に割り当てた要求オブジェクトがどうかを特定できます。このメソッドが TRUE を返す場合、ドライバーでは、EvtIoAllocateResourcesForReservedRequest コールバック関数が予約したリソースを使用する必要があります。
フレームワークは、予約済み要求オブジェクトのいずれかを使用した場合、ドライバーが要求を完了した後に、そのオブジェクトを一連の予約済みオブジェクトに返します。フレームワークは、要求オブジェクト、およびドライバーが WdfDeviceInitSetRequestAttributes または WdfObjectAllocateContext を呼び出すことで作成したコンテキスト領域を保存し、別のメモリ不足の状況が発生した場合に再使用できるようにします。
フレームワークおよびドライバーで保証進行機能をサポートする方法
ドライバーおよびフレームワークで I/O キューの保証進行機能をサポートするには、次の手順を実行します。
ドライバーは WdfIoQueueAssignForwardProgressPolicy を呼び出します。
フレームワークは、応答として、ドライバーが指定する数の要求オブジェクトを割り当てて保存します。ドライバーが事前に WdfDeviceInitSetRequestAttributes を呼び出している場合、各割り当てには、WdfDeviceInitSetRequestAttributes により指定されたコンテキスト領域が含まれます。
また、ドライバーで EvtIoAllocateResourcesForReservedRequest コールバック関数が提供されている場合、フレームワークは、要求オブジェクトを割り当てて保存するたびに、そのコールバック関数を呼び出します。
I/O マネージャーによりドライバーに送信された I/O 要求パケット (IRP) をフレームワークが受け取ります。
フレームワークは、IRP に対して要求オブジェクトの割り当てを試行します。ドライバーが要求タイプ用に作成した I/O キューが保証進行機能をサポートしている場合、次の手順は、割り当ての成否によって異なります。
要求オブジェクトの割り当てが成功した場合
ドライバーが EvtIoAllocateRequestResources コールバック関数を提供している場合、フレームワークはそれを呼び出します。コールバック関数が STATUS_SUCCESS を返すと、フレームワークは要求を I/O キューに追加します。コールバック関数がエラー状態値を返すと、フレームワークは作成された要求オブジェクトを削除し、事前に割り当てられた要求オブジェクトのいずれかを使用します。ドライバーの要求ハンドラーは、要求オブジェクトを受け取ると、その要求オブジェクトが事前に割り当てられたものかどうか、つまり、事前に割り当てられたドライバー リソースを使用する必要があるかどうかを判定します。
ドライバーが EvtIoAllocateRequestResources コールバック関数を提供していない場合、ドライバーが保証進行機能を有効にしていない場合と同様に、フレームワークは要求を I/O キューに追加します。**
要求オブジェクトの割り当てが失敗した場合
フレームワークが次に実行する内容は、ドライバーが WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY 構造体の ForwardProgressReservedPolicy メンバーに提供した値に基づきます。このメンバーは、予約済み要求をいつ使用するかをフレームワークに通知します。I/O 要求がページング I/O 操作である場合、または EvtIoWdmIrpForForwardProgress コールバック関数が予約済み要求の使用を指定している場合に限り、常に予約済み要求が使用されます。
すべての場合において、ドライバーの要求ハンドラーは、WdfRequestIsReserved を呼び出して、フレームワークが予約済み要求オブジェクトを使用したかどうかを特定できます。使用した場合、ドライバーは、EvtIoAllocateResourcesForReservedRequest コールバック関数が割り当てた要求リソースを使用する必要があります。
保証進行のシナリオ
システムのページング ファイルを格納する可能性があるストレージ デバイス用のドライバーを作成しているとします。ここで重要なのは、ページング ファイルからの読み取り操作、およびページング ファイルへの書き込み操作が成功することです。
読み取り操作用および書き込み操作用の個別の I/O キューを作成し、これらの両方の I/O キューに対して保証進行機能を有効にします。また、他の全種類の要求用の I/O キューを作成します。この 3 番目の キューに対しては、保証進行機能を有効にしません。
ドライバー スタックおよびデバイスは、4 つの書き込み操作を並列処理できるため、WdfIoQueueAssignForwardProgressPolicy を呼び出す前に、WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY 構造体の TotalForwardProgressRequests メンバーを 4 に設定します。
ドライバーのデバイスがページング デバイスであり、そのためドライバーが WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY 構造体の ForwardProgressReservedPolicy メンバーを WdfIoForwardProgressReservedPolicyPagingIO に設定した場合に限り、保証進行機能を重要視します。
ドライバーには、各読み取り要求用および各書き込み要求用のフレームワーク メモリ オブジェクトが必要であるため、メモリ不足時に WdfIoTargetFormatRequestForRead および WdfIoTargetFormatRequestForWrite を呼び出すために使用するメモリ オブジェクトをドライバーで事前に割り当てる必要があります。
したがって、ドライバーで読み取りキュー用の EvtIoAllocateResourcesForReservedRequest コールバック関数、および書き込みキュー用の別のコールバック関数を用意します。フレームワークがこれらのコールバック関数のいずれかを呼び出すたびに、そのコールバック関数は WdfMemoryCreate を呼び出し、返されたオブジェクト ハンドルをメモリ不足の状況に備えて保存します。コールバック関数は、事前に割り当てられた要求オブジェクトへのハンドルを受け取るので、要求オブジェクトに対してメモリ オブジェクトを生成できます。DMA デバイス用のドライバーでは、フレームワーク DMA オブジェクトも事前に割り当てることがあります。
読み取りキュー用および書き込みキュー用の要求ハンドラーは、受信済みの各要求オブジェクトが、フレームワークによりメモリ不足の状況に備えて予約された要求オブジェクトかどうかを特定する必要があります。要求ハンドラーは WdfRequestIsReserved を呼び出すことができます。または、その要求オブジェクト ハンドルと、EvtIoAllocateResourcesForReservedRequest コールバック関数が以前に受け取った要求オブジェクト ハンドルを比較できます。
また、ドライバーは、読み取りキュー用の EvtIoAllocateRequestResources コールバック関数、および書き込みキュー用の別のコールバック関数も提供します。フレームワークは、I/O マネージャーから読み取り要求または書き込み要求を受け取り、要求オブジェクトを正常に作成すると、これらのコールバック関数のいずれかを呼び出します。これらのコールバック関数はそれぞれ WdfMemoryCreate を呼び出して、メモリー オブジェクトを要求に割り当てます。割り当てが失敗した場合、コールバック関数はエラー状態値を返して、メモリ不足の状況が発生したことをフレームワークに通知します。フレームワークは、エラーの戻り値を検出すると、作成した要求オブジェクトを削除し、事前に割り当てられたオブジェクトのいずれかを使用します。
このドライバーは EvtIoWdmIrpForForwardProgress コールバック関数を提供しません。これは、フレームワークが読み取り IRP または書き込み IRP を I/O キューに追加する前に、ドライバーでそれらを個別に検査する必要がないためです。
ドライバーがデバイス用の保証進行機能を実装している場合、デバイスのドライバー スタック内のすべての低レベル ドライバーも保証進行機能を実装する必要があることに留意してください。