如何追踪DMA?

如何追踪DMA?

我正在开发通过直接内存访问 (DMA) 事务与 PCI 卡进行通信的软件。我的程序使用一套驱动程序和一个处理 DMA 的库。一切都在 Red Hat Linux 上运行。

为了测试和测量程序的性能,我想跟踪 DMA 事务的开始和结束。现在我通过查看库中的几个函数来做到这一点:

  • dma_from_hostdma_to_host通过配置卡寄存器中的值并写入1名为的寄存器来启动交易DMA_DESC_ENABLE
  • dma_wait通过不断检查寄存器的值来等待事务完成DMA_DESC_ENABLE

但我希望能够更可靠地确认交易已开始,并在交易结束时获得更精确的信号。来自 Linux 或硬件本身的东西将是最好的。

我知道原则上这是一个麻烦的情况。 DMA 的思想是硬件(PCI 卡或主板上的 DMA 控制器)绕过 CPU 和操作系统,直接将数据复制到进程的内存中。但我希望它不会只是将内容复制到 RAM 中而不以某种方式通知 CPU。是否有一些标准方法来跟踪这些交易,或者它是特定于平台的?

是否有一些特殊的中断通知CPU DMA 的开始和结束?我在我使用的驱动程序中找不到类似的东西。但我对司机没有经验,所以我很容易看错地方。

另一个想法,是否有类似 PMU 的硬件监视器可以提供此信息?只计算 PCI 通道上的事务的东西?

还有一个想法,我是否理解正确,可以将自定义 DMA 跟踪器编写为 Linux 模块或 BPF 程序来连续检查该DMA_DESC_ENABLE寄存器的值?这是一个可行的方法吗?是否有类似的已知示踪剂?

答案1

受到@dirkt 评论的鼓励,我更好地查看了驱动程序,并找到了与这些 DMA 事务相对应的 PCI MSI 中断。

驱动程序通过调用启用这些中断

pci_enable_msix(.., msixTable,..)

这设置了struct msix_entry msixTable[MAXMSIX].然后它static irqreturn_t irqHandler()通过request_irq()循环调用将它们分配给处理程序:

request_irq(msixTable[interrupt].vector, irqHandler, 0, devName,...)

处理程序仅对本地数组中的中断进行计数int。这些计数器导出到/proc/<devName>该驱动程序为诊断等创建的文件中。事实上,proc 文件是我开始搜索中断的地方。

但有一个更好的方法:/proc/interrupts文件。启用的 MSI-X 中断显示在如下行中:

$ cat /proc/interrupts 
            CPU0       CPU1  ...  CPU5       CPU6       CPU7       
  66:          0          0  ...     0          0          0  IR-PCI-MSI-edge      <devName>
  67:          0          0  ...     0          0          0  IR-PCI-MSI-edge      <devName>
  68:         33          0  ...     0          0          0  IR-PCI-MSI-edge      <devName>
  69:          0          0  ...     0          0          0  IR-PCI-MSI-edge      <devName>
  70:          0          0  ...     0          0          0  IR-PCI-MSI-edge      <devName>
  71:          0          0  ...     0          0          0  IR-PCI-MSI-edge      <devName>
  72:          0          0  ...     0          0          0  IR-PCI-MSI-edge      <devName>
  73:          0          0  ...     0          0          0  IR-PCI-MSI-edge      <devName>

另一种方法是在输出中查找卡的 PCI 地址lspci,并在目录中检查分配给卡的中断/sys

$ ls  /sys/bus/pci/devices/0000:17:00.0/msi_irqs
66  67  68  69  70  71  72  73

# but these are empty
$ cat  /sys/bus/pci/devices/0000:17:00.0/irq
0

事务结束时会触发编号 68 的中断。irq:irq_handler_entryLinux 中的中断处理程序有一个静态跟踪点。跟踪点参数字段中/sys/kernel/debug/tracing/events/irq/irq_handler_entry/format有中断号int irq。因此,可以通过带有过滤条件的跟踪点使用标准 Linux 设施来跟踪此中断:

# setup the ftrace
trace-cmd start -e irq:irq_handler_entry -f "irq == 68"
# for live stream
cat /sys/kernel/debug/tracing/trace_pipe
# or just
trace-cmd stop
trace-cmd show
trace-cmd reset

# with perf
perf record -e "irq:irq_handler_entry" --filter "irq == 68"

好处是您可以获得中断的时间戳。例如:

$ sudo trace-cmd start -e irq:irq_handler_entry -f "irq == 99"
$ sudo trace-cmd stop
$ sudo trace-cmd show | head -n 20
# tracer: nop
#
# entries-in-buffer/entries-written: 860/860   #P:12
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
          <idle>-0       [009] d.H.  6090.224339: irq_handler_entry: irq=99 name=xhci_hcd
...

有了这个,仍然值得确认的一件事是,这些中断对于 DMA 至关重要,以确保我监视与系统相关的某些内容,而不仅仅是一个proc在其他情况下可能无法实现的文件的方便计数器。但我无法通过观察它们在/proc/interrupts.设备有一些中断dmar[0123],看起来像是 DMA 的事情,但它们从未增加。这是可以预料的,因为在这种情况下,DMA 引擎必须作为 PCI 卡本身的 FPGA 内核来实现。

此外,当然中断不会让您访问有关事务本身的信息,例如传输的内存大小。您需要确保卡中不存在可能阻止中断的错误,并且在交易后立即触发中断。

相关内容