虚拟化技术之设备直接分配(passthrough)

1、设备直接分配技术

        如何提高虚拟化设备的性能问题是虚拟化领域长期的研究重点。如前所述,设备模拟模型会导致虚拟化性能大大下降;泛虚拟化设备模型虽然在性能上拥有一定的优势,但由于需要修改操作系统,具有局限性.。并且当前的两种I/O设备虚拟化模型已无法满足高速的数据交换需求,应运而生的就是另外一种设备虚拟化模型——设备直接分配模型。

        传统的实现I/O虚拟化的技术中,所有的虚拟机都共享物理平台上的硬件设备。如果物理条件好,有足够的硬件,就可以考虑让每个虚拟机独占一个物理设备,这样无疑会提高系统的性能。把某一个设备直接分配给一个虚拟机,让虚拟机可以直接访问该物理设备而不需要通过虚拟机监视器或被虚拟机监视器截获,这就是设备直接分配技术。如下图所示为设备直接分配的I/O模型。


         在设备直接分配模型中,虚拟机操作系统可直接拥有某一物理设备的访问控制权限,虚拟机监视器不再干涉其访问操作。因此,该模型可以较大地改善虚拟化设备的性能,降低监视器程序的复杂性,易于实现,并且不需要修改操作系统,保证了高可用性。

2、设备直接分配模块设计与实现

        设备直接分配设备模型的设计主要包括了四个部分,分别是:设备隐藏模块、建立抽象设备模块,客户机N启动模块以及设备分配模块。

2.1 设备隐藏子模块设计

       在虚拟化系统中,有一个特权虚拟域domain 0,这个虚拟域会在其它所有虚拟域之前启动,并且所有的设备都会被分配给这个domain 0,再由domain 0去分配和管理。而domain 0除了接管这些设备外还可以使用这些设备。Linux操作系统在启动设备的时候会检测是否存在相关驱动,若存在,则使用这个驱动。设备直接分配技术的关键点在于,设备被一个虚拟机独占,其他虚拟机不能够使用这个设备。因此会存在一个问题:如果domain 0里有这个设备的驱动便会占领该设备,那么如何再将设备分配给本来想分配的虚拟机呢?

        隐藏设备的意义便是在于让domain 0接管设备但是不使用设备,在xen中提供了一个pciback模块,这个模块用于对设备进行隐藏。由于Linux里面的设备驱动是有优先级的,当一个设备存在多个不同的驱动可以使用时,Linux只会为设备加载优先级最高的那个驱动,而pciback正是具有最高优先级的驱动,所以Linux默认会为设备加载pciback作为驱动。pciback模块本身并不是一个真正的驱动,它只是起到对设备的一个接管作用,保证设备不被其他的设备驱动所占领。

设备隐藏的实现方式有两种:

(1)启动时的隐藏:PCIBACK模块提供了一个内核参数,称为pcibackhide。它的格式如下:

pciback. hide=(busdevicefunction)(domainbusdevicefunction) 其中busdevicefunction分别代表需要隐藏的设备的总线号、设备号、功能号。

(2)启动后的隐藏:

        在xen启动后,并且加载了pciback模块之后才能够进行。加载之后会存在一个文件夹/sys/bus/pci/drivers/pciback。在这个文件夹下面会出现3个文件:

new_slot:把设备的BDF(按照busdevicefunction格式)写入到这个文件,告知PCIBACK模块该设备需要被隐藏,做好相关工作。

bind:当new_slot存在设备的BDF后,再把设备的BDF(格式同上)写入到这个文件,就可以实现对设备的隐藏。

unbind:当客户机销毁后,把对应客户机的设备的BDF写入到这个文件,那么该设备就又可以由客户机0使用了。

目前使用的较多的还是后者。

2.2 建立抽象设备子模块设计

       建立抽象设备子模块又可以分为三个部分:初始化设备链表、建立抽象设备以及完成BAR(Base Address Register)映射。

QEMU作为一个模拟软件本身是运行在用户态,初始化设备就是把平台上所有的PCI设备报告给QEMU,以供接下来进行设备直接分配时使用。初始化时,首先会分配一个结构struct pci_access该结构里面包含一个指针devices,它指向一个PCI设备链表,等到抽象设备初始化完成后,该指针就指向了平台上所有的PCI设备组成的链表的表头,然后再把得到的struct pci_access结构的地址赋值给QEMU的全局结构变量dpci_infospci_access成员,这样PCI设备链表的初始化就完成了。当以后向客户机直接分配物理设备时,QEMU就能够通过查找此链表完成后续工作。

       在完成设备链表的初始化后,就可以向虚拟机分配设备,QEMU会根据设备的BDF来查询设备链表,如果能找到对应的设备,则认为是直接分配,否则会采用设备模拟的方式来完成。目前来说,直接分配设备通常都是通过在虚拟机的配置文件里面写入设备的BDF来实现。在启动虚拟机时,首先会去读取虚拟机的配置文件,若存在这些BDF则采用直接分配的方式分配设备,并且如果有多个设备,彼此之间用逗号隔开。

        QEMU为抽象设备分配虚拟PCI BAR后,再通过设备链表获得物理设备的BAR,最后再来完成抽象设备和物理设备之间映射的建立。QEMU在通过超调用把需要建立PCI BAR映射的相关信息报告给虚拟机监视器后,监视器将根据获得的信息来建立转换表,至此,QEMU就完成了它的工作,整个抽象设备在QEMU里面的建立也就完毕了

2.3客户机N启动子模块

       这个模块主要是完成I/O端口转换表和MMIO转换表的建立过程。

       通过把虚拟机的ID,虚拟机的虚拟端口号,真实的设备端口号以及端口数量等信息报告给虚拟机监视器,交由虚拟机监视器来建立I/O端口转换表。建立和解除端口转换表的都是同一个超级调用,如果被虚拟机监视器截获后判断是建立I/O端口转换表映射关系时,会去遍历原来的端口转换表,查看是否已经存在与该设备之间的映射,如果存在则更新虚拟端口,如不存在则建立映射关系。在建立好I/O端口转换表后,下次虚拟机再通过虚拟端口访问设备,被虚拟机监视器截获后就可以通过转换表找到真正的物理端口了。

      建立MMIO转换表的过程与建立I/O转换表的过程类似,都是通过超级调用,被虚拟机监视器截获后,会判断是否建立MMIO转换表映射关系,如果是,则会根据虚拟机的ID去查找对应的客户机物理地址与机器物理地址转换关系表,简称P2M表,遍历该表,找到对应的MMIO的内存地址所在项,把机器物理地址写入。映射建立完成后,当虚拟机发起MMIO访问时就可以直接找到对应的机器物理地址

2.4 设备分配模块

    在设备隐藏子模块中,为防止设备被占用,设备被交由domain 0所接管,而在这个模块中要负责完成设备的分配过程。设备分配的操作也是一个超级调用,被虚拟机监视器截获后会根据虚拟机的ID以及在虚拟机的配置文件中所读取到的设备的BDF来完成设备的分配过程,主要是解除设备与domain 0的一个映射关系,然后建立起设备与被分配到的虚拟机之间的映射关系。

3、设备直接分配技术的使用


下面以Xen平台为例介绍如何把一个设备直接分配给一个虚拟机。
(1)老版本的内核注意开启IOMMU,在grub中加入对应的启动参数项
title Xen-Linux (2.6.18-xen) 
 root (hd0,0) kernel /boot/xen.gz iommu=1 
 module /boot/vmlinuz-2.6.18.8-xen root=LABEL=/ 
 module /boot/initrd-2.6.18-xen.img


(2)用lspci命令查看要直接分配的设备的BDF号

[root@localhost ~]# lspci |grep 'Eth'
01:00.0 Ethernet controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)
01:00.1 Ethernet controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)


(3)将设备隐藏,例如一个BDF为08:00.0的设备

echo 0000:08:00.0 > /sys/bus/pci/devices/0000:08:00.0/driver/unbind

echo 0000:08:00.0 > /sys/bus/pci/drivers/pciback/new_slot

echo 0000:08:00.0 > /sys/bus/pci/drivers/pciback/bind


(4)查看可直接分配的设备

# xm pci-list-assignable-devices

08:00.0

如果是xl命令,那么查看的命令如下:

# xl pci-assignable-list

08:00.0


(5)将设备直接分配给虚拟机

修改虚拟机的配置文件,加上下面一行,如果有多个分给该虚拟机的设备,用逗号隔开

pci=['08:00.0','08:00.1']





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章