我正在使用高速串行卡将数据从外部源高速传输到带有 PCIe 卡的 Linux 机器。 PCIe 卡带有一些第 3 方驱动程序,这些驱动程序使用 dma_alloc_coherent 分配 dma 缓冲区以接收数据。然而,由于 Linux 的限制,此方法将数据传输限制为 4MB。我一直在阅读并尝试多种分配大型 DMA 缓冲区的方法,但一直无法使一种方法起作用。
这个系统有 32GB 内存,运行的是内核版本为 3.10 的 Red Hat,我想为连续的 DMA 提供 4GB 内存。我知道首选方法是分散/聚集,但在我的情况下这是不可能的,因为有一个硬件芯片将串行协议(protocol)转换为我无法控制的 DMA,我唯一可以控制的是向输入地址(即,从外部系统看到的地址零可以映射到本地总线上的地址 0x700000000)。
由于这是一台一次性实验室机器,我认为最快/最简单的方法是使用 mem=28GB 启动配置参数。我有这个工作正常,但从虚拟空间访问该内存的下一步是我遇到问题的地方。这是我的代码浓缩到相关组件:
在内核模块中:
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 );*/
在应用程序中:
// 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
另一件有趣的事情是,在执行所有这些操作之前,从 dma_alloc_coherent 返回的物理地址大于系统上的 RAM 数量 (0x83d000000)。我认为在 x86 中,RAM 将始终是最低地址,因此我希望地址小于 32GB。
如有任何帮助,我们将不胜感激。
最佳答案
不要通过mem
限制系统内存量,而是尝试使用CMA:https://lwn.net/Articles/486301/
使用 CMA 内核命令行参数允许您为保证连续的 DMA 操作保留一定数量的内存。内核将允许非 DMA 进程访问该内存,但一旦 DMA 操作需要该内存,非 DMA 进程将被逐出。因此,我建议不要更改您的 mem
参数,而是将 cma=4G
添加到您的 cmdline。 dma_alloc_coherent
应该会自动从该保留空间中提取数据,但您可以在内核配置中启用 CMA 调试以确保这一点。
关于c++ - 大型 PCIe DMA Linux x86-64,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33817481/