虚拟化与云计算硬核技术内幕 (12) —— 独立自主,自力更生 (中)

2022-09-08 16:54:54 浏览数 (1)

在上期,大家了解了虚拟机中的纯虚拟化设备(Emulation)和半虚拟化(Para-virtualiazation)是如何工作的。

纯虚拟化设备是让Hypervisor接管GuestOS所有的IO操作,模拟操作完毕后再返回到GuestOS中。这种工作方式的好处在于无需对GuestOS做任何修改,但性能非常低下,不适用于磁盘、网卡等数据平面的设备,实际上常用于模拟键盘、鼠标、串口等低速设备。

而半虚拟化的出现,让虚拟机可以通过前端和后端驱动直接使用真实硬件,具备较高的性能。它的问题在于,需要在GuestOS中安装Hypervisor提供的驱动。对于Windows Server或Unix等非开放操作系统,这是难以接受的。

有没有办法把二者的优势结合,既不需要修改GuestOS操作系统,安装客制化驱动,又能够让GuestOS最终能直接操作真实硬件呢?

工程师们想到的第一个解决方案是,硬件直通虚拟机。

我们在前几期提到过,无论是虚拟机还是物理机,对外设的操作,本质上是实现三类操作:

  1. 对寄存器使用IO指令读写;
  2. 对中断的处理;
  3. 对DMA内存的分配和映射;

那么,我们如果能让虚拟机直接对真实硬件的IO寄存器空间、中断和DMA进行这三类操作,就可以实现将硬件直通给虚拟机了!

如图,GuestOS如能像右边那样,直通真实的物理设备,就省去了左边一系列低效的操作,让性能大大提升。

最常见的硬件直通虚拟机的场景,是将GPU直通给虚拟机。GPU属于PCIe设备,将PCIe设备直通给虚拟机所需要的,就是让虚拟机能够访问到PCIe的配置空间,并为PCIe设备在虚拟机的内存地址空间中分配DMA空间,以及让PCIe设备的MSI中断可以直通给虚拟机。

在《虚拟化与云计算硬核技术内幕 (10) —— 事事有人管,人人有事管》中,我们实际上已经给出了让PCIe的MSI中断通往物理外设的方法。这样,我们只需要实现搞定前两个问题就可以了。

对PCIe有所了解的同学可能会发现,PCIe规范下,每个设备都有自身4KB的配置空间,PCIe设备上的PCI控制器内部所有的寄存器都位于这4KB的配置空间中。由于Intel x86的体系架构下IO地址空间只有64K,因此,在Intel处理器集成的PCIe root complex (根联合体,可以视为PCIe总线的总发起端)上,还集成了一个映射单元,将所有PCIe设备的配置空间映射为CPU的内存地址空间。这样,在驱动程序中,对所有PCIe设备不使用in和out这样的IO指令操作,而是使用普通的内存读写指令进行操作。这样,虚拟机读写PCIe配置空间的问题,和为PCIe设备分配DMA地址问题,实质上就成了一个问题——将PCIe设备在宿主机上的配置空间地址和DMA地址,映射成为虚拟机可以访问的地址!

为了实现这一需求,Intel的VT-d技术中,包括了这一地址映射技术,Intel管这项技术叫IOMMU(Input Output Memory Management Unit)。

IOMMU的功能,就是帮助虚拟机完成把虚拟机上的虚拟地址GVA,虚拟机上的物理地址GPA,宿主机上的物理地址HPA相互转换的工作。

虚拟机GuestOS在启动时,会找到Hypervisor给它分配的直通PCIe设备,并调用对应的驱动程序对PCIe设备的配置空间进行虚拟化。

此时,虚拟机对PCIe配置空间读写的指令中,包含的地址为GVA(Guest Virtual Addesss)。CPU上MMU可以通过GVA结合VPID,把GVA翻译为HPA(Host Physical Address),最后访问到实际的配置空间。

当PCIE设备完成第一阶段的初始化后,驱动程序会向操作系统申请用于给外设进行DMA的内存空间。显然,GuestOS向操作系统申请到的内存地址是GVA。同时,由于GuestOS上的内核与驱动都没有做任何修改,驱动程序会调用操作系统提供的dma_map_page()函数,将GVA翻译为GPA,并将这个地址提供给PCIE设备。当PCIE设备需要DMA的时候,会对GPA进行操作——

在这里出问题了。PCIE DMA地址应当是GPA对应的HPA!

幸好,IOMMU阻止了这一切。

如上图,Linux内核对dma_map_mage函数和pci_map_page()函数做了修改。如果发现自己是虚拟机(非DMA Direct Mapping),会将地址翻译的工作重定向到IOMMU。IOMMU会拿到GVA对应的HPA。

这样,GuestOS最终可以告诉物理硬件,将DMA地址设定为HPA,并在接收到来自物理硬件的HPA地址的时候,转换回GVA,并读写GVA指向的缓冲区地址。

在IOMMU的帮助下,虚拟机终于可以完成直接操纵真实的硬件了。但是,Hypervisor为了避免冲突,需要将一个物理硬件分配给一台虚拟机专门使用。

如果一台物理服务器上有20个虚拟机的场合,我们有没有办法给每个虚拟机分配一个物理网卡呢?

当然,在服务器上安装20块网卡的笨办法是不可行的。我们要使用其他的办法搞定。

请看下回分解。

0 人点赞