次の方法で共有


DMA 転送要求の分割

ドライバーは、以下に応じて、転送要求を分割し、特定のIRPを満たすために複数のDMA転送操作を実行しなければならない場合があります。

  • IoGetDmaAdapterが返す map registers の数

  • IRPドライバーの I/Oスタック場所の Length メンバーに含まれる、転送対象データのバイト数

  • ドライバーがデータを転送するバッファリングの、システム物理メモリ内ページ境界の数

  • ドライバーのDMA操作に対するデバイス固有の制約。 たとえば、システム "AT" ディスクドライバーは、ディスクコントローラーの制限により、256セクターを超える転送要を分割する必要があります。

ドライバーは、IRPで指定されたすべてのデータを転送するために必要なマップレジスタの数を、以下のように決定することができます。

  1. Irp->MdlAddressにMDLへのポインタを渡して MmGetMdlVirtualAddressを呼び出し、バッファの開始仮想アドレスを取得します。 ドライバーはこの仮想アドレスを使ってメモリにアクセスしてはならないことに注意してください。 MmGetMdlVirtualAddress が返す値はMDLへのインデックスであり、必ずしも有効なアドレスとは限りません。

  2. ADDRESS_AND_SIZE_TO_SPAN_PAGES マクロに、返されたインデックスとIRPのドライバーのI/Oスタック位置の Length の値を渡します。

ADDRESS_AND_SIZE_TO_SPAN_PAGES によって返される値が、 IoGetDmaAdapterによって返される NumberOfMapRegisters 値よりも大きい場合、ドライバーはこのIRPに対して要求されたすべてのデータを1回のDMA操作で転送できません。 代わりに、次のことを実行する必要があります。

  1. 利用可能なマップレジスタの数(およびデバイス固有のDMA制約)に合わせてバッファリングを分割します。

  2. 転送要求を満たすのに必要なだけのDMA操作を実行します。

例えば、 ADDRESS_AND_SIZE_TO_SPAN_PAGES は転送要求を満たすために12個のマップレジスタが必要であることを示していますが、 IoGetDmaAdapter が返す NumberOfMapRegisters 値は5個しかないとします。 (デバイス固有のDMA制約がないと仮定します。) この場合、ドライバーは、IRPによって要求されたすべてのデータを転送するために MapTransfer を3 回呼び出し、3つの DMA 転送操作を実行する必要があります。

システムのDMAデバイスドライバーは、1回のI/O操作でIRPを満たすのに十分なマップレジスタがない場合、DMA転送を分割するためにさまざまなテクニックを使用します。 使用するテクニックの1つに、以下のものがあります:

  1. IoAllocateMdl を呼び出して、ユーザーバッファの一部を記述するMDLを割り当てます。

  2. MmProbeAndLockPages をコールして、ユーザーバッファリングのその部分をロックダウンします。

  3. バッファリングのその部分のデータを転送します。

  4. MmUnlockPages を呼び出し、以下のいずれかを実行します:

    • 手順1でドライバーが割り当てたMDLが次の転送に十分なサイズであれば、 MmPrepareMdlForReuse を呼び出し、手順2から4を繰り返します。
    • そうでなければ、 IoFreeMdl を呼び出し、手順1から4を繰り返します。
  5. すべてのデータが転送されたら、 MmUnlockPagesIoFreeMdl を呼び出します。

メモリが制限されているマシンで、最上位ドライバーが MmProbeAndLockPages を使用してユーザーバッファリング全体をロックダウンできない場合、次のようにすることができます:

  1. IoBuildSynchronousFsdRequest を呼び出して、部分転送IRPを割り当て、ユーザーバッファリングの一部をロックダウンします。 ロックダウンされた領域は通常、 PAGE_SIZE の倍数か、基盤となるデバイスの転送容量に適したサイズでする。

  2. IoCallDriver を部分転送IRPのために呼び出し、 KeWaitForSingleObject を呼び出して、下位のドライバーがSTATUS_PENDINGを返す場合、ドライバーがその部分転送IRPに関連付けられるように設定したイベントオブジェクトを待ちます。

  3. 制御を取り戻したら、すべてのデータが転送されるまで手順1と2を繰り返し、その後、元のIRPを完了します。

ストレージクラスドライバーが、基盤となる SCSIポート/ミニポートドライバーのために大容量の転送要求を分割する場合、転送要求の各部分に対して追加のIRPを割り当てます。 ドライバーが割り当てた各IRPに対して、 IoCompletion ルーチンを登録し、完全な転送要求の状態を追跡し、ドライバーが割り当てたIRPをリリースします。 次ぎに、 IoCallDriverを使用して、これらのIRPをポートドライバーに送信します。

他のクラス/ポートドライバーは、クラスドライバがポートドライバーが使用可能なマップレジスタの数を決定できる場合にのみ、この手法を使用できます。 ポートドライバーは、この構成情報をペアとなるクラスドライバーのレジストリに格納しなければなりません。または、ペアとなるドライバーは、ポートドライバーからクラスドライバーに利用可能なマップレジスタの数に関する構成情報を渡すために、内部デバイスI/O制御要求を使用してプライベートインタフェースを定義しなければなりません。

DMAデバイス用のモノリシックドライバー(つまり、クラス/ポートのペアに属さないドライバー)は、それ自体のために大きな転送要求を分割しなければなりません。 このようなドライバーは通常、大きな要求を分割し、IRPを満たすために一連のDMA操作を実行します。

転送要求が大きすぎて基盤となるデバイスドライバーが処理できない場合、上位ドライバーは MmGetMdlVirtualAddressIoBuildPartialMdlを呼び出し、基盤となるデバイスドライバーのための部分転送IRPのシーケンスを設定することができます。