webdevqa.jp.net

Linux PCIe DMAドライバー(ザイリンクスXDMA)

現在、ザイリンクスXDMAドライバを使用しており(ソースコードについてはこちらを参照してください: XDMA Source )、実行しようとしています(質問する前に:テクニカルサポートの連絡先に連絡しました。ザイリンクスフォーラムには、同じ問題を抱えている人々がたくさんいます)。ただし、ザイリンクスのコードに問題があり、それが私にとって取引の妨げになる可能性があります。考えていないことがあるといいのですが。

まず、ドライバーには、AXI-Memory Mapped(AXI-MM)とAXI-Streaming(AXI-ST)の2つの主要なモードがあります。私の特定のアプリケーションでは、データがデバイスから継続的に流れるため、AXI-STが必要です。

ドライバーは、スキャッターギャザーリストを利用するように作成されています。 AXI-MMモードでは、読み取りがかなりランダムなイベントであるため、これは機能します(つまり、デバイスからのデータの流れはなく、ユーザースペースアプリケーションは必要なときにデータを要求するだけです)。そのため、DMA転送が構築され、データが転送されてから、転送が破棄されます。これは、get_user_pages()pci_map_sg()の組み合わせです。 、およびpci_unmap_sg()

AXI-STの場合、状況はおかしくなり、ソースコードは正統派とはほど遠いものになります。ドライバーは、データが継続的に流入することを目的とした循環バッファーを割り当てます。ユーザースペースアプリケーションがドライバーを忘れて、後で受信データを処理できる一時的なイベントを処理できるようにするため、このバッファーのサイズは通常、ある程度大きくなります(私の設定は32MB程度です)。

ここで問題が発生します...循環バッファはvmalloc32()を使用して割り当てられ、その割り当てからのページは、ユーザースペースバッファがAXI-MMモードの場合と同じ方法で(つまり、pci_map_sg()インターフェース)。その結果、循環バッファはデバイスとCPUの間で共有されるため、read()を呼び出すたびに、pci_dma_sync_sg_for_cpu()pci_dma_sync_sg_for_device()を呼び出す必要があり、パフォーマンスが完全に低下します(これはバッファ全体で機能するため、デバイスについていけません!)。おかしなことに、ザイリンクスはこれらの同期呼び出しをコードに含めなかったため、テストスクリプトを編集して、終了する前に複数のDMA転送を試み、結果のデータバッファを作成したときに問題が発生したことを最初に知りました。破損しました。

結果として、私はこれをどのように修正できるのか疑問に思っています。 pci_alloc_consistent()/dma_alloc_coherent()を使用して割り当てられた独自のバッファを構築するようにコードを書き直すことを検討しましたが、これは口で言うほど簡単ではありません。つまり、コードはどこでもスキャッターギャザーリストを使用することを想定するように設計されています(スキャッターギャザーリストとFPGAが理解するメモリ記述子の間に奇妙な独自のマッピングがあるようです)。

知っておくべき他のAPI呼び出しはありますか?バッファ全体を同期しないように、何らかの変換メカニズムを介して「単一の」バリアント(つまり、pci dma_sync_single_for_cpu())を使用できますか?あるいは、vmalloc()で割り当てられた循環バッファをコヒーレントにすることができる関数はおそらくありますか?

6
It'sPete

了解しました。わかりました。

基本的に、同期APIに関するカーネルドキュメントの私の仮定や理解は完全に間違っていました。つまり、私は2つの重要な仮定について間違っていました。

  1. バッファがCPUによって書き込まれることがない場合は、デバイスを同期する必要はありません。この呼び出しを削除すると、read()のスループットが2倍になりました。
  2. スキャッターリスト全体を同期する必要はありません。代わりに、read()呼び出しで、copy_to_user()呼び出しの影響を受けるページ(つまり、循環バッファーから何がコピーされるか)を把握し、気になるページだけを同期します。基本的に、私はpci_dma_sync_sg_for_cpu(lro->pci_dev, &transfer->sgm->sgl[sgl_index], pages_to_sync, DMA_FROM_DEVICE)のようなものを呼び出すことができます。ここでsgl_indexは、コピーが開始されると私が考えた場所であり、pages_to_syncは、ページ数で表したデータの大きさです。

上記の2つの変更により、コードはスループット要件を満たしています。

4
It'sPete

XDMAはもともとx86用に書かれたと思いますが、その場合、同期関数は何もしません。

循環バッファを変更しない限り、単一同期バリアントを使用できる可能性は低いようです。循環バッファを送信するバッファのリストに置き換えることは、私には良い考えのように思えます。このようなバッファーをいくつか事前に割り当て、送信するバッファーのリストと、アプリが再利用できる無料のリストを用意します。

Zynq FPGAを使用している場合は、DMAエンジンをACPポートに接続して、FPGAメモリアクセスがコヒーレントになるようにすることもできます。または、代わりにメモリ領域をキャッシュなし/バッファ付きとしてマップすることもできます。キャッシュされたの。

最後に、FPGAアプリケーションでは、制御レジスタとバッファーをアプリケーションプロセスにマップし、ドライバーにmmap()とpoll()のみを実装して、アプリがDMAを実行する方法をより柔軟にします。私は通常、独自のDMAエンジンを実装しています。

2
Jamey Hicks