Linux中DMA会使用硬件IOMMU如AMD IOMMU, INTEL VT-D, 也会使用软件的SWIOTLB
这篇梳理一下LINUX内核在有AMD IOMMU的情况下,是如何做DMA的,内容包括如下
1. struct iommu_ops amd_iommu_ops
2. struct dma_map_ops iommu_dma_ops
3. DMA struct dma_map_ops 与 struct iommu_ops的关系
Consistent, Streaming
4. struct io_pgtable_ops, 及与struct iommu_ops amd_iommu_ops的关系
1. 两处会设置struct iommu_ops amd_iommu_ops;
const struct iommu_ops amd_iommu_ops = {
.capable = amd_iommu_capable,
.domain_alloc = amd_iommu_domain_alloc, // 分配一个iommu_domain
.domain_free = amd_iommu_domain_free,
.attach_dev = amd_iommu_attach_device, //针对独立设备(即所在Group里只有自己),将设备所在Group与domain进行绑定
.detach_dev = amd_iommu_detach_device,
.map = amd_iommu_map, //用于映射domain内的iova,将长度为size
以iova
为起始地址的iova区域映射到以paddr为起始地址的物理地址。该函数只能用于UNMANAGED
类型和DMA
类型的domain
.iotlb_sync_map = amd_iommu_iotlb_sync_map,
.unmap = amd_iommu_unmap,
.iova_to_phys = amd_iommu_iova_to_phys, // 将iova转换成物理地址
.probe_device = amd_iommu_probe_device,
.release_device = amd_iommu_release_device,
.probe_finalize = amd_iommu_probe_finalize,
.device_group = amd_iommu_device_group,
.get_resv_regions = amd_iommu_get_resv_regions,
.put_resv_regions = generic_iommu_put_resv_regions,
.is_attach_deferred = amd_iommu_is_attach_deferred,
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
.iotlb_sync = amd_iommu_iotlb_sync,
.def_domain_type = amd_iommu_def_domain_type,
};
一处在struct iommu_device的iommu ops;
另一处在struct bus_type的iommu ops;
amd_iommu_init ->
iommu_go_to_state ->
state_next ->
amd_iommu_init_pci ->
iommu_init_pci ->
iommu_device_register
struct iommu_device {