当前,我正在使用example driver进行学习,并以此为基础建立了自己的自定义驱动程序。 mmap代码几乎相同,除了以下事实之外:我允许用户管理他们自己要求的大小,并以此为基础确定我的内存分配,以及我在/dev中自动创建char设备的事实。

为了解释上下文,对于我的用例,我想缩小我遇到的问题。 dma_mmap_coherent在使用kmalloc的内存时可以正常工作,但是当我有一个保留的物理地址区域要使用remap_pfn_range时,它似乎可以正常工作,并且dmesg不会报告任何错误,但是当我去阅读时,无论如何我在那里写的东西总是返回0xff字节。无论是在对内存进行Ioremap之后还是在内核中使用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。

最佳答案

dma_mmap_coherent()在dma-mapping.h中定义为dma_mmap_attrs()的包装。 dma_mmap_attrs()尝试查看一组dma_mmap_ops是否为
与您正在使用的设备(结构设备* dev)关联的设备(如果没有,则调用dma_common_mmap(),最终将页面保护设置为不可缓存后,导致对remap_pfn_range()的调用(请参见dma-中的dma_common_mmap() c。

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

  • 通过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;
            ...
    }
    
  • 定义一个mmap文件ops:
    static const struct file_operations my_fops = {
            .open = my_open,
            .close = my_close,
            .mmap = my_mmap,
            .unlocked_ioctl = my_ioctl,
    };
    

    不要忘记在驱动程序的探测函数中的某个位置注册my_fops结构。
  • 实现mmap文件ops:
     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缓冲区。释放缓冲区是加分的一种练习!

    在应用程序中,您将打开()文件并获得有效的文件描述符fd,在执行“复制到内核”之前调用分配的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。

    关于c - dma_mmap_coherent和remap_pfn_range有什么区别?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34516847/

    10-16 18:24