2015-11-20 15 views
8

Sto lavorando con una scheda seriale ad alta velocità per trasferimenti di dati ad alta velocità da una fonte esterna a una scatola Linux con una scheda PCIe. La scheda PCIe è stata fornita con alcuni driver di terze parti che utilizzano dma_alloc_coherent per allocare i buffer dma per ricevere i dati. Tuttavia, a causa delle limitazioni di Linux, questo approccio limita i trasferimenti di dati a 4 MB. Ho letto e provato diversi metodi per allocare un grande buffer DMA e non sono riuscito a far funzionare uno.Grande PCIe DMA Linux x86-64

Questo sistema ha 32 GB di memoria ed esegue Red Hat con una versione del kernel di 3.10 e vorrei rendere 4 GB di quello disponibile per un DMA contiguo. So che il metodo preferito è scatter/gather, ma questo non è possibile nella mia situazione in quanto c'è un chip hardware che traduce il protocollo seriale in un DMA oltre il mio controllo, dove l'unica cosa che posso controllare è aggiungere un offset al gli indirizzi in entrata (cioè, indirizzo zero visto dal sistema esterno possono essere mappati all'indirizzo 0x700000000 sul bus locale).

Poiché si tratta di una macchina da laboratorio one-off, ritengo che l'approccio più veloce/più semplice sarebbe utilizzare il parametro di configurazione di avvio mem = 28GB. Sto funzionando bene, ma il prossimo passo per accedere a quella memoria dallo spazio virtuale è dove sto avendo problemi. Qui è il mio codice condensato per i componenti rilevanti:

Nel modulo del kernel:

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);*/ 

Nell'applicazione:

// 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 

Un'altra cosa interessante è che prima di fare tutto questo, il fisico l'indirizzo restituito da dma_alloc_coherent è maggiore della quantità di RAM sul sistema (0x83d000000). Ho pensato che in x86 la RAM sarebbe sempre stata l'indirizzo più basso e quindi mi sarei aspettato un indirizzo inferiore a 32 GB.

Qualsiasi aiuto sarebbe apprezzato.

+1

Err ... '0x770000000ULL' è 29,75 GB, non 28 ... Provare' 0x700000000' invece. –

+0

Dope, stupido errore di matematica. Ancora non dovrebbe importare come quell'area dovrebbe essere ancora RAM valida. Non ero ancora arrivato a un caso di test da 4 GB e utilizzavo solo 4 MB. Aggiornerà la domanda – LINEMAN78

+0

Ho a portata di mano un sistema di memoria da 32 GB. Potresti postare un barebone assoluto, ma completare il file sorgente del modulo del kernel, oltre a un programma di usermode assoluto minimo da testare? Inoltre, perché hai taggato con [C++] quando il kernel Linux è esclusivamente [c], e lo snippet di usermode che mostri utilizza esclusivamente API C? –

risposta

0

Invece di limitare la quantità di memoria di sistema tramite mem, provare a utilizzare CMA: https://lwn.net/Articles/486301/

Utilizzando l'argomento della riga di comando del kernel CMA permette di riservare un certo quantitativo di memoria per le operazioni DMA che è garantito per essere contigui. Il kernel consentirà ai processi non DMA di accedere a quella memoria, ma non appena un'operazione DMA richiede tale memoria, i processi non DMA verranno eliminati. Pertanto, consiglierei di non modificare il parametro mem, ma di aggiungere cma=4G alla cmdline. dma_alloc_coherent dovrebbe estrarre automaticamente da quello spazio riservato, ma è possibile abilitare il debug CMA nella configurazione del kernel per essere sicuri.

Problemi correlati