背景

管理 VM guest 内存的 VMM 需要识别工作集:热页留在内存,冷页回收或下沉到分层、远端存储。userfaultfd 的写保护(uffd-wp)能跟踪写访问,但读访问完全不可见,工作集识别缺了一半。

只看写访问的工作集识别缺一半
┌─────────────────────────────────────┐
│ VMM 识别 guest 内存工作集,冷页下沉 │
│ 热页留住                            │
└──────────────────┬──────────────────┘
                   ▼
┌─────────────────────────────────────┐
│  uffd-wp 只能跟踪写,读访问不可见   │
└──────────────────┬──────────────────┘
                   ▼
┌─────────────────────────────────────┐
│     以读为主的热页被误判为冷页      │
└─────────────────────────────────────┘

问题

  • uffd-wp 只能跟踪写访问,读访问对 VMM 不可见
  • 以读为主的热页被误判为冷页,可能被错误下沉
  • 同步 fault 处理要阻塞 faulting 线程,纯扫描场景代价过高

方案

给 userfaultfd 增加 RWP(读写保护)跟踪:把 guest 内存的 PTE 设成 PAGE_NONE 并打上 uffd 位,读与写都会触发 fault 送给 VMM。

RWP 让读与写都触发 fault
┌─────────────────────────────────────────────────┐
│ UFFDIO_RWPROTECT 把 PTE 设成 PAGE_NONE          │
│ 加 uffd 位                                      │
└────────────────────────┬────────────────────────┘
                         ▼
┌─────────────────────────────────────────────────┐
│ 读写都触发 fault,按 PTE、PMD、hugetlb          │
│ 分级投递消息                                    │
└────────────────────────┬────────────────────────┘
                         ▼
┌─────────────────────────────────────────────────┐
│ async 模式内核就地恢复,VMM 事后用 PAGEMAP_SCAN │
│ 重建访问情况                                    │
└─────────────────────────────────────────────────┘

UAPI 由 3 个部分组成:UFFDIO_REGISTER_MODE_RWP 注册区间,UFFDIO_RWPROTECT 在区间上安装或解除 RWP,UFFDIO_SET_MODE 在运行时切换同步与异步模式。RWP 与 WP 不能同挂一个区间,二者共用同一个 PTE 标记位;重复注册时若已有模式带 per-PTE 标记则返回 -EBUSY,免得标记悬空。

fault 按页表级别分级投递:PTE 级走 do_uffd_rwp(),PMD 级走 do_huge_pmd_uffd_rwp(),hugetlb 走 hugetlb_handle_userfault(),都向 VMM 送 UFFD_PAGEFAULT_FLAG_RWP 消息。uffd 位同时充当身份标记,把 RWP fault 与普通 mprotect(PROT_NONE)、NUMA hinting fault 区分开。

同步模式下 faulting 线程阻塞到 VMM 解决为止。纯扫描场景不需要消息内容,只需要知道哪些页被碰过,异步模式(UFFD_FEATURE_RWP_ASYNC)为此而生:内核就地恢复访问,线程不停顿,VMM 事后用 PAGEMAP_SCAN 重建访问情况,uffd 位还在的页就是上个周期没被再访问的。异步恢复时对可写私有匿名 PTE 顺手 pte_mkwrite() 升级,免得下次访问再吃一记 COW fault。UFFDIO_SET_MODE 让 VMM 免重新注册即可切换,例如扫描期用异步、驱逐期切同步拦截 fault。

mm 侧的铺垫包括:protnone helper 与 CONFIG_NUMA_BALANCING 解耦、uffd-wp 的 PTE accessor 改名归 uffd 公用、change_protection() 增加 MM_CP_UFFD_RWP、PTE 重写时保留 RWP 标记、khugepaged 与 rmap 和 GUP 识别 VM_UFFD_RWP。

收益

作者未提供性能数据,从代码逻辑推断的预期收益:

  • guest 内存的读与写都可跟踪,工作集识别覆盖完整,冷页下沉不再误伤只读热页
  • 异步模式让扫描期跟踪零阻塞,同步模式留给驱逐期精确拦截
  • RWP 与既有 WP、NUMA hinting 路径严格区分,互不误判