1 前言
VirtIO驱动定义了一组规范,只要guest和host按照此规范进行数据操作,就可以使虚拟机IO绕过内核空间而直接再用户空间的两个进程间传输数据,以此达到提高IO性能的目的。
VirtIO驱动的实现可以有很多种,最广泛的就是VirtIO Over PCI Bus,其它实现:VirtIO Over MMIO和VirtIO Over Channel IO不熟悉,不做介绍,这里只介绍qemu实现的VirtO驱动。
2 VirtIO基础组件
VirtIO设备被发现和识别的方法,通常和基于哪种总线实现方式有关。但无论是基于何种方式,一个VirtIO设备必须具备以下几个功能组件:
- 设备状态域
- 特性标志位
- 设备配置空间
2.1 设备状态域
VirtIO设备的初始化必须按照以下步骤顺序进行才被认为被识别:
- 重启设备。
- 标记ACKNOWLEDGE状态标志位,表示客户机发现了此设备。
- 标记DRIVER状态标志位,表示客户机知道怎么驱动这个设备。
- 读取设备特性标志位并给VirtIO设备设置特性标志,在此阶段,驱动可以读取特定设备相关的一些域的数据,驱动依赖检查自己是否支持这些特性,如果支持,就采纳之。
- 设置FEATURES_OK状态标志位,此阶段之后,驱动就不能再采纳新的特性了。
- 再次读取设备状态,确认FEATURES_OK标志已经被设备成功:如果没有被设置成功,表示这个设备不支持该特性,设备无法就无法使用。
- 执行具体设备的设置操作:包括扫描设备关联的virtqueues,总线设置,如果是VirtIO over PCI还需要写设备的VirtIO配置空间,还有virtqueues的使能以及运用。
- 最后标记DRIVER_OK状态标志位,自此VirtIO设备就初始化完成,可以使用了。
以上步骤中涉及的状态标志位,由设备状态域表示。它指出完成初始化时需要drivert做的动作。可以把它想象成指示灯,同设备状态域,外部可以知道这个设备处于什么阶段。设备状态域定义如下:
ACKNOWLEDGE 1
表示客户机发现了此设备,并识别到它是一个有效地VirtIO设备。
DRIVER 2
表示客户机知道怎么驱动这个设备,设置该标志位后可能有一段较长延时。比如在Linux系统中,驱动作为可加载模块,需要等模块加载完毕。
FAILED 128
表示客户机出错,该设备被置弃。出现这种情况的原因可能是内部错误,或者driver在设备操作过程中产生了致命错误等。
FEATURES_OK 8
表示客户机已经识别并接受设备的所有特性,并完成了特性协商。
DRIVER_OK 4
表示驱动设置完毕,可以对设备进行正常的操作。
DEVICE_NEEDS_RESET 64
表示设备出错,并且无法恢复,只能重启设备。
驱动对设备状态域的支持:
驱动需要更新设备状态域,实时标记设备状态域以此指示驱动初始化处于哪一个阶段,驱动禁止清楚设备状态位。如果驱动将设备状态置为FAILED,那么必须在执行设备复位操作后才能重新初始化。
驱动不能根据DEVICE_NEEDS_RESET位是否置位来判断操作是否完成。比如,如果DEVICE_NEEDS_RESET被置位,驱动不能根据这个信息认为当前的请求已经完成或者未完成。好的驱动实现,是重启设备并恢复设备。
设备对设备状态域的支持:
设备被复位后,需要将设备状态域置0,这个动作由设备负责。
在DRIVER_OK状态前,设备禁止使用buffer,禁止向驱动发送通知消息。
设备遇到错误时,负责将状态域设备为DEVICE_NEEDS_RESET,通知驱动该设备需要复位。DEVICE_NEEDS_RESET状态后如果DRIVER_OK 被置位,设备需要将其配置更改通知到驱动。
2.2 特性标志位
每个VirtIO设备都需要支持各种特性。在设备初始化阶段,驱动读取这个特性标志,然后通知VirtIO设备驱动可以接受的特性子集。特性一定确定,只有复位VirtIO设备,才能重新协商。特性标志前后端必须一致,必须向后兼容,如果驱动支持的特性设备不支持,以设备的特性为准;如果设备支持的特性驱动不支持,以驱动特性为准。
特性标志位各个字段含义:
0~23:特性标志
24~32:预留的特性标志位,用于queue和特性协商机制的扩展
33及以上:保留,用于将来的扩展
比如,特性标志为0,对网络设备来说,表示支持包checksum。如果设备中增加了新的配置空间,也会通过新增特性标志bit来表示。
驱动对特性标志的支持:
驱动禁止接受设备没有提供的特性,也不能接受依赖其它未提供特性的特性。同时,驱动必须向后兼容:如果设备提供的特性驱动无法理解,则必须以驱动的为准。
设备对特性标志的支持:
设备提供的特性,不能依赖其它无法支持的特性。驱动接受的任何特性子集,设备都应该支持,否则,在将设备状态域设置为FEATURES_OK时会失败。
2.3 设备配置空间
设备配置空间,通常用于配置不常变动的参数,或者初始化阶段设置的参数。特性标志位包含表示配置空间是否存在的bit位,后面的版本会通过在特性标志位的末尾新添新的bit位来扩展配置空间。
Virtio设备的每一次数据传递,都附带一个配置空间的统计计数,该计数可变。因此对同一配置空间进行访问,两个访问者可能获取到不同的配置空间版本。
驱动对设备配置空间的支持:
驱动对配置空间的读取大于32-bit位宽,就不能保证读操作时原子的。驱动应该按照以下方式访问配置空间条目:
u32 before, after;
do {
before = get_config_generation(device);
// read config entry/entries.
after = get_config_generation(device);
} while (after != before);
对可选的配置空间域,驱动在访问时需要通过特性标志位预先检查其是否被支持。
驱动不能限制设备配置空间的长度。相反,驱动只能检查设备配置空间是否足够长,是否能满足必要的设备操作。举例:比如Virtio规范申明设备配置空间包含8-bit长度的空间,对驱动来说,除了这已经申明的8-bit空间外,配置空间可能还包括末尾扩展的任意长度的空间,驱动必须要处理长度大于8-bit的配置信息。
设备对设备配置空间的支持:
在FEATURES_OK状态被设置前,设备需要支持驱动对所有配置空间的访问,这些空间包含特性标志中指明提供的特性对应的配置空间域。