저는 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와 운영 체제를 우회하고 프로세스의 메모리에 직접 데이터를 복사한다는 것입니다. 하지만 어떤 식으로든 CPU에 알리지 않고 내용을 RAM에 복사만 하는 것은 아니길 바랍니다. 이러한 거래를 추적하는 표준 방법이 있습니까, 아니면 플랫폼에 따라 다르나요?
DMA의 시작과 끝을 CPU에 알리는 특별한 인터럽트가 있습니까? 내가 사용하고 있는 드라이버에서 비슷한 것을 찾을 수 없습니다. 하지만 저는 운전자에 대한 경험이 없기 때문에 쉽게 엉뚱한 곳을 찾을 수 있습니다.
또 다른 생각으로, 이 정보를 제공할 수 있는 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 코어로 구현되어야 하기 때문에 이는 예상된 것입니다.
또한 물론 인터럽트는 전송된 메모리 크기와 같은 트랜잭션 자체에 대한 정보에 대한 액세스를 제공하지 않습니다. 중단을 방지할 수 있는 카드 오류가 없는지, 거래 직후 중단이 발생하는지 확인해야 합니다.