webentwicklung-frage-antwort-db.com.de

Linux PCIe DMA Treiber (Xilinx XDMA)

Ich arbeite derzeit mit dem Xilinx XDMA-Treiber (siehe Quellcode: XDMA Source ) und versuche, ihn zum Laufen zu bringen (bevor Sie fragen: Ich habe mich an meinen technischen Support und das Xilinx-Forum gewandt ist mit Leuten durchsetzt, die das gleiche Problem haben). Ich habe jedoch möglicherweise einen Haken in Xilinx 'Code gefunden, der für mich ein Dealbreaker sein könnte. Ich hoffe, dass es etwas gibt, über das ich nicht nachdenke.

Zunächst einmal gibt es zwei Hauptmodi des Treibers: AXI-Memory Mapped (AXI-MM) und AXI-Streaming (AXI-ST). Für meine spezielle Anwendung benötige ich AXI-ST, da ständig Daten vom Gerät fließen.

Der Treiber ist so geschrieben, dass er die Scatter-Collect-Listen nutzt. Im AXI-MM-Modus funktioniert dies, weil Lesevorgänge eher zufällige Ereignisse sind (d. H. Es gibt keinen Datenfluss aus dem Gerät, stattdessen fordert die Anwendung des Benutzerbereichs lediglich Daten an, wenn dies erforderlich ist). Somit wird die Übertragung DMA aufgebaut, die Daten werden übertragen und die Übertragung wird anschließend abgebrochen. Dies ist eine Kombination aus get_user_pages(), pci_map_sg() und pci_unmap_sg().

Für AXI-ST werden die Dinge merkwürdig und der Quellcode ist alles andere als orthodox. Der Treiber weist einen Ringpuffer zu, in den die Daten kontinuierlich hineinfließen sollen. Dieser Puffer ist im Allgemeinen etwas groß (mein Wert liegt in der Größenordnung von 32 MB), da Sie vorübergehende Ereignisse verarbeiten möchten, bei denen die Benutzeranwendung den Treiber vergessen hat und später die eingehenden Daten abarbeiten kann.

Hier werden Dinge wahnsinnig ... Der Umlaufpuffer wird mit vmalloc32() zugewiesen, und die Seiten dieser Zuordnung werden auf dieselbe Weise abgebildet, wie sich der Benutzerpuffer im AXI-MM-Modus befindet (d. H. Mit der pci_map_sg()-Schnittstelle). Da der Ringpuffer von Gerät und CPU gemeinsam genutzt wird, muss ich bei jedem Aufruf von read()pci_dma_sync_sg_for_cpu() und pci_dma_sync_sg_for_device() aufrufen, was meine Leistung absolut zerstört (ich kann nicht mit dem Gerät mithalten!), Da dies auf dem gesamten Gerät funktioniert Puffer. Witzigerweise hat Xilinx diese Synchronisationsaufrufe nie in ihren Code aufgenommen. Daher wusste ich zuerst, dass ich ein Problem hatte, als ich das Testskript bearbeitete, um vor dem Beenden mehr als eine DMA -Übertragung zu versuchen und der resultierende Datenpuffer beschädigt war.

Daher frage ich mich, wie ich das beheben kann. Ich habe mir überlegt, den Code umzuschreiben, um einen eigenen Puffer aufzubauen, der mit pci_alloc_consistent()/dma_alloc_coherent() zugewiesen wurde. Dies ist jedoch leichter gesagt als getan. Es wird nämlich angenommen, dass der Code davon ausgeht, dass überall Scatter-Gather-Listen verwendet werden (es scheint eine seltsame, proprietäre Zuordnung zwischen der Scatter-Gather-Liste und den Speicherbeschreibungen zu geben, die der FPGA versteht).

Gibt es andere API-Aufrufe, auf die ich aufmerksam gemacht werden sollte? Kann ich die "Single" -Varianten (d. H. pci dma_sync_single_for_cpu()) über einen Übersetzungsmechanismus verwenden, um nicht den gesamten Puffer zu synchronisieren? Gibt es vielleicht eine Funktion, die den zirkularen Puffer mit vmalloc() kohärent machen kann?

6
It'sPete

Okay, ich habe es herausgefunden.

Grundsätzlich waren meine Annahmen und/oder mein Verständnis der Kernel-Dokumentation bezüglich der Synchronisations-API völlig falsch. Ich hatte nämlich zwei grundlegende Annahmen falsch:

  1. Wenn der Puffer niemals in die CPU geschrieben wird, müssen Sie keine Synchronisierung für das Gerät durchführen. Das Entfernen dieses Anrufs verdoppelte meinen Durchsatz von read().
  2. Sie müssen nicht die gesamte Streuliste synchronisieren. Stattdessen finde ich jetzt in meinem read()-Aufruf heraus, welche Seiten von dem copy_to_user()-Aufruf betroffen sind (d. H., Was aus dem Ringpuffer kopiert wird), und synchronisiere nur die Seiten, die mir wichtig sind. Grundsätzlich kann ich etwas wie pci_dma_sync_sg_for_cpu(lro->pci_dev, &transfer->sgm->sgl[sgl_index], pages_to_sync, DMA_FROM_DEVICE) aufrufen, wobei sgl_index der Ort ist, an dem die Kopie beginnen soll, und pages_to_sync wie groß die Daten in Seitenanzahl sind.

Mit den obigen beiden Änderungen erfüllt mein Code nun meine Durchsatzanforderungen.

4
It'sPete

Ich denke, XDMA wurde ursprünglich für x86 geschrieben. In diesem Fall machen die Synchronisierungsfunktionen nichts.

Es ist nicht wahrscheinlich, dass Sie die einzelnen Synchronisationsvarianten verwenden können, wenn Sie nicht den Ringspeicher ändern. Den Ringpuffer durch eine Liste von zu sendenden Puffern zu ersetzen, scheint mir eine gute Idee zu sein. Sie weisen eine Anzahl solcher Puffer vorab zu und verfügen über eine Liste von Puffern, die Sie senden können, sowie eine kostenlose Liste, die von Ihrer App wiederverwendet werden kann.

Wenn Sie einen Zynq-FPGA verwenden, können Sie die DMA-Engine an den ACP-Port anschließen, damit der FPGA-Speicherzugriff kohärent ist. Alternativ können Sie die Speicherbereiche nicht zwischengespeichert, sondern als nicht zwischengespeichert/speichern.

Schließlich ordne ich in meinen FPGA-Anwendungen die Steuerregister und Puffer in den Anwendungsprozess ein und implementiere nur mmap () und poll () im Treiber, um Apps mehr Flexibilität in der DMA-Funktion zu geben. Im Allgemeinen implementiere ich meine eigenen DMA-Engines.

2
Jamey Hicks

Pete, ich bin der ursprüngliche Entwickler des Treibercodes (bevor das X von XMDA eingeführt wurde). 

Der Ringpuffer war immer eine unorthodoxe Sache und für Cache-kohärente Systeme gedacht und standardmäßig deaktiviert. Ihr ursprünglicher Zweck bestand darin, die DMA -Latenz (neu) zu beseitigen; Selbst bei vollständiger asynchroner E/A-Unterstützung (in einigen Fällen sogar mit der Verkürzung der Zero-Latenz-Deskriptoren) hatten wir Anwendungsfälle, bei denen dies nicht garantiert werden konnte und wo ein echter Hardware-Ringpuffer-/Zyklik-/Schleifenmodus erforderlich war.

Es gibt kein Äquivalent zu einer Ringbuffer-API in Linux, daher ist es etwas offen codiert. 

Ich bin froh, das IP-/Treiberdesign neu zu überdenken.

Kannst du deine Korrektur teilen?

0