CoCo VM(TDX / SEV-SNP):同一段内存在 shared 与 private 间切换
shared
由 userspace_addr 映射提供
与宿主 DMA 共享数据
private
由 guest_memfd 提供
启动时可整片转 private
旧模型:两种属性对应两个独立后备
先 tear down
hole-punch + zap stage 2
再按新状态 populate
CoCo guest 每页重新 accept
zapping 不可逆,转换变成破坏性拆建
guest 只 accept 自己显式 fault 过的页,重建后还得再 fault 一次
转换发生在运行期,不是一次性的开机动作
旧路径留下的代价
shared / private 两个后备,转换必须 tear down 重建
stage 2 zapping 不可逆,CoCo 每页重新 accept
转换前的页可能仍被内核或 userspace 引用
已处于目标状态的范围也要挨 zap;整 DRAM 转换会误毁数据
转换路径的新开销不应波及未启用转换的 VM
五处代价,根因只有一个:转换被建模成拆建
一个后备:memslot 只认 guest_memfd
memslot
KVM_MEMSLOT_GMEM_ONLY
shared / private 共用
guest_memfd
唯一后备
缺页都从这里满足
shared 与 private 的缺页不再分属两个后备
失效只打这个 guest_memfd
PUNCH_HOLE 不会误 zap
VMA 里无关的 shared 映射
userspace_addr 仍保留
仅供 kvm_read_guest() 等访问
指错算用户错误,内核不校验
TDX / SEV 初始化接口:源页参数改为可选
原地转换不再需要单独的源页
增量转换:失败可定位,可重试
KVM_SET_MEMORY_ATTRIBUTES2
复用原编号;属性记录在 maple tree
失败写回 error_offset,返回 -EAGAIN
userspace 就地重试或处理,替代全有或全无回滚
已是目标属性 → 直接返回
ARM CCA 开机把整片 DRAM 转 private
没有 shared 页就整个跳过
混着一个 shared 页 → 仍要转换
zap 只落在 shared 映射
不能连 private 数据一起毁
失效 helper 改收映射类型 filter
shared 转 private 只 zap shared 映射,不再两边全打
转 private 前,页必须真的无人使用
直接转,写入会落进 private 页
内核或 userspace 仍可能引用
解除映射后逐 folio 查 refcount
refcount 要排除批缓存
新分配的 folio 可能挂在
per-CPU lru_add 批缓存
先排干两个批缓存
per-CPU lru_add fbatch
mlock fbatch
排干后再数引用
提前判出外部引用
DMA-pinned
仍被映射的 folio
发现就停,不再白排
扫范围时记住排干状态
避免逐 folio 重复排干;发现引用就停扫
经 error_offset 报告失败位置
排干后 refcount 仍高,才是真有外部引用
get_pfn 时清零,防止宿主内存泄漏
in-place 让已有内容进入 guest
guest_memfd 页内容会经平台 ioctl
populate 进 guest
不先清零 = 把宿主未初始化内存递出去
uptodate:一页一生清一次
清零挪进 __kvm_gmem_get_pfn()
populate 失败不重清
SNP firmware 用页内容回传 CPUID 期望值
kvm_arch_has_gmem_convert()
SEV 需要运行期钩子
TDX 的准备工作是纯开销
CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT=n
死代码消除把调用整体拿掉
未启用转换的 VM 零开销
只有支持转换的 VM 才走架构钩子
收益:作者未提供性能数据
以下为代码逻辑推断的预期收益
转换不再 tear down 与重建
stage 2 页表少拆少建,CoCo 重新 accept 次数减少
已是目标状态的范围与无关映射不再挨 zap
避免误毁数据与无谓的重新 accept
error_offset 精确上报
增量重试替代全有或全无的回滚
未启用 in-place 转换的 VM 不付转换路径开销
原地换属性,而不是拆掉重建
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x