【问题标题】:Physical Memory Allocation in Kernel内核中的物理内存分配
【发布时间】:2014-08-24 16:16:29
【问题描述】:

我正在编写一个内核模块,它将触发和外部 PCIe 设备从我的内部存储器中读取数据块。为此,我需要向 PCIe 设备发送一个指向我要发送的数据的物理内存地址的指针。最终,这些数据将通过write() 函数(用户空间)和copy_from_user()(内核空间)从用户空间写入内核。据我了解,我的内核模块将看到的地址仍然是虚拟内存地址。我需要一种方法来获取它的物理地址,以便 PCIe 设备可以找到它。

1) 我可以只使用来自用户空间的mmap() 并将我的数据放在DDR 内存中的已知位置,而不是使用copy_from_user() 吗?不过,我不想意外覆盖内存中的另一个进程数据。

2) 我的内核模块在初始化时使用ioremap_nocache() 保留了PCIe 数据空间,我可以从我的内核模块中做同样的事情还是把这个内存当作io 内存是个坏主意?如果可以,如果我尝试保留的内存已在使用中会怎样?我不想硬编码一个静态内存位置,然后发现它正在使用中。

提前感谢您的帮助。

【问题讨论】:

标签: memory-management linux-kernel kernel-module device-driver


【解决方案1】:

您无需选择内存位置并将数据放在那里。相反,您要求内核告诉您数据在物理内存中的位置,并告诉开发板读取该位置。每页内存 (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() 的最干净的方法。

【讨论】:

  • 好的,我实际上并没有从我的一端启动 DMA 传输。 PCIe 总线上的终点是从我的内存空间进行 DMA 读取。我还需要使用 dma_map_page() 吗?最终我希望不必为 PCIe 设备更改我的 BAR,因此我希望我的缓冲区位于某个空间中。有什么选择吗? (我面前有LDD3)
  • 是的,这就是总线主控 DMA 的工作方式。您正在告诉设备要在主机内存空间中读取哪些地址。 BAR 与此完全无关。它仅适用于设备中映射到内核空间的寄存器。您使用这些寄存器告诉设备从内存空间中的其他地方(任何地方!)获取数据,这就是map_page 的用途。我的回答正是你需要做的,并且是做总线主控 DMA 的标准方法。
猜你喜欢
  • 2011-09-26
  • 1970-01-01
  • 2012-04-06
  • 1970-01-01
  • 2011-12-15
  • 1970-01-01
  • 2011-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多