我正在编写一个内核模块,它将触发并从外部PCIe设备读取我的内部存储器中的数据块。为此,我需要向PCIe设备发送指向我想要发送的数据的物理内存地址的指针。最终,这些数据将使用write()
函数(用户空间)和copy_from_user()
(内核空间)从用户空间写入内核。据我了解,我的内核模块将看到的地址仍然是虚拟内存地址。我需要一种方法来获取它的物理地址,以便PCIe设备可以找到它。
1)我可以从用户空间使用mmap()
并将我的数据放在DDR内存中的已知位置,而不是使用copy_from_user()
吗?我不想意外地在内存中覆盖另一个进程数据。
2)我的内核模块在初始化时使用ioremap_nocache()
保留PCIe数据空间,我可以从我的内核模块执行相同的操作,还是将此内存视为io内存是一个坏主意?如果可以的话,如果我试图保留的内存已经被使用,会发生什么?我不想硬编码静态内存位置,然后发现它正在使用中。
先谢谢你的帮助。
答案 0 :(得分:2)
您没有选择内存位置并将数据放在那里。相反,您要求内核告诉您数据在物理内存中的位置,并告诉主板读取该位置。每页内存(4KB)将位于不同的物理位置,因此如果您发送的数据多于此数据,您的设备可能会支持"分散收集" DMA,因此它可以读取内存中不同位置的一系列页面。
API就是:dma_map_page()
返回类型dma_addr_t
的值,您可以将其提供给电路板。转移完成后再dma_unmap_page()
。如果您正在进行分散 - 聚集,则将该值替换为您提供给电路板的描述符列表。同样,如果支持scatter-gather,dma_map_sg()
和朋友将帮助将大缓冲区映射到一组页面。您仍有责任以设备所需的格式设置页面描述符。
这一切都在Linux设备驱动程序(第15章)中写得非常好,这是必读的。 http://lwn.net/images/pdf/LDD3/ch15.pdf。一些API在书写之后发生了变化,但概念保持不变。
最后,mmap()
:当然,您可以将内核缓冲区mmap()
分配给用户空间并将其填充到那里,然后将dma_map缓冲区传输到设备。事实上,这可能是避免copy_from_user()
的最简洁方法。