云计算百科
云计算领域专业知识百科平台

memory vmf_can_call_fault

vmf_can_call_fault 是内核为每-VMA 锁(per-VMA lock)缺页处理引入的“守门人”函数,由 Matthew Wilcox 在 2023 年 10 月提交。它的职责是:在只持有 VMA 读锁的情况下,判断当前是否安全调用文件系统的 ->fault 回调;如果不安全,则主动释放 VMA 锁并返回 VM_FAULT_RETRY 要求调用者以 mmap_lock 重试。

核心实现

static inline vm_fault_t vmf_can_call_fault(const struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;

if (vma->vm_ops->map_pages || !(vmf->flags & FAULT_FLAG_VMA_LOCK))
return 0;
vma_end_read(vma);
return VM_FAULT_RETRY;
}

两项放行条件

函数返回 0 表示“可以继续”,有两种情况:

1. VMA 实现了 ->map_pages 回调

这是最关键的判定依据。Matthew Wilcox 在提交信息中解释:所有实现了 ->map_pages 的 vm_ops 最终都会调用 filemap_fault(),而 filemap_fault() 已经过审计,不依赖 mmap_lock。因此,只要 VMA 有 ->map_pages,就可以安全地在 VMA 读锁下调用其 ->fault 回调。

2. 当前缺页根本没有使用 VMA 锁

!(vmf->flags & FAULT_FLAG_VMA_LOCK) 意味着本次缺页是在传统的 mmap_lock 保护下进行的,自然不需要额外检查。

拒绝路径:释放 VMA 锁并重试

如果 VMA 没有 ->map_pages,且当前缺页持有 FAULT_FLAG_VMA_LOCK,函数会:

  • 调用 vma_end_read(vma) 释放 VMA 读锁。

  • 返回 VM_FAULT_RETRY。

  • 调用者收到 VM_FAULT_RETRY 后,会放弃当前缺页处理,重新以 mmap_lock 获取路径开始。这是因为那些没有 ->map_pages 的 ->fault 实现可能依赖 mmap_lock 来保证正确性,不能冒险在仅持有 VMA 锁的情况下调用它们。

    调用场景与引入背景

    该函数在以下路径中被调用,用于替换原先硬编码的 FAULT_FLAG_VMA_LOCK 检查:

    • do_read_fault:读缺页路径,在调用 __do_fault 之前检查。

    • do_cow_fault:COW 写缺页路径。

    • do_shared_fault:共享映射写缺页路径。

    • wp_pfn_shared 和 wp_page_shared:写时复制的共享映射路径,在释放 PTE 锁后调用。

    在 vmf_can_call_fault 引入之前,这些位置都重复着相同的模式:

    if (vmf->flags & FAULT_FLAG_VMA_LOCK) {
    vma_end_read(vmf->vma);
    return VM_FAULT_RETRY;
    }

    Matthew Wilcox 将其提取为独立函数,既消除了代码重复,又将“哪些 vm_ops 可以安全地在 VMA 锁下调用”这一知识集中到一处,便于未来扩展。注释中提到的“We could add a bitflag somewhere”暗示,未来可能会用显式标志替代对 ->map_pages 存在的依赖,但当前这一启发式规则已经足够。

    设计意义

    vmf_can_call_fault 是 per-VMA 锁缺页优化 的安全阀。它让内核能够在大多数文件映射缺页场景下避免获取昂贵的 mmap_lock,只在遇到未经审计的 ->fault 实现时才回退到全局锁。这一机制显著降低了高并发文件映射缺页场景下的锁争用。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » memory vmf_can_call_fault
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!