像往常一样将物理内存映射到用户空间,struct page backed mapping

Map physical memory to userspace as normal, struct page backed mapping

我有一个自定义设备驱动程序,它实现 mmap 操作以将共享 RAM 缓冲区(在 OS 之外)映射到用户空间。通过将 mem=32M 作为 OS 的启动参数传递来保留缓冲区,将剩余的 512MB 留作缓冲区。我想从映射内存执行零拷贝操作,如果 vm_flags 包含 VM_PFNMAPVM_IO,这是不可能的。

我的驱动程序当前通过调用 vm_iomap_memory(vma, start, size) 执行映射,后者又调用 io_remap_pfn_rangeremap_pfn_range,后者使用 VM_PFNMAPVM_IO 设置。这可以将内存映射到用户空间,但是由于设置了 VM_PFNMAP 标志或缺少结构页面,零拷贝套接字操作在 get_user_pages 失败。 remap_pfn_range 的注释表明这是预期的行为,因为 pfn 映射内存不应被视为 'normal'。但是,对于我来说,它只是一块保留的 RAM,所以我不明白为什么它不应该被视为正常。我已经设置缓存 invalidation/flushing 来手动管理内存。

我已经尝试在映射期间和之后取消设置 vm_area_struct 上的 VM_PFNMAPVM_IO 标志,但 get_user_pages 仍然失败。我也看过 dma 库,但看起来它们依赖于在幕后调用 remap_pfn_range

我的问题是如何将物理内存映射为正常的、非 pfn、结构页面支持的用户空间地址?还是我应该以其他方式看待它?谢谢!

我找到了在内核外部映射内存缓冲区的解决方案,需要更正我上面提到的几个错误起点。此处不可能 post 完整的源代码,但实现它的步骤是:

  1. 设备树:为没有关联驱动程序的缓冲区定义保留内存区域。不要使用 mem 或 memmap bootargs。内核将限制自己使用此保留内存之外的内存 space,但现在将能够为保留内存创建结构页面。
  2. 在设备驱动程序(在我的例子中是 LKM)中,将物理地址映射到内核虚拟地址需要使用 memremap 而不是 ioremap,因为我们映射的是真实内存。
  3. 在设备驱动程序 mmap 例程中,不要使用 remap_pfn_range 的任何变体来设置要使用的 vma space,而是将自定义 fault nopage 例程分配给 vma->vm_ops.fault在使用userspace虚拟地址时查找页面。 lddv3 ch15.
  4. 中描述了这种方法
  5. 驱动程序中的 nopage 函数应该使用传递给它的 vm_fault 结构参数来计算需要页面的地址在 vma 中的偏移量。然后使用该偏移量计算内核虚拟地址(针对 memremap 的地址),并通过调用 page = virt_to_page(pageptr); 获取页面,然后调用 get_page(page);,并将其分配给 vm_fault 结构 vmf->page = page; 后半部分也在 lddv3 第 15 章中说明。

据我所知,使用 mmap 对自定义设备驱动程序以这种方式映射的内存可以像普通 malloc 内存一样使用。可能有一些方法可以通过 DMA 库实现类似的结果,但我有限制阻止该路由,或将设备树节点与驱动程序相关联。