dpdk vhost-uesr对vIOMMU的支持
关于什么是IOMMU前面的文章已经分析过,这里再简单回顾一下。如下图所示:
IOMMU几乎等同于用于I / O空间的MMU(设备直接使用DMA访问内存)。它位于主内存和设备之间,为每个设备创建虚拟I / O空间并提供一种机制将虚拟I / O内存动态映射到物理内存。因此,当驱动程序配置设备的DMA(例如网卡)时,它会配置虚拟地址,并且当设备尝试访问它们时,它们将被 IOMMU重新映射。
它具有许多优点,例如:
· 可以分配较大的连续虚拟内存区域,而不必在物理内存中连续。
· 某些设备不支持足够长的地址来访问整个物理内存,IOMMU解决了该问题。
· 内存受到保护,不会受到恶意设备执行的DMA攻击,这些设备尝试访问未明确为其分配的内存。这些设备仅“看到”虚拟地址空间,并且运行中的操作系统专门控制IOMMU的映射。
· 在某些架构,支持中断重新映射,可以允许中断隔离和迁移。
像往常一样,一切都是有代价的,对于IOMMU来说,其不足是:
· 与页面转换服务有关的性能下降
· 添加的页面转换表会占用物理内存
最后,IOMMU提供了PCIe地址转换服务接口,设备可以使用该接口在内部设备表后备缓冲区(TLB)中查询和缓存地址转换。
那么什么是vIOMMU呢?vIOMMU就是在vm(guest)中的IOMMU功能,它和IOMMU的关系可以用下图表示。
在qemu-kvm架构下vIOMMU是由qemu来模拟实现的,而vhost-user作为dpdk的vhost实现也对其进行了适配。其对于虚拟机的功能就类似于IOMMU对于物理机。那么为什么要有支持vIOMMU的需求呢?最关键的原因就是为了“安全”,为了设备内存是在合法范围内的。我们知道IOMMU的主要核心功能有两个:DMA remapping,Interrupt remmapping。那么我们就看这两个功能分别是怎么实现的。
DMA remapping
其实这里说DMA remapping不够准确,因为本质是vhost-uesr是采用memcpy的cpu内存拷贝内存实现了,而非DMA,但是大概流程是类似的。我们先来看一下没有vIOMMU的情况,数据包的拷贝方式。如下图所示:
vm前端virtio-net将guest物理地址(gpa)写入virtio ring的desc中,而后端vhost-user则需要在进行报文拷贝时,对gpa和hva进行转换。这里所说的hva是指host上vhost-uesr所在进程的host虚拟地址。当vm收发包时,vhost-uesr需要将desc中的gpa转换为hva,让后再进行操作。
下面我们看下支持了vIOMMU后的处理流程,如下图所示:
前端不再使用自己的物理地址gpa,而是使用io虚拟地址,即iova。Virtio ring的desc中记录的是guest的iova,vhost-user拷贝报文需要先将iova转换为vhost-uesr进程的hva,而iova到hva的转换还需要qemu参与,这个过程需要和qemu交互。如果guset支持vIOMMU除了对应的qemu和dpdk版本需要支持,还需要支持VIRTIO_F_IOMMU_PLATFORM这个feature。
前面说过vhost-user每次将iova转换为gpa需要让qemu协助,这是通过一个单独的unix socket连接完成的,其中关键的转换有以下三个:
1. QEMU的vIOMMU将IOVA(I / O虚拟地址)转换为GPA(客户机物理地址)。
2. Qemu的内存管理将GPA(客户物理地址)转换为HVA(qemu进程地址空间内的主机虚拟地址)。
3. Vhost-user库将(QEMU的)HVA转换为自己的HVA。这通常很简单,只需在vhost-user库映射QEMU的内存时将QEMU的HVA添加到mmap返回的地址中即可。
但是如果每个desc的地址转换都需要和qemu进行一次通信,无疑会使转发性能大打折扣。所以vhost-uesr进行了一些优化,具体就是实现一个IOTLB的cache。下图是vhost-user开启vIOMMU后的地址转换流程。
这个图的几个关键点如下:
· 客户机的物理内存空间是客户机所感知的物理内存,但显然,它位于QEMU的进程(主机)虚拟地址内部。分配了虚拟队列内存区后,该内存最终会落在客户机的物理内存空间中的某个位置。
· 将I / O虚拟地址分配给包含虚拟队列的内存范围时,带有其关联的客户机物理地址(GPA)的条目将添加到vIOMMU的TLB表中。
· 另一方面,QEMU的内存管理系统知道客户机物理内存空间在其自己的内存空间中的位置,因此,它能够将客户机物理地址转换为主机(QEMU)的虚拟地址。
· 当vhost-user库尝试访问其没有转换的IOVA时,它将通过辅助unix套接字发送IOTLB未命中消息。
· IOTLB API接收请求并查找地址,首先将IOVA转换为GPA,最后将GPA转换为HVA,然后通过主机unix套接字将转换后的地址发送回vhost-user库。
· 最后,vhost-user库必须进行一次最终转换。由于已将qemu的内存映射到自己的内存,因此必须将qemu的HVA转换为自己的HVA并访问共享内存。
Interrupt remapping
上面介绍完vIOMMU的DMA remaping后再来看下interrupt remapping。X86系统的中断相关架构图如下所示:
传统的INT中断通过IOAPIC转发到对应的Local APIC。如果没有interrupt remapping(IR)时,MSI中断会直接被送入guest,否则就会查询IR table,具体过程如下图。
而vhost-uesr又是通过irqfd传递中断消息的。Qemu在支持IR前后对irqfd的相关设置也是不同的,具体如下图所示。
总结
vIOMMU增加了设备对guest的内存访问的安全性,同时vIOMMU的支持可以让guest内部可以使用vfio驱动允许dpdk,否则就只能使用uio或者vifo的noiommu模式:
/sbin/modprobe vfio enable_unsafe_noiommu_mode=1
/sbin/modprobe vfio-pci
最后再一张图看一下IOMMU和vIOMMU的对比和不同的位置。