惠伟:DMA和IOMMU(一)-简单介绍zhuanlan.zhihu.com
做过DPDK/SPDK开发或者用kvm做过pci passthrough的一定知道以下的配置:
BIOS中enable vt-d,内核参数配置intel_iommu=on iommu=pt
好多人对这些配置很疑惑,不知道这些配置的是做什么的,配或者不配对性能有什么影响。
包括我自己曾经也一知半解,今天整理一下,争取让大家柳暗花明。
enable vt-d
意思很明确,BIOS收集IOMMU硬件相关的信息以及它和PCI设备连接关系的信息,通过ACPI的表上报给操作系统
intel_iommu=on
用intel_iommu驱动来驱动IOMMU硬件单元,IOMMU硬件有intel/amd/arm的等,我们一般用intel的硬件,当然用intel的iommu驱动了。
iommu=pt
我们先看内核中关于这个配置的注释
代码语言:javascript复制/*
* This variable becomes 1 if iommu=pt is passed on the kernel command line.
* If this variable is 1, IOMMU implementations do no DMA translation for
* devices and allow every device to access to whole physical memory. This is
* useful if a user wants to use an IOMMU only for KVM device assignment to
* guests and not for driver dma translation.
*/
光看注释理解还不太深刻,下面分析一下代码加深理解。
早期kvm pci passthrough的实现,qemu用到的参数是-device pci-assign,host=01:00.0,kvm最终调用了iommu的代码domain_pfn_mapping。
代码语言:javascript复制kvm_iommu_map_pages
└─iommu_map
└─intel_iommu_map(domain->ops->map)
└─domain_pfn_mapping
kvm也用了vfio-pci,qemu参数-device vfio-pci,host=b1:00.0,最终也调用了iommu的代码domain_pfn_mapping。
代码语言:javascript复制vfio_iommu_type1_ioctl
└─vfio_dma_do_map
└─vfio_pin_map_dma
└─vfio_iommu_map
└─iommu_map
└─intel_iommu_map(domain->ops->map)
└─domain_pfn_mapping
我们再看内核i40e的代码,它也调用到了domain_pfn_mapping。
代码语言:javascript复制i40e_alloc_mapped_page
└─dma_map_page
└─intel_map_page
└─__intel_map_single
├─if(iommu_no_mapping) return paddr;
├─intel_alloc_iova
└─domain_pfn_mapping
发现kvm/vfio/i40e都调用到了函数domain_pfn_mapping,相比于kvm和vfio,i40e多了一个if判断,条件是函数iommu_no_mapping的返回值。
代码语言:javascript复制/* Check if the dev needs to go through non-identity map and unmap process.*/
static int iommu_no_mapping(struct device *dev)
{
int found;
if (iommu_dummy(dev))
return 1;
if (!iommu_identity_mapping)
return 0;
found = identity_mapping(dev);
if (found) {
if (iommu_should_identity_map(dev, 0))
return 1;
else {
/*
* 32 bit DMA is removed from si_domain and fall back
* to non-identity mapping.
*/
dmar_remove_one_dev_info(si_domain, dev);
pr_info("32bit %s uses non-identity mappingn",
dev_name(dev));
return 0;
}
} else {
/*
* In case of a detached 64 bit DMA device from vm, the device
* is put into si_domain for identity mapping.
*/
if (iommu_should_identity_map(dev, 0)) {
int ret;
ret = domain_add_dev_info(si_domain, dev);
if (!ret) {
pr_info("64bit %s uses identity mappingn",
dev_name(dev));
return 1;
}
}
}
return 0;
}
再回头看iommu初始化的代码
代码语言:javascript复制static int __init init_dmars(void)
{
if (iommu_pass_through)
iommu_identity_mapping |= IDENTMAP_ALL;
if (iommu_identity_mapping) {
ret = si_domain_init(hw_pass_through);
if (ret)
goto free_iommu;
}
if (iommu_identity_mapping) {
ret = iommu_prepare_static_identity_mapping(hw_pass_through);
if (ret) {
pr_crit("Failed to setup IOMMU pass-throughn");
goto free_iommu;
}
}
}
总结起来就是
代码语言:javascript复制配置了iommu=pt就identity mapping
if hw_pass_through==0
hardware identity mapping
else
software identity mapping
说明配置了iommu=pt上面的函数iommu_no_mapping返回1,那么i40e驱动就直接return paddr,并不会真正调用到domain_pfn_mapping,直接用了物理地址少了一次映射性能当然会高一些。
总结
iommu=pt并不会影响kvm/dpdk/spdk的性能,这三者本质上都是用户态驱动,iommu=pt只会影响内核驱动,能让内核驱动设备性能更高。
补充
kvm一定要用intel_iommu=on,DPDK/SPDK如果绑定vfio-pci那也一定要求intel_iommu=on,如果绑定uio/igb_uio那么就不需要intel_iommu=on,推荐都用vfio-pci,后面kvm中的pci-assign,DPDK/SPDK用到的igb_uio都得淘汰。
还有一个内核参数是nointremap,iommu实现了dma remapping和intr remaping,kvm二者都要用,但DPDK/SPDK用轮询模式,可以不用int remapping功能,那nointremap就派上用场了。