1、虚拟化技术 IO虚拟化1虚拟化技术 - I/O虚拟化 在虚拟化系统中,I/O外设只有一套,需要被多个guest VMs共享。VMM/hypervisor提供了两种机制来实现对I/O设备的访问,一种是透传(passthrough),一种是模拟(emulation)。2Device Passthrough所谓passthrough,就是指guest VM可以透过VMM,直接访问I/O硬件,这样guest VM的I/O操作路径几乎和无虚拟化环境下的I/O路径相同,性能自然是非常高的。在虚拟化环境下,guest VM使用的物理地址是GPA(参考这篇文章),如果直接用guest OS中的驱动程序去操作I
2、/O设备的话(这里的I/O限定于和内存统一编址的MMIO),那么设备使用的地址也是GPA。这倒不难办,使用CPU的EPT/NPT MMU查询对应guest VM的nPT页表,进行一下GPA-HPA的转换就可以了。可是别忘了,有一些I/O设备是具备DMA(Direct Memory Access)功能的。由于DMA是直接在设备和物理内存之间传输数据,必须使用实际的物理地址(也就是HPA),但DMA本身是为了减轻CPU的处理负担而存在的,其传输过程并不经过CPU。对于一个支持DMA传输的设备,当它拿着GPA去发起DMA操作时,由于没有真实的物理内存地址,传输势必会失败。那如何实现对进行DMA传输的
3、设备的GPA-HPA转换呢?再来一个类似于EPT/NPT的MMU?没错,这种专门转换I/O地址的MMU在x86的阵营里就是IOMMU。然而,不和AMD使用相同的名字是Intel一贯的路数,所以Intel通常更愿意把这种硬件辅助的I/O虚拟化技术叫做VT-d(Virtualization Technology for Direct I/O)。作为后起之秀的ARM自然也不甘示弱,推出了对应的SMMU(System MMU)。IOMMU查找的页表通常是专门的I/O page tables。既然都是进行GPA-HPA的转换,为什么不和EPT/NPT MMU共享nPT页表呢?这个问题将在接下来的文章中给
4、出解答。为了加速查找过程,IOMMU中也有类似于EPT/NPT TLB的IOTLB硬件单元。以Intel的VT-d为例,它规定了一个domain对应一个IO页表。在具体的实现中,通常是一个guest VM作为一个domain,因此分配给同一个guest VM的设备将共享同一个IO页表。这里为了支持device passthrough模式下的DMA传输,IOMMU进行的是GPA-HPA的转换。既然EPT/NPT MMU都可以同时支持GVA-GPA和GPA-HPA的转换,那IOMMU是否也可以呢?这个问题也将留在后续的文章中讨论。Device passthrough机制要求VMM为guest VM
5、分配好设备,并提供隔离。假设系统中现在有三个guest VMs,编号分别是0, 1, 2,如果VM 0分配到了网卡A,就要阻止VM 1和VM 2对网卡A的访问。可以采用的方法是在拥有设备的guest VM加载驱动程序前,先给要分配出去的设备加载一个伪驱动作为占位符,由于没有真正的驱动程序,这个设备对于其他的guest VM来说就相当于是“隐藏”了。这同时也暴露了使用device passthrough存在的一个问题,就是同一个I/O设备通常无法在不同的guest VM之间实现共享和动态迁移(比如PCI设备的热插拔)。下文将介绍的device emulation机制将可以解决设备共享和迁移的问题
6、。3虚拟化技术 - I/O虚拟化 二上文介绍了无需VM exit(参考这篇文章),性能优良,但不利于实现设备共享和迁移的device passthrough(I/O透传)机制,本文将介绍I/O虚拟化的另一种实现方式。4Device Emulation前面的文章提到,直接基于bare-metal的VMM分为两种,一种是由加入了虚拟化功能的操作系统组成的hypervisor模型,一种是像Xen和Acrn这种VMM层相对精简,主要负责CPU管理和内存管理的混合模型。在hypervisor模型中,VMM直接就可以提供各种外设驱动,因此实现对guest VM所访问设备的模拟是很方便的。对于混合模型,设备
7、模拟的方法就要稍微复杂一些。在混合模型中,guest VM访问设备的请求依然是会被VMM“截获”,根据I/O编址类型的不同,截获的方法也不一样。对于I/O单独编址的PMIO(Port Mapped I/O),是使用in/out, ins/outs指令来读写的,所以只需要将这几个指令设定为会产生VM exit的敏感指令就可以了。对于和memory统一编址的MMIO(Memory Maped I/O),如果不为其使用的I/O地址设置对应的页表项,那么在访问这些地址的时候势必会产生page fault,这样的I/O访问也可以被VMM成功截获。如果是一些简单的外设,比如RTC(参考这篇文章),VMM只
8、需要读取一下现在硬件RTC的时间信息,返回给guest VM就可以了。如果是比较复杂的外设,VMM中没有相应的驱动,那么它就会把这个请求转发给一个拥有该设备驱动程序的guest VM,在Xen中,承担这个角色的VM是Dom0,其他的guest VM则是DomU,分别对应Acrn中的SOS(Service OS)和UOS(User OS)。上图描述的是Acrn hypervisor中I/O处理的流程,当UOS发出访问I/O的请求触发VM exit后,VMM将解析产生VM exit的原因,并判断这个I/O请求是不是自己能提供驱动服务的,如果是(图的左下部分),就直接调用对应的I/O handler
9、处理,如果不是(图的右下部分),就提交一个I/O request给SOS。由SOS为UOS提供设备的驱动服务,就相当于实现了对UOS所使用I/O的模拟。在这个过程中,VMM只是起了一个中间调度的作用,并不直接参与驱动数据的传输,用通信的数据来说就是,VMM负责的是control plane,而data plane则留给了SOS和UOS自己去实现。SOS和UOS之间关于驱动数据的交互可以有很多种方式,目前最常用的一种方式是virtio。Virtio最早由Rusty Russell于2007年在IBM工作期间开发,之后迅速成为KVM等主流虚拟化方案中默认的I/O虚拟化机制。还是以Acrn为例,在v
10、irtio模型中,UOS为驱动数据的交互提供的接口被称为Frontend virtio driver (FE),FE只需要负责建立共享缓冲区,并产生I/O请求即可。SOS中提供的驱动交互接口则被称为Backend virtio driver (BE),BE会接收VMM转发的来自FE的请求,并交给SOS中的设备驱动程序处理。当SOS中的设备驱动程序处理完成了这一请求,BE将通过VMM告知对接的FE。BE和FE是一一对应的关系,共享同一个硬件设备的多个UOS会拥有独立的FE, BE实例对,并维护各自的状态信息。BE和FE之间是通过一个被称为virtqueue的结构来传递驱动数据的。一个驱动程序可以
11、使用一个或多个virtqueue,其数量取决于具体的需求,比如virtio网络驱动程序通常使用两个virtqueue(一个用于接收,另一个用于发送),而virtio块驱动程序则通常只使用一个virtqueue。virtqueue由ringbuffer和descriptor机制组成,本质上是一块基于授权机制的共享内存,由请求服务的FE创建。作为一个standard,同时考虑到扩展性,它会包含一些feature bits,需要BE和FE之间就此进行negotiate。一个guest VM可以申明它所拥有的哪些内存页可以被其他VM共享,另一个VM可以将这些内存页映射到自己的地址空间中。每个VM都有一
12、个授权表,来控制其他VM访问自己所拥有的共享页的权限,授权表的每个entry定义了对于当前VM的某一内存页,其他的某个VM具有哪些访问权限(读/写)。这里介绍的device emlution需要修改guest OS的代码,将普通的设备驱动程序转换为FE和BE的形式,因此属于I/O para virtualization(I/O半/类/准虚拟化),而对guest OS完全透明的模拟方式则属于I/O full virtualization(I/O全虚拟化)。要想使用I/O全虚拟化,必须从设备硬件的最底层开始模拟,尽管这样可以模拟得很彻底,以至于guest OS完全不会感知到自己是运行在一个模拟环境中,但它的效率相对较低。结合上文讲的I/O透传,三者的实现原理分别大概是这样的:
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1