Linux驱动程序:不使用nopage的mmap()内核缓冲区到用户空间
作者:互联网
这个问题已经在这里有了答案: > Mapping DMA buffers to userspace [closed] 5个
我正在为数据获取设备实现Linux设备驱动程序,该设备不断将数据流到在内核中分配的循环缓冲区中(使用__get_free_pages()).循环缓冲区(由PCIe硬件写入)位于RAM中,我希望用户空间能够mmap()该RAM区域,以便用户空间可以读取其内容.
根据LDD3:
An interesting limitation of remap_pfn_range is that it gives access only to reserved pages and physical addresses above the top of physical memory.
…
Therefore, remap_pfn_range won’t allow you to remap conventional addresses, which include the ones you obtain by calling get_free_page.
…
The way to map real RAM to user space is to usevm_ops->nopage
to deal with page faults one at a time.
就我而言,我确切地知道在调用mmap()时,整个缓冲区将需要将哪些地址映射到给定的VMA位置,所以为什么我必须使用nopage()的方法来在一次访问一个页面?
为什么不能仅设置VMA,以便将整个环形缓冲区立即映射到用户的地址空间?有没有办法做到这一点?
我还希望用户空间程序将顺序访问我的缓冲区,从而在每次跨越页面边界时调用nopage()函数时都会导致性能下降.在实践中,这是否会严重影响性能? (我的缓冲区很大,例如16 MB.)
(值得注意的是,我在以前的设备驱动程序之一中从__get_free_pages()返回的内存上使用了remap_pfn_range(),但从未遇到任何问题,但是我可能对该系统很幸运.)
解决方法:
经过更多的研究,根据LWN(稍晚一些),LDD3的声明似乎已过时:
> http://lwn.net/Articles/162860/-“驱动程序页面重新映射的演变”
TL; DR:过去,驱动程序可能已经在kmalloc()/ __ get_free_pages()分配的页面上手动设置了PG_reserved,随后又使用了remap_pfn_range(),但是现在驱动程序现在应该使用vm_insert_page()来做同样的事情.
vm_insert_page()显然仅适用于0级(单页)分配,因此,如果要分配N页,则必须调用vm_insert_page()N次.
在Firewire驱动程序中可以看到这种用法的示例:drivers / firewire / core-iso.c
请注意如何通过重复调用fw_iso_buffer_alloc()中的alloc_page()来分配单个页面,然后通过重复调用fw_iso_buffer_map_vma()中的vm_insert_page()将这些页面映射到用户空间VMA中. (fw_iso_buffer_map_vma()由drivers / firewire / core-cdev.c中的mmap处理程序调用.)
标签:linux-device-driver,c-3,linux 来源: https://codeday.me/bug/20191122/2058258.html