Linux

如何追踪 DMA?

  • February 11, 2022

我正在開發通過直接記憶體訪問 (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寄存器的值?這是一種可行的方法嗎?有這樣的已知示踪劑嗎?

受到@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 核心來實現。

此外,當然中斷不會讓您訪問有關事務本身的資訊,例如傳輸的記憶體大小。並且您需要確保卡中沒有可以防止中斷的錯誤,並且在交易後立即觸發它們。

引用自:https://unix.stackexchange.com/questions/604585