postcopy

背景

precopy:precopy 策略在迁移开始后,在将内存状态拷贝到目的节点的同时,源物理主机上的虚拟机仍在继续运行。如果一个内存在拷贝之后再次被修改,那么将在后续迁移过程中再次发送到目的节点。整个内存拷贝是个迭代的过程,知道最后剩余的脏页内存数量可以在一个短时间内迁移到目的主机时,才停止源端虚拟机,迁移剩余内存和虚拟机状态,启动目的虚拟机;

postcopy:与precopy不同, postcopy首先把CPU状态和设备状态等复制到目的主机上,在目的主机上开启虚拟机, 然后源主机继续将剩余的内存页推送到目的主机上的虚拟机中. 与此同时, 目的主机上的虚拟机运行时可能会访问到不存在页面(还未被推送)而发生缺页异常, 虚拟机将向源主机发起请求传输相应的页,目的端在获取到相关页后继续pagefault的后续处理. 可以看到postcopy方式对每个内存页面只会传送一次, 降低了预拷贝中冗余拷贝的开销。

postcopy-after-precopy:根据以上介绍, 我们知道 precopy适用于内存read-intensive的虚拟机热迁移, 对于write-intensive的虚拟机, 在Pre-Copy的每轮迭代中都会有大量新的内存页被修改, 导致迁移的效率低下(当然也可以通过某种手段来控制脏页产生的速度,比如控制vcpu执行的时间). 对于 postcopy则相反, 对于read-intensive的虚拟机, postcopy将会产生大量的缺页异常, 导致虚拟机内应用性能的降低. postcopy更适用于内存比较大, 内存write-intensive的虚拟机热迁移。为了使热迁移更具有一般性, 通用性, 一种自然的想法是将 precopy和 postcopy结合起来.具体过程如下:

  1. 采用Pre-Copy进行初次迭代, 将虚拟机的全部内存页拷贝到目的主机, 与此同时, 虚拟机在源主机上继续运行;
  2. 当第一轮拷贝结束后, 虚拟机被暂停, 其处理器状态和设备状态信息等拷贝到目的主机;
  3. 在目的主机上恢复虚拟机, 开始使用Post-Copy模式继续虚拟机迁移。

Postcopy 内部机制

状态机

Postcopy 迁移经历一系列状态(参见 postcopy_state):ADVISE -> DISCARD -> LISTEN -> RUNNING -> END

Advise

在迁移开始时,如果启用了 postcopy,则该状态被设置,即使未收到开始命令。在此阶段,目标检查其操作系统是否具有支持 postcopy 的必要条件,并执行设置以确保 RAM 映射适合后续的 postcopy。如果所需的操作系统支持不存在,目标将在此点提前失败(由接收 POSTCOPY_ADVISE 命令触发)。

Discard

在接收到第一个“discard”命令时进入该状态;在执行第一个丢弃之前,停用大页面(使用 madvise),以确保在 postcopy 阶段不会创建新的大页面,同时导致有丢弃请求的大页面被分解。

Listen

包中的第一条命令 POSTCOPY_LISTEN 将目标状态切换为 Listen,并启动一个新线程(“监听线程”),该线程负责接收来自迁移流的页面,而主线程继续处理数据 blob。通过这个线程,目标能够处理页面接收,同时基于“敏感化”的 RAM 可以检测对缺失页面的任何访问(在 Linux 中使用 “userfault” 系统)。

Running

POSTCOPY_RUN 会使目标同步所有状态,并启动 CPU 和 I/O 设备。主线程现在完成处理迁移包,并将继续执行正常的 precopy 迁移(尽管它无法执行正常迁移结束时的清理)。

End

监听线程现在可以退出,并执行迁移状态的清理,迁移现在完成。

设备传输

加载设备数据可能导致仿真设备访问 guestRAM,这可能触发必须由源来解决的故障。因此,迁移流必须能够在设备加载期间响应页面数据请求,因此设备数据必须在设备加载开始之前从流中完全读取,以释放流。这是通过将设备数据“打包”成一个一次性读取的 blob 来实现的。

源行为

在进入 postcopy 之前,迁移流与正常的 precopy 相同,除了在开始时增加了一个“postcopy advise”命令,告诉目标可能会发生 postcopy。当 postcopy 开始时,源会发送页面丢弃(discard) 数据,然后形成包含的“包”:

  • 命令:postcopy listen
  • 设备状态
  • 一系列与 precopy 流中的设备状态流相同的部分,包含除可 postcopy 设备(即 RAM)之外的所有内容。
  • 命令:postcopy run

该“包”作为 CMD_PACKAGED 的数据部分发送,内容格式与主迁移流相同。

在 postcopy 期间,源扫描脏页面列表并将其发送到目标,而不必请求(与 precopy 相似),然而当接收到来自目标的页面请求时,脏页面扫描将从请求的位置重新开始。这导致请求页面迅速发出,也导致请求页面后面的页面迅速发出,因为这些页面很可能会被目标尽快使用。

目标行为

最初,目标的行为与 precopy 相同,只有一个线程在读取迁移流;处理“postcopy advise”和“discard”命令以改变 RAM 的管理方式,但不会影响流处理。

------------------------------------------------------------------------------
                        1      2   3     4 5                      6   7
main -----DISCARD-CMD_PACKAGED ( LISTEN  DEVICE     DEVICE DEVICE RUN )
thread                             |       |
                                   |     (页面请求)
                                   |        \___
                                   v            \
listen thread:                     --- 页面 -- 页面 -- 页面 -- 页面 -- 页面 --

                                   a   b        c
------------------------------------------------------------------------------

在接收到 CMD_PACKAGED (1) 后

与该包相关的所有数据 - 图中的 ( … ) 部分 - 被读取到内存中,主线程递归进入 `qemu_loadvm_state_main` 来处理包的内容 (2),其中包含命令 (3,6) 和设备 (4…)

在接收到‘postcopy listen’ - 3 -(即包中的第一条命令)时

启动一个新线程 (a),接管服务迁移流,而主线程继续加载包。它加载正常的后台页面数据 (b),但如果在加载设备期间发生故障 (5),返回的页面 (c) 将由监听线程加载,允许主线程的设备加载继续。

在 CMD_PACKAGED 的最后一部分是一个‘RUN’命令 (6)

让目标 CPU 开始运行。在 CMD_PACKAGED 的末尾 (7),主线程返回正常运行行为,并不再被迁移使用,而监听线程继续服务页面数据,直到迁移结束。

源方页面位图

Postcopy 中的“迁移位图”基本与 precopy 中相同,每个位表示该页面为“脏” - 即需要发送。在 precopy 阶段,当 CPU 修改页面时,这都会被更新;然而在 postcopy 期间,CPU 会被停止,并且不应该再有任何修改。相反,当相关页面在 postcopy 期间发送时,脏位将被清除。

Postcopy 特性

Postcopy 恢复

与 precopy 相比,postcopy 在错误处理方面具有特殊性。当发生任何错误时(在这种情况下,主要是网络错误),QEMU 难以轻易地失败迁移,因为虚拟机数据同时存在于源和目标的 QEMU 实例中。另一方面,当问题发生时,两边的 QEMU 将进入暂停状态。需要一个恢复阶段来继续暂停的 postcopy 迁移。

恢复阶段通常包含以下几个步骤:

- 当网络问题发生时,两个 QEMU 将进入 `POSTCOPY_PAUSED` 迁移状态。

- 当网络恢复(或提供新的网络)后,管理员可以使用 QMP 命令 `migrate-recover` 在目标节点设置新的迁移通道,以准备恢复。

- 在源主机上,管理员可以使用 QMP 命令 `migrate` 继续中断的 postcopy 迁移,并设置 `resume=true` 标记。源 QEMU 将进入 `POSTCOPY_RECOVER_SETUP` 状态,尝试重新建立通道。

- 当两边的 QEMU 成功重连使用新的或修复后的通道时,它们将进入 `POSTCOPY_RECOVER` 状态,此时需要一些握手程序,确保在两个 QEMU 之间正确同步虚拟机状态,以继续 postcopy 迁移。例如,可能会在网络中断期间发送页面,因此握手将保证在途丢失的页面会被再次发送。

在适当的握手同步之后,QEMU 将在两边继续 postcopy 迁移并返回到 `POSTCOPY_ACTIVE` 状态。postcopy 迁移将继续。

在暂停的 postcopy 迁移期间,虚拟机逻辑上仍然可以继续运行,对已经迁移到目标虚拟机的页面的访问不会受到影响。然而,如果任何缺失的页面在目标虚拟机上被访问,虚拟机线程将被阻止,等待页面迁移,这意味着它可以被阻止,直到恢复完成。

访问缺失页面的影响可能会与 guest 的不同配置相关。例如,在启用异步页面故障时,逻辑上guest可以主动调度访问缺失页面的线程。

Postcopy 与巨页

Postcopy 现在可以与 hugetlbfs 支持的内存一起工作:

- 目标的 Linux 内核必须支持对巨页的 userfault。

- 源和目标虚拟机的巨页配置必须相同;即两边的 RAMBlock 必须使用相同的页面大小。

请注意:`-mem-path /dev/hugepages` 如果没有足够的巨页,则将退回为分配普通 RAM,这将导致触发 (b) 失败。使用 `-mem-prealloc` 强制使用巨页分配。

使用的巨页的大小需要谨慎;2MB 巨页的 postcopy 运行良好,但是 1GB 巨页可能会有问题,因为通过 10Gbps 链路转移 1GB 巨页大约需要 1 秒,且在整个页面传输完成之前,目标线程被阻塞。

Postcopy 与共享内存

带有共享内存的 postcopy 迁移需要其他共享内存进程和 QEMU 的明确支持。对于 userfault 可以支持的内存类型有限制。

Linux 内核的 userfault 支持工作在 `/dev/shm` 内存和 hugetlbfs 上(虽然内核没有为 hugetlbfs 提供相当于 `madvise(MADV_DONTNEED)` 的功能,这在某些配置中可能会变成问题)。

QEMU 中的 vhost-user 代码支持具有 postcopy 支持的客户端,并且在 `vhost-user-bridge`(在测试中)和 DPDK 包中进行了改进以支持 postcopy。

客户端需要打开一个 userfaultfd 并注册其映射的内存区域。然后,客户端必须将 userfaultfd 传递回 QEMU,连同一个映射表,以便在客户端地址空间中的故障地址转换回 RAMBlock/偏移量。客户端的 userfaultfd 被加入到 postcopy 故障线程中,QEMU 将代表客户端发起页面请求。当页面到达后,QEMU 会在客户端的 userfaultfd 上执行“唤醒”操作,以使其继续处理。

注意

未来有两个改进是希望实现的:

- 一种使 QEMU 忽略客户端地址空间中地址的方式;

- 避免 QEMU 在页面到达后执行 ufd-wake 调用的需要。

为了现有客户端进行 postcopy 的 retrofitting 是可行的:

需要有一种机制与 userfault 进行注册,如上所述,并且该注册需要与 postcopy 的各个阶段协调。在 vhost-user 中为现有控制通道添加了额外的消息。

必须识别可能因guest内存访问而被阻塞的任何线程,并理解其含义;例如,如果在持有锁时访问 guest 内存,则等待该锁的所有其他线程也将被阻塞。

Postcopy 抢占模式

Postcopy 抢占是一项在 8.0 QEMU 发布版中引入的新功能,它允许紧急页面(那些被目标 QEMU 明确请求的页面故障)通过单独的抢占通道发送,而不是排队在后台迁移通道。任何关心 postcopy 迁移期间页面故障延迟的人都应启用此功能。默认情况下,此功能是禁用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值