像往常一样将物理内存映射到用户空间,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_PFNMAP
和 VM_IO
,这是不可能的。
我的驱动程序当前通过调用 vm_iomap_memory(vma, start, size)
执行映射,后者又调用 io_remap_pfn_range
和 remap_pfn_range
,后者使用 VM_PFNMAP
和 VM_IO
设置。这可以将内存映射到用户空间,但是由于设置了 VM_PFNMAP
标志或缺少结构页面,零拷贝套接字操作在 get_user_pages
失败。 remap_pfn_range
的注释表明这是预期的行为,因为 pfn 映射内存不应被视为 'normal'。但是,对于我来说,它只是一块保留的 RAM,所以我不明白为什么它不应该被视为正常。我已经设置缓存 invalidation/flushing 来手动管理内存。
我已经尝试在映射期间和之后取消设置 vm_area_struct
上的 VM_PFNMAP
和 VM_IO
标志,但 get_user_pages
仍然失败。我也看过 dma 库,但看起来它们依赖于在幕后调用 remap_pfn_range
。
我的问题是如何将物理内存映射为正常的、非 pfn、结构页面支持的用户空间地址?还是我应该以其他方式看待它?谢谢!
我找到了在内核外部映射内存缓冲区的解决方案,需要更正我上面提到的几个错误起点。此处不可能 post 完整的源代码,但实现它的步骤是:
- 设备树:为没有关联驱动程序的缓冲区定义保留内存区域。不要使用 mem 或 memmap bootargs。内核将限制自己使用此保留内存之外的内存 space,但现在将能够为保留内存创建结构页面。
- 在设备驱动程序(在我的例子中是 LKM)中,将物理地址映射到内核虚拟地址需要使用
memremap
而不是 ioremap
,因为我们映射的是真实内存。
- 在设备驱动程序 mmap 例程中,不要使用
remap_pfn_range
的任何变体来设置要使用的 vma space,而是将自定义 fault
nopage 例程分配给 vma->vm_ops.fault
在使用userspace虚拟地址时查找页面。 lddv3 ch15. 中描述了这种方法
- 驱动程序中的 nopage 函数应该使用传递给它的 vm_fault 结构参数来计算需要页面的地址在 vma 中的偏移量。然后使用该偏移量计算内核虚拟地址(针对 memremap 的地址),并通过调用
page = virt_to_page(pageptr);
获取页面,然后调用 get_page(page);
,并将其分配给 vm_fault 结构 vmf->page = page;
后半部分也在 lddv3 第 15 章中说明。
据我所知,使用 mmap 对自定义设备驱动程序以这种方式映射的内存可以像普通 malloc 内存一样使用。可能有一些方法可以通过 DMA 库实现类似的结果,但我有限制阻止该路由,或将设备树节点与驱动程序相关联。
我有一个自定义设备驱动程序,它实现 mmap
操作以将共享 RAM 缓冲区(在 OS 之外)映射到用户空间。通过将 mem=32M
作为 OS 的启动参数传递来保留缓冲区,将剩余的 512MB 留作缓冲区。我想从映射内存执行零拷贝操作,如果 vm_flags
包含 VM_PFNMAP
和 VM_IO
,这是不可能的。
我的驱动程序当前通过调用 vm_iomap_memory(vma, start, size)
执行映射,后者又调用 io_remap_pfn_range
和 remap_pfn_range
,后者使用 VM_PFNMAP
和 VM_IO
设置。这可以将内存映射到用户空间,但是由于设置了 VM_PFNMAP
标志或缺少结构页面,零拷贝套接字操作在 get_user_pages
失败。 remap_pfn_range
的注释表明这是预期的行为,因为 pfn 映射内存不应被视为 'normal'。但是,对于我来说,它只是一块保留的 RAM,所以我不明白为什么它不应该被视为正常。我已经设置缓存 invalidation/flushing 来手动管理内存。
我已经尝试在映射期间和之后取消设置 vm_area_struct
上的 VM_PFNMAP
和 VM_IO
标志,但 get_user_pages
仍然失败。我也看过 dma 库,但看起来它们依赖于在幕后调用 remap_pfn_range
。
我的问题是如何将物理内存映射为正常的、非 pfn、结构页面支持的用户空间地址?还是我应该以其他方式看待它?谢谢!
我找到了在内核外部映射内存缓冲区的解决方案,需要更正我上面提到的几个错误起点。此处不可能 post 完整的源代码,但实现它的步骤是:
- 设备树:为没有关联驱动程序的缓冲区定义保留内存区域。不要使用 mem 或 memmap bootargs。内核将限制自己使用此保留内存之外的内存 space,但现在将能够为保留内存创建结构页面。
- 在设备驱动程序(在我的例子中是 LKM)中,将物理地址映射到内核虚拟地址需要使用
memremap
而不是ioremap
,因为我们映射的是真实内存。 - 在设备驱动程序 mmap 例程中,不要使用
remap_pfn_range
的任何变体来设置要使用的 vma space,而是将自定义fault
nopage 例程分配给vma->vm_ops.fault
在使用userspace虚拟地址时查找页面。 lddv3 ch15. 中描述了这种方法
- 驱动程序中的 nopage 函数应该使用传递给它的 vm_fault 结构参数来计算需要页面的地址在 vma 中的偏移量。然后使用该偏移量计算内核虚拟地址(针对 memremap 的地址),并通过调用
page = virt_to_page(pageptr);
获取页面,然后调用get_page(page);
,并将其分配给 vm_fault 结构vmf->page = page;
后半部分也在 lddv3 第 15 章中说明。
据我所知,使用 mmap 对自定义设备驱动程序以这种方式映射的内存可以像普通 malloc 内存一样使用。可能有一些方法可以通过 DMA 库实现类似的结果,但我有限制阻止该路由,或将设备树节点与驱动程序相关联。