virtio
virtio 由 rusty russell 开发,最初是为了支持自己开发的 lguest hypervisor,其设计目标是在虚拟化环境下提供与物理设备相近的 i/o 功能和性能,并且避免在虚拟机中安装额外的驱动程序。基于这一目标,后来通过开源的方式将 virtio 延伸为一种虚拟化设备接口标准,并广泛地支持 kvm、qemu、xen 和 vmware 等虚拟化解决方案。
为了追求更高的性能和更低的开销表现,virtio 采用了 para-virtualizaiton(半虚拟化/准虚拟化)技术路线,本质区别于性能低下的 full-virtualizaiton(e.g. qemu i/o emulation)。这也意味着使用 virtio 的 guestos 需要经过一定的代码修改(安装非原生的 device driver),这也是为什么需要将 virtio 定位于 “虚拟化设备接口标准“ 的原因,其整体的集成架构需要由 front-end(guestos kernel device driver)和 back-end(vmm provider)共同遵守统一的传输协议。
在应用 virtio 的场景中,除了 guestos 需要安装额外的 virtio front-end device driver 这一点不足之外(通常在制作 qcow2 镜像时会安装好),相对地带来了下列诸多好处:
高性能:virtio 省去了 full-virtualizaiton 模式下的 traps(操作捕获)环节,guestos 通过 virtio interfaces 可以直接与 hypervisor 中的 device emulation 进行交互。
低开销:virtio 优化了 cpu 在内核态和用户态之间频繁切换,以及 cpu 在 vm exit 和 vm entry 之间频繁陷入陷出所带来的性能开销。
标准化:virtio 实现了统一的虚拟设备接口标准,可以应用在多种虚拟化解决方案中。
virtio 虚拟设备接口标准
为了抑制 para-virtualizaiton 所具有的跨平台兼容性缺点,virtio 明智地走上了开源之路,通过构建标准而统一的生态来争取最小化的兼容性排斥问题,反而让其逆转成为了一种优势。
在 virtio 提出的虚拟化设备接口标准中,包括多个子系统,每个子系统都定义了一组虚拟设备类型和协议。例如:
virtio-block(块设备):提供虚拟磁盘设备的接口。
virtio-net(网络设备):提供虚拟网络设备的接口。
virtio-serial(串口设备):提供虚拟串口设备的接口。
virtio-memory(内存设备):提供虚拟内存设备的接口。
virtio-input(输入设备):提供虚拟输入设备的接口。
此外还有 virtio-scsi、virtio-nvme、virtio-gpu、virtio-fs、virtio-vsock 等等虚拟设备类型和协议。
virtio 的前后端分层架构
virtio 虚拟设备接口标准的分层软件架构如下图所示:
front-end:是一组通用的,安装在 guestos kernel 中的 virtio device driver,通过 hyper-call 的方式对 back-end 进行调用。
back-end:是一组 hypervisor 专用的,运行在 vmm 中的设备模拟程序,提供了 hyper-call 调用接口。
transport:是一组标准的传输层接口,基于环形队列的方式来批量处理 i/o 请求。
virtio 的数控路径分离架构
virtio 还定义了 “控制路径” 和 “数据路径” 的分离架构,两者的侧重各有不同,控制平面追求尽可能的灵活以兼容不用的设备和厂商,而数据平面则追求更高的转发效率以快速的交换数据包。
控制路径:控制建立或删除 front-end 和 back-end 之间的数据路径,提供可管理的灵活性。
数据路径:在 front-end 和 back-end 之间进行数据传输,追求性能。
对应地,virtio 标准也可以分为两个部分:
virtio spec:定义了如何在 front-end 和 back-end 之间构建控制路径和数据路径的标准,例如:数据路径规定采用环形队列缓冲区布局。
vhost protocol:定义了如何降数据路径的高性能实现标准,例如:基于 kernel、dpdk、smartnic offloading 的实现方式。
以 qemu 为例,其根据 virtio spec 实现了控制路径,而数据路径则可以 bypass qemu,使用 vhost-net、vhost-user 等实现。
virtio networking
virtio networking 是一种高功能、高性能、可扩展的虚拟化网络设备,支持多种网络功能和虚拟网络技术,并可以通过多种实现方式进行部署。
网络功能:mac 地址、流量限制、流量过滤、多队列收发等;
虚拟网络技术:vlan、gre、vxlan 等;
多种实现方式:kernel、dpdk、硬件网卡等。
virtio-net driver 和 virtio-net device(由 qemu 模拟的后端)
virtio-net 是 virtio networking 的默认实现。其中,front-end 是 virtio-net driver(虚拟网卡驱动程序);back-end 是由 qemu 软件模拟的 virtio-net device。
qemu 会在 vm 启动时,为 vm 创建好相应的 virtio-net device(虚拟设备),并在 vm 启动后,通过在 guestos kernel 中安装的 virtio-net driver 来 probe(探知)到该 virtio-net device。
virtio-net driver 和 virtio-net device 的软件架构如下图所示,我们需要关注 3 个方面:
control plane:virtio-net driver 和 virtio-net device 之间使用了 pci 传输协议,如下图中蓝线部分。
data plane:virtio-net driver 和 virtio-net device 之间使用了 virtqueues 和 vrings,如下图中红虚线部分。而 virtio-net device 和 kernel network stack 之间使用了 tap 虚拟网卡设备,作为 user space(qemu)和 kernel space(network stack)之间的数据传输通道,如下图中红实线部分。
notification:作为 virtio-net driver、virtio-net device 和 kvm 之间交互方式,用于实现 “vcpu 硬中断“ 的功能。
其中值得细究的就是 virtio-net driver 和 virtio-net device 之间的 transport(传输层)实现。virtio-net transport 采用了非常类似于物理网卡设备(nic rx/tx queues 和 kernel rx/tx rings)的设计思路。
front-end 和 back-end 之间存在 2 个 virtqueues,对应到 nic 的 rx/tx queues。virtqueues 的底层利用了 ipc 共享内存技术,在 hostos 和 guestos 之间建立直接的传输通道,绕开了 vmm 的低效处理。
receive queue:是一块 buffer 内存空间,存放具体的由 back-end 发送到 front-end 数据。
send queue:是一块 buffer 内存空间,存放具体的由 front-end 发送到 back-end 数据。
同时,每个 virtqueue 都具有 2 个 vrings,对应到 kernel 的 rx/tx rings:
available ring:存放 front-end 向 back-end 发送的 queue 中可用的 buffer 内存地址。
used ring:存放 back-end 向 front-end 发送的 queue 中已使用的 buffer 内存地址。
当然,front-end 和 back-end 之间还需要一种双向通知机制,对应到 nic 和 cpu 之间的硬中断信号:
available buffer notification:front-end 使用此信号通知 back-end 存在待处理的缓冲区。
used buffer notification:back-end 使用此信号标志已完成处理某些缓冲区。
具体的,当 guestos 发送数据时,front-end 将数据填充到 send queue 内存空间,然后将地址记录到 available ring,并在适当的时候向 back-end 发送 available buffer notification;qemu 接收到通知后,从 available ring 中获取到数据的数据,完成数据接收,然后记录 new buffer head index 到 used ring,并在适当的时候向 front-end 发送一个 used buffer notification。front-end 接收到通知后释放对应的 send queue 内存空间,available ring 指针指向下一位。
当 guestos 接收数据时,front-end 首先分配好 null 的 receive queue 内存空间,然后将地址记录到 available ring,并在适当的时候向 back-end 发送 available buffer notification;qemu 接收到通知后,从 available ring 中获取到数据填充入口地址,完成数据填充,然后记录 new buffer head index 到 used ring,并在适当的时候向 front-end 发送一个 used buffer notification。front-end 接收到通知后从 receive queue 中读取数据,available ring 指针指向下一位。
值得注意的是,在 pci passthrough 场景中,可以使用 virtio-pci(更多的是使用 vfio)。虽然此时依旧会沿用了 available/used buffer notification 这一控制面的双向通知机制,但实际的数据面,则是由 dma 和专用队列来完成的,具有更高的性能。这体现了数控分离带来的好处。
综上所述,下述流程图总结了 2 个关键流程:
virtio-net device 初始化流程:包括 device discovery 和 device configuration。对于 virtio-net driver 而言,virtio-net device 就是一个 pci 设备,遵守标准的 pci 设备初始化流程,并且 driver 和 device 使用 pci 协议进行通信。
virtio-net driver 发包流程:
当 virtio-net driver 发送数据包时,将数据包放入 send queue,将数据包的访问地址放入 available ring,触发一次 available buffer notification 到 kvm,然后 vm exit,由 qemu 接管 cpu,执行 i/o 控制逻辑,将此数据包传递到 virtio-net device,然后 virtio-net device 降数据包通过 tap 设备传入 kernel network stack;
完成后,qemu 将 new buffer head index 放入到 used ring 中,同时 virtio-net device 发出一个 virq 中断注入到 kvm,然后 kvm 发出一个 used buffer notification 到 virtio-net driver,此时 virtio-net driver 就会从 used ring 中取出 buffer head index;
至此一次发送操作就完成了。
另外,由于 tap 设备支持 tx/rx 多队列,所以实际上也会存在 n 个 virtqueues peer 进行一一映射。
vhost-net(由 kernel 提供的后端)
由 qemu 模拟的 virtio-net device 显然性能不佳,具有频繁的模式和上下文切换、低效的数据拷贝、线程间同步等性能问题。于是,virtio 社区在 kernel 中实现了一个新的 vhost-net 后端,以解决上述问题。
更低的延迟(latency):比普通 virtio-net 低 10%。
更高的吞吐量(throughput):比普通 virtio-net 高 8 倍,达到 7~8 gigabits/sec。
vhost-net 的核心思想就是将 data path bypass qemu,定义了一种新的 data path 传输方式,使得 guestos virtio-net driver 和 hostos kernel vhost-net 可以直接通信。如下图所示:
control plane:virtio-net driver 和 virtio-net device 之间使用了 pci 传输协议,virtio-net device 和 vhost-net 之间使用 ioctl() 接口,如下图中蓝线部分。
data plane:virtio-net driver 和 vhost-net 直通,如下图中虚线部分。而 vhost-net 和 kernel network stack 之间使用了 tap 虚拟网卡设备连接,如下图中红实线部分。
notification:作为 virtio-net driver、vhost-net 和 kvm 之间交互方式,用于实现 “vcpu 硬中断“ 的功能。
vhost-net 的本质是一个 hostos kernel module,实现了 vhost-net protocol 标准,当 kernel 加载 vhost-net 后,会暴露一个设备接口文件 /dev/vhost-net。
当 qemu 在 vhost-net 模式下启动时,qemu(virtio-net device)首先会 open() 该文件,并通过 ioctl() 与 vhost-net 进行交互,继而完成 vhost-net 实例的 setup(初始化)。初始化的核心是建立 virtio-net driver 和 vhost-net 之间的 data path 以及 notification,包括:
hypervisor memory layout(虚拟化内存布局):用于 data path 传输,让 vhost-net 可以在 kvm 的内存空间中找到特定 vm 的 virtqueues 和 vrings 的地址空间。
ioeventfd 和 irqfd(文件描述符):用于 notification 传输,让 vhost-net 和 kvm 之间可以在 kernel space 中完成 notification 的直接交互,不再需要通过 user space 中的 qemu。
同时,在 vhost-net 实例初始化的过程中,vhost-net 会创建一个 kernel thread(vhost worker thread),名为 vhost-$pid(pid 是 qemu 进程 pid),qemu 进程就和 vhost-net 实例以此建立了联系。vhost worker thread 用于处理 i/o event(异步 i/o 模式),包括:
使用 ioeventfd 轮询 tap 事件和 notify 事件,并转发数据。
使用 irqfd 允许 vhost-net/kvm 通过对其进行写入来将 vcpu 的 virq 注入到 virtio-net driver。
综上,在 vhost-net 实例初始化完成之后,virtio-net device 将不再负责数据包的处理(对 virtqueues 和 vrings 的读/写操作),取而代之的是 vhost-net。vhost-net 可以直接访问 vm 的 virtqueues 和 vrings 内存空间,也可以通过 kvm 与 virtio-net driver 进行 notification 交互。
最后需要注意的是,vhost-net 在某些应用场景中的性能未必就会比 virtio-net device 更好。例如:从 hostos 传输到 guestos 的 udp 流量,如果 guestos 接受 udp 数据包的速度比 hostos 发送的速度要慢,在这种情况下使用 vhost-net 将会导致 guestos udp socket receive buffer 更容易溢出,导致丢包,继而使得 guestos 的整体性能出现下降。此时使用慢速的 virtio-net,让 hostos 和 guestos 之间的传输速度稍微慢一点,反而会提高整体的性能。
可见,在虚拟机场景中,全链路的性能均衡需要给予更加全面的考虑。
vhost-user(由 dpdk 实现的用户态后端)
vhost-user 是一个基于 dpdk vhost-user library 开发的后端,运行在 user space,应用了 dpdk 所提供的 cpu 亲和性,大页内存,轮询模式驱动等数据面转发加速技术。
区别于 vhost-net protocol,vhost-user library 实现了一种新的 vhost-user protocol,两者间最大的区别体现在通信信道的实现方式上。
vhost-net:使用 /dev/vhost-net 字符设备和 ioctl() 实现了 user process(qemu)和 kernel(vhost-net)之间的 control plane 通信信道。
vhost-user:使用 unix socket 实现了 user process(qemu 和 dpdk app)之间的 control plane 通信信道。
qemu 通过 unix socket 来完成对 vhost-user 的 data plane 配置,包括:
特性协商配置:qemu(virtio-net device)与 vhost-user 协商两者间功能特性的交集,确认哪些功能特性是有效的。
内存区域配置:vhost-user 使用 mmap() 来映射分配给 qemu 的内存区域,建立两者间的直接数据传输通道。
virtqueues/vrings 配置:qemu 将 virtqueues/vrings 的数量和访问地址发送给 vhost-user,以便 vhost-user 访问。
notification 配置:vhost-user 仍然使用 ioeventfd 和 irqfd 来完成和 kvm 之间的通知交互。
目前最常见的 vhost-user dpdk app 就是 ovs-dpdk,除了支持 vhost-user back-end 之外,还支持 virtio-pmd front-end。在追求极致性能的 vhost-user / virtio-pmd 架构中,hostos user space 和 guestos user space 都运行着 dpdk。
用户可以自由选择创建 vhost-user 类型的 vswitch port(对应一个 vhost-user 实例)。如下图所示。
hw vdpa(使用硬件加速的后端)
目前性能最好的 data plane 无疑是 sr-iov vf passthrough,但原生的 sr-iov 缺少控制面逻辑,在实际使用中多有不便。
hw vdpa(hardware vhost data path acceleration,硬件 vhost 数据面加速)就是一种使用 sr-iov vf 来充当 data plane、使用 vdpa framework 来充当控制面的软硬件融合加速技术,能够同时兼具软件的可管理性、灵活性以及硬件的高性能。同时也解决了 vf passthrough 虚拟机不支持热迁移的难题。
note:sw vdpa(e.g. vdpa on ovs-dpdk)不在本文讨论范围中。
下图中展示了 ovs-dpdk 和 hw vdpa 的性能对比。
目前 vdpa framework 的底层实现方式还尚未统一,从 qemu 的角度来看,主要有 2 种方式:
适应性更好的 vhost-user + vdpa driver:在 dpdk vhost-user library 的基础之上,再实现一个 vdpa driver 适配层。由 vdpa driver 完成与 hw device(e.g. smartnic、dpu)之间的交互。从下图中可以看到 qemu 与 hw 的 control plane 通过 vdpa driver 进行传递,并配置好 hw 与 vm 之间的 passthrough data plane。目前 mellanox mlx5 和 intel ifc 对应的 vdpa driver 都已经合入到 dpdk 和 kernel 社区。(注:iommu/vfio 实现原理请浏览《虚拟化技术 — 硬件辅助的虚拟化技术》。)
架构更合理的 vhost-vdpa:实现一种新的 vhost-vdpa protocol,qemu 通过 ioctl() 调用 kernel 中的 vdpa module,再由 vdpa module 通过 vdpa driver 完成与 hw device 之间的交互。
目前,随着 smartnic / dpu 的快速发展,一种结合了 sw vdpa 和 hw vdpa 特性的 hw vdpa on ovs-dpdk 方案也逐渐成熟中,其实现会更加复杂、但功能也更加完备。值得期待。
华为被发布禁令后 美国半导体公司股票大跌
华为mate10什么时候上市?华为mate10最新消息:华为Mate10或将延期11月发布,荣耀9与华为mate10双保险!
一文读懂熔断器与断路器的区别
竞逐欧洲工业4.0 主流生态圈中,华为如何发声?
OPPO R11plus最新消息:青年一族必备,OPPO R11plus曝光,骁龙820+后置1600w双摄
VirtIO Networking虚拟网络设备实现架构
Marvell正式推出了双端口400GbE MACsec PHY收发器
MOS管基本参数有哪些?以下10个必不可少!
三星停止生产OLED电视或许是一个错误 将高端电视市场让给竞争对手索尼和LG
国家重点研发计划“主动健康与老龄化科技应对”项目阶段性开发成果报告会在拓普联科召开
拉绳位移传感器温度产生漂移及安装
联手华为发布业界第一份云网融合白皮书 天翼云助力企业数字化转型
硬件光追才是好光追,联发科让手游的画质终于不再“假”
商业物联网以及保护软件更新的意义
智慧隧道门禁系统集成施工专家
日立推出石英玻璃数据永久存储技术
iPad的回归之路:新款iPad的前世今生,iPad系列由繁入简引人深思
苏州能讯高能半导体有限公司4英寸氮化镓芯片产线建成 总投资3亿元
SpaceX星链互联网服务已在英国实施
霍尼韦尔发布工业控制系统设计的新方案