QEMU虚拟机热迁移

什么是热迁移

虚拟机的迁移可以分为两大类: 离线迁移(Offline Migration)和热迁移(Live Migration)。
离线迁移是指在迁移之前, 将虚拟机暂停, 拷贝其状态到目的主机,在目的主机主机重新建立运行状态, 恢复执行。这种迁移技术适用于对服务可用性要求不高的场合。
热迁移, 是在保证虚拟机中服务正常运行的同时, 把虚拟机从一个物理主机拷贝到另一个物理主机。整个迁移过程对用户是透明的, 即用户感觉不到虚拟机位置的变化。

QEMU虚机热迁移基本流程

QEMU虚拟机热迁移是指在host上QEMU命令行中使用migrate命令把指定的虚拟机从一个host迁移到另外一个host。
热迁移过程中会有短暂的停机时间,但目的是尽可能减少停机时间。
热迁移的性能好坏,一般会考虑3个指标:

  • 整体迁移时间:越长越差
  • 停机时间:越长越差
  • 对业务性能的影响:迁移对于虚机中运行的服务的影响程度,可以通过每秒指令数来观察业务被中断的时间。

热迁移完整流程

流程基本如下:

  1. Setup

Start guest on destination, connect, enable dirty page logging and more

  1. Transfer Memory

    • Guest continues to run
    • Bandwidth limitation (controlled by the user)
    • First transfer the whole memory
    • Iteratively transfer all dirty pages (pages that were written to by the guest).
  2. Stop the guest

    • sync VM image(s) (guest’s hard drives).
  3. Transfer State

    • As fast as possible (no bandwidth limitation)
    • All VM devices’ state and dirty pages yet to be transferred
  4. Continue the guest

    • On destination upon success,broadcast “I’m over here” Ethernet packet to announce new location of NIC(s).
    • On source upon failure (with one exception).

QEMU虚机热迁移实现

热迁移关键stage是上图中的stage2、3和5,QEMU中对应的实现如下:

  1. 将虚拟机所有RAM pages设置成dirty,主要函数ram_save_setup
  2. 持续迭代将虚拟机的dirty pages发送到dst,直到达到一定条件,比如dirty pages数量比较少, 主要函数ram_save_iterate
  3. 停止src上面的guest,把剩下的dirty pages发送到dst,之后发送设备状态,主要函数qemu_savevm_state_complete_precopy

其中1和2是上图中的灰色区域,3是灰色和左边的区域。
之后就可以在dst上面继续运行qemu程序了。

源端的关键实现

migration_thread
 qemu_savevm_state_setup //标记所有RAM pages为dirty
  ram_save_setup[save_setup]
   ram_init_all
    ram_init_bitmaps
     ram_list_init_bitmaps
      bitmap_new
      bitmap_set

migration_iteration_run //迭代拷贝脏内存
 qemu_savevm_state_pending
  ram_save_pending[save_live_pending] //确定还要传输的字节数
 qemu_savevm_state_iterate
  ram_save_iterate[save_live_iterate] //把dirty pages传到dst上面
   ram_find_and_save_block
    send //调用具体的传输方法,比如TCP、RDMA等

migration_completion
 vm_stop_force_state
 qemu_savevm_state_complete_precopy
  qemu_savevm_state_complete_precopy_iterable
   ram_save_complete[save_live_complete_precopy]
    ram_find_and_save_block //拷贝最后的脏内存

目的端的关键实现

migration_incoming_process
 qemu_loadvm_state 
  qemu_loadvm_section_start_full
   find_se //处理源端发过来的各个section
   vmstate_load
    ram_load[load_state] //接收到的数据拷贝到目的端虚拟机的内存上
     ram_load_precopy
      qemu_get_buffer

QEMU VDPA设备热迁移

vdpa当前支持virtio-net,这块的迁移流程已经很成熟,因为virito设备的规范已经有virtio spec覆盖。

VDPA直通设备热迁移完整流程

VFIO设备热迁移

VFIO直通设备在热迁移过程中,主要涉及到设备发起的DMA内存标脏,设备停流和设备状态保存恢复。

在热迁移过程中涉及到很多回调,QEMU中主要涉及到SaveVMHandlers结构体,针对内存的回调基本在savevm_ram_handlers中。
那如果虚机中有VFIO直通设备,同样也需要实现该回调,针对VFIO设备的savevm_vfio_handlers, 另外也要实现qdev_add_vm_change_state_handler_fullmigration_add_notifier 这两个回调。

  • SaveVMHandlers 是数据面,它定义了设备状态如何被持久化传输,没有它,设备状态就无法迁移,解决 “我的状态是什么?怎么存?怎么读?” 的问题。
  • qdev_add_vm_change_state_handler_full 是控制面,它允许设备在迁移过程的关键节点(开始前、完成后、失败时、加载前后等)执行管理操作,确保设备在迁移前后行为正确,但这些操作本身不产生迁移流数据。它调用 SaveVMHandlers 来传输实际状态。解决 “VM 要暂停/恢复了,我需要做点啥准备/收尾?” 的问题(特别是与迁移相关的暂停/恢复)。
  • migration_add_notifier 是错误处理,它提供系统级别对迁移状态变化的感知,针对VFIO设备,主要执行设备状态回滚操作。解决 “迁移这个事儿本身现在怎么样了?(开始了吗?成功了吗?失败了吗?)” 的问题。

内核态的vfio厂商驱动中,也要对应实现上面的ioctl,以及migration序列化结构体,结构体中除了保存寄存器以外,还有硬件上不是通过寄存器呈现的硬件状态,比如通过mailbox下发给硬件的DMA地址。

VFIO直通设备热迁移完整流程

VFIO直通设备热迁移完整流程2

intel E810网卡的VF热迁移实际流程

intel E810 VFIO直通设备热迁移完整流程

pre-copy减少停机时间

由于设备的状态保存和恢复发生在停机阶段,为了尽可能减少虚机停机时间,也会考虑pre-copy。

直通设备热迁移流程

在QEMU的官网上,针对VFIO设备迁移在QEMU内的实现,专门有一段描述:(括号中的状态,分别代表 VM状态,迁移状态,VFIO设备状态)

VFIO直通设备热迁移QEMU流程

VFIO设备迁移相关patchset

VFIO热迁移的历史可以追踪一下patch set:

内核态的驱动适配可以参考这个patch set:

未来演进

  • ARM架构特性演进,比如在iommu侧也实现mmu侧的相关特性,比如:FEAT_TLBIRANG减少TLBI次数,FEAT_BBM=2, MMU/IOMMU硬件标脏等。

参考

知道是不会有人点的,但万一有人呢:)