以前基于DPDK做NFV,转发程序跑在虚拟机中,先把硬件网卡passthrough给虚拟机,然后在虚拟机中把网卡绑定内核模块igb_uio,问题是igb_uio的代码没有upstream,依赖于内核版本,提前编译好的内核模块换个版本就不能运行,就想着用vfio-pci,这家伙早早upsteam,一般linux发行版本内核都自带,且不省事,理想是丰满的,现实是骨感的。
vfio-pci内核模块的probe函数返回了错误:
[51376781.097090] vfio-pci: probe of 0000:00:06.0 failed with error -22
代码语言:javascript复制static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int ret;
if (pdev->hdr_type != PCI_HEADER_TYPE_NORMAL)
return -EINVAL;
group = vfio_iommu_group_get(&pdev->dev);
if (!group)
return -EINVAL;
ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev);
if (ret) {
vfio_iommu_group_put(group, &pdev->dev);
kfree(vdev);
return ret;
}
return ret;
}
22是EINVAL,看代码是iommu group导致的,再分析是因为机器没有iommu硬件单元,意思就是qemu没有模拟出iommu硬件单元,把模拟的iommu称为虚拟的iommu,就是viommu。
其实viommu和嵌套虚拟化关系很大,比如把pci passthroughed device再次passthrough给虚拟机中的虚拟机,把emulated pci device passthrough给虚拟机中的虚拟机。
qemu最早支持intel-iommu和iommu=smmuv3,后来有virtio-iommu,又演化出vhost-iommu,把virtio-iommu部分功能拿到内核来实现。整体上分为full emulated和hardware assisted emulation,full emulation就是virtio-iommu,纯软件实现iommu的所有功能,性能低,但virtio应用广,能和现有的实现结合起来,比如kernel中的vhost device-iotlb,dpdk中的vhost-user device-iotlb,iommu功能分布在qemu和virtio其它backend处。hardware assisted emulation肯定借助了硬件的好处,硬件实现了两层翻译和各种隔离,qemu做guest里driver和真正硬件之间的翻译,qemu不能直接给硬件提交工作,需要内核提供通道IOMMU Userspace API,通道下去再调用到硬件的驱动,好处就是可以和内核vfio-iommu那一套结合起来,自然而然就可以利用到fault handling,cache invalidation和PASID等,坏处就是guest里运行着厂商自己的driver,如果guest里运行virtio-iommu driver,外面能实现hardware assisted那么就更好。
还有一个问题就是,创建一个虚拟机,然后给这个虚拟机passthrough一个网卡,虚拟机的qemu进程并不需要用到hugepage,在这个虚拟机里运行DPDK需要用到hugepage,hugepage的好处就是长在内存pin住,虚拟机里DPDK用到的hugepage本质上还是qemu进程的非hugepage虚拟内存空间,如何保证DPDK的hugepage一直在真正的物理内存中?这和虚拟机中DPDK用不用vfio-pci没关系。
intel vt-d标准3.2版本第六章《Caching Translation Information》和第七章《Address Translation Faults》讲的东西似乎能解决这个问题,第六章节讲的是IOMMU硬件单元的cache,因为它用的转换表都在内存中,每次去内存读然后再遍历一遍开销很大,所以尽量cache(Context-cache,PASID-cache,IOTLB和Paging-structure Caches),有cache就得维护cache项的状态,是否有效,什么时候清理掉。还有一点就是Device-TLB,device发起一个address translation,IOMMU硬件单元查找自己的cache,然后把结果通过Translation-Completion给了device,device把结果缓存在自己的TLB。第七章讲了Non-recoverable Faults和Recoverable Faults,device发起一个request,IOMMU查找自己的page table发现没有对应的page,就给device-TLB返回一个有问题的Translation-Completion,device-TLB就能检测到这个错误,device上报给自己的driver,driver处理完后让device重试,这样的问题就是不够通用,device和driver各个厂商实现不一样。所以vt-d标准又说,如果device支持pcie page request service,那么device就给IOMMU发送一个page-request消息,IOMMU生成一个struct放在page request queue中,然后给CPU来个page request event中断,IOMMU驱动开始处理,指导IOMMU给device回复一个Page Group Response消息,device再重试刚才的request,这样的好处就是软件通用,代码都在IOMMU驱动中,device硬件要配合IOMMU硬件,整体上来说对硬件的要求很高。
好的,到此IOMMU系列写完,希望对大家有用。
参考文献
IOMMU Userspace API
vt-d 3.2 spec