DPDK 作为旁路内核方案,其接受和转发的包可以不经过内核进行高速转发,但其中可能存在部分“异常”的包(比如发给该节点本身的包)不需要转发,而是需要通过内核处理,这就是 Exception path,virtio-user 则实现 Exception path 的方案之一。
Virtio-user
Virtio-user 一系列概念原本是用来处理虚拟机与主机之间的 I/O。
virtio
:即 virtual I/O,用于虚拟机和主机间高速 I/O 操作的开放标准。vhost
:主机中加速 virtio 的后端驱动实现方案。最初 virtio 在主机中的后端处理由 QEMU 进程实现,后升级为 vhost 加速方案,vhost 又分为内核态的vhost-net
和在用户态的vhost-user
。virtio-net
:在虚拟机内部的前端驱动,遵循 virtio 标准收发网络包,需要主机有对应的后端来配合。vhost-net
:在主机的内核空间的后端驱动,可以处理来自虚拟机virtio-net
的包,并将包交给内核网络协议栈。vhost-user
:在主机的用户空间的后端规范,规定了如何绕过内核,通过用户态应用(如 OVS-DPDK)来实现一个后端驱动,用来处理来自虚拟机virtio-net
的包virtio-user
:DPDK 对virtio-net
的具体实现的后端驱动,在主机的用户空间,如通过 DPDK 创建端口与虚拟机通信使用的就是这种驱动。
由此便有了虚拟机与主机之间的 I/O 的三种后端路径:
- 传统的 QEMU 进程:速度很慢
- 内核态的 vhost-net 后端驱动:速度快于 QEMU
- 用户态基于 vhost-user 协议的 virtio-user 后端驱动:速度最快
DPDK Exception Path — Virtio_user
Virtio_user,作为虚拟设备,原本是与 vhost-user 后端(另一个进程)一起作为 IPC(进程间通信) 和用户空间容器网络的高性能解决方案。但在 Exception Path 中,其舍弃了 vhost-user 后端,而是与 vhost-net 进行通信,从而将包转入内核。
下图展示了 Virtio_user 作为 DPDK Exception Path 的架构。
图中共有两条包路径:
- Fast Path (
NIC <-> other PMDs <-> ETHFEV API <->
):包的快速转发路径, 纯用户态转发,包从网卡通过各种 DPDK 的 PMD 驱动收发,DPDK 应用通过调用如rte_eth_rx_burst()
和rte_eth_tx_burst()
等 ETHDEV API 来与 PMD 驱动交互,实现包处理后,不经内核,快速转出 - Exception Path (
NIC -> other PMDs -> ETHFEV API -> ... -> ETHFEV API -> virtio PMD -> tap -> kernel network stack
):包的异常路径- DPDK -> 内核(发送路径)
- DPDK 应用调用
rte_eth_tx_burst(virtio_user0, ...)
控制 PMD 驱动 - virtio PMD 驱动将包通过 vhost 协议,将数据包经过
/dev/vhost-net
文件交给内核的 vhost-net 驱动 - vhost-net 驱动将包写入 tap 设备
- 内核网络协议栈中 tap 设备中读到包,开始处理
- DPDK 应用调用
- 内核 -> DPDK(接收路径)
- 内核网络协议栈决定发一个包,将包写入 tap 设备
- vhost-net 驱动从 tap 设备抓取包
- vhost-net 驱动通过 vhost 协议和共享内存,将包放到 virtio_user0 的接受队列
- DPDK 应用调用
rte_eth_rx_burst(virtio_user0, ...)
从 virtio_user0 的接受队列收包
- DPDK -> 内核(发送路径)
DPDK 通过下面代码示例来创建 virtio_user 端口
1 | rte_eal_hotplug_add("vdev", "virtio_user0", "path=/dev/vhost-net"); |
rte_eal_hotplug_add
函数用于动态添加一个设备vdev
指这个设备的类型是虚拟设备(Virtual Device),而不是物理 PCI 设备virtio_user0
指定设备名称,对应图中的 virtio PMD,之后 DPDK 就可通过rte_eth_tx_burst(virtio_user0, ...)
和rte_eth_rx_burst(virtio_user0, ...)
来操作这个端口path=/dev/vhost-net
指定的设备的后端路径,即使用 vhost-net 内核驱动与内核交互,对应了图中的对 vhost.ko(vhost-net 的内核模块)的用户态 vhost adapter 适配行为
此处添加的
virtio_user0
虚拟设备并非 Linux 内核的虚拟网卡 tap,该virtio_user0
是用户态的 DPDK 的 virtio PMD 的一个虚拟端口,tap 是与之在内核对应的那种虚拟网卡,可以同样命名为 virtio_user0,但不是同一个东西。
Virtio-user Exception Path 示例
- DPDK 从物理网卡的 RX 取下包
- DPDK 判断收包是否走 Exception Path
- 如果是走 Exception Path 的包,DPDK 将其放到 EP ring 中,之后统一发到 virtio_user0 的 TX(推荐使用一个 ring 来作为中间缓冲)
- virtio_user0 通过 vhost-net 将包发到内核协议栈,最后交给相应用户态进程
- 用户态进程收包后,将响应的回包交给内核协议栈
- vhost-net 将回包放到 virtio_user0 的 RX
- DPDK 从 virtio_user0 的 RX 收包,并通过物理网卡的 TX 发出
注:RX、TX 都是相对 DPDK 而言的