前言
VFIO 的热迁移技术是一个相对来说比较新的特性,它相较于 vdpa 是可以直通到虚拟机内部,这样会有着更好的性能,但也存在一个问题就是通用性差一点,如不是标准的 virtio 设备,那么就需要在虚拟机里面安装专属的设备驱动,这在云厂商来说是一个极大的挑战,但是对于一些非网络设备如 GPU、RDMA 等设备追求性能优先的设备有着极大的优势,同时 virtio 协议已经支持这一特性,我们就借着 virtio-net 设备好好的来理解一下这个技术,同时也梳理一下,实现该方案,我们需要做什么,如放在 RDMA、SNIC等卡上,逻辑和软件需要做什么具体的事情。
迁移整体框架图
这个图主要是在热迁移时候,QEMU 的函数调用路径和 kernel 驱动里面的一些状态机变化,主要涉及到源虚拟机和目的虚拟机
sequenceDiagram
participant Source QEMU
participant Dest QEMU
participant Source VFIO
participant Dest VFIO
Note over Source QEMU, Dest QEMU: 初始状态: VM 在源端运行
rect rgba(200, 255, 200, 0.3)
Note over Source QEMU, Source VFIO: 阶段 1: 迁移设置与预拷贝 (Pre-copy)
Source QEMU->>Source VFIO: .save_setup()
Note right of Source VFIO: 设备状态: _RUNNING -> _PRE_COPY
Source QEMU->>Dest QEMU: 迁移设置握手
Note over Dest QEMU, Dest VFIO: VFIO设备状态已为 _STOP (由VM状态变化触发)
Dest QEMU->>Dest VFIO: .load_setup()
Note right of Dest VFIO: 设备状态: _STOP -> _RESUMING
loop 迭代预拷贝
Source QEMU->>Source VFIO: .save_live_iterate()
Source VFIO-->>Source QEMU: 预拷贝数据块
Source QEMU->>Dest QEMU: 传输数据块
Dest QEMU->>Dest VFIO: .load_state_buffer() (存储数据)
Note right of Dest VFIO: 设备状态: _RESUMING (累积状态中)
end
Note over Source QEMU, Source VFIO: 预拷贝持续直到数据收敛...
end
rect rgba(255, 228, 181, 0.4)
Note over Source QEMU, Source VFIO: 阶段 2: 切换 (Stop and Copy)
Source QEMU->>Source QEMU: 暂停 vCPUs
Note over Source QEMU, Source VFIO: 进入 P2P 静默状态
Source QEMU->>Source VFIO: 转换到 P2P 静默状态
Note right of Source VFIO: 设备状态: _PRE_COPY -> _PRE_COPY_P2P
Note over Source QEMU, Source VFIO: 进入停止并拷贝状态
Source QEMU->>Source VFIO: .save_live_complete_precopy()
Note right of Source VFIO: 设备状态: _PRE_COPY_P2P -> _STOP_COPY
Source VFIO-->>Source QEMU: 最终设备状态数据
Source QEMU->>Dest QEMU: 传输最终状态数据
end
rect rgba(173, 216, 230, 0.4)
Note over Dest QEMU, Dest VFIO: 阶段 3: 恢复与运行
Dest QEMU->>Dest VFIO: .load_state() / .load_state_buffer()
Note right of Dest VFIO: 加载最终状态. 设备状态: _RESUMING (持续加载)
Dest QEMU->>Dest VFIO: .load_cleanup()
Dest QEMU->>Dest QEMU: 恢复 vCPUs
Note over Dest QEMU, Dest VFIO: VM状态变为RUNNING,触发VFIO设备状态转换
Dest VFIO-->>Dest VFIO: (vmstate_change_prepare 被调用)
Note right of Dest VFIO: 设备状态: _RESUMING -> _RUNNING_P2P
Dest VFIO-->>Dest VFIO: (vmstate_change 被调用)
Note right of Dest VFIO: 设备状态: _RUNNING_P2P -> _RUNNING
Note over Dest QEMU, Dest VFIO: VM 现在在目标端运行
end
rect rgba(211, 211, 211, 0.5)
Note over Source QEMU, Source VFIO: 阶段 4: 清理
Source QEMU->>Source VFIO: .cleanup()
Note right of Source VFIO: 设备状态: _STOP_COPY -> _STOP
end
原虚拟机
stateDiagram-v2
direction LR
[*] --> _RUNNING: 正常运行
_RUNNING --> _PRE_COPY: migrate_init() / .save_setup()
_PRE_COPY --> _PRE_COPY: .save_live_iterate()
_PRE_COPY --> _PRE_COPY_P2P: vCPUs 停止 / P2P 静默
_PRE_COPY_P2P --> _STOP_COPY: .save_live_complete_precopy()
_STOP_COPY --> _STOP: 迁移完成 / .cleanup()
_STOP --> [*]: 清理完成
目的虚拟机
stateDiagram-v2
direction LR
[*] --> _STOP: 接收迁移 / .load_setup()
_STOP --> _RESUMING: .load_state() / .load_state_buffer()
_RESUMING --> _RUNNING_P2P: .load_cleanup() / vCPUs 启动
_RUNNING_P2P --> _RUNNING: 转换到完全运行状态
_RUNNING --> [*]: 正常运行
总结
1、通过上面三个图,我们大概了解了在热迁移 QEMU 的一些行为,主要是状态机的变化,但还是比较浅显的
2、虽然很清晰了状态机的变化,但是这些命令是如何下发下来的,virtio 设备又需要做什么适配、DMA 的脏页是如何记录的、原虚拟机的设备vring 状态在目的虚拟机是如何恢复的..etc,都需要我们一一了解