背景
内核走页表的通用入口是 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 语义逐处核实并妥善处理,行为无变化