快照协议
说明快照捕获的配置、客户机准备、捕获事务、状态单元顺序、提交点以及客户机与宿主屏障。
译自microsoft/nvx · doc/design/5.3-snapshot-protocol.md。技术细节以英文原文为准。
快照由客户机发起,并会停止源代次。它不是宿主侧的内存转储,也不是捕获后继续运行的操作。
5.3.1 捕获配置
快照目的地在启动时或通过管理 RPC 配置,并且必须尚不存在。当没有提供内存后备文件时,VMM 会在目的地旁创建基于文件的 RAM,从而无需复制即可发布内存。带沙箱块的捕获需要一个层级(tier);层级不一致、克隆或续跑策略与全新或配对暂存盘的组合都会被拒绝。绑定块的捕获需要标识这些块的存储代次(storage generation);参见快照身份。
5.3.2 客户机准备
在写入快照端口之前,客户机代理:
- 等待启动一致性检查完成,然后运行捕获检查。
- 保存并抑制 RCU 停滞检测,以及调试停滞检测器。
- 对于配对暂存盘,以有界等待冻结工作负载 cgroup、执行同步,并冻结已挂载的暂存文件系统;对于全新暂存盘,则要求暂存盘处于未挂载状态。
- 把捕获请求及其暂存盘策略写入快照端口。
已提交的捕获会终止源,因此该写入只有在捕获没有目的地、或在提交点之前失败时才会返回到源中。客户机代理通过恢复包状态位区分这些情况:在续跑的源中该位被清除,在被恢复的进程中该位被置位。在续跑的源中,它们移除各自的屏障并恢复检测器设置,若无法完成则将客户机关机。被拒绝的捕获同样会解冻每一个由客户机拥有的屏障,然后解除停滞检测抑制。
5.3.3 捕获事务
图 5.2 展示了从客户机请求到发布的捕获事务。
图 5.2。 捕获事务。
参与方:客户机(Guest)、快照端口(Snapshot port)、VM 工作线程(VM worker)、状态单元(State units)、控制器(Controller)、快照存储(Snapshot storage)。
事务步骤:
- 客户机 → 客户机:冻结工作负载、同步并冻结暂存盘(仅当使用配对的已挂载暂存盘)。
- 客户机 → 快照端口:写入快照请求与暂存盘策略。
- 快照端口 → VM 工作线程:有界的异步通知。
- VM 工作线程 → 状态单元:对宿主输入加门控。
- VM 工作线程 → VM 工作线程:在 I/O 边界处停止 vCPU。
- VM 工作线程 → 快照端口:完成端口写入。
- VM 工作线程 → 控制器:边界就绪。
- 控制器 → 控制器:对目的地与契约做预检。
- 分支:
- 没有目的地,或预检被拒绝:控制器 → VM 工作线程:释放边界;VM 工作线程 → 状态单元:恢复宿主输入;VM 工作线程 → 客户机:继续执行。
- 捕获被接受:控制器 → VM 工作线程:为快照静默;VM 工作线程 → 状态单元:按依赖顺序停止并保存;状态单元 → 控制器:状态、清单、时间契约和 CPU 配置文件;控制器 → 快照存储:复制状态、内存和可选暂存盘;控制器 → 快照存储:以无覆盖重命名发布;控制器 → VM 工作线程:终止已提交的源。
VMM 把捕获作为一次有界的事务来执行:
- 对宿主输入加门控,并推迟快照端口写入的完成。
- 在完成写入的同时于 I/O 边界处停止虚拟处理器,使保存下来的状态从输出之后的下一条指令开始。
- 对机器配置文件、目的地、后端、外部挂载和快照资格做预检;目的地缺失或预检被拒绝时会释放边界,让客户机继续执行。
- 按依赖顺序使其余设备工作线程和 VM 时间静默,其中包括有界的块队列排空。
- 建立时间契约:要求没有 LAPIC 定时器处于周期模式或 TSC 截止模式,且 PIT 通道 0 不在周期计数;取得捕获锚点和宿主身份;并记录声明的速率、CPU 配置文件、生效 CPUID 和代次计数器。
- 保存精确的状态单元清单、处理器状态、设备私有状态和内存,并从保存下来的 VM 时间状态推导出
Q。 - 把状态、内存和任何配对暂存盘复制并刷新到一个唯一的同级暂存目录。
- 用一次无覆盖重命名发布该目录。
- 发布之后终止源工作线程和进程。
第 5 步中的失败属于可安全回滚的捕获失败。推导 Q 永远不会导致捕获失败;当无法推导时,它会被省略并给出警告。
5.3.4 状态单元顺序
如图 5.3 所示,状态单元的转换遵循已注册的依赖关系,而不是一条全局串行列表。分区依赖于设备前置单元和 VM 时间。保存时的静默按依赖关系的逆序停止各单元,而恢复与回滚按依赖顺序启动它们。设备前置单元和 VM 时间可以并行转换。
图 5.3。 捕获、恢复与回滚期间的状态单元顺序。
分组一:捕获与保存(方向:自左向右)
- 对宿主输入加门控(Gate host input)
- 在 PMIO 边界处停止 vCPU(Stop vCPU at PMIO boundary)
- 停止分区状态单元(Stop partition state unit)
- 停止设备前置单元(Stop device prerequisite units)
- 停止 VM 时间(Stop VM time)
- 保存已停止的状态清单(Save stopped state inventory)
分组二:恢复或回滚启动(方向:自左向右)
- 恢复已停止的状态(Restore stopped state)
- 启动设备前置单元(Start device prerequisite units)
- 启动 VM 时间(Start VM time)
- 启动分区与 vCPU(Start partition and vCPU)
- 恢复宿主输入(Resume host input)
组内与跨组的转换关系(→ 表示无条件依赖,⇢ 表示带标注的虚线连接):
| 源节点 | 关系 | 目标节点 |
|---|---|---|
| 对宿主输入加门控 | → | 在 PMIO 边界处停止 vCPU |
| 在 PMIO 边界处停止 vCPU | → | 停止分区状态单元 |
| 停止分区状态单元 | → | 停止设备前置单元 |
| 停止设备前置单元 | → | 保存已停止的状态清单 |
| 停止分区状态单元 | → | 停止 VM 时间 |
| 停止 VM 时间 | → | 保存已停止的状态清单 |
| 恢复已停止的状态 | → | 启动设备前置单元 |
| 启动设备前置单元 | → | 启动分区与 vCPU |
| 恢复已停止的状态 | → | 启动 VM 时间 |
| 启动 VM 时间 | → | 启动分区与 vCPU |
| 启动分区与 vCPU | → | 恢复宿主输入 |
| 保存已停止的状态清单 | ⇢(已提交的快照) | 恢复已停止的状态 |
5.3.5 提交点
目录重命名就是提交点。在它之前,一次捕获失败不会暴露任何最终快照,而且只有在已知回滚有效时客户机才会恢复执行。在它之后,源永不恢复。这给了客户机一个可观察的边界:快照请求之后的代码在每个被恢复的进程中运行一次,而在成功捕获的源中永不运行。通过管理 RPC 驱动的捕获同样会在已提交的边界处停止源并终止其进程,因此客户端会观察到传输通道关闭。
建立边界本身也可能失败。如果宿主输入无法被门控,工作线程会回滚该门控并释放写入,让客户机继续执行;如果该回滚并不确定,工作线程就终止。一旦处理器停止已经开始,边界失败就是终止性的:宿主输入保持门控状态,VM 被拆除而不是恢复。恢复之后的确认边界遵循同样的规则。
5.3.6 客户机与宿主屏障
客户机屏障与宿主屏障各有分工。工作负载 cgroup 在被捕获的客户机状态中被冻结,VMM 对外部输入加门控,而带工作线程的客户机代理必须把它们停放在一个对捕获安全的位置上。这些机制彼此都不能替代。设备工作线程和客户机内核必须在带门控的恢复期间运行,这样处理器和内存才能被修复,因此门控并不意味着中断保持禁用。修复完成后,客户机代理先确认 VMM 门控,再解冻暂存盘,随后解冻工作负载。