C 从内存到用户空间中的设备进行DMA时出现内存不一致问题

C 从内存到用户空间中的设备进行DMA时出现内存不一致问题,c,linux,linux-kernel,operating-system,arm,C,Linux,Linux Kernel,Operating System,Arm,我花了好几个小时试图找出用户空间驱动程序内存行为不一致的根本原因。我似乎已经设法至少让它工作,但是,我仍然不理解根本原因本身,我不完全满意这个解决方案,因为它绕过了内核的内存管理系统 首先,介绍一下此设置的背景知识: 该平台基于ARM,我正在运行Linux(4.4) 我正在开发一个用户空间驱动程序(UIO)来控制一个非常简单的硬件。该设备需要从主存储器(DMA)读取数据,对数据进行一些计算,并将结果输出到其中一个寄存器中。 该设备与ARM一致,因为它连接到ACP端口(这实际上意味着我不需要刷新缓

我花了好几个小时试图找出用户空间驱动程序内存行为不一致的根本原因。我似乎已经设法至少让它工作,但是,我仍然不理解根本原因本身,我不完全满意这个解决方案,因为它绕过了内核的内存管理系统

首先,介绍一下此设置的背景知识:

该平台基于ARM,我正在运行Linux(4.4) 我正在开发一个用户空间驱动程序(UIO)来控制一个非常简单的硬件。该设备需要从主存储器(DMA)读取数据,对数据进行一些计算,并将结果输出到其中一个寄存器中。 该设备与ARM一致,因为它连接到ACP端口(这实际上意味着我不需要刷新缓存或分配DMA一致内存)。 访问寄存器和中断非常容易,因为设备与uio通用驱动程序兼容。棘手的部分是从用户空间中分配物理上连续的内存,并确保设备在正确的时刻从它应该读取的位置进行读取

现在,我尝试了两种分配内存的方法,其中一种似乎有效,而另一种则无效

非工作解决方案基本上由一个非常简单的内核模块组成,该模块将内核空间中的物理连续内存映射到用户空间中的vm区域

struct page *page = alloc_page(GFP_KERNEL);
vma->vm_pgoff = page_to_phys(page) >> PAGE_SHIFT;
SetPageReserved(page);
remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, 1 << PAGE_SHIFT, vma->vm_page_prot))
struct page*page=alloc\u页面(GFP\u内核);
vma->vm_pgoff=page_to_phys(page)>>page_SHIFT;
SetPageReserved(第页);
重新映射pfn_范围(vma,vma->vm_开始,vma->vm_关闭,1个vm_页面保护))
在这种情况下,我观察到的是,当设备从“内存”中读取时(引用是因为它实际上也可能从一级/二级缓存中读取),它会得到过时的数据。我还观察到,如果我在设置内存内容之后,在启动设备之前等待(例如,通过睡眠(10)),此选项也可以工作(显然这是不可接受的,但至少它表明我在正确的位置写入)

至于有效的解决方案,我通过修改uboot中的bootparams变量保留了2MB的内存,这样内核甚至不知道它在那里。然后,在用户空间应用程序端,在适当的地址输入mmap/dev/mem。这总是有效的,这表明,不仅我似乎正在写入正确的位置,而且设备的读取与CPU一致


我更喜欢基于内核的解决方案。有人知道这里可能出了什么问题吗?我猜内核设置页表的方式会阻止一致性端口正常工作,但我不知道接下来该看什么。有人知道这两种方法之间的根本区别以及可能出现的问题吗?

设备是否在AXI级别实际发出具有适当可缓存性/可共享性的事务?(换句话说,仅仅因为设备可以窥探缓存并不意味着它一定会窥探缓存)我认为/dev/mem应该在默认情况下为内核RAM之外的东西提供一个不可缓存的映射,因此,当写入直接进入内存而不弄脏缓存时,设备可以看到写入就不那么令人惊讶了。设备本身正在发出缓存事务(根据AxCACHE AXI信号判断——除非我还缺少其他东西)。但是,如果/dev/mem给我提供了不可缓存的映射,那么我认为这并不重要,因为这些事务最终将到达内存控制器。想知道是否有办法证实这一点——我看了一下/dev/mem代码,但我不太清楚。我做了一个小的微基准测试,mmaps/dev/mem(就像以前一样)将2MB的数据复制到内存中,然后在一个循环中计算所有字节的总和。我已经使用ARM PMU测量了一级未命中和一级命中的数量,它看起来大部分时间都会命中缓存。看起来它实际上是缓存的内存,并且映射/dev/mem时ACP端口工作正常。您找到导致内核版本工作不正常的原因了吗?我没有,恐怕是通过/dev/mem完成的。