将晦涩难懂的技术讲的通俗易懂
分类: LINUX
2021-10-17 15:32:32
通常virtio设备都是基于PCI总线进行模拟实现的,基于PCI总线模拟实现的virtio设备称为virtio-pci设备。但在virtio spec中描述了基于另外两种总线的实现,一种是virtio over mmio,另一种是virtio over channel I/O。今天我们重点讨论一下前者(channel I/O使用场景太少)。
首先我们要知道virtio只是一个半虚拟化标准,或者说是一个协议,协议本身实现的载体和总线并无绑定。virtio协议实现过程中,CPU与外设之间的通知机制以及外设访问内存方式由实际连接CPU与外设的总线协议决定,如下图所示。换句话说,virtio协议可以基于多种不同的总线协议来实现。虚拟化场景中,主要采用PCI总线协议和MMIO总线协议:采用PCI总线协议的virtio设备叫virtio-pci设备,它可以支持virtio设备的热插拔特性(基于PCI总线的设备热插拔机制),并可应用于真实物理外设;采用mmio总线协议的virito设备叫virito-mmio设备,它完全是针对虚拟机设计的,是一种轻量的虚拟总线机制,支持快速设备发现,但是无法使用在真实物理外设中。
其次,我们要知道virtio采用不同总线实现的优缺点。采用PCI总线实现的好处首先是通用,其次可以具备PCI设备的很多优势,如热插拔,最后如果我们使用物理设备实现virtio也是可以采PCI总线的,换句话说虚拟机内部感知不到这个virtio设备是一个虚拟设备还是一个物理设备。但采用PCI总线实现的缺点是复杂,具体可以参考qemu中关于PCI设备的模拟逻辑,物理是PCI主桥的模拟,还是PCI设备本身配置空间和中断分配的模拟都十分复杂。而采用mmio总线由于直接是内存映射模拟设备,所以只能用于虚拟设备,无法用于物理设备,另外也不具有PCI设备的热插拔能力,但其优点就是实现简单。所以mmio方式多用于轻量级虚拟场景,例如aws的firecracker就是通过mmio总线来实现virtio设备的。
mmio设备的发现
mmio设备无法像PCI设备一样支持动态发现,guest os需要知道mmio设备使用的具体内存地址和中断号。mmio总线预留了0xD000000~0xFFFFFFFF的地址空间作为所有mmio设备的配置空间,并使用5~15号irq作为所有mmio设备可使用的中断号;每个mmio设备通过虚拟机内核启动时的命令行参数来上报设备资源信息(如配置空间地址范围和使用的中断号),如下图,这是一种静态的设备发现机制,它不像PCI设备的总线枚举机制那么灵活,因此不支持设备热插拔等高级特性。
mmio设备寄存器layout
mmio设备同样需要类似PCI设备配置空间一样实现一些寄存器来存放virtio设备的基本配置信息。具体如下图所示(只列出部分,完整的寄存器可以参考virtio spec)。
注意如上图,virtio-mmio设备的MagicValue寄存器值必须是0x74726976,version寄存器必须是 0x2。驱动只有确认以上信息正常才能进行后续virtio-mmio设备的协商配置。
虚拟机内部系统通过内核命令行参数识别virtio-mmio设备并调用对应的驱动函数对设备进行驱动。通过命令行参数(如virtio-mmio.device 4K@0xD0000000:5),前端系统可知配置空间地址范围(如0xD0000000~0xD0000FFF)和中断资源(如irq为5),接着便可以通过读写配置空间与设备进行协商操作。Virtio-mmio设备的配置空间概览如下:
关于virtio-mmio设备的前后端协商具体过程和virtio-pci设备类似,这里不再详细展开,具体可以参考firecracker中virtio-mmio设备的实现: