【问题标题】:Map multiple kernel buffer into contiguous userspace buffer?将多个内核缓冲区映射到连续的用户空间缓冲区?
【发布时间】:2020-01-22 00:29:01
【问题描述】:

我使用dma_alloc_coherent 分配了多个内核可访问缓冲区,每个缓冲区大小为 4MiB。目标是将这些缓冲区映射到连续的用户空间虚拟内存中。问题是remap_pfn_range 似乎没有工作,因为用户空间内存有时工作,有时不工作,或者有时会复制缓冲区的页面映射。

 // in probe() function
 dma_alloc_coherent(&pcie->dev, BUF_SIZE, &bus_addr0, GFP_KERNEL);
 dma_alloc_coherent(&pcie->dev, BUF_SIZE, &bus_addr1, GFP_KERNEL);

 // ...

 // in mmap() function
 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

 pfn = dma_to_phys(&pcie->dev, &bus_addr0) >> PAGE_SHIFT;
 remap_pfn_range(pfn, vma->vm_start + 0, pfn, BUF_SIZE, vma->vm_page_prot);

 pfn = dma_to_phys(&pcie->dev, &bus_addr1) >> PAGE_SHIFT;
 remap_pfn_range(pfn, vma->vm_start + BUF_SIZE, pfn, BUF_SIZE, vma->vm_page_prot);

我不确定将多个内核缓冲区映射到连续的用户空间内存的最佳方法,但我感觉我做错了。提前致谢。

【问题讨论】:

  • 你能显示你的驱动程序的完整 mmap() 文件操作吗? DMA 也可能很棘手,您是否先尝试过简单的 kmalloc() + virt_to_phys()?

标签: linux linux-kernel linux-device-driver


【解决方案1】:

我不知道为什么没有更好的接口将多个缓冲区连续映射到用户空间。理论上,您可以多次调用remap_pfn_range(),但在某些平台(例如ARM)上,为dma_alloc_coherent() 分配的内存获取正确的pfn 基本上是不可能的。

我想出了一个解决这个问题的方法,它可能被认为不是“好”,但在我在多个平台(x86_64 和各种 ARM)上的使用中似乎运行良好。解决方法是在多次调用dma_mmap_coherent()的同时临时修改struct vm_area_struct中的开始和结束地址,每个缓冲区一次。只要您将 VMA 开始和结束地址重置为其原始值,一切似乎都可以正常工作(请参阅我之前的免责声明)。

这是一个例子:

static int mmap(struct file *file, struct vm_area_struct *vma)
{

    . . . 

    int rc;
    unsigned long vm_start_orig = vma->vm_start;
    unsigned long vm_end_orig = vma->vm_end;

    for (int idx = 0; idx < buffer_list_size; idx++) {

        buffer_entry = &buffer_list[idx];
        
        /* Temporarily modify VMA start and end addresses */
        if (idx > 0) {
            vma->vm_start = vma->vm_end;
        }
        vma->vm_end = vma->vm_start + buffer_entry->size;
        
        rc = dma_mmap_coherent(dev, vma,
                               buffer_entry->virt_address, 
                               buffer_entry->phys_addr, 
                               buffer_entry->size);
                               
        if (rc != 0) {
            pr_err("dma_mmap_coherent: %d (IDX = %d)\n", rc, idx);
            return -EAGAIN;
        }
    }
    
    /* Restore VMA addresses */
    vma->vm_start = vm_start_orig;
    vma->vm_end = vm_end_orig;
    
    return rc;
}

【讨论】:

    【解决方案2】:

    不幸的是,目前唯一支持 mmap()ing DMA 相干内存的方法是宏 dma_mmap_coherent() 或函数 dma_mmap_attrs()(由 dma_mmap_coherent() 调用)。不幸的是,这不支持将单个 VMA 拆分为多个单独分配的 DMA 相干内存块。

    (我希望有一种受支持的方法可以将 VMA 的 mmap()ing 拆分到 DMA 相干内存的多个分配中,因为它会影响我帮助维护的内核子系统中的缓冲区分配。我必须更改它以分配缓冲区作为单个 DMA 相干内存块,而不是许多页面大小的块。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-26
      • 1970-01-01
      相关资源
      最近更新 更多