NDIS Scatter/Gather DMA

注意

Arm および Arm64 プロセッサの場合、NDIS ドライバー ライターが NDIS スキャッター/ギャザー DMA の代わりに WDF DMA または WDM DMA を使用することを強くお勧めします。

WDF DMA について詳しくは、「KMDF ドライバーでの DMA 操作の処理」をご覧ください。

WDM DMA について詳しくは、「ドライバーの入力/出力の管理」の DMA 関連の子トピックをご覧ください。

NDIS ミニポート ドライバーは、スキャッター/ギャザー DMA (SGDMA) メソッドを使用して、NIC とシステム メモリの間でデータを転送できます。 DMA 転送を成功させるには、データの物理アドレスが NIC でサポートされているアドレス範囲に収まっている必要があります。 HAL は、ドライバーが MDL チェーンの物理アドレス一覧を取得するためのメカニズムを提供し、必要に応じて、物理アドレス範囲にデータをダブル バッファリングします。

NDIS 6.0 より前のバージョンの NDIS では、ミニポート ドライバーと NDIS における SGDMA のサポートはいくつかの点で制限されており、特にマルチパケット送信シナリオではうまく機能しません。 NDIS 6.0 SGDMA のサポートは、ミニポート ドライバー用のシンプルなインターフェイスを提供しながら、これらの制限を克服します。

NDIS SGDMA の歴史

NDIS 6.0 より前のバージョンの NDIS では、NDIS はミニポート ドライバーにパケットを送信する前に、各パケットのスキャッター ギャザー (SG) リストを取得します。 NDIS は、過剰な断片化が原因で SG リストを取得する最初の試行が失敗したケースも扱います。 この場合、NDIS は、連続するバッファーにパケットをダブル バッファリングし、再試行します。 たとえば、データの物理アドレスが 32 ビットの最大値を超え、NIC が 64 ビット DMA をサポートしていない場合など、HAL は、NIC によりサポートされる物理アドレスにデータをダブル バッファリングすることもできます。

デッドロックの状況を回避するため、NDIS はパケットの SG リストを取得し、一度に 1 つのパケットを送信します。 NDIS がミニポート ドライバーに送信する前にすべてのパケットをマッピングしようとした場合、システムがリソースを使い果たす可能性があります。 この場合、NDIS は、一部のマップ レジスタが未送信パケットに対してロックダウンされている間、マップ レジスタが使用可能になるまで待機します。 ロックダウンされたパケットは再利用できません。

SGDMA サポートに対するこのアプローチには、以下の制限があります。

  • パケットはミニポート ドライバーに到達する前にマッピングされるため、ドライバーは小さいパケットや、過剰に断片化されたパケットを最適化できません。 ミニポート ドライバーは、既知の物理アドレスにパケットをダブル バッファリングすることが できません。

  • NDIS ミニポート ドライバーに渡された物理アドレス配列が、元のデータの仮想アドレスにマッピングされる保証はありません。 したがって、MDL チェーンの仮想アドレスにあるデータをドライバーが送信前に変更した場合、データに加えられた変更は物理アドレスのデータに反映されません。 この場合、NIC は変更されていないデータを送信します。

  • NDIS は、リソースの問題によるデッドロックを回避するため、一度に 1 つのパケットを送信するよう制限されています。 これは、複数のパケットを送信するより効率が低くなります。

  • NDIS はミニポート ドライバーの送信機能を判断できないため、SG リスト バッファーのストレージを事前に割り当てることはできません。 そのため、NDIS は、実行時に必要なストレージを割り当てる必要があります。 これは、ストレージの事前割り当てより効率が低くなります。

  • SG リストを割り当てる HAL 関数は、IRQL = DISPATCH_LEVEL で呼び出される必要があります。 NDIS には最新の IRQL 情報がないため、既に DISPATCH_LEVEL にある場合でも、IRQL を DISPATCH_LEVEL に設定する必要があります。 IRQL が既に DISPATCH_LEVEL にある場合、これは効率的ではありません。

NDIS SGDMA サポートの利点

NDIS 6.0 以降の SGDMA インターフェイスでは、NDIS はミニポート ドライバーに送信する前にデータ バッファーをマッピングしません。 代わりに、NDIS は、ネットワーク データをマッピングするドライバーのインターフェイスを提供します。

この方法には次の利点があります。

  • NDIS は、ネットワーク データをマッピングするための HAL へのインターフェイスを提供するため、NDIS は、マッピング プロセスの複雑さと細部からミニポート ドライバーを保護します。

  • ミニポート ドライバーは、マッピングされる前にデータにアクセスできます。 したがって、元のデータに加えられた変更は、NDIS または HAL がデータをダブル バッファリングする場合でも、SG リストで表されるデータに反映されます。

  • ミニポート ドライバーは、既知の物理アドレスを持つ事前に割り当てられたバッファーにコピーすることによって、小さいパケットや高度に断片化されたパケットの送信を最適化できます。 このアプローチでは、不要なマッピングが回避されるため、システムのパフォーマンスが向上します。

  • NDIS は、ミニポート ドライバーに対して複数のバッファーを安全に送信できます。 これにより、ミニポート ドライバーへの呼び出しが減少するため、システムのパフォーマンスが向上します。

  • ミニポート ドライバーは、送信記述子ブロックの一部として SG リストのメモリを事前割り当てすることができます。 したがって、NDIS またはミニポート ドライバーが実行時に SG リストのメモリを割り当てる必要はありません。

  • ミニポート ドライバーは IRQL = DISPATCH_LEVEL で実行できるため、ミニポート ドライバーは、IRQL を DISPATCH_LEVEL に発生させる不要な呼び出しを回避できます。 たとえば、送信の完了は割り込み DPC のコンテキストで行われるため、ミニポート ドライバーは IRQL を発生させることなく SG リストを解放できます。

DMA チャネルの登録と登録解除

NDIS ミニポート ドライバーは、NDIS を使用して DMA チャネルを登録するため、MiniportInitializeEx 関数から NdisMRegisterScatterGatherDma 関数を呼び出します。

ミニポート ドライバーは、DmaDescription パラメーターで DMA の説明を NdisMRegisterScatterGatherDma に渡します。 NdisMRegisterScatterGatherDma は、スキャッター/ギャザー リストを保持するのに十分な大きさのバッファー サイズを返します。 ミニポート ドライバーは、スキャッター/ギャザー リストのストレージを事前に割り当てるには、このサイズを使用する必要があります。

ミニポート ドライバーは、NDIS がスキャッター/ギャザー リストを処理するために呼び出す MiniportXxx 関数のエントリ ポイントに NdisMRegisterScatterGatherDma を渡します。 NDIS は、HAL がバッファーのスキャッター/ギャザー リストを構築した後、ミニポート ドライバーの MiniportProcessSGList 関数を呼び出します。 NdisMRegisterScatterGatherDmapNdisMiniportDmaHandle パラメーターにハンドルを提供します。このパラメーターは、NDIS スキャッター/ギャザー DMA 関数への後続の呼び出しでミニポート ドライバーが使用する必要があります。

NDIS ミニポート ドライバーは、その MiniportHaltEx 関数から NdisMDeregisterScatterGatherDma 関数を呼び出し、スキャッター/ギャザー DMA リソースをリリースします。

スキャッター/ギャザー リストの割り当てと解放

NDIS ミニポート ドライバーは、その MiniportSendNetBufferLists 関数で NdisMAllocateNetBufferSGList 関数を呼び出します。 ミニポート ドライバーは、マッピングする必要がある各 NET_BUFFER 構造に対して NdisMAllocateNetBufferSGList を 1 回呼び出します。 リソースが使用可能になり、HAL で SG リストの準備ができたら、NDIS はドライバーの MiniportProcessSGList 関数を呼び出します。 NDIS は、ミニポート ドライバーによる NdisMAllocateNetBufferSGList の呼び出しが返される前または後に、MiniportProcessSGList を呼び出すことができます。

システムのパフォーマンスを高めるため、関連する NET_BUFFER_DATA 構造の CurrentMdl メンバーで指定された MDL の先頭から始まるネットワーク データから、スキャッター/ギャザー リストが生成されます。 SG リスト内のネットワーク データの開始は、関連する NET_BUFFER_DATA 構造の CurrentMdlOffset メンバーで指定された値によって、SG リストの先頭からオフセットされます。

送信完了割り込みの DPC を処理している間、ミニポート ドライバーが SG リストを必要としなくなったら、ミニポート ドライバーが NDISMFreeNetBufferSGList 関数を呼び出して SG リストを解放する必要があります。

ドライバーまたはハードウェアがスキャッター/ギャザー リストに関連付けられた NET_BUFFER 構造によって記述されているメモリにアクセスしている間は、NdisMFreeNetBufferSGList を呼び出さないでください。 

受信データにアクセスする前に、ミニポート ドライバーは NdisMFreeNetBufferSGList を呼び出してメモリ キャッシュをフラッシュする必要があります。