背景
DAMON 的物理地址空间监控(paddr)靠 access bit 采样工作:每个采样间隔先清掉采样页的 accessed bit,下一个间隔再读回来,重新置位就说明这期间被访问过。虚拟化宿主机上这套监控的用武之地本来最大:guest 内存通常由 hugetlbfs 的 hugetlb 页支撑,作者的生产宿主机上约 95% 的 RAM 是 1GiB hugetlb 页。但对这样的内存,paddr 监控一直给不出有用的访问信号,原因叠了两层。
访问信号在两层上断掉
┌────────────────────────────────────────┐
│ paddr 监控的访问检查从 damon_get_folio │
│ 开始,拒收非 LRU folio │
└───────────────────┬────────────────────┘
▼
┌────────────────────────────────────────┐
│ hugetlb folio 本就不上 LRU,全部报成 │
│ 从未访问,第一层断在不可见 │
└───────────────────┬────────────────────┘
▼
┌────────────────────────────────────────┐
│ 清 access bit 不带 TLB flush,1GiB │
│ 映射下 TLB 几乎不 refill,第二层断在 │
│ 信号失真 │
└───────────────────┬────────────────────┘
▼
┌────────────────────────────────────────┐
│ 观察值钉死在 TLB-refill 率,饱和 guest │
│ 与空闲 guest 无法区分 │
└────────────────────────────────────────┘
第一层是不可见:每次访问检查都从 damon_get_folio() 开始,而它拒收不在 LRU 链表上的 folio,hugetlb folio 按设计就不上 LRU,于是所有 hugetlb 内存被静默报成从未访问。
第二层是信号失真:就算 folio 可见,DAMON 为了低开销,清 access bit 时故意不带 TLB flush,缓存翻译命中的访问不走页表、不会重新置位;普通负载下 TLB 压力本身会频繁重走热页,这个取舍问题不大,但 1GiB 映射下几百 GiB 内存只占几百个 TLB 项,翻译几乎永不驱逐,观察到的访问率被钉死在 TLB-refill 率上,饱和的 guest 与空闲的 guest 看起来一样。
问题
- damon_get_folio() 拒收非 LRU folio,hugetlb 内存全被报成从未访问
- 清 access bit 不带 TLB flush,缓存翻译命中的访问不会重新置位
- 1GiB 映射下 TLB 几乎不 refill,访问率观察值钉死在 TLB-refill 率
- 1 TiB 主机实测:guest 内 842 GiB 随机访问一小时与空闲统计上不可区分
方案
让 hugetlb folio 对监控可见;信号真实度一层不在本系列,推迟另发。
可见性一层,folio 粒度的 rmap walker 学会处理 hugetlb folio:page_vma_mapped_walk() 对 hugetlb folio 返回的是 huge PTE,walker 原先按普通 PTE 粒度读写与通知,现在改为按整页大小 aging(清 young bit 走 set_huge_pte_at()),mmu_notifier 通知也覆盖整页。guest 侧的访问记录挂在 secondary MMU(KVM/EPT)的 accessed bit 上,通知覆盖整页才能让它浮出来。paddr 的两个监控原语(mkold 与 young 检查)换用新加的 damon_get_monitor_folio(),它额外接纳 hugetlb folio;空闲 hugetlb pool 里的 folio 引用计数为零,照旧被自然拒掉。DAMOS action 的施加方(reclaim、LRU 调整、迁移)仍用原来的 damon_get_folio():这些动作本来就作用不了 hugetlb folio,行为不变。注意监控粒度随之变为整页:1GiB 页里一个字节被碰,整页报 accessed。
信号真实度一层推迟:带 TLB flush 的 aging_flush 选项要等 workload 侧影响与工作集测量的量化数据补齐后再发。
修复 hugetlb 监控的可见性
┌──────────────────────────────────────────┐
│ rmap walker 学会按 huge PTE 粒度 aging, │
│ mmu_notifier 覆盖整页,EPT accessed │
│ bit 浮出 │
└────────────────────┬─────────────────────┘
▼
┌──────────────────────────────────────────┐
│ paddr 监控原语换用接纳 hugetlb 的取 │
│ 值,DAMOS action 保持 LRU 限定不变 │
└──────────────────────────────────────────┘
收益
Intel EPT 宿主机,944 GiB guest 跑 842 GiB masim 负载,估计访问率的空闲到负载对比:
| 配置 | 空闲到负载访问率(GB/s) | 分离度 |
|---|---|---|
| 未打补丁 | 0.08→0.21 | 无(guest 内存不可见) |
| 本系列(可见性修复) | 1.70→1.96 | 1.2 倍 |