使用过虚拟机pci passthrough或者做过DPDK/SPDK开发的同学一定很熟悉下面的配置:
BIOS中enable vt-d,内核参数配置intel_iommu=on
那这个IOMMU是什么呢,下面来一探究竟
IOMMU的引入
早期,设备访问内存(DMA)就只接受物理地址,所以CPU要把一个地址告诉设备,就只能告诉物理地址。
假如设备的地址长度还比CPU的总线长度短(例如CPU是64位但设备是32位的),就只能分配低地址来给设备用。
如果设备要访问高端内存呢,那就在32位设备能够访问到的地方设置一个叫做"bounce buffers"的东西,当设备要访问高端内存时,操作系统就要在高端内存和"bounce buffers"之间做一个拷贝。
后来走有了IOMMU,这个问题就迎刃而解了。在设备做DMA访问内存的时候,系统返回给设备驱动的不再是物理地址,而是虚拟地址,这个地址一般叫IOVA,设备访问内存时,由IOMMU来将这个虚拟地址转换为物理地址。但是当iommu bypass的时候,设备也可以直接使用物理地址来进行dma。
看到这里大家会看到,这怎么这么像MMU呢?是的,两者都是可以把虚拟地址翻译为物理地址,MMU是将CPU发出的虚拟地址通过页表翻译为物理地址,IOMMU是将设备发出的虚拟地址IOVA通过页表翻译为物理地址。
上面就是IOMMU的一个基本功能:DMA Remapping。IOMMU还有其他的一些功能,例如Interrupt Remapping,在这里不具体展开了。
IOMMU是个硬件单元
IOMMU和MMU一样是个硬件单元,通常是实现在北桥之中,现在北桥通常被集成进SOC中,所以IOMMU通常都放在SOC内部了。在SOC上可能有一个或多个IOMMU硬件单元,例如多路服务器上可能集成有多个IOMMU硬件单元。
每个IOMMU硬件单元负责管理挂载到它所在的PCIe Root Port下所有设备的DMA请求。BIOS会将平台上的IOMMU信息通过ACPI协议报告给操作系统,再由操作系统来初始化和管理这些硬件设备。
不同芯片厂商的实现大同小异,Intel的芯片上叫VT-d (Virtualization Technology for Directed I/O ),AMD就叫做IOMMU,ARM中叫SMMU。
IOMMU对虚拟化的支持
IOMMU还有一个重要的功能就是加入了对虚拟化的支持,就是文章一开头提到的设备Passthrough。
透传到虚拟机中的设备看到的是GPA(guest physical address),但是访问的是HPA(host physical address)。而IOMMU就可以完成GPA到HPA的转换。具体的转换过程可以参考CPU厂家的spec说明,在此不具体介绍。
IOMMU Group
在虚拟化设备透传时还涉及到PCI设备的iommu group分组问题。
根据IOMMU拓扑结构,一个iommu group里可能有一个或多个设备。设备透传的时候一个group里面的设备必须都给一个虚拟机,不能给不同的VM,也不能部分被分配到给虚拟机。
下面看一下为什么要对PCI设备进行iommu分组。
我们知道PCIe允许peer-to-peer通信,即PCIe设备发出的数据包可以不一直往上提交到PCIe的根节点,而是在中间的PCIe Switch直接进行转发,转发到其他PCIe设备上。不经过根节点这样就会导致IOMMU无法控制到这种peer-to-peer的数据传输,达不到设备隔离的目的。例如p2p的两个设备一个在虚拟机内一个在物理机上或者另一个虚拟机内,不经过IOMMU的话,地址完全乱了。
Access Control Services
(PCIe ACS)
如果不希望P2P直接通信则可以使用PCIe的ACS特性。ACS是PCIe的访问控制服务,控制PCIe数据流向的。
ACS可以将peer-to-peer转发的功能关闭,强制将其下所有设备通信送到RootComplex。ACS可以应用于PCIe Switch以及带有VF的PF等所有具有调度功能的节点。所以iommu分组的依据就是ACS。
从PCIe设备向上通往PCIe根节点的路径上,所有downstream port和multi-function device都要具有ACS特性,若某个downstream port或multi-function的ACS特性关闭,则下面的所有设备都必须归到同一个iommu group,否则该PCIe设备就可以独立成一个iommu group。另外PCI总线上的设备都归一个iommu group。最后同一个iommu group中所有的设备将会共享一个IOVA地址空间。
关于IOMMU的介绍以及在虚拟化中的作用就介绍这些。如果感觉有用,记得关注我