【问题标题】:Linux Driver and API architecture for a data acquisition device用于数据采集设备的 Linux 驱动程序和 API 架构
【发布时间】:2015-10-30 19:31:57
【问题描述】:

我们正在尝试为自定义数据采集设备编写驱动程序/API,该设备可捕获多个数据“通道”。为了便于讨论,我们假设这是一个多通道视频捕获设备。该设备通过 8xPCIe Gen-1 链路连接到系统,理论吞吐量为 16Gbps。我们的实际数据速率约为 2.8Gbps(~350MB/秒)。

由于数据速率要求,我们认为我们必须小心驱动程序/API 架构。我们已经实现了基于描述符的 DMA 机制和相关的驱动程序。例如,我们可以从设备启动一个 256KB 的 DMA 事务并成功完成。然而,在这个实现中,我们只在内核驱动程序中捕获数据,然后将其删除,我们根本没有将数据流式传输到用户空间。本质上,这只是一个小型 DMA 测试实现。

我们认为我们必须将问题分成三个部分:1.内核驱动程序2.用户空间API 3.用户代码

采集设备在 PCIe 地址空间中有一个寄存器,指示是否有数据要从设备的任何通道读取。所以,我们的内核驱动程序必须轮询这个位向量。当内核驱动程序看到该位设置时,它会启动一个 DMA 事务。然而,用户应用程序不需要知道所有这些 DMA 事务和数据,直到整个数据块准备好(例如,假设设备为我们提供每个事务的 16 行视频数据,但我们需要通知仅当整个视频帧准备好时用户)。我们只需要将整个帧传输到用户应用程序。

这是我们的第一次尝试:

  1. 我们的用户端 API 允许用户应用程序为“通道”注册函数回调。
  2. 用户端 API 有一个“启动”函数,用户应用程序可以调用该函数,用户应用程序使用 ioctl 向内核驱动程序发送启动消息。
  3. 在内核驱动程序中,在收到启动消息后,我们启动了一个内核线程,该线程持续监控“数据就绪”位向量,当它看到新数据时,将其复制到驱动程序分配的 (kmalloc)缓冲。它会一直这样做,直到收集到的数据大小达到“帧大小”。
  4. 此时,自定义 linux 信号(类似于 SIGINT、SIGHUP 等)被发送到运行驱动程序的进程。我们的 API 捕捉到这个信号,然后回调适当的用户回调函数。
  5. 用户回调函数调用API中的一个函数(transfer_data),该函数通过ioctl调用向内核发送一个用户空间缓冲区地址,内核通过对通道帧数据进行copy_to_user到用户空间完成数据传输.

上述所有工作都正常,除了性能糟糕透顶。我们只能达到大约 2MB/秒的传输速率。我们需要完全重写它,并且我们愿意接受任何建议或指向示例的指针。

其他说明:

  • 很遗憾,我们无法更改硬件设备中的任何内容。所以我们必须轮询“数据就绪”位并基于该位启动 DMA。

  • 有些人建议将 Infiniband 驱动程序作为参考,但我们完全迷失在该代码中。

【问题讨论】:

  • 我建议在内核中寻找类似的驱动程序。也许您会对驱动程序架构有所了解。尝试寻找同时使用与您相同 API 的驱动程序(例如 DMA、PCI、视频等)。然后调查你找到的代码,并尝试了解如何重用代码背后的想法。当我为 MFD 设备(从头开始)编写无线电驱动程序时,我做了完全相同的事情:找到了一些已实现的驱动程序,并想出了如何以优雅的方式拆分代码以及如何实现该驱动程序的不同部分之间的交互。跨度>
  • 对不起,有点广告。 ohwr.org/projects/zio 这是我为这个特定目的而写的,它是一个通用的 Linux I/O 框架。它不是专门用于视频的,它只是将数据从硬件移动到用户空间。我们要求最高的主板是 1.4Gbps,我们将在接下来的几周内推出另一款 2.5Gbps 的主板。如果您想对其进行评估或需要更多信息,可以给我写一封电子邮件(您可以在项目网站上找到该电子邮件)。
  • 有一个新的驱动程序支持英特尔处理器跟踪技术,它可以产生大约海量的数据(~200MB/s)。我建议查看该代码。 lwn.net/Articles/629480

标签: memory-management linux-kernel driver linux-device-driver dma


【解决方案1】:

你现在可能已经过去了,但如果不是,这就是我的 2p。

  1. 很难相信您的卡在以下情况下不会产生中断 它已传输数据。它有一个 DMA 引擎,它可以处理 “描述符”,可能是分散聚集的元素 列表。我假设它可以生成 PCIe“中断”; YMMV。
  2. 不要费心在内核中寻找现有的类似驱动程序。你 可能会很幸运,但我怀疑不会。

您需要编写一个阻塞读取,为它提供一个大的内存缓冲区。驱动程序读取操作 (a) 获取用户缓冲区的用户页面列表并将它们锁定在内存中 (get_user_pages); (b) 创建一个带有pci_map_sg 的分散列表; (c) 遍历列表 (for_each_sg); (d) 对于每个条目,将相应的物理总线地址和数据长度写入 DMA 控制器,因为我假设您称之为“描述符”。

卡现在有一个描述符列表,这些描述符对应于您的大型用户缓冲区的物理总线地址。当数据到达卡时,它直接将其写入用户空间,写入您的用户缓冲区,而您的用户级读取仍然被阻止。当它完成描述符列表时,卡必须能够中断,否则它是无用的。驱动程序响应中断并取消阻止您的用户级读取。

就是这样。当然,细节很糟糕,而且文档记录也很差,但这应该是基本架构。如果你真的没有中断,你可以在内核中设置一个计时器来轮询传输是否完成,但如果它真的是一张定制卡,你应该拿回你的钱。

【讨论】:

    猜你喜欢
    • 2013-03-25
    • 1970-01-01
    • 2012-06-22
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 2018-04-26
    • 1970-01-01
    相关资源
    最近更新 更多