Linux Kernel 支持很多 Hypervisor,比如 KVM、Xen 和 VMware 的 VMI 等。每个 Hypervisor 都有自己独特的 block、network、console 等设备模型,设备驱动多样化的特性和优化方式使得各个平台共有性的东西越来越少,亟需提供一种通用的框架和标准接口来减少各 Hypervisor 虚拟化设备之间的差异,从而减少驱动开发的负担。
虚拟化主要包括处理器的虚拟化, 内存的虚拟化以及 I/O 的虚拟化等,从 2006 年开始,KVM 上设备 I/O 虚拟化的性能问题也显现了出来,此时由 Rusty Russell 开发的 virtio 引起了开发者们的注意并逐渐被 KVM 等虚拟化平台接纳并作为了其 I/O 虚拟化最主要的一个通用框架。
Virtio 使用 virtqueue 来实现其 I/O 机制,每个 virtqueue 就是一个承载大量数据的 queue。vring 是 virtqueue 的具体实现方式,针对 vring 会有相应的描述符表格进行描述。框架如下图所示:
图 1.virtio 框架virtio 提供了一套有效,易维护、易开发、易扩展的中间层 API。virtio 使用 Feature Bits 来进行功能扩展,使用 vring buffer 传输数据。使用 virtio 的设备在配置上于其他 PCI 设备没有太多不同,只不过它只应用于虚拟化环境。
Virtio 设备具备以下特点:
1. 简单易开发
virtio PCI 设备使用通用的 PCI 的中断和 DMA 机制,对于设备驱动开发者来说不会带来困难。
2. 高效
virtio PCI 设备使用针对输入和输出使用不同的 vring,规避了可能的由高速缓存带来的影响。
3. 标准
virtio PCI 不假定其所处的环境一定需要对 PCI 的支持,实际上当前很多 virtio 设备已经在非 PCI 总线上实现了,这些设备根本不需要 PCI。
4. 可扩展
virtio PCI 设备包含一组 Feature Bits,在设备安装过程中,可以告知 guest OS。设备和驱动之间相互协调,驱动可以根据设备提供的特性以及驱动自身能够支持的特性来最终确定在 guest OS 里面能够使用的设备特性。这样可以顾及到设备的前后兼容性。
因此,对与 guest OS 来说,只需要添加一个 PCI 设备驱动,然后 Hypervisor 添加设备的 vring 支持即可以添加一个 virtio 设备。
本文面向对 virtio 设备有一定了解的程序员,对 virtio 驱动和虚拟设备开发人员提供一定的参考和帮助。
基本概念设备的类型virtio 设备的 Vendor ID (厂商编号)为 0x1AF4,Device ID(设备编号)范围为 0x1000~0x103F,subsystem device ID(子系统设备编号)如下:
表 1. virtio device IDSubsystem Device IDVirtio Device1Network card2Block device3Console4Entropy source5Memory ballooning6IoMemory7Rpmsg8SCSI host99P transport10Mac80211 wlan
virtio 设备的配置空间需要使用 PCI 设备的第一块 I/O region 来对 PCI 设备进行配置。针对 virtio 设备来说,在 device 特定的配置区域后会有一块区域存放 virtio header(下表)。
表 2.virtio 设备的配置空间Bits323232161616R/WRR+WR+WRR+WR+WPurposeDevice FeaturesGuest
FeaturesQueue
AddressQueue
SizeQueue SelectQueue Notify
最开始的 32bits 为设备的 feature bits,紧跟着的 32bits 为 Guest(driver) feature bits,然后依次为 Queue Address(32 bits),Queue Size(16bits),Queue Select(16bits),Queue Notify(16bits),Device Status(8 bits),ISR status(8 bits)。
如果设备开启了 MSI-X(Message Signalled Interrupt-Extended),则在上述 bits 后添加了两个域:
表 3.virtio 设备的配置空间--MSI-X 开启下的附加配置Bits1616R/WR+WR+WPurpose(MSI-X)Configuration VectorQueue Vector
紧接着这些通常的 bits,可能会有指定设备专属的 headers:
表 4.virtio 设备的配置空间-设备专属配置BitsDevice SpecificR/WDevice SpecificPurposeDevice Specific...
各段的具体含义和作用会在后面给予解释。
特征位(Feature Bits)使用 feature bits(32bits)来指定设备支持的功能和特性。
0~23:根据设备类型的不同而不同
24~32:保留位,用于 queue 和 feature 协商机制的扩展
有 2 组 feature bits,device 端列出支持的特性写入 device feature bits 域,guest 端把它支持的 feature bits 写入 guest feature bits 域,双方相互协商,开始协商的唯一途径是 reset device。
在设备配置 header 里面添加一个新的域通常会提供一个 feature bit,所以 guest 应当在读取新配置域之前检查 feature bits.
考虑到设备的前后兼容性,如果 device 使用新的 feature bits,guest 无法把新的 feature bits 写入 guest feature bits,guest 进入向后兼容的模式。同样如果 guest driver 使用了 device 无法支持的 feature bits,guest 在 device feature bits 中看不到 device 不支持的 feature bits,guest 也同样进入向后兼容的模式。
设备状态(Device Status)Device Status 域主要由 guest 来更新,表示当前 drive 的状态。状态包括:
0:写入 0 表示重启该设备
1:Acknowledge,表明 guest 已经发现了一个有效的 virtio 设备
2:Driver,表明 guest 已经可以驱动该设备,guest 已经成功注册了设备驱动
3:Driver_OK,表示 guest 已经正确安装了驱动,准备驱动设备
4:FAILED,在安装驱动过程中出错
每次试图重新初始化设备前,需要设置 Device Status 为 0。
配置修改/队列向量/中断如果 MSI-X 没有启用的话,在 header 20 bytes 偏移的地方为设备指定的配置空间,而在 MSI-X 开启的情况下,移动到 Queue Vector 的后面。
PCI 设备具有并且启用 MSI-X 中断时,在 virtio header 里会有 4 bytes 的区域用于把“配置更改”和“queue 中断/events”映射到对应的 MSI-X 中断向量,通过写入 MSI-X table 入口号(有效值范围:0x0~0x7FF)来映射中断,写入 VIRTIO_MSI_NO_VECTOR 来关闭中断取消映射。
1
| #define VIRTIO_MSI_NO_VECTOR 0xFFFF
|
读取这些寄存器返回映射到指定 event 上的 vector,如果取消映射了返回 NO_VECTOR。默认情况下,为 NO_VECTOR。
映射一个 event 到 vector 上需要分配资源,可能会失败,此时读取寄存器的值,返回 NO_VECTOR。当映射成功后,驱动必须读取这些寄存器的值来确认映射成功。如果映射失败的化,可能会尝试映射较少的 vector 或者关闭 MSI-X 中断。
在 Linux3.5.2 内核中,驱动会首先尝试为“配置修改”中断映射一个 vector,为每个 queue events 映射一个 vector。如果映射失败,会为所有的 queue events 映射一个共享的 vector.如果再失败,则关闭 MSI-X 中断,为每个 event 和“配置修改”中断申请常规中断。 |