我正在使用带有自定义linux的SoC.我通过指定内核启动参数mem = 512M保留了512MB的1GB总RAM.
我可以通过打开/ dev / mem来访问用户空间程序的高端内存,然后mmap内核不使用的高位512MB.
知道我想通过memcpy()在这个区域内复制大块内存,但性能大约是50MB / sek.当我通过内核和memcpy分配缓冲区时,我可以达到大约500MB / sek.
我很确定是由于我的特殊内存区域禁用了缓存,但不知道如何告诉内核在这里使用缓存.
有谁知道如何解决这个问题?
解决方法:
注意:其中很多都以我的评论开头,所以我会尽量避免逐字重复.
关于DMA,内核访问和用户空间访问的缓冲区.可以通过任何合适的机制分配缓冲区.
如上所述,在用户空间中使用mem = 512M和/ dev / mem与mmap,mem驱动程序可能无法设置最佳缓存策略.此外,mem = 512M更常用于告诉内核永远不会使用内存(例如,我们想用较少的系统内存进行测试),而且我们不会使用上部512M进行任何操作.
一个更好的方法可能是留下mem = 512M并使用你提到的CMA.另一种方法可能是将驱动程序绑定到内核并让它在系统启动期间保留完整的内存块[可能使用CMA].
可以通过内核命令行参数[来自grub.cfg]选择内存区域,例如mydev.area =和mydev.size =.这对于在系统启动的“早期”阶段必须知道这些值的“绑定”驱动程序非常有用.
所以,现在我们有了“大”区域.现在,我们需要让设备获得访问权限,并让应用程序对其进行映射.内核驱动程序可以执行此操作.打开设备时,ioctl可以使用正确的内核策略设置映射.
因此,根据分配机制,ioctl可以由应用程序给出地址/长度,或者它可以将它们传递回应用程序[适当映射].
当我不得不这样做时,我创建了一个描述内存区域/缓冲区的结构.它可以是整个区域,也可以根据需要细分大区域.我没有使用等同于malloc的可变长度动态方案[就像你写的那样],我发现固定大小的子池工作得更好.在内核中,这称为“slab”分配器.
该结构具有给定区域的“id”数字.它还有三个地址:地址应用程序可以使用,地址内核驱动程序可以使用,以及将给予H / W设备的地址.此外,在多个设备的情况下,它可能具有当前与其关联的特定设备的id.
所以,你采取大面积,并像这样细分. 5个设备. Dev0需要10个1K缓冲区,Dev1需要10个20K缓冲区,Dev3需要10个2K缓冲区,……
应用程序和内核驱动程序将保留这些描述符结构的列表.应用程序将使用另一个带有描述符ID号的ioctl启动DMA.对所有设备重复此操作.
然后,应用程序可以发出等待完成的ioctl.驱动程序填写刚刚完成的操作的描述符.该应用程序处理数据和循环.它“就地”这样做 – 见下文.
你担心memcpy速度很慢.正如我们所讨论的,这可能是由于您在/ dev / mem上使用mmap的方式.
但是,如果您从设备DMA到内存,CPU缓存可能会变得陈旧,所以你必须考虑到这一点.真正的设备驱动程序有很多内核支持例程来处理这个问题.
这是一个很大的问题:为什么你需要做一个memcpy?如果设置正确,应用程序可以直接对数据进行操作而无需复制.也就是说,DMA操作将数据放在应用程序所需的位置.
在猜测,现在,你已经让你的memcpy“竞争”设备.也就是说,您必须快速复制数据,这样您就可以启动下一个DMA而不会丢失任何数据.
应该细分“大”区域[如上所述],内核驱动程序应该知道这些部分.因此,驱动程序启动DMA到id 0.完成后,它立即[在ISR中]启动DMA到id 1.完成后,它将进入其子池中的下一个.对于每个设备,这可以以类似的方式完成.应用程序可以使用ioctl轮询完成
这样,驱动程序可以使所有设备以最大速度运行,并且应用程序可以有足够的时间来处理给定的缓冲区.而且,再一次,它不需要复制它.
另一件要谈的事情.您设备上的DMA寄存器是否经过双重缓冲?我假设您的设备不支持复杂的分散/收集列表,并且相对简单.
在我的特定情况下,在H / W的rev 1中,DMA寄存器不是双缓冲的.
因此,在缓冲区0上启动DMA之后,驱动程序必须等到缓冲区0的完成中断,然后再将DMA寄存器设置为下一次传输到缓冲区1.因此,驱动程序必须“竞争”才能进行设置.下一个DMA [并且有一个非常短的时间窗口].启动缓冲区0后,如果驱动程序更改了设备上的DMA寄存器,则会中断已经激活的请求.
我们通过双缓冲在rev 2中修复了这个问题.当驱动程序设置DMA regs时,它将命中“start”端口.所有DMA端口都被设备立即锁存.此时,驱动程序可以*地为缓冲区1进行完整设置,当缓冲区0完成时,设备将自动切换到[无需驱动程序干预].驱动程序会得到一个中断,但几乎可以花费整个传输时间来设置下一个请求.
因此,对于rev 1风格的系统,uio方法无法工作 – 它会太慢.对于rev 2,uio可能是可能的,但我不是粉丝,即使它是可能的.
注意:在我的情况下,我们根本没有对设备读/写回调使用read(2)或write(2).一切都是通过特殊的ioctl调用来处理的,这些调用采用了上面提到的各种结构.在早期的某个时刻,我们确实以类似于uio使用它们的方式使用读/写.但是,我们发现映射是人为的并限制[和麻烦],所以我们转换为“唯一的ioctl”方法.
更重要的是,有什么要求?每秒传输的数据量.做什么设备的数量?它们都是输入还是输出?
在我的情况下[进行了广播质量hidef H.264视频的R / T处理],我们能够在驱动程序和应用程序空间以及自定义FPGA逻辑中进行处理.但是,我们使用了完整的[非uio]驱动程序方法,尽管从架构上看它看起来像uio.
我们对可靠性,R / T可预测性,保证延迟有严格的要求.我们必须处理60个视频帧/秒.如果我们跑了过来,即使只是一小部分,我们的客户也开始尖叫. uio不能为我们这样做.
所以,你用一个简单的方法开始了这个.但是,我可能会退后一步,查看需求,设备功能/限制,获取连续缓冲区的替代方法,R / T吞吐量和延迟,以及重新评估事物.您当前的解决方案是否真正满足了所有需求?目前,您已经遇到热点[应用和设备之间的数据竞争]和/或限制.或者,你是否会更好地使用本机驱动程序,为您提供更大的灵活性(即可能还有一个未知的将强制本机驱动程序).
Xilinx可能在他们的SDK中提供了一个合适的骨架完整驱动程序,您可以很快地进行攻击.