dma_mmap_coherent和remap_pfn_range有什么区别?

时间:2015-12-29 18:28:16

标签: c linux linux-kernel

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

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

我已经完成了关于这个主题的尽可能多的研究。我可以找到的有关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回调中如何使用不同的apis。

1 个答案:

答案 0 :(得分: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()) mapping.c)。

关于mmap内核内存到用户空间的一般概述,以下是我从用户空间mmap'ED 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文件ops:

    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;
     }
    
  4. 这样你的内核驱动程序应该具有分配和mmap缓冲区的最小值。释放缓冲区是奖励积分的练习!

    在应用程序中,您将打开()文件并获取有效的文件描述符fd,在执行copy-to-kernel之前调用allocate IOCTL并设置缓冲区ID。在mmap中,您将通过offset参数提供缓冲区ID:

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

    PAGE_SHIFT是内核中修复的体系结构编译时MACRO。 希望这可以帮助。

    这不是checkpatch.pl兼容的代码,也不是最佳实践,但这是我知道如何执行此操作的一种方式。欢迎评论/改进/建议!

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