合并受阻
变更摘要
本 PR 为 StratoVirt 虚拟机新增增量内存 checkpoint 能力,并引入独立的 conch-cow 常驻服务在恢复阶段通过 memfd 和 userfaultfd 按需填充 Guest 内存页面,避免每次 checkpoint 重复保存完整内存。同时保留 Cloud Hypervisor 原有全量内存流程,通过全局配置 sandbox.memory_mode(full/incremental/auto)控制模式选择,并在 OCI 镜像标注 io.conch.memory-format 注解以区分 full-v1 与 incremental-v1 格式。
主要改动
-
新增
conch-cow守护进程与 Unix Socket 协议层: 新增cmd/conch-cow/main.go入口和internal/cow包(含Server、Client、protocol.go、fdpass.go、probe.go、uffd.go),实现Capabilities/Attach/WaitAttachmentReady/Detach四个内部接口,通过SCM_RIGHTS传递 memfd、UFFD 和 pidfd 文件描述符;缺页时读取 manifest 对应 layer 页面后pwrite写入 memfd 并调用UFFDIO_WAKE,而非使用UFFDIO_COPY。 -
新增增量内存快照格式与 checkpoint 捕获: 新增
internal/memsnap包定义Manifest/BuildMap/BuildRange结构和 base/delta layer 的创建与原子写入;新增internal/sandbox/incremental_checkpoint.go(IncrementalCheckpointCapture),通过 QMP 查询 StratoVirt 的 dirty bitmap 或 page state,结合internal/vmm/memory_export.go从/proc/<pid>/mem按页导出,生成 delta layer 并更新BuildMap所有权。 -
新增内存模式决策与 OCI 格式标注: 新增
internal/memorymode包(Resolve函数),根据RequestedMode(full/incremental/auto)、cow 能力探测结果和 artifact 格式进行模式决策;在internal/image/types.go定义AnnotationMemoryFormat常量,BootIndexInfo和ResolvedBoot结构传播MemoryFormat字段,模式与 checkpoint 格式冲突时返回 HTTP 412。 -
StratoVirt 适配增量恢复与内存适配器接口: 在
internal/vmm/stratovirt/stratovirt.go新增incrementalRestoreScriptStratovirt模板(memory-backend-memfd+share=off+uffd_sock),新增 QMP 命令query-mem-mappings/query-mem-page-state/query-mem-dirty-bitmap/clear-mem-dirty-bitmap和CreateExternalMemorySnapshot;在internal/vmm/driver/types.go定义IncrementalMemoryAdapter接口和MemoryMapping/MemoryPageState/MemoryDirtyBitmap数据结构。 -
Sandbox 生命周期集成与 checkpoint poison 保护:
Manager.Create中集成prepareIncrementalMemory(cow Attach)和incrementalMemoryAttachment生命周期;增量 checkpoint 时通过checkpointPoisoned原子标记防止失败后重复 checkpoint,CompleteCheckpoint在 OCI 内容发布与 head 推进后清除标记;IncrementalCheckpointCapture保证 pause → poison → capture → clear dirty → resume 的事务顺序。


Capability探测并没有必须在conch-cow做的理由,建议放到conchd来做降低复杂度


同上,unknown不用单独区分,可以直接和unsupported一起导致报错退出


暂不实现auto,让用户显式指定,不支持就报错


这里的定义和image/types.go重复


这里为了处理socket被其他进程占用的边界情况引入过多复杂度,建议删除


当前PR是否有AI参与:
[x] 否
[ ] 是
__1. AI Agent 平台:
__2. AI 模型:
__3. Prompt上下文 :
参考:openEuler社区《生成式AI工具使用与开源贡献政策》
PR功能描述 / 为什么需要这个合入**:
背景
upstream/dev 中 StratoVirt checkpoint 仅支持全量内存:
暂停 VM
→ 捕获完整 StratoVirt state 和 memory
→ 恢复 VM
→ 发布独立的全量 memory component
恢复时,StratoVirt 直接映射完整内存文件。每次 checkpoint 都会重新保存全部 Guest 内存,无法复用上一代 checkpoint,也不支持按需加载页面。
本 PR 在保留原有全量流程的基础上,新增:
full 仍为默认模式,因此默认行为与 upstream/dev 保持一致。
———
用户配置变化
在 conchd 配置文件的 sandbox 段新增:
sandbox:
# full、incremental 或 auto,默认 full
memory_mode: full
约束如下:
三种模式
full-v1incremental-v1fullincrementalautofull恢复incremental恢复auto 的具体规则:
Cloud Hypervisor 始终使用 full 路径,不进入 StratoVirt 增量内存流程。
———
conch-cow 组件
组件职责
conch-cow 负责增量 checkpoint 的运行时内存恢复:
conch-cow 不管理 StratoVirt 进程,不持有 pidfd,也不负责停止 StratoVirt。
控制协议
conchd 通过本地 Unix Socket 调用以下接口:
Capabilitiessupported、unsupported或unknown,以及missing_features、probe_errorAttachsandbox_id、memory_snapshot_rootSCM_RIGHTS返回 memfdWaitAttachmentReadytoken、sandbox_idDetachtokentoken 用于标识一次 attachment,并关联后续的 WaitAttachmentReady 和 Detach。
能力探测
Capabilities 检查:
返回语义:
conchd 启动时只要收到合法的三种能力状态之一,就认为 COW 控制协议已经就绪。具体是否允许创建 incremental Sandbox,由内存模式决策处理。
memfd 与 UFFD 交接
恢复 incremental checkpoint 时:
conchd
→ Attach(sandbox_id, memory_snapshot_root)
→ conch-cow 创建 memfd
→ SCM_RIGHTS 将 memfd 传给 conchd
→ conchd 将 memfd 作为继承 FD 传给 StratoVirt
→ StratoVirt 注册 Guest 内存 UFFD
→ StratoVirt 通过一次性 UFFD Socket 将 userfaultfd 和 mappings 传给 conch-cow
→ conchd 调用 WaitAttachmentReady
→ UFFD 交接完成后恢复 VM
memfd 使用 share=off。本方案不注册或调用 UFFDIO_COPY。
Guest 缺页时:
UFFD page fault
→ 根据 fault HVA 找到对应 mapping
→ 换算为 Guest memory offset
→ 根据 manifest.build_map 找到最终 layer
→ 从 layer 读取页面
→ pwrite(memfd)
→ UFFDIO_WAKE
SCM_RIGHTS 会在接收进程中创建独立的 FD 引用。因此 COW Detach 只会关闭 COW 持有的 memfd,conchd 和 StratoVirt 必须分别管理各自的 FD。
如果 Sandbox 在 StratoVirt 接管 memfd 之前创建失败,conchd 会关闭自己收到的 memfd,再调用 COW Detach,完成整个 attachment 回滚。
———
checkpoint 内存格式
在 Boot Index 引用的 mem-snapshot descriptor annotation 中新增:
io.conch.memory-format=full-v1
io.conch.memory-format=incremental-v1
annotation 位于 Boot Index 的 memory component descriptor 上,不写入 memory 文件,也不重复写到每一个 OCI layer。
full-v1
包含完整 StratoVirt state 和 memory,可以独立恢复。
incremental-v1
memory component 内部包含:
Boot Index 仍只引用一个 mem-snapshot component。该 component 的 OCI manifest 保存完整 layer 列表。
Containerd 负责保存和解包不可变 OCI 内容;Conch 负责解释 manifest、维护 layer 所有权、构建运行时 memfd,并推进 checkpoint head。
本 PR 不兼容缺少 io.conch.memory-format 的旧 StratoVirt checkpoint。
———
full 模式完整流程
冷启动
Service.CreateSandbox
→ memorymode.Resolve(full)
→ Sandbox Manager
→ BootPreparer
→ StratoVirt 普通冷启动
full 业务路径不会调用 COW 的 Attach、WaitAttachmentReady 或 Detach。
与 upstream/dev 相比,仅新增创建前的模式解析;实际 VMM 冷启动流程保持不变。
生成 checkpoint A
暂停 VM
→ StratoVirt 生成完整 state 和 memory
→ 恢复 VM
→ 发布完整 mem-snapshot component
→ 标记 full-v1
→ 校验 Boot Index
→ 创建 checkpoint Template 并推进 checkpoint head
checkpoint A 可以独立恢复。
从 checkpoint A 恢复
检查 Boot Index
→ 识别 full-v1
→ 准备完整内存目录
→ StratoVirt 使用 mapped=true 恢复
→ ResumeVM
不创建 memfd,不建立 COW attachment,也不使用 UFFD。
恢复内存大小来自 checkpoint 中记录的 memory_size_mb,不会被创建请求中的 ram_mb 覆盖。
再次生成 checkpoint B
仍走完整全量捕获流程。B 包含独立的完整 state 和 memory,不依赖 A。
———
incremental 模式完整流程
冷启动
Service.CreateSandbox
→ memorymode.Resolve(incremental)
→ cow.Capabilities
→ Sandbox Manager
→ StratoVirt 普通冷启动
此时:
冷启动后的第一个 checkpoint:创建 base A
暂停 VM/mem 导出页面
→ checkpointPoisoned=true
→ 捕获外置 StratoVirt state
→ 查询 Guest memory mappings
→ 查询 page state
→ 从 /proc/
→ 创建稀疏 base layer
→ 写 manifest.json
→ 恢复 VM
→ 发布 incremental-v1 memory component
→ 校验 Boot Index
→ 创建 checkpoint Template 并推进 checkpoint head
→ checkpointPoisoned=false
因为冷启动实例没有父 manifest,第一个 checkpoint 必须建立能够覆盖完整 Guest 地址空间的逻辑 base。
这不代表必须物理写满整个内存文件:确定为零或未驻留的页面可以保持稀疏。
从 base A 恢复
containerd 解包 memory component
→ conchd 调用 COW Attach
→ COW 加载并固定 manifest/layers
→ COW 创建 memfd 和 UFFD Socket
→ memfd 通过 SCM_RIGHTS 返回 conchd
→ conchd 启动 StratoVirt
→ StratoVirt 将 UFFD 和 mappings 交给 COW
→ conchd 调用 WaitAttachmentReady
→ 恢复 VM
→ 缺页时由 COW 按需填充 memfd
Sandbox 删除时,conchd 调用 Detach 释放 attachment。
基于 A 生成 delta B
暂停 VM
→ checkpointPoisoned=true
→ 捕获外置 StratoVirt state
→ 查询 Guest memory mappings
→ 查询带 generation 的 dirty bitmap
→ 只导出本代脏页
→ 创建新的 delta layer 和 manifest
→ 清除本次读取的准确 dirty generation
→ 恢复 VM
→ 向父 memory component 追加一个 OCI layer
→ 校验 Boot Index
→ 原子创建 checkpoint Template 并推进 bbolt head
→ checkpointPoisoned=false
B 的 memory component 包含:
build_map 直接记录每段 Guest 内存最终应从哪个 layer 读取,因此恢复时不需要从 base 开始逐层重放。
checkpointPoisoned 只标记 Sandbox Manager 持有的运行时对象,不写入 bbolt,也不是用户可见的 Sandbox 状态。只有 OCI 发布、Boot Index 校验和 checkpoint head 推进全部成功后,才允许该运行实例再次
checkpoint。
———
conch-cow 生命周期与 systemd
本 PR 不新增 conch-cow.service。
systemd 只管理 conchd.service,conchd 根据配置启动:
/usr/bin/conch-cow --socket /run/conch/cow.sock
conchd 启动顺序:
启动 conch-cow
→ 轮询 Capabilities,等待控制协议就绪
→ 初始化其他 conchd 子系统
→ 向 systemd 发送 READY=1
正常关闭顺序:
停止 HTTP 服务
→ 删除全部 Sandbox,并执行 Detach
→ 向 conch-cow 发送 SIGTERM
→ Wait 回收 conch-cow
→ 关闭嵌入式 containerd
→ 关闭状态库
conchd.service 继续使用 KillMode=process。正常停止时 systemd 只向 conchd 主进程发送信号,StratoVirt 和 conch-cow 由 conchd 的正常清理流程处理。
本 PR不处理:
Makefile 新增:
make build-conch-cow
输出为 bin/conch-cow。
———
对外接口变化
CLI
CLI 命令及参数结构不变:
conch sandbox create ...
conch sandbox checkpoint
不增加 per-Sandbox memory mode 参数。
HTTP
创建和 checkpoint 请求结构不变:
POST /api/v1/sandboxes
POST /api/sandbox/checkpoint
新增行为:
本地 COW 协议
新增 conchd 与 conch-cow 之间的 Unix Socket 协议:
该协议不是对外 HTTP API。
OCI 格式
新发布的 StratoVirt checkpoint 必须携带:
io.conch.memory-format=full-v1
或:
io.conch.memory-format=incremental-v1
———
单元测试
新增或调整的测试覆盖以下内容。
配置与模式决策
COW 控制协议
能力探测和页面恢复
base、delta 和 OCI
Sandbox 与生命周期
该PR关联的issue
(格式为fixes #<issue号>, 或者resolves #<issue号>): fixes #
希望检视人员了解: