ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

[virtio](八):vhost-user 与独立用户态数据面

[virtio](八):vhost-user 与独立用户态数据面

第七篇讨论了 virtio-net 和 vhost-net。本篇继续看 vhost-user:如果 virtio 数据面不在 QEMU,也不在 host kernel,而是在另一个用户态进程里,QEMU 如何把 guest memory、virtqueue 和 eventfd 交给它?

1. vhost-user 解决什么问题

vhost-kernel 把数据面下沉到 host kernel。

vhost-user 则把数据面交给一个独立的用户态 backend process。

典型场景是高性能网络,例如 DPDK 相关后端。

模型如下:

QEMU process | | vhost-user protocol over Unix socket v vhost-user backend process | v high-performance packet processing

它的目标是:

  • 让数据面独立演进
  • 让 backend 使用 DPDK/SPDK 等用户态高性能框架
  • 避免 QEMU 主循环处理高频数据包
  • 在 QEMU 之外部署专门的 I/O backend

代价是系统结构更复杂。

2. vhost-user 与 vhost-kernel 的区别

vhost-kernel:

QEMU | | ioctl to kernel vhost device v host kernel vhost backend

vhost-user:

QEMU | | Unix socket messages v user-space backend process

二者都接管 virtqueue 数据面。

区别在于 backend 运行位置不同。

vhost-kernel 适合利用内核网络栈和 tap。

vhost-user 适合独立用户态包处理、DPDK、用户态存储后端或定制数据面。

3. QEMU 仍然是控制面

即使使用 vhost-user,QEMU 仍然负责 virtio 设备控制面。

包括:

  • 创建设备
  • 暴露 transport
  • feature negotiation
  • device status
  • queue setup
  • reset
  • migration coordination
  • 管理接口
  • 与 backend 建立连接

vhost-user backend 处理的是数据面。

结构是:

QEMU control plane | | vhost-user protocol v backend data plane

这和 vhost-net 的思想一致,只是 backend 从 kernel 变成了另一个用户态进程。

4. vhost-user 协议传什么

QEMU 和 vhost-user backend 通过 Unix socket 交换控制消息。

常见信息包括:

  • feature bits
  • protocol feature bits
  • guest memory regions
  • vring addresses
  • vring size
  • kick eventfd
  • call eventfd
  • queue enable state
  • backend configuration
  • migration/inflight state

其中 guest memory region 和 vring 信息最关键。

backend 要直接处理 guest virtqueue,就必须知道:

guest physical address 如何映射到 backend 可访问的 host virtual address descriptor table 在哪里 avail ring 在哪里 used ring 在哪里 kick/call eventfd 是哪些

5. guest memory 如何共享给 backend

vhost-user backend 是独立进程。

它不能天然访问 QEMU 进程中的 guest RAM。

因此 QEMU 需要通过协议把 guest memory region 描述和文件描述符传给 backend。

常见方式是基于共享内存 fd。

抽象模型:

QEMU owns guest RAM mappings | | send memory region metadata + fd v vhost-user backend maps same memory | v backend can access virtqueue and packet buffers

这一步非常敏感。

如果 memory table 错误,backend 解析 descriptor 时就无法正确访问 guest buffer。

6. vring 如何交给 backend

Guest 配置 virtqueue 后,QEMU 知道 vring 的地址和大小。

QEMU 会通过 vhost-user 协议告诉 backend:

queue index queue size descriptor table address avail ring address used ring address

backend 拿到这些信息后,就可以像 QEMU 或 vhost-kernel 一样解析 descriptor chain。

路径是:

Guest writes descriptors | v virtqueue in guest RAM | v vhost-user backend maps memory and reads vring | v backend processes I/O

QEMU 不再逐个解析高频 descriptor。

7. kick 和 call eventfd

vhost-user backend 还需要通知机制。

QEMU 会把 eventfd 传给 backend。

kick fd: guest -> backend,有新 descriptor call fd: backend -> guest,有 completion

完整路径:

Guest updates avail ring | | kick eventfd v vhost-user backend wakes | v backend processes descriptors | | call eventfd v KVM injects interrupt / QEMU handles notify | v Guest sees completion

如果配置了 irqfd,completion 可以更快进入 guest interrupt injection 路径。

8. vhost-user-net 的典型场景

vhost-user-net 常见于 DPDK 场景。

结构类似:

Guest virtio-net driver | v virtqueue in guest RAM | v vhost-user socket | v DPDK backend process | v physical NIC / userspace switch

数据包可以在用户态网络栈中高速处理,减少传统内核网络栈和 QEMU 主循环开销。

常见后端可以是:

  • OVS-DPDK
  • VPP
  • 自定义 DPDK 程序
  • 用户态虚拟交换机

9. vhost-user 的复杂度

vhost-user 的灵活性来自进程解耦。

但复杂度也来自进程解耦。

需要处理的问题包括:

  • backend 进程生命周期
  • socket 重连
  • feature 兼容
  • memory region 更新
  • queue 启停顺序
  • migration 协调
  • backend 崩溃恢复
  • 安全隔离
  • NUMA 和 hugepage placement

如果 backend 崩溃,QEMU 需要决定 VM 如何表现。

如果 memory mapping 不一致,backend 可能读错 guest buffer。

如果 migration 时 backend 状态没保存,target 恢复后可能丢包或 queue 卡住。

10. vhost-user 与迁移

vhost-user 迁移比基础 QEMU device model 更复杂。

因为设备状态分散在 QEMU 和 backend 中。

迁移时需要:

pause or quiesce backend data plane | v collect virtqueue state and inflight descriptors | v serialize QEMU virtio state | v coordinate backend state | v restore on target | v reconnect or start target backend | v resume queues

如果存在 in-flight packet 或 request,需要确保它不会丢失或重复完成。

这也是 vhost-user protocol feature 和 backend migration 能力重要的原因。

11. vhost-user 的安全边界

vhost-user backend 能访问 guest memory 中的 virtqueue 和 data buffer。

这意味着它处在敏感边界上。

安全设计需要考虑:

  • backend 进程权限
  • shared memory 范围
  • fd 传递
  • sandboxing
  • backend 输入验证
  • descriptor 合法性检查
  • guest 和 backend 之间的隔离

vhost-user 并不因为在用户态就天然安全。

它获得了高性能访问能力,也承担了正确解析 guest 提供数据结构的责任。

12. 源码阅读入口

本篇可以看:

  • hw/virtio/vhost-user.c
  • hw/virtio/vhost.c
  • include/hw/virtio/vhost.h
  • include/hw/virtio/vhost-user.h
  • hw/net/vhost_net.c
  • net/vhost-user.c
  • docs/interop/vhost-user.rst

阅读问题:

  • QEMU 如何连接 vhost-user socket?
  • memory region 如何发送给 backend?
  • vring address 如何设置?
  • kick/call eventfd 如何传递?
  • backend feature 如何协商?
  • backend 启停和 reset 如何处理?
  • migration 相关状态如何协调?

13. 本篇小结

vhost-user 把 virtio 数据面交给独立用户态 backend。

QEMU 通过 Unix socket 把 guest memory、vring、eventfd、feature 和 queue 状态传递给 backend。

backend 直接处理 guest virtqueue,QEMU 保留控制面和迁移编排。

可以把本篇压缩成一句话:

vhost-user 用进程边界换取数据面灵活性,让 virtio backend 可以独立优化,但也把 memory sharing、queue state、迁移和故障恢复变成必须认真处理的系统问题。

14. 下一篇预告:virtio 设备迁移状态

下一篇讨论 migration。

会回答:

  • virtio 迁移为什么不只是复制 guest RAM?
  • queue index、feature、config 和 pending interrupt 如何保存?
  • in-flight request 如何处理?
  • vhost backend 如何配合迁移?

下一篇的问题可以写成:

一台 VM 迁移到 target host 后,guest driver 为什么还能相信 virtio 设备只是暂停了一下?

返回列表