2015-11-20 9 views
8

Ich arbeite mit einer seriellen Hochgeschwindigkeits-Karte für High-Rate-Datenübertragungen von einer externen Quelle zu einer Linux-Box mit einer PCIe-Karte. Die PCIe-Karte kam mit einigen Treibern von Drittanbietern, die dma_alloc_coherent verwenden, um die dma-Puffer zuzuweisen, um die Daten zu empfangen. Aufgrund von Linux-Beschränkungen begrenzt dieser Ansatz die Datenübertragung auf 4 MB. Ich habe mehrere Methoden zum Zuweisen eines großen DMA-Puffers gelesen und ausprobiert und war nicht in der Lage, einen zu aktivieren.Große PCIe DMA Linux x86-64

Dieses System hat 32 GB Speicher und läuft Red Hat mit einer Kernel-Version von 3.10 und ich möchte 4 GB davon für eine zusammenhängende DMA verfügbar machen. Ich weiß, dass die bevorzugte Methode Scatter/Gather ist, aber das ist in meiner Situation nicht möglich, da es einen Hardwarechip gibt, der das serielle Protokoll in einen DMA außerhalb meiner Kontrolle übersetzt, wobei das einzige, was ich kontrollieren kann, ein Offset ist eingehende Adressen (dh die Adresse 0, wie sie von dem externen System aus gesehen wird, kann auf die Adresse 0x700000000 auf dem lokalen Bus abgebildet werden).

Da dies eine einmalige Labor-Maschine ist, denke ich, der schnellste/einfachste Ansatz wäre, mem = 28GB Boot-Konfigurationsparameter zu verwenden. Ich habe das funktioniert gut, aber der nächste Schritt zum Zugriff auf diesen Speicher aus dem virtuellen Raum ist, wo ich Probleme habe. Hier ist mein Code an die entsprechenden Komponenten kondensiert:

Im Kernel-Modul:

size_t len = 0x100000000ULL; // 4GB 
size_t phys = 0x700000000ULL; // 28GB 
size_t virt = ioremap_nocache(phys, len); // address not usable via direct reference 
size_t bus = (size_t)virt_to_bus((void*)virt); // this should be the same as phys for x86-64, shouldn't it? 

// OLD WAY 
/*size_t len = 0x400000; // 4MB 
size_t bus; 
size_t virt = dma_alloc_coherent(devHandle, len, &bus, GFP_ATOMIC); 
size_t phys = (size_t)virt_to_phys((void*)virt);*/ 

In der Anwendung:

// Attempt to make a usable virtual pointer 
u32 pSize = sysconf(_SC_PAGESIZE); 
void* mapAddr = mmap(0, len+(phys%pSize), PROT_READ|PROT_WRITE, MAP_SHARED, devHandle, phys-(phys%pSize)); 
virt = (size_t)mapAddr + (phys%pSize); 

// do DMA to 0x700000000 bus address 

printf("Value %x\n", *((u32*)virt)); // this is returning zero 

Eine weitere interessante Sache ist, dass vor allem dies zu tun, die physische Die von dma_alloc_coherent zurückgegebene Adresse ist größer als die RAM-Menge auf dem System (0x83d000000). Ich dachte, dass in x86 der RAM immer die niedrigsten Adressen sein würde und daher würde ich eine Adresse von weniger als 32GB erwarten.

Jede Hilfe wäre willkommen.

+1

Err ... '0x770000000ULL' ist 29,75 GB, nicht 28 ... Versuchen Sie' 0x700000000' stattdessen. –

+0

Dopen, dummer Mathefehler. Trotzdem sollte das egal sein, da dieser Bereich immer noch ein gültiger RAM sein sollte. Ich war noch nicht zu einem 4GB Testfall gegangen und benutzte immer noch nur 4MB. Wird die Frage aktualisieren. – LINEMAN78

+0

Ich habe ein 32GB Speichersystem zur Hand. Könnten Sie eine Absolut-Barebone-Datei, aber vollständige Kernel-Modul-Quelldatei sowie ein absolut-Minimum-Usermode-Programm zum Testen bereitstellen? Warum haben Sie auch mit [C++] taggen lassen, wenn der Linux-Kernel ausschließlich [c] ist, und der von Ihnen gezeigte usermode-Snippet ausschließlich C-APIs verwendet? –

Antwort

0

Statt die Größe des Systemspeichers über mem zu begrenzen, versuchen CMA mit: https://lwn.net/Articles/486301/

Mit der CMA Kernel-Kommandozeilenargument ermöglicht es Ihnen, eine bestimmte Menge an Speicher für DMA-Operationen zu behalten, die als zusammenhängende gewährleistet ist. Der Kernel erlaubt Nicht-DMA-Prozessen, auf diesen Speicher zuzugreifen, aber sobald eine DMA-Operation diesen Speicher benötigt, werden Nicht-DMA-Prozesse entfernt. Daher würde ich empfehlen, den Parameter mem nicht zu ändern, sondern cma=4G zu Ihrer cmdline hinzuzufügen. dma_alloc_coherent sollte automatisch aus diesem reservierten Bereich ziehen, aber Sie können CMA-Debugging in Ihrer Kernel-Konfiguration aktivieren, um sicherzustellen.

Verwandte Themen