背景

内核走页表的通用入口是 walk_page_range(),它按地址范围遍历,每次遍历先用 find_vma() 把起始地址定位到 VMA。但有一类调用方早就知道自己的范围落在单个 VMA 里,这次定位纯属重复劳动。walk_page_range_vma() 是给这类调用方准备的入口:直接传 VMA,跳过查找。

find_vma() 对单 VMA 调用方属冗余
┌─────────────────────────────────────┐
│     调用方已知范围在单个 VMA 内     │
└──────────────────┬──────────────────┘
                   ▼
┌─────────────────────────────────────┐
│ walk_page_range() 仍每次 find_vma() │
│ 定位                                │
└──────────────────┬──────────────────┘
                   ▼
┌─────────────────────────────────────┐
│              冗余查找               │
└─────────────────────────────────────┘

问题

  • walk_page_range() 每次页表遍历都做一次 find_vma()
  • mincore、mprotect、mlock、migrate_device 四处调用方都已知单 VMA 范围,查找冗余

方案

把这四处改用 walk_page_range_vma()。

四处改用 walk_page_range_vma()
┌───────────────────────────────────────┐
│ mincore mprotect mlock migrate_device │
└───────────────────┬───────────────────┘
                    ▼
┌───────────────────────────────────────┐
│  调用方保证单 VMA,直传 VMA 不再查找  │
└───────────────────┬───────────────────┘
                    ▼
┌───────────────────────────────────────┐
│        PFNMAP 差异各自妥善处理        │
└───────────────────────────────────────┘

单 VMA 的保证各有出处:do_mincore() 的调用方传入的 start 与 end 总在同一 VMA 内;mprotect_fixup() 由 do_mprotect_pkey() 逐 VMA 迭代调用;mlock_vma_pages_range() 由 apply_vma_lock_flags() 逐 VMA 调用;migrate_vma_collect() 的范围由 migrate_vma_setup() 事先验证过。

有一处语义差异要交代:walk_page_range_vma() 不调 walk_page_test(),而 walk_page_test() 里对 VM_PFNMAP 的处理是经 pte_hole() 钩子跳过页表遍历。少了这一层,PFNMAP 的 PTE 会被当成普通项处理。四处分别处理:mincore 历史上把 PFNMAP 映射报为非驻留,do_mincore() 显式检查 VM_PFNMAP 并直接按未映射处理,行为不变;mprotect 的 prot_none_test() 恒返回 0,钩子直接删掉;mlock 与 migrate_device 的 VM_PFNMAP 早已分别被 vma_supports_mlock() 和 migrate_vma_setup() 过滤,无须额外处理。

收益

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

  • 四处页表遍历各省一次 find_vma() 查找
  • mprotect 顺带删掉恒为空的 prot_none_test() 钩子
  • PFNMAP 语义逐处核实并妥善处理,行为无变化