pte_marker_clear是handle_pte_marker()用于处理特定类型 PTE marker(主要是uffd-wp marker)的清除函数。它的逻辑非常简洁,但核心在于一个必须保证的竞态条件。
核心实现与逻辑
static vm_fault_t pte_marker_clear(struct vm_fault *vmf) { /* * 谨慎行事:我们只会将一个特殊的 uffd-wp PTE 恢复为 none PTE。 * 否则意味着 PTE 可能已经改变,因此需要重试。 */ if (pte_same(vmf->orig_pte, ptep_get(vmf->pte))) pte_clear(vmf->vma->vm_mm, vmf->address, vmf->pte); pte_unmap_unlock(vmf->pte, vmf->ptl); return 0; }关键设计点:pte_same而非is_pte_marker
这是该函数最核心的设计考量。早期实现使用is_pte_marker(*vmf->pte)来判断是否清除,但这不够安全。
考虑以下竞态场景:
缺页处理开始时,读取到的
orig_pte是一个PTE_MARKER_UFFD_WP。在获取 PTE 锁之前,另一个 CPU 将该 PTE 快速替换为
PTE_MARKER_SWAPIN_ERROR(例如发生了交换错误)。此时
is_pte_marker(*vmf->pte)仍然返回true,因为两者都是 marker 类型。如果直接清除,就会错误地丢弃交换错误 marker,导致后续访问无法正确处理。
修复方案是使用pte_same(vmf->orig_pte, ptep_get(vmf->pte))进行精确比对:
orig_pte是 uffd-wp marker:这是缺页处理路径进入pte_marker_clear的前提。当前 PTE 必须与
orig_pte完全相同:只有完全相同,才能确认它仍是那个 uffd-wp marker,而非在锁外被替换为其他类型的 marker。
如果 PTE 已经改变(pte_same返回false),函数不清除,直接解锁并返回。调用者会看到返回值0(成功),但由于 PTE 未被修改,后续访问会再次触发缺页,由新的缺页处理逻辑根据新的 PTE 类型做出正确决策。
调用上下文
pte_marker_clear由handle_pte_marker()调用,后者在缺页处理中检测到非存在 PTE 是 marker 时被触发:
} else if (softleaf_is_marker(entry)) { ret = handle_pte_marker(vmf); }handle_pte_marker()内部根据 marker 的具体类型分派:uffd-wp marker走pte_marker_clear路径(表示用户态处理程序已确认可以清除保护),而swapin error marker等其他类型走不同的处理逻辑。
返回值语义
函数始终返回0(VM_FAULT_NOPAGE的隐式等价),表示“缺页已处理”。即使 PTE 因竞态未被清除,也返回成功——因为调用者的职责只是“尝试清除”,真正的正确性由后续重试和pte_same检查保证。
历史演进
该函数在 2022 年 12 月由 Peter Xu 的补丁修复了上述竞态问题。在此之前,is_pte_marker()的宽松检查可能在某些罕见的快速状态切换场景下导致 marker 被错误丢弃。修复后,pte_same的精确比对成为标准做法,并一直沿用到现代内核。