用于数据采集设备的Linux驱动程序和API架构

时间:2015-08-07 11:09:58

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

我们正在尝试为自定义数据采集设备编写驱动程序/ 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有一个" start"函数,可以由用户应用程序调用,它使用ioctl向内核驱动程序发送启动消息。
  3. 在内核驱动程序中,收到启动消息后,我们启动了一个内核线程,该线程持续监视"数据就绪"位向量,当它看到新数据时,将其复制到驱动程序分配的(kmalloc)缓冲区。它一直这样做,直到收集的数据的大小达到"帧大小"。
  4. 此时,自定义的Linux SIGNAL(类似于SIGINT,SIGHUP等)被发送到运行驱动程序的进程。我们的API捕获此信号,然后回调相应的用户回调函数。
  5. 用户回调函数调用API(transfer_data)中的函数,该函数使用ioctl调用向内核发送用户空间缓冲区地址,内核通过对用户空间执行通道帧数据的copy_to_user来完成数据传输
  6. 以上所有方法都运行正常,但性能非常糟糕。我们只能达到约2MB /秒的传输速率。我们需要完全重写这一点,并且我们对任何建议或指针示例都持开放态度。

    其他说明:

    • 不幸的是,我们无法更改硬件设备中的任何内容。所以我们必须轮询数据就绪"数据就绪"位并根据该位启动DMA。

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

1 个答案:

答案 0 :(得分:0)

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

  1. 很难相信你的卡片在什么时候不会产生中断 它传输了数据。它有一个DMA引擎,它可以处理 '描述符',它们可能是分散 - 聚集的元素 名单。我假设它可以产生PCIe'中断&#39 ;; YMMV。
  2. 不要为现有的类似驱动程序搜索内核。您 可能会很幸运,但我怀疑没有。
  3. 您需要编写一个阻塞读取,为其提供大容量内存缓冲区。驱动程序读取操作(a)获取用户缓冲区的用户页面列表并将其锁定在内存中(get_user_pages); (b)使用pci_map_sg创建分散列表; (c)遍历清单(for_each_sg); (d)对于每个条目,将相应的物理总线地址和数据长度写入DMA控制器,就像我假设您正在调用“描述符”一样。

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

    就是这样。当然,细节是令人讨厌的,并且记录不完整,但这应该是基本架构。如果你真的没有中断,你可以在内核中设置一个计时器来轮询完成传输,但如果它真的是一张自定义卡,你应该收回你的钱。