次の方法で共有


記憶域クラス ドライバーの SplitTransferRequest ルーチン

GetDescriptor ルーチンに返される STORAGE_ADAPTER_DESCRIPTOR データは、特定の HBA のクラス ドライバーへの転送機能を示します。 特に、このデータは MaximumTransferLength をバイト単位で示し、MaximumPhysicalPages を示します。つまり、システム バッファーをサポートする物理メモリ内で HBA が管理できる非連続ページの数 (つまり、分散/収集サポートの範囲) です。

記憶域クラス ドライバーは、データを転送する HBA の機能を超えるすべての転送要求を分割する責任があるため、ほとんどのクラス ドライバーは、各デバイス オブジェクトのデバイス拡張子にこの構成データへのポインターを格納します。 つまり、クラス ドライバーの DispatchReadWrite ルーチンは、各 IRP が 1 回の転送操作で処理できる HBA を超える転送を要求するかどうかを決定する必要があります。

たとえば、このような DispatchReadWrite ルーチンには、次のようなコードが含まれる場合があります。

PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor = 
    commonExtension->PartitionZeroExtension->AdapterDescriptor;
ULONG transferPages;
ULONG maximumTransferLength = 
    adapterDescriptor->MaximumTransferLength;
    :        : 
// 
// Calculate number of pages in this transfer 
// 
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES( 
                    MmGetMdlVirtualAddress(Irp->MdlAddress), 
                        currentIrpStack->Parameters.Read.Length);
// 
// Check whether requested length is greater than the maximum number 
// of bytes that can be transferred in a single operation 
// 
if (currentIrpStack->Parameters.Read.Length > maximumTransferLength ||
        transferPages > adapterDescriptor->MaximumPhysicalPages) { 
    transferPages = adapterDescriptor->MaximumPhysicalPages - 1;
    if (maximumTransferLength > transferPages << PAGE_SHIFT) { 
        maximumTransferLength = transferPages << PAGE_SHIFT; 
    } 
    IoMarkIrpPending(Irp); 
    SplitTransferRequest(DeviceObject, 
                            Irp, 
                            maximumTransferLength); 
    return STATUS_PENDING; 
} 
    :        : 

クラス ドライバーは、バッファーがマップされた後に持つ物理的な中断の数を示すことができないので、転送内の各ページがあいまいでないと想定し、許容される物理的な中断の数とページの数を比較する必要があります。

このようなドライバーの DispatchReadWrite ルーチンは、IoMarkIrpPending を呼び出し、元の IRP で SplitTransferRequest ルーチンの呼び出しの直後に STATUS_PENDING を返すことに注意してください。

元の転送要求を実行するために、ドライバーの SplitTransferRequest ルーチンは、HBA の機能に合わせてサイズ設定されたサブバッファーを処理する 1 つ以上の IRP を作成します。 このような IRP ごとに、 SplitTransferRequest ルーチンは次を実行します。

  • 通常、内部 BuildRequest ルーチンを呼び出して SRB を設定します (記憶域クラス ドライバーの BuildRequest ルーチンを参照してください)

  • 元の IRP から新しい IRP に MDL アドレスをコピーします

  • SRB の DataBuffer を、この転送の MDL へのオフセット (バイト単位) に設定します

  • IoCallDriver を使用してポート ドライバーに IRP を送信する前に、その IoCompletion ルーチンを設定します。

転送の各部分を追跡するために、SplitTransferRequest は、次の下位ドライバーに送信するドライバー割り当て IRP ごとに IoCompletion ルーチンを登録します。 IoCompletion ルーチンは、InterlockedIncrementInterlockedDecrement を使用して、カウントが正確であることを確認して、元の IRP で完了した部分転送要求の数を保持します。

このような IoCompletion ルーチンは、ドライバーが割り当てた IRP や SRB を解放する必要があります。要求されたすべてのデータが転送されたとき、またはクラス ドライバーが IRP の再試行を使い果たし、デバイス転送エラーのために失敗する必要がある場合は、元の IRP を完了する必要があります。