繁体   English   中英

内核ram外的/ dev / mem的memcpy性能

[英]Memcpy performance on /dev/mem outside kernel ram

我正在使用带有自定义linux的SoC。 我通过指定内核启动参数mem = 512M保留了512MB的1GB总RAM。 我可以通过打开/ dev / mem来访问用户空间程序的高端内存,然后mmap内核不使用的高位512MB。 知道我想通过memcpy()在这个区域内复制大块内存,但性能大约是50MB / sek。 当我通过内核和memcpy分配缓冲区时,我可以达到大约500MB / sek。 我很确定是由于我的特殊内存区域禁用了缓存,但不知道如何告诉内核在这里使用缓存。

有谁知道如何解决这个问题?

注意:其中很多都以我的评论开头,所以我会尽量避免逐字重复。

关于DMA,内核访问和用户空间访问的缓冲区。 可以通过任何合适的机制分配缓冲区。

如上所述,在用户空间中使用mem=512M/dev/memmmapmem驱动程序可能无法设置最佳缓存策略。 此外, mem=512M更常用于告诉内核永远不会使用内存(例如,我们希望使用较少的系统内存进行测试),而且我们不会使用上部512M进行任何操作。

一个更好的方法可能是留下mem=512M并使用你提到的CMA 另一种方法可能是将驱动程序绑定到内核并让它在系统启动期间保留完整的内存块[可能使用CMA ]。

可以通过内核命令行参数[来自grub.cfg ]选择内存区域,例如mydev.area=mydev.size= 这对于在系统启动的“早期”阶段必须知道这些值的“绑定”驱动程序非常有用。

所以,现在我们有了“大”区域。 现在,我们需要让设备获得访问权限,并让应用程序对其进行映射。 内核驱动程序可以执行此操作。 打开设备时, ioctl可以使用正确的内核策略设置映射。

因此,根据分配机制, ioctl可以由应用程序给出address/length或者它可以将它们传递回应用程序[适当映射]。

当我不得不这样做时,我创建了一个描述内存区域/缓冲区的结构。 它可以是整个区域,也可以根据需要细分大区域。 我没有使用等同于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中提供了一个合适的骨架完整驱动程序,您可以很快地进行攻击。

非常感谢你的时间。 你的答案对我来说非常有用。 我喜欢从驱动程序本身管理缓冲区(dma缓冲区)的想法。

正如您在/ dev / mem的源代码中所阐明的那样,当我在内核中使用mem = 512M排除的区域上使用mmap时,内核威胁就像设备内存并禁用缓存。

我发现了一个中间解决方案。 我删除了内核启动参数,并在我的设备树中添加了一个保留内存区域,如下所示:

/ { 
    reserved-memory {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        my_reserved: databuffer@20000000 {
            reg = <0x20000000 0x20000000>;
        };
    };
};

这样做,我从cat / proc / iomem获得0x00000000 - 0x3fffffff的系统ram。 cat / proc / meminfo给我一个只有500 MB的空闲内存,所以我的区域没有被使用。

当我现在打开/ dev / memmmap这个区域时,我从memcpy()获得大约260 MB / sek,从memset()获得大约1200 MB / sek。 该区域被视为内存和缓存。 我不知道为什么它只是malloc区域性能的一半,但更好。

我认为对于我的案例来说,完美的解决方案可能是像/ dev / cma设备驱动程序更好的/ dev / mem,它可以从我在bootargs中定义的cma区域分配缓冲区。 在那个设备上,我可以通过ioctl()设置缓存,一致性策略之类的东西。 这将使我有机会在该区域自己设置偏好。

我在这个问题上找到了有趣的帖子,其他人如何解决它。 ARM上的连续内存和缓存一致性

以前,它将介绍我为您创建的设备驱动程序。 请参考。

https://github.com/ikwzm/udmabuf

udmabuf(用户空间可映射DMA缓冲区)

概观

介绍udmabuf

udmabuf是一个Linux设备驱动程序,它将内核空间中的连续内存块分配为DMA缓冲区,并使它们可以从用户空间获得。 当用户应用程序使用UIO(用户空间I / O)在用户空间中实现设备驱动程序时,这些内存块旨在用作DMA缓冲区。

可以通过设备文件(例如/ dev / udmabuf0)和映射到用户存储空间,或使用read()/ write()函数从用户空间访问由udmabuf分配的DMA缓冲区。

通过在打开设备文件时设置O_SYNC标志,可以禁用分配的DMA缓冲区的CPU高速缓存。 在保持启用CPU高速缓存的同时,还可以刷新或使CPU高速缓存无效。

可以通过读取/sys/class/udmabuf/udmabuf0/phys_addr来获取udmabuf分配的DMA缓冲区的物理地址。

可以在加载设备驱动程序时指定DMA缓冲区的大小和设备次要编号(例如,通过insmod命令加载时)。 某些平台允许在设备树中指定它们。

udmabuf建筑学

图1.架构

图1.架构


支持的平台

  • 操作系统:Linux内核版本3.6 - 3.8,3.18,4.4
    (作者在3.18和4.4上测试过)。
  • CPU:ARM Cortex-A9(Xilinx ZYNQ / Altera CycloneV SoC)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM