CHAPTER 06
6. Endpoint bus-master DMA를 iATU, IOMMU와 cache 관점에서 분리한다
Endpoint의 DMA engine이 Memory Read/Write TLP를 만들면 requester ID가 Root Port와 IOMMU를 통과합니다. DWC eDMA와 endpoint DMA는 별도 엔진이며, dma_map 결과가 IOVA인지 PA인지도 platform에 따라 달라집니다.
DMA 경로의 소유권
| 단계 | 소유자 | 핵심 상태 | 완료 증거 | 수명 오류 |
|---|---|---|---|---|
| buffer allocation | memory allocator/driver | CPU VA/PA, size | allocation 성공 | free 후 device 접근 |
| DMA mapping | DMA API/IOMMU | DMA addr, direction, attrs | mapping 성공/IOMMU PTE | unmap 시점 오류 |
| descriptor build | driver | addr/len/flags | ownership 전환 전 내용 | 잘못된 endian/stride |
| publication barrier | driver/architecture | dma_wmb 등 | device가 완전한 descriptor 관찰 | doorbell이 먼저 보임 |
| bus mastering | PCI Command/endpoint | MASTER bit | Memory TLP 발생 | enable 전 queue 시작 |
| PCIe routing | DWC/bridge | RID/address/attributes | TLP/AER | inbound window/routing 오류 |
| IOMMU translation | SMMU/IOMMU | stream ID/RID, IOVA→PA | fault 없음/PTE hit | domain/RID mismatch |
| cache visibility | DMA API/coherency fabric | sync/barrier | CPU/device가 같은 data 관찰 | stale cacheline |
| completion | device/driver | status/index/MSI | queue entry 회수 | IRQ만 오고 data 미완료 |
DMA API direction을 device 관점으로 읽기
| Direction | Device 동작 | CPU 동작 | map/sync에서 중요한 것 | 오해 |
|---|---|---|---|---|
| DMA_TO_DEVICE | Memory Read | buffer를 준비 | CPU write가 device에 보이게 | CPU 기준 'send' 이름만 봄 |
| DMA_FROM_DEVICE | Memory Write | 완료 후 buffer 읽음 | device write가 CPU에 보이게 | posted write 완료를 MSI만으로 추정 |
| DMA_BIDIRECTIONAL | 양방향 | 양방향 | 더 강한 관리 비용 | 모르면 전부 BIDIR 사용 |
| Coherent allocation | descriptor/status 공유 | 일반적으로 별도 sync 불필요 | ordering barrier는 여전히 필요할 수 있음 | coherent=순서 자동 보장 |
| Streaming mapping | payload 등 | map/unmap 또는 sync | ownership 전환마다 API 준수 | 같은 mapping을 무기한 재사용 |
IOMMU fault를 queue entry로 되돌리는 항목
| Fault field | 연결할 대상 | 예 |
|---|---|---|
| Requester/Stream ID | PCI BDF와 iommu-map | 03:00.0 RID |
| IOVA | descriptor DMA address | 0x124000000 |
| Access type | DMA direction/TLP | write fault |
| Length/page | descriptor length와 mapped span | 4KiB boundary |
| Domain/PASID | device/domain/process | default domain |
| Timestamp | doorbell/completion/IRQ trace | queue entry 127 |
| Reason | translation/permission/abort | write permission 없음 |
IOMMU group은 주소 변환과 다른 격리 경계입니다
VM에 PCIe 장치를 직접 할당할 때는 DMA 주소 변환뿐 아니라 다른 장치와 독립적으로 격리할 수 있는지도 확인합니다. 다기능 장치 내부의 우회 경로, ACS(Access Control Services)가 없는 브리지, PCIe 토폴로지 때문에 격리 단위가 한 BDF(Bus·Device·Function)보다 클 수 있습니다. IOMMU group은 다른 장치들로부터 함께 격리할 수 있는 장치 집합이며, VFIO는 이를 DMA 소유권의 단위로 사용합니다.
Linux v6.18.37의 device cdev·IOMMUFD 경로도 이 제약을 유지합니다. 같은 그룹의 장치를 서로 다른 iommufd_ctx에 연결하거나, 일반 호스트 드라이버와 VFIO 등 서로 다른 DMA 소유자가 나누어 사용할 수 없습니다. 소유권 제약을 어기면 VFIO_DEVICE_BIND_IOMMUFD가 실패합니다. 따라서 BDF 하나와 “IOMMU 활성” 표시만으로 할당이 안전하다고 판단하지 말고, 그룹 구성과 그룹 안의 드라이버 소유권을 함께 확인하십시오. 이 설명은 실제 장치 할당이나 격리 시험을 수행한 결과가 아닙니다.
근거: Linux v6.18.37 VFIO의 Groups, Devices, and IOMMUs·Device cdev. API 객체와 buffer 종료 조건은 SMMU 문맥 수명의 VFIO 절에서 이어서 확인합니다.
VF 생성과 안전한 VM 할당은 다른 단계입니다
SR-IOV에서는 PF 드라이버 또는 sriov_numvfs 인터페이스를 통해 VF 수를 설정할 수 있습니다. 각 VF가 PCI function으로 열거되어 BDF와 설정 공간을 갖는다는 사실과 독립적으로 격리·할당할 수 있다는 사실은 구분해야 합니다. 활성화·할당·사용 중 VF 수 변경은 읽기 전용 관측이 아니며 여기서는 실행하지 않습니다.
할당 검토에서는 VF의 실제 IOMMU group과 드라이버 소유권, PF에 대한 의존성, reset의 영향 범위를 따로 확인합니다. VF라는 이름만으로 독립 reset, 성능 보장 또는 group 분리를 보증할 수 없습니다. 기존 group 제약은 SR-IOV에서도 생략되지 않습니다.
Linux PCI SR-IOV HOWTO · VFIO 격리·소유권 문서. Ferdinand Castelino의 Part 12는 원저자 측 연속 자료로 참고했으며 사용자가 재공유했다고 확인한 기록과 합치지 않습니다. 실제 VF 생성·passthrough 시험 결과는 아닙니다.
실제 검증 절차
| 순서 | 실행 | 남길 증거 | 판정 목적 |
|---|---|---|---|
| 1 | DMA mask와 coherent mask 협상 결과를 저장합니다. | device address width 안의 DMA addr | 32/64bit truncation 배제 |
| 2 | descriptor raw bytes와 driver가 해석한 addr/len/flags를 함께 dump합니다. | endianness/stride/ownership 일치 | 구조체 pretty print만 믿지 않음 |
| 3 | doorbell 직전 barrier와 bus-master enable을 trace합니다. | descriptor 게시 뒤 device fetch | ordering 문제 분리 |
| 4 | IOMMU fault의 RID/IOVA를 정확한 queue index로 연결합니다. | fault가 해당 mapping span 안에 있음 | 무관한 device fault와 혼합 방지 |
| 5 | IRQ를 끄고 polling completion으로도 data visibility를 확인합니다. | DMA 문제와 MSI routing 분리 | interrupt가 원인인지 결과인지 구분 |
주의Root Complex의 DWC inbound iATU가 항상 endpoint DMA를 최종 DRAM PA까지 변환한다고 가정하지 않습니다. SoC 통합에 따라 interconnect/SMMU 경로가 다르므로 실제 requester path를 확인합니다.
공개적으로 다시 확인할 수 있는 자료
공개되지 않은 제조사 레지스터나 제품별 오프셋은 추측하지 않았습니다. 아래의 제조사 자료, 아키텍처 문서, Linux v6.18.37 원본으로 확인할 수 있는 내용만 다뤘습니다.
Linux v6.18.37 DWC common source
iATU programming, version/capability와 eDMA detection
Linux v6.18.37 DWC host source
ECAM/CFG access, Root Port setup, MSI irqdomain와 host lifecycle
Linux v6.18.37 PCI probe
Vendor ID read, pci_dev 생성, BAR sizing와 bridge scan
Linux v6.18.37 PCI resource assignment
bridge window와 endpoint BAR resource 배치
Linux DWC Devicetree binding
reg/ranges, iATU window 수, lane/speed와 MSI/IOMMU 연결
DMA mapping과 DMAengine 전송 시작을 분리합니다
dma_map_single()이나 dma_map_sg()는 장치가 쓸 DMA 주소와 필요한 매핑을 준비합니다. 그것만으로 전송이 시작되지는 않습니다. 주변장치가 별도의 DMA controller를 사용하는 slave DMA에서는 DMAengine 채널 요청·설정, descriptor 준비, 제출, 실행 요청, 완료 확인이 이어집니다. PCIe endpoint가 자기 bus-master 엔진을 직접 구동하는 경로와 구분합니다.
slave scatter-gather 예에서는 dmaengine_get_dma_device(chan)의 device로 SG를 매핑하고, 반환된 매핑 개수와 방향으로 descriptor를 준비합니다. 매핑 오류를 확인하고 전송이 끝날 때까지 매핑을 유지합니다. 나중의 sync 역시 같은 DMA device를 사용합니다. client 주변장치의 device와 실제 DMA controller의 device를 무조건 같은 것으로 가정하지 않습니다.
dmaengine_submit()은 pending queue에 제출하며 성공 cookie와 오류를 확인해야 합니다. 제출한 descriptor는 엔진 소유이므로 포인터를 계속 수정하면 안 됩니다. dma_async_issue_pending()이 대기 작업의 실행을 요청하며, 반환 자체가 완료 통지는 아닙니다. 취소 때도 dmaengine_terminate_async()만 호출하고 버퍼를 해제하지 않습니다. 필요하면 허용된 문맥에서 dmaengine_synchronize()로 진행 중 전송·callback의 종료를 확인한 뒤 반환합니다.
기준: DMA Engine API Guide. 이 글은 API 계약을 대조했으며 특정 DMA controller를 실행·성능 측정하지 않았습니다.
English summary. Mapping does not launch a transfer. For slave DMA, map against the DMA controller device, prepare and submit a descriptor, issue pending work, then confirm completion. Asynchronous termination alone does not make its buffer safe to free.