加速包处理的vhost优化方案
vhost的演进和原理
virtio-net的后端驱动经过从virtio-net后端,到内核态vhost-net,再到用户态vhost-user的演进过程。
Qemu和virtio-net
virtio-net后端驱动最基本要素是虚拟队列机制、消息通知机制和中断机制。虚拟队列机制连接着客户机和宿主机的数据交互。消息通知机制主要用于客户机到宿主机的消息通知。中断机制主要用于从宿主机到客户机的中断请求和处理。
下图是virtio-net后端模块进行报文处理的系统架构图。其中KVM是负责为程序提供虚拟化硬件的内核模块,而Qemu利用KVM来模拟整个系统运行的环境,包括处理器和外设等;tap则是内核中的虚拟以太网设备。
当客户机发送报文时,它会利用消息通知机制(通路2),通知KVM,并退出到用户空间Qemu进程,然后由Qemu开始对Tap设备进行读写(通路1)。
在这个模型中,由于宿主机、客户机和Qemu之间的上下文切换频繁带来的多次数据拷贝和CPU特权级切换,导致Virtio-net性能不如人意。
可以看出性能瓶颈主要存在于数据通道和消息通知路径这两块:
数据通路是从Tap设备到Qemu的报文拷贝和Qemu到客户机的报文拷贝,两次报文拷贝导致报文接收和发送上的性能瓶颈。
消息通知路径是当报文到达Tap设备时内核发出并送到Qemu的通知消息,然后Qemu利用LOCTL向KVM请求中断,KVM发送中断到客户机。这样的路径带来了不必要的性能开销。
Linux 内核态vhost-net
为了解决报文收发性能瓶颈,Linux内核设计了vhost-net模块,目的是通过卸载virtio-net在报文收发处理上的工作,使Qemu从virtio-net的虚拟队列工作中解放出来,减少上下文切换和数据包拷贝,进而提高报文收发的性能。此外,宿主机上的vhost-net模块还需要承担报文到达和发送消息通知及中断的工作。
上图展现了加入Linux内核vhost-net模块后virtio-net模块进行报文处理的系统架构图。
报文接收仍然包括数据通路和消息通知路径两个方面:
数据通路是从Tap设备接收数据报文,通过vhost-net模块把该报文拷贝到虚拟队列中的数据区,从而使客户机接收报文。
消息通路是当报文从Tap设备到达vhost-net时,通过KVM模块向客户机发送中断,通知客户机接收报文。
Linux内核态vhost-net的设计使建立在Qemu能共享以下信息的基础上的:
Qemu共享在客户机上的内存空间的布局:vhost-net能够得到相应的地址转换信息,主要是指客户机物理地址(GPA)到宿主机物理地址(HPA)的转换。
Qemu共享虚拟队列的地址:vhost-net能直接对这些虚拟队列进行读写操作,从而进行报文收发处理。由于虚拟队列的地址是Qemu进程上虚拟空间中的地址,实际使用时需要转换成vhost-net所在进程的虚拟地址。
Qemu共享KVM中配置的用户向客户机上的Virtio-net设备发送的事件文件描述符(eventfd):通过这种方式,vhost-net收到报文后可以通过客户机取走接收队列中的报文。
Qemu共享KVM中配置的用户virtio-net PCI配置空间写操作触发的事件文件描述符:该描述符在Virtio-net端口的PCI配置空间写入操作时被触发,客户机可以在有保温需要发送时利用这种方式通知vhost-net。
用户态vhost
Linux内核态的vhost-net模块需要在内核态完成报文拷贝和消息处理,这会给报文处理带来一定的性能损失,因此便出现了用户态vhost。
用户态vhost采用了共享内存技术,通过共享的虚拟队列来完成报文传输和控制,大大降低了vhost和virtio-net之间的数据传输成本。
DPDK vhost是用户态vhost的一种实现,其实现原理与Linux内核态vhost-net类似,它实现了用户态API,卸载了Qemu在Virtio-net上所承担 的虚拟队列功能,同样基于Qemu共享内存空间布局、虚拟化队列的访问地址和事件文件描述符给用户态的vhost,使得vhost能进行报文处理以及跟客户机通信。同时,由于报文拷贝在用户态进行,因此Linux内核的负担得到减轻。DPDK vhost同时支持Linux virtio-net驱动和DPDK virtio PMD驱动的前端,其包含简易且轻量的2层交换功能以及如下基本功能:
-
virtio-net网络设备的管理,包括virtio-net网络设备的创建和virtio-net网络设备的销毁。
-
虚拟队列中描述符列表、可用环表和已用环表在vhost所在进程的虚拟地址空间的映射和解除映射,以及实际报文数据缓冲区在vhost所在进程的虚拟地址空间的映射和解除映射。
-
当收到报文时,触发发送到客户机的消息通知;当发送报文时, 接收来自客户机的消息通知。
-
virtio-net设备间(虚拟队列)以及其与物理设备间(网卡硬件队列)的报文交换。可用VMDQ机制来对数据包进行分类和排序,避免软件方式的报文交换,从而减少报文交换的成本。
-
virtio-net网络后端的实现以及部分新特性的实现,如合并缓冲区实现巨帧的接收,虚拟端口上多队列机制等。
基于DPDK的用户态vhost设计
DPDK vhost支持vhost-cuse(用户态字符设备)和vhost-user(用户态socket服务)两种消息机制,它负责为客户机中的virtio-net创建、管理和销毁vhost设备。前者是一个过渡性技术,这里着重介绍目前通用的 vhost-user方式。
-
消息机制
当使用vhost-user时,首先需要在系统中创建一个Unix domain socket server,用于处理Qemu发送给vhost的消息。
如果有新的socket连接,说明客户机创建了新的virtio-net设备,因此vhost驱动会为之创建一个vhost设备,如果Qemu发给vhost的消息中已经包含有socket文件描述符,说明该Unix domain socket已创建,因此该描述符可以直接被vhost进程使用。
最后,当socket连接关闭时,vhost会销毁相应的设备。
-
地址转换和映射虚拟机内存
Qemu支持一个参数选项(mem-path),用于传送目录/文件系统, Qemu在该文件系统中分配所需的内存空间。因此,必须保证宿主机上有足够的大页空间,同时总是需要指定内存预分配(mem-prealloc)。
为了vhost能访问虚拟队列和数据包缓冲区,所有的虚拟队列中的描述符表、可用环表和已用环表的地址,其所在的的页面必须被映射到 vhost的进程空间中。
vhost收到Qemu发送的VHOST_SET_MEM_TABLE消息后,使用消息中的内存分布表(文件描述符、地址偏移、块大小等信息),将 Qemu的物理内存映射到自己的虚拟内存空间。
-
vhost特性协商
在设备初始化时,客户机virtio-net前端驱动询问vhost后端所支持的特性。当其收到回复后,将代表vhost特性的字段与自身所支持特性的字段进行与运算,确定二者共同支持的特性,并将最终可用的特性集合发送给vhost。
-
virtio-net设备管理
一个virtio-net设备的生命周期包含设备创建、配置、服务启动和设 备销毁四个阶段。
-
vhost中的Checksum和TSO功能卸载
为了降低高速网络系统对CPU的消耗,现代网卡大多都支持多种功能卸载技术,其中,较为重要的两种功能是 Checksum(校验和)的计算和TSO(TCP分片卸载)。
Checksum(校验和)被广泛应用于网络协议中,用于检验消息在传递过程中是否发生错误。如果网卡支持Checksum功能的卸载,则 Checksum的计算可以在网卡中完成,而不需要消耗CPU资源。
TSO(TCP Segmentation Offload,TCP分片卸载)技术利用网卡的处理能力,将上层传来的TCP大数据包分解成若干个小的TCP数据包, 完成添加IP包头、复制TCP协议头并针对每一个小包计算校验和等工作。