我正在开发通过直接内存访问 (DMA) 事务与 PCI 卡进行通信的软件。我的程序使用一套驱动程序和一个处理 DMA 的库。一切都在 Red Hat Linux 上运行。
为了测试和测量程序的性能,我想跟踪 DMA 事务的开始和结束。现在我通过查看库中的几个函数来做到这一点:
dma_from_host
并dma_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_entry
Linux 中的中断处理程序有一个静态跟踪点。跟踪点参数字段中/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 内核来实现。
此外,当然中断不会让您访问有关事务本身的信息,例如传输的内存大小。您需要确保卡中不存在可能阻止中断的错误,并且在交易后立即触发中断。