2015-11-20 16 views
8

Harici bir kaynaktan bir PCIe kartlı bir Linux kutusuna yüksek hızlı veri aktarımı için yüksek hızlı seri kartla çalışıyorum. PCIe kartı, verileri almak için dma arabelleklerini ayırmak için dma_alloc_coherent kullanan bazı üçüncü taraf sürücülerle birlikte geldi. Ancak Linux sınırlamaları nedeniyle, bu yaklaşım veri aktarımlarını 4MB'a sınırlar. Büyük bir DMA arabelleği ayırmak için birden fazla yöntem okumaya ve denemeye çalıştım ve işe yaramaya başlayamadım.Büyük PCIe DMA Linux x86-64

Bu sistem 32GB belleğe sahip ve 3.10 çekirdek sürümüne sahip Red Hat çalıştırıyor ve bitişik bir DMA için 4 GB kullanılabilir hale getirmek istiyorum. Tercih edilen yöntemin dağılım/toplama olduğunu biliyorum, ancak bu durum benim durumumda, seri protokolü kontrolümün ötesinde bir DMA'ya çeviren bir donanım çipi olduğu için mümkün değil; kontrol edebileceğim tek şey, Gelen adresler (yani, harici sistemden görüldüğü üzere adres sıfırı, yerel veri yolunda 0x700000000 adresine eşlenebilir).

Bu bir kerelik bir laboratuar makinesi olduğundan, en hızlı/en kolay yaklaşımın mem = 28GB önyükleme yapılandırma parametresini kullanacağını düşünüyorum. Bu iyi çalışıyor, ama sanal belleğe erişmek için bir sonraki adım, sorun yaşıyorum. Çekirdek modülünde

: uygulamasında

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

: İşte benim kod ilgili bileşenlere yoğunlaştırılan

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

başka ilginç şey tüm bu yapmadan önce, fizikseldir dma_alloc_coherent öğesinden döndürülen adres, sistemdeki RAM miktarından daha büyüktür (0x83d000000). X86'da RAM'in her zaman en düşük adres olacağını ve bu yüzden 32 GB'den daha az bir adres bekleyeceğini düşündüm.

Herhangi bir yardım

mutluluk duyacağız.

+1

Err ... '0x770000000ULL '29,75 GB, değil 28 ... Bunun yerine' 0x700000000' deneyin. –

+0

Uyuşuk, aptal matematik hatası. Yine de bu alanın geçerli bir RAM olması gerektiği kadar önemli değil. Henüz 4GB'lık bir test vakasına gitmemiştim ve hala sadece 4MB kullanıyordum. Soruyu günceller. – LINEMAN78

+0

Kullanışlı bir 32GB bellek sistemim var. Bir mutlak-barebone ama tam bir çekirdek modülü kaynak dosyası ve test etmek için mutlak minimum usermode programı tamamlayabilir misiniz? Ayrıca, Linux çekirdeği yalnızca [c] olduğunda [C++] ile nasıl etiketlendiniz ve gösterdiğiniz usermode snippet'i yalnızca C API'lerini kullanıyor? –

cevap

0

yerine mem yoluyla sistem belleği miktarını sınırlamanın, CMA kullanmayı deneyin: https://lwn.net/Articles/486301/

Eğer bitişik olması sağlanır DMA işlemleri için bellekte belirli bir miktarda rezerv sağlar CMA çekirdek komut satırı argümanı kullanma. Çekirdek DMA olmayan işlemlerin bu belleğe erişmesine izin verecek, ancak bir DMA işlemi bu belleğe ihtiyaç duyduğunda, DMA dışı süreçler tahliye edilecektir. Yani, mem parametresini değiştirmemenizi, ancak cmdline'ınıza cma=4G eklemenizi tavsiye ederim. dma_alloc_coherent otomatik olarak bu ayrılmış alandan çekmelidir, ancak emin olmak için çekirdek yapılandırmanızda CMA hata ayıklamasını etkinleştirebilirsiniz.