Linux kernel 如何';免费';mmap的get_page()手动计数的页面引用?

Linux kernel 如何';免费';mmap的get_page()手动计数的页面引用?,linux-kernel,linux-device-driver,mmap,Linux Kernel,Linux Device Driver,Mmap,我正在将多个物理上不连续的内存缓冲区映射到单个线性用户空间地址。我使用vm_insert_page()和get_page()。我需要在所有分配的页面上使用get_page(),因为只有给定缓冲区的第一个页面的ref计数大于0,而vm_insert_page()需要ref计数大于0。 据推测(根据网上的一些帖子),如果不再需要,我必须通过调用get_page()来“释放”我递增ref count的页面。然而,我不太确定如何“释放”这些页面。我是否需要跟踪get_page()返回的每个页面结构,然后

我正在将多个物理上不连续的内存缓冲区映射到单个线性用户空间地址。我使用vm_insert_page()和get_page()。我需要在所有分配的页面上使用get_page(),因为只有给定缓冲区的第一个页面的ref计数大于0,而vm_insert_page()需要ref计数大于0。 据推测(根据网上的一些帖子),如果不再需要,我必须通过调用get_page()来“释放”我递增ref count的页面。然而,我不太确定如何“释放”这些页面。我是否需要跟踪get_page()返回的每个页面结构,然后在取消映射期间调用相应的API释放页面?看起来操作系统不会自动为我做这件事。也就是说,在用户进程存在之后,页面上的ref计数保持不变,并在用户空间的下一个mmap上再次增加

我的伪代码如下所示: allocate multiple phys non-contiguous memory buffers using calls to pci_alloc_consistent() for all 4K chunks in all buffers allocated above create a page struct using virt_to_page(phys_chunk_addr) // this is needed because only the very first page of a phys buffer // will have ref count > 0 which is needed by vm_insert_page()! increment page ref count by calling get_page() place the page in vma using vm_insert_page() 使用对pci_alloc_constant()的调用分配多个PHY非连续内存缓冲区 对于上面分配的所有缓冲区中的所有4K块 使用virt_to_page(phys_chunk_addr)创建页面结构 //这是必需的,因为只有phys缓冲区的第一页 //将具有vm_insert_page()所需的ref count>0! 通过调用get_page()增加页面引用计数 使用vm_insert_page()将页面放入vma 谢谢你的建议/指点。
Dan。

DMA一致性内存可能需要特定于体系结构的缓存标志,因此您不能简单地将其映射到用户空间

pci\u alloc\u consistent
已弃用。 要映射单个连续内存块,请使用
dma_-alloc_-coherent
dma_-mmap_-coherent


如果您不太关心可移植性,您可以避免同时使用
dma\u alloc\u coherent
,而是使用单独的页面:

  • 使用
    alloc\u page
    分配一组页面(对于PCI,您通常需要
    GFP\u DMA32
  • 使用
    DMA_map_page
    获取每页的DMA地址
  • 使用
    vm\u insert\u页面
    将它们映射到用户空间
请注意,这不是一致的DMA映射,而是流式DMA映射。 在x86上,这并不重要,但在许多其他体系结构上,您必须在适当的时间调用
dma\u sync.
*函数

有关内核中的示例,请参见
drivers/firewire/core iso.c
core cdev.c


如果需要将多个大型物理连续缓冲区映射到一个几乎连续的区域,则不能使用
dma_mmap_coherent
,因此必须以艰难的方式执行此操作:

  • 根据需要随时调用
    dma\u alloc\u coherent
  • mmap
    实现中,只需设置
    vm_area_struct->vm_ops
  • vm\u operations\u struct.fault
    中,调用
    virt\u to\u page
    ,调用
    get\u page
    ,并将
    vm\u fault->page
    设置到页面

  • 有关示例,请参见
    sound/core/pcm_native.c

    谢谢您的回复。该驱动程序仅适用于x86。在我们的系统上,pci_alloc_一致映射到dma_alloc_一致。但是,我仍然不清楚如何“释放”引用计数随get_page()递增的页面。取消映射将处理由vm_insert_页面递增的引用计数,但我应该处理由我通过get_page()递增的引用计数。有什么想法吗?你不必为
    get\u page()
    操心。为什么不使用
    dma_mmap_coherent()
    ?看起来dma_mmap_coherent可以映射使用dma_coherent分配的单个内存块。我需要将来自多个调用的多个块映射到dma_alloc_相干到用户空间中的单个线性virt addr。dma_alloc_coherent只能分配4MB的块,但我需要一个5MB或更多的缓冲区。我可以很容易地处理多块缓冲区的dma操作,但我确实需要一个将块视为单个线性地址的过程。顺便说一句,我在最初的博文中描述的过程,除了使用get_页面更改的ref计数在取消映射(即过程结束)后不会下降。那么这就是一个漏洞。你可以为每页调用
    dma\u alloc\u coherent
    一次。但是您不应该对返回的缓冲区页面执行任何操作(
    virt\u to\u page
    在许多情况下都不起作用);这就是
    dma\u map\u页面的作用。再次感谢您提供的信息。然而,我认为你的建议对我不起作用。我需要dma~5MB缓冲区(最多2或3块)到我们的设备。不支持分散/聚集dma。alloc_页面将只分配一个页面,因此我需要处理每个缓冲区的许多dma传输,这是不可接受的。我不关心可移植性-驱动程序和设备都是定制的,用于内部目的。所以,如果我不需要是可移植的(即仅限x86),您知道一种安全的方法来将两个大缓冲区映射到用户空间,以便将它们视为单个线性地址吗?