vhost-vDPA:队列配置、硬件数据路径与迁移
一、vDPA 的 virtio 接口与队列卸载
高报文率虚拟机常把大量宿主 CPU 时间花在队列解析、缓冲区访问和网络转发上。vhost-net 把处理放进宿主内核,vhost-user 允许独立进程接管;如果设备本身能够消费 virtio 队列,还可以进一步减少软件参与。vDPA 为这类设备提供框架:数据路径遵循 virtio,设备的配置方式可以由厂商驱动转换。
本文以 QEMU 中的 vhost-vDPA 网络后端为主线,先看组件分工,再追踪直接模式下的一次发送,随后展开队列、DMA 地址和通知配置,最后说明 Shadow Virtqueue 怎样参与迁移。vDPA 框架支持硬件和软件实现;本文的发送例子使用硬件,迁移部分再引入 QEMU 中转的影子队列。
源码版本:QEMU 10.2.0、Linux 6.18。
二、客户机、QEMU 与宿主 vDPA 驱动的分工
客户机仍看到 virtio-net PCI 设备,并使用标准驱动协商特性、建立 RX/TX 队列。QEMU 提供客户机可见的 PCI 和 virtio 控制接口,打开 /dev/vhost-vdpa-N;宿主的 vhost_vdpa 驱动把 vhost 用户接口转换为 vDPA 配置操作,具体设备驱动再把这些操作落到硬件或软件实现。
| 组件 | 所在位置 | 主要责任 |
|---|---|---|
| 客户机 virtio-net 驱动 | 客户机内核 | 准备缓冲区、发布请求、回收完成 |
| QEMU 前端与 vhost-vDPA 后端 | 宿主用户态 QEMU 进程 | 设备能力、队列配置、内存映射及生命周期协调 |
| vhost_vdpa | 宿主内核 | 接收 ioctl / IOTLB 更新,连接队列与通知 |
| vDPA 设备驱动与实现 | 宿主驱动及对应硬件或软件后端 | 设置真实队列、处理请求、报告完成 |
Mermaid 源码
flowchart TB
accTitle: vhost-vDPA 控制配置与直接硬件数据路径
accDescr: 客户机使用 virtio 驱动。QEMU 通过 vhost_vdpa 内核接口和设备驱动配置队列、DMA 映射与通知,硬件在授权映射内直接访问客户机队列和报文。
G["客户机 virtio-net 驱动"]
R["客户机 RAM<br/>virtqueue + 报文缓冲区"]
Q["QEMU<br/>PCI / virtio 控制接口"]
V["宿主 vhost_vdpa<br/>队列 / IOTLB / 通知"]
D["vDPA 设备驱动<br/>厂商配置适配"]
H["硬件 vDPA 数据面"]
N["外部网络"]
G -->|"发布与回收"| R
G -.->|"协商 / 配置"| Q
Q -.->|"ioctl / IOTLB 更新"| V
V -.->|"vDPA 配置操作"| D
D -.-> H
H <-->|"经过 DMA 地址映射<br/>读取请求 / 写回完成"| R
H <--> N
vhost-vDPA 与 vhost-user 的宿主接口不同:前者使用内核字符设备和 vDPA 驱动框架,后者通过 Unix socket 协议与用户态进程交接资源。VFIO 直通通常让客户机控制实际设备接口并加载该设备的驱动;vDPA 则维持客户机 virtio 数据接口,由宿主驱动适配控制操作。SR-IOV 可以为某些实现提供硬件功能划分,但不是所有 vDPA 的必备条件。QEMU vhost-vDPA 定义
三、发送请求的硬件数据路径
设 TX 队列已配置完成,客户机准备报文 P1。本例采用硬件 vDPA、直接队列访问、合法 DMA 映射,先不启用数据队列的 Shadow Virtqueue。
- 发布。客户机写网络头、报文和描述符链,再发布 available 进度。
- 通知。若设备需要通知,驱动写门铃;已经配置好的 kick/通知路径让设备检查队列。
- 读取。设备按 IOVA 映射访问描述符和数据缓冲区,执行网络发送所需的处理。
- 完成。当缓冲区可以归还时,设备更新 used,并按客户机通知配置触发完成路径。
- 回收。客户机读 used,解除对应映射或引用,回收描述符及数据缓冲区。
直接模式由设备访问队列与报文,省去了 QEMU 普通软件收发路径的逐包解析和复制。网络处理、DMA、IOMMU 转换和通知仍消耗资源,其他环节的复制取决于设备、驱动及工作模式。设备发布 TX used 后,客户机可回收本地缓冲区;远端应用接收由网络协议确认。
设备失联、映射错误或队列停止时,要先确认设备不会继续访问旧内存,再释放队列和缓冲区。只关闭客户机驱动或停止发新请求,不足以证明之前的 DMA 已结束。可恢复状态、队列暂停、设备复位和资源释放之间必须有明确的完成边界。
四、队列启动前,需要建立三类关系
1. 能力协商与设备状态
QEMU 读取设备与后端能力,结合前端配置向客户机提供可用特性,再设置协商结果。split/packed、队列数、卸载功能、控制队列等都影响后续处理。客户机设置 DRIVER_OK 前,特性和队列必须准备好;迁移目标也要能维持客户机已经看到的接口。
2. 队列与通知:设备从哪里开始处理
配置包含队列大小、描述符/可用/完成地址和消费进度,以及 kick、call 文件描述符。常见控制操作包括 VHOST_SET_VRING_NUM、VHOST_SET_VRING_ADDR、VHOST_SET_VRING_BASE 和 VHOST_VDPA_SET_VRING_ENABLE。内核 vDPA 配置层再调用设备的队列设置方法。
kick 方向是客户机到队列消费者,call 方向是设备完成到客户机。常规路径可以连接 KVM ioeventfd / irqfd;部分配置还允许映射设备通知区域以缩短 doorbell 路径。具体是否绕过某个软件环节取决于设备和平台能力,不能把所有 vDPA 都画成完全没有宿主参与。
3. 地址与权限:设备看到的 IOVA 对应哪段 RAM
队列里写着地址,但硬件不能拿 QEMU 进程的虚拟地址直接发起 DMA。QEMU 通过 IOTLB 更新传递设备地址范围、长度、宿主用户地址和访问权限,宿主后端据此建立设备可用的映射。配置客户机 IOMMU 时,还要把映射变化与失效同步给后端。
VHOST_IOTLB_UPDATE
iova : 设备使用的地址起点
size : 映射长度
uaddr: QEMU 进程中相应 RAM 的地址
perm : 只读或读写权限
asid : 已支持时区分设备地址空间
解除对应访问:VHOST_IOTLB_INVALIDATE
没有客户机 IOMMU 时,设备地址可以按 GPA 建立映射;有客户机 IOMMU 或影子队列时,IOVA 与 GPA 未必相等。地址转换、页固定和 DMA 隔离还涉及宿主 IOMMU 与具体驱动,不等于发送一条 UPDATE 就自动满足全部安全条件。QEMU 10.2 的 vhost-vDPA 初始化还会限制客户机 RAM discard,以配合固定内存的要求。映射更新与失效、初始化与内存生命周期约束
五、Shadow Virtqueue 与设备写入跟踪
热迁移必须知道客户机 RAM 哪些位置被修改。KVM 脏页日志主要观察客户机 CPU 对内存的写入,硬件 DMA 的修改还需要设备日志或其他协作。直接访问客户机队列的设备若不能提供所需追踪能力,迁移层就无法仅靠 CPU 脏页信息得到完整结果。
Shadow Virtqueue 在 QEMU 中增加一套面向后端的影子队列。客户机仍发布原队列;QEMU 取出请求、转换或登记相应地址,再把描述符转交到影子队列。设备消费影子队列并写回其 used,QEMU 根据关联找回客户机请求,完成内存访问记账、发布客户机 used,再通知客户机。
Mermaid 源码
sequenceDiagram
accTitle: vDPA Shadow Virtqueue 的描述符和完成转发
accDescr: QEMU 从客户机原队列取请求,建立地址关联并放入影子队列;设备使用影子队列并访问映射后的数据缓冲区。QEMU 收到完成后向客户机回写并参与脏页记账。
participant G as 客户机 virtqueue
participant Q as QEMU SVQ
participant S as 影子 virtqueue
participant H as vDPA 设备
participant B as 客户机数据缓冲区
G->>Q: 发布请求,按需 kick
Q->>Q: 保存原请求关联,翻译 DMA 地址
Q->>S: 写入影子描述符并发布
S->>H: 设备读取影子队列
H->>B: 在授权映射中读写数据
H->>S: 写影子 used 并通知
S->>Q: 取得完成 ID / len
Q->>Q: 校验关联,回收影子描述符
Q->>G: 发布客户机 used,完成写入记账
Note over Q,B: 数据区可映射为同一份 RAM;描述符中转不等于复制所有报文
例如一个 RX 请求允许设备写入客户机缓冲区 B。直接模式下,硬件可以写 B 和客户机 used,QEMU 未必看到每次变化;SVQ 模式把对应完成先交给 QEMU,QEMU 根据原请求的可写区域完成记账,再向客户机发布 used。迁移还必须停止和收尾在途访问,最终同步这些变化,不能只在某一时刻扫描 CPU 脏页位图。
数据队列中,QEMU 转发描述符和完成记录,维护影子请求与客户机请求的关联;普通数据缓冲区仍可映射到同一份客户机 RAM。控制队列还可能使用独立的影子缓冲区,由 QEMU 检查或重放 MAC、队列数量、过滤等配置命令。两类队列因此有不同的缓冲区安排。
QEMU 10.2 的网络后端在预拷贝准备阶段可停启后端,将数据队列切换为 SVQ;失败回退时再恢复相应模式。x-svq 也允许从开始就启用实验性的影子队列。一次切换依次停止处理、读取队列进度、重配映射并恢复访问,原有请求状态随这些步骤交接。迁移事件与数据队列切换、请求转发
六、能记录脏页,还要解决状态与兼容性
SVQ 提供软件协作办法,但一个设备能否迁移,还取决于暂停与恢复能力、特性集、队列状态、控制命令状态,以及目的端支持情况。QEMU 对 SVQ 支持的特性进行验证,并会设置迁移阻止原因。启用了 vDPA 或 x-svq,不足以推导“任意硬件之间都能迁移”。
| 需要保留的状态 | 遗漏时的后果 | 应核对什么 |
|---|---|---|
| 客户机已协商特性与队列布局 | 目的端按不同格式解释相同内存 | 源/目的能力交集及运行中的既定选择 |
| 在途请求与队列进度 | 请求重复执行或被跳过 | 停止完成、消费位置、恢复位置 |
| 设备写入 RAM | 目的端拿到旧数据或旧完成记录 | DMA 脏页追踪或 SVQ 记账,以及停机后的最终同步 |
| MAC、过滤、队列等控制状态 | 接口可见但收发行为不同 | 状态保存、受支持命令与目的端重放 |
| 目的端 DMA 映射与网络连接 | 设备访问无效地址或报文不能交付 | 新宿主映射、路由和外部网络接入 |
具体排错时,先查看 QEMU 的迁移阻止原因,再核对后端能力、映射错误和队列停启日志。吞吐下降若只在迁移期间出现,也应检查数据队列是否已经切到 SVQ,以及 QEMU 线程是否成为新的瓶颈。性能变化可能是为了获得迁移可见性而付出的代价,需要与迁移窗口一起评价。
七、源码:配置、映射与完成转发
| 源码 | 跟踪内容 |
|---|---|
| QEMU net/vhost-vdpa.c | 网络队列与控制队列、SVQ 特性检查、迁移阶段切换 |
| QEMU hw/virtio/vhost-vdpa.c | vhost ioctl、IOTLB UPDATE/INVALIDATE、队列启动、暂停与映射 |
| QEMU vhost-shadow-virtqueue.c | 客户机队列 pop、影子描述符、完成 ID 关联与回写 |
| Linux drivers/vhost/vdpa.c | 用户接口到 vDPA 配置及地址映射的转换 |
| Linux include/linux/vdpa.h | 设备驱动实现的配置操作和能力约定 |
SVQ 的完成路径先从后端 used 取回关联的 VirtQueueElement,检查 ID 和有效链,再调用 virtqueue_fill/flush 向客户机发布完成。设备写出的 ID 必须引用已提交的影子请求;来自设备的完成元数据仍需要验证,不能直接当作可信内存索引。
八、从软件后端到硬件数据面,再补齐可迁移性
vhost 系列的发展把“客户机使用什么接口”与“宿主哪里执行请求”逐渐分开。vhost-net 减少 QEMU 用户态往返,vhost-user 允许专用进程和轮询框架,vDPA 则进一步允许具有 virtio 数据面能力的设备接手。保留标准客户机驱动降低了客户机适配负担,但宿主仍要负责具体硬件的配置、内存隔离与生命周期。
直接硬件访问缩短正常路径,却让软件更难观察在途请求和 DMA 写入。2022 年的 QEMU SVQ 讨论把影子队列作为迁移脏页追踪的协作方案,后续实现可在迁移阶段切换,平时继续使用较短的数据路径。它用额外 CPU 和状态管理换取迁移可见性,并没有消除设备特性限制。SVQ 初期设计讨论
vDPA 也支持软件实现;VDUSE 提供用户态数据面与内核控制、隔离之间的接口,支持的设备类型和能力取决于版本。后端选择需要同时考虑正常吞吐、CPU 开销、尾延迟、迁移降速和故障恢复。直接模式与 SVQ 的对比尤其要标明测试处于正常运行还是迁移阶段。
九、参考资料
- QEMU 10.2.0 Vhost-vdpa Protocol:数据接口与控制适配的职责边界。
- QEMU vhost-vDPA 后端、网络后端:映射、队列和迁移阶段管理。
- QEMU Shadow Virtqueue:描述符转发、地址翻译和完成发布。
- Linux vhost_vdpa、vDPA 配置接口:内核后端与设备驱动之间的约定。
- Linux 6.18 VDUSE 文档:用户态数据面、IOTLB 映射和设备控制。