【问题标题】:What is the difference between dma_mmap_coherent and remap_pfn_range?dma_mmap_coherent 和 remap_pfn_range 有什么区别?
【发布时间】:2016-04-03 16:18:48
【问题描述】:

目前,我正在使用example driver 来学习,并以此为基础建立了自己的自定义驱动程序。 mmap 代码几乎完全相同,除了我允许用户管理他们自己请求的大小并以此为基础分配内存以及我在 /dev 中自动创建 char 设备这一事实。

为了解释上下文,对于我的用例,我想缩小我遇到的一个问题。 dma_mmap_coherent 在使用 kmalloc 的内存时可以测试工作,但是当我有一个保留的物理地址区域时,我想使用它的 remap_pfn_range 安静地工作,并且 dmesg 没有报告任何错误,但是当我去阅读时,无论我在那里写了什么,它总是返回 0xff 字节。无论我是在 ioremap'ing 内存之后在内核空间中使用 iowrite 和 ioread 还是尝试使用小型 mmap'ing 用户空间测试在用户空间中写入,这都是正确的。

我已经对这个主题进行了我能想到的尽可能多的研究。对于 remap_pfn_range 的文档,我只能找到kernel.org page,以及 remap_pfn_range 上的一些内核 gmain 邮件列表存档替换 remap_page_range。至于 dma_mmap_coherent,我能找到更多,including a presentation from the linux archives

最终必须有所不同;似乎有很多不同的方法可以将内核内存映射到用户空间。我的具体问题是:dma_mmap_coherentremap_pfn_range 有什么区别?

编辑最好提供将内核内存映射到一般用户空间的方法的总体概述,涵盖如何在内核驱动程序 mmap 回调中使用不同的 api。

【问题讨论】:

  • 你有没有想过这个问题,即一起使用 mmap 和 dma_mmap_coherent?我做不到,我自己做了读写函数。
  • 是的,我确实使用了那些特定的功能。你想要一个例子吗?我特别在寻找 remap_pfn_range 和 dma_mmap_coherent 之间的区别,以便我知道要走哪条路线。
  • 如果你能发布一个这个工作的例子,那就太好了。我猜用户端代码是标准的,而 kmod 是我出错的地方。
  • 当然我会在有时间的时候发布一些东西。我很确定我自己也从一个例子中学到了东西,但我很快就会制作并测试一个。你用的是什么内核版本?

标签: c linux linux-kernel


【解决方案1】:

dma_mmap_coherent() 在dma-mapping.h 中定义为 dma_mmap_attrs() 的包装器。 dma_mmap_attrs() 尝试查看是否有一组 dma_mmap_ops 与您正在操作的设备(struct device *dev)相关联,如果不是,它会调用 dma_common_mmap(),最终导致调用 remap_pfn_range(),在将页面保护设置为不可缓存后(参见 dma- 中的 dma_common_mmap()映射.c)。

关于将内核内存映射到用户空间工作的一般概述,以下是我从用户空间映射 DMA 缓冲区的快速简单的方法:

  1. 通过 IOCTL 分配一个缓冲区,并为每个缓冲区指定一个缓冲区 ID,并带有一些标志:

    /* A copy-from-user call needs to be done before in the IOCTL */
    static int my_ioctl_alloc(struct my_struct *info, struct alloc_info *alloc)
    {
    
            ...
            info->buf->kvaddr = dma_alloc_coherent(dev, alloc->size, info->buf->phyaddr, GFP_KERNEL);
            info->buf->buf_id = alloc->buf_id;
            ...
    }
    
  2. 定义一个 mmap 文件操作:

    static const struct file_operations my_fops = {
            .open = my_open,
            .close = my_close,
            .mmap = my_mmap,
            .unlocked_ioctl = my_ioctl,
    };
    

    不要忘记在驱动程序的探测函数中注册 my_fops 结构体。

  3. 实现 mmap 文件操作:

     static int my_mmap(struct file *fptr, struct vm_area_struct *vma)
     {
             ...
             desc_id = vma->vm_pgoff;
             buf = find_buf_by_id(alloc, desc_id);
             vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
             ret = remap_pfn_range(vma, vma->vm_start, buf->phyaddr >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot);
             if (ret) {
                  /* Error Handle */
             }
             return 0;
     }
    

有了这个,您的内核驱动程序应该有最少的分配和 mmap 缓冲区。释放缓冲区是一种奖励积分的练习!

在应用程序中,您将 open() 文件并获取有效的文件描述符 fd,调用 allocate IOCTL 并在执行复制到内核之前设置缓冲区 ID。在 mmap 中,您可以通过 offset 参数给出缓冲区 ID:

      mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer_id << PAGE_SHIFT);

PAGE_SHIFT 是内核中固定的依赖于架构的编译时间宏。 希望这可以帮助。

这不是符合 checkpatch.pl 的代码,也不是最佳实践,但这是我知道如何做到这一点的一种方式。欢迎评论/改进/建议!

请参阅 Linux 设备驱动程序 - 第 15 章:内存映射和 DMA 以获取教科书示例和为感兴趣的读者提供的良好背景信息。

【讨论】:

  • 这个答案对我很有帮助。谢谢!
  • 我不是驱动专家,但它几乎是通过 linux 内核 cscoping 来查找 dma 调用映射到什么。当需要连续的缓冲区或物理地址时,通常没有编写内核驱动程序的捷径!
  • 你为什么说这不是好习惯?表现?稳定?那么最佳做法是什么?
  • 理想情况下,如果您的目标是执行 DMA,并且您有能力使用分散-聚集列表,那么您宁愿跳过分配内核内存,而只在用户空间之间执行 DMA记忆。或者如果可能的话,使用 HugePages 和 HugeTLB 完全跳过 linux VM。从某种意义上说,这会更好,因为不必浏览 Linux VM 子系统。
  • @hit.at.ro 谢谢,这看起来很有趣。您能否提供有关如何直接对用户空间内存执行 DMA(实际上是 PCIe 总线主控)的链接?我们仍然需要分配非缓存的支持 DMA 的内存,这可能在用户空间中吗?!对于相对较小的缓冲区大小,我认为我不需要分散-聚集列表,我可以使用 PAGE_SIZE 中 4KB 的连续内存拆分。
猜你喜欢
  • 2015-02-12
  • 2010-10-02
  • 2011-12-12
  • 2010-09-16
  • 2012-03-14
  • 2012-02-06
  • 2011-02-25
  • 2011-11-22
  • 2015-03-26
相关资源
最近更新 更多