dma_mmap_coherent和remap_pfn_range有什么区别?

问题描述 投票:7回答:1

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

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

我已经做了尽可能多的关于这个主题的研究。我所能找到的有关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。

c linux linux-kernel
1个回答
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-mapping.c中的dma_common_mmap())。

至于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; }

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

在应用程序中,您将打开()文件并获取有效的文件描述符fd,在执行复制到内核之前调用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以获取教科书示例和良好的背景信息。

© www.soinside.com 2019 - 2024. All rights reserved.