Virtio feature negotiation from QEMU perspective

QEMU中的Virtio features协商流程

下面以virtio-net对接vhost-net(kernel)为例,分析整个Virtio features协商的过程。

vhost-net

vhost_net作为描述后端的数据结构,与真正使用的后端(vhost_user or vhost_net)对应。
每个vhost_net包含一个vhost_dev,vhost_net的features实际就是vhost_dev的features,与features相关的成员有:featuresacked_featuresbackend_featuresprotocol_features

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct vhost_net {
struct vhost_dev dev;
struct vhost_virtqueue vqs[2];
int backend;
NetClientState *nc;
};

struct vhost_dev {
……
uint64_t features;
uint64_t acked_features;
uint64_t backend_features;
uint64_t protocol_features;
……
};

vhost_dev::features

vhost_dev_init中,通过后端对应的接口(ioctl or unix socket),从后端获取到的后端支持的features。

vhost_dev::acked_features

前端依据从后端获取的host features进行协商,协商完成后真正向后端传递的features(过滤了后端不支持的features),即后端真正工作的features。

vhost_dev::backend_features

从命名看是描述后端的features,但和features字段重复。vhost-net相关的实际代码调用如下:

  1. vhost_net_ack_features: 在设置acked_features之前,用backend_features的值为acked_features初始化。这里acked_features为何不初始化为0,从QEMU开发者的邮件讨论看,之前在初始化0时,acked_features在在某些情况下会被初始化为unexpected value(This will result an unexpected value of acked_features which may fail the features setting of vhost.)。不过初始化为backend_features似乎在vdpa的场景下有新的问题,且问题仍然open中;^1
  2. vhost_net_init: 在后端为vhost-net的条件下,如果后端对应的tap设备不支持vnet_hdr,则backend_features中的VHOST_NET_F_VIRTIO_NET_HDR被置位,否则为0;
  3. vhost_user_backend_init: 在后端为vhost-user的条件下,如果后端支持VHOST_USER_F_PROTOCOL_FEATURES,则backend_features中的VHOST_USER_F_PROTOCOL_FEATURES被置位。

此外,内核vhost代码中定义了VHOST_NET_BACKEND_FEATURES,对应的ioctl操作为VHOST_GET_BACKEND_FEATURESVHOST_SET_BACKEND_FEATURES,不过目前QEMU代码中没有这两个ioctl操作对应的逻辑。

vhost_dev::protocol_features

协议的扩展features。为了保持向下兼容性,vhost-user协议新增的features使用protocol_features进行描述,由VHOST_USER_F_PROTOCOL_FEATURES进行控制,标识protocol_features是否使用。

1
#define VHOST_USER_F_PROTOCOL_FEATURES 30

在vhost-net场景下,protocol_features被设置为0。

virtio-net

virtio-net作为描述后端的数据结构,与Guest的前端驱动(virtio_net)对应。在QEMU中,用VirtIONet来描述virtio-net,每个VirtIONet结构中包含了其对应的VirtIODevice。其中与features有关的成员有:VirtIONet的host_features,VirtIODevice的guest_featureshost_featuresbackend_features

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct VirtIONet {
VirtIODevice parent_obj;
……
uint64_t host_features;
……
};

struct VirtIODevice
{
……
uint64_t guest_features;
uint64_t host_features;
uint64_t backend_features;
……
};

VirtIONet::host_features

从virtio设备的角度来看,只区分Guest与Host,并不关心Host的具体实现。所以VirtIONet::host_features代表了Host理论可以支持的所有features。

VirtIONet::host_features在virtio_net_class_init中使用QEMU中virtio-net设备的默认属性virtio_net_properties(其中包含了offload相关的features)来初始化,同时在每个virtio-net设备实例化时,依据后端设备(e.g. tap)的配置,对部分features进行设置。

VirtIONet::host_features唯一的调用处在virtio_net_get_features,即响应Guest前端驱动的get_features请求,VirtIONet::host_features作为初始值,在一系列的过滤动作后,向前端返回Host所支持的features(细节见下文)。

VirtIODevice::host_features

VirtIODevice::host_features在virtio_bus_device_plugged中调用对应virtio设备的get_features进行初始化,保存的是get_features的返回值,即真正返回给前端驱动的features。

virtio_net_get_features向前端返回features的逻辑如下:

  1. VirtIONet::host_features作为初始features;
  2. 如果后端设备(e.g. tap)不支持vnet_hdr,则将features中对应的offload features取消置位;
  3. QEMU内部预定义了后端支持的feature_bits(kernel_feature_bits or user_feature_bits),将feature_bits支持但vhost_dev::features中不支持的bit在features中取消置位;
  4. VIRTIO_NET_F_MTU进行特殊处理(和mtu_bypass_backend有关,对整体协商流程不重要);
  5. 将上述流程中处理后的features返回给前端驱动。

要注意VirtIODevice::host_features和VirtIONet::host_features的区别,VirtIONet::host_features是Host理论可以支持的所有features,而VirtIODevice::host_features是对VirtIONet::host_features中依据后端、设备的设置进行处理后,真正返回给前端驱动的features。

VirtIODevice::guest_features

Guest前端驱动用自身features和get_features得到的VirtIODevice::host_features进行协商后,传递给QEMU,与VirtIODevice::host_features取交集,保存得到guest_features。即前后端在Guest协商后得到的features。

VirtIODevice::backend_features

看代码调用是为了特殊处理mtu_bypass_backend保存的,在virtio_net_get_features处理流程中,特殊处理VIRTIO_NET_F_MTU前保存的features,除了VIRTIO_NET_F_MTU和VirtIODevice::host_features有差异外,其他一致。

QEMU正常启动流程中的features协商

QEMU正常启动流程中,从开始运行到GuestOS启动的RunState的变化:preconfig->prelaunch->running

上述RunState变化过程中,virtio设备的features相关流程如下:

  1. preconfig: qemu_init->net_init_clients->……->net_init_tap->……->vhost_net_init,初始化tap和vhost_dev。其中vhost_dev初始化时,调用后端对应的接口,获取后端支持的所有features,用来初始化vhost_dev::features,同时依据对端设备(tap)支持的特性,初始化vhost_dev::backend_features,以及初始化vhost_dev::protocol_features为0;
  2. prelaunch: 调用virtio_bus_device_plugged,通过对应设备类型的get_features,初始化VirtIODevice::host_features
  3. running:
    • GuestOS启动,内部virtio_net驱动被加载,调用virtio_dev_probe->……->vp_get_features获取设备的features,即QEMU侧的VirtIODevice::host_features,再与自身驱动支持的features进行协商,协商完成后调用virtio_finalize_features->……->vp_set_features将协商好的features传给QEMU;
    • 此时发生VM-EXIT,QEMU侧调用virtio_set_features->……->virtio_net_set_features->……->vhost_ack_features,把前端驱动传递过来的features和后端对应feature_bits共同支持的bit置位,将结果保存在vhost_dev::acked_features
    • GuestOS中virtio_finalize_features->……->vp_set_features执行完成后,会设置VIRTIO_CONFIG_S_FEATURES_OK,进而触发QEMU侧的对应调用virtio_set_status->……->vhost_dev_start。在vhost_dev_start中,调用vhost_dev_set_features,将前端协商好的vhost_dev::acked_features通过后端对应的接口传递到后端。

QEMU热迁移流程中的features协商

QEMU热迁移流程中,源虚拟机从开始运行到迁移结束的RunState变化:running->finish_migrate->postmigrate;目的虚拟机从开始运行到GuestOS启动的RunState的变化:preconfig->inmigrate->running

源端virtio features在热迁移流程中的变化

  1. running: 正常运行状态,无相关操作;
  2. finish_migrate: virtio_save中保存VirtIODevice::guest_features的低32bit,迁移到目的端。

目的端virtio features在热迁移流程中的变化

  1. preconfig: 和正常启动流程一致;
  2. inmigrate:
    • 调用virtio_bus_device_plugged,通过对应设备类型的get_features,初始化VirtIODevice::host_features
    • virtio_load->……->virtio_net_set_features->……->vhost_ack_features,在virtio_load中接收源端协商完成的VirtIODevice::guest_features,在vhost_ack_features中将接收到的VirtIODevice::guest_features和后端对应feature_bits共同支持的bit置位,将结果保存在vhost_dev::acked_features
  3. running: qemu_main_loop->main_loop_wait->……->vm_start->……->virtio_set_features,使用inmigrate过程中设置好的vhost_net::acked_features通过后端对应的接口传递到后端。

正常启动流程和热迁移流程对比

硬件设备的初始化流程都一致,除了PCI设备的一些状态也是通过迁移初始化之外,最主要的区别在于热迁移流程中的features协商过程:协商必须的VirtIODevice::guest_features通过热迁移从源端获取,后端直接和QEMU进行协商流程;协商流程控制不再需要前端驱动修改设备status,而是在QEMU热迁移流程中触发。前端驱动不参与、不感知,避免了重新初始化。

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2021-2023 Martzki
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信