remove_device_exclusive_entry是设备独占访问机制中的恢复函数。当 CPU 尝试访问一个被设备“独占”的页面时,硬件触发缺页,该函数负责解除设备的独占状态,并将页表项恢复为 CPU 可正常访问的形态。
核心实现
static vm_fault_t remove_device_exclusive_entry(struct vm_fault *vmf) { struct folio *folio = page_folio(vmf->page); struct vm_area_struct *vma = vmf->vma; struct mmu_notifier_range range; vm_fault_t ret; /* * 需要获取 folio 引用后才能锁定,因为此时未持有 PTL, * 并发的线程可能移除设备独占条目并解除映射。 */ if (!folio_try_get(folio)) return 0; ret = folio_lock_or_retry(folio, vmf); if (ret) { folio_put(folio); return ret; } mmu_notifier_range_init_owner(&range, MMU_NOTIFY_EXCLUSIVE, 0, vma->vm_mm, vmf->address & PAGE_MASK, (vmf->address & PAGE_MASK) + PAGE_SIZE, NULL); mmu_notifier_invalidate_range_start(&range); vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd, vmf->address, &vmf->ptl); if (likely(vmf->pte && pte_same(ptep_get(vmf->pte), vmf->orig_pte))) restore_exclusive_pte(vma, vmf->page, vmf->address, vmf->pte); if (vmf->pte) pte_unmap_unlock(vmf->pte, vmf->ptl); folio_unlock(folio); folio_put(folio); mmu_notifier_invalidate_range_end(&range); return 0; }关键设计点
1. 页面引用保护(2023 年修复)
函数开头的folio_try_get(folio)是 2023 年 3 月补丁引入的关键修复。在此之前的实现中,函数在未持有 PTL的情况下直接尝试锁定 folio,这存在竞态风险:
设备独占条目本身持有一个页面引用,保证在条目存在时
struct page有效。但多个线程并发触发同一地址的缺页时,一个线程可能先移除条目并释放引用,其他线程则可能在引用计数已归零的页面上等待锁。
虽然
mmap_lock阻止了munmap释放页面,但迁移操作仍可能释放它,导致PAGE_FLAGS_CHECK_AT_FREE警告。
folio_try_get确保在尝试锁定之前先获得一个有效引用,从而消除这个竞态窗口。
2. 锁与 PTE 重检
folio_lock_or_retry获取 folio 锁(可能阻塞),随后通过pte_offset_map_lock获取 PTE 锁。pte_same检查确保在等待锁期间 PTE 未被其他线程修改——如果已被修改(例如另一个线程已完成了恢复),则跳过restore_exclusive_pte调用。
3. MMU Notifier 通知
MMU_NOTIFY_EXCLUSIVE通知驱动设备即将失去独占访问权。驱动(如 GPU 驱动)据此更新其内部状态,撤销原子访问权限。通知在restore_exclusive_pte之前发送,确保驱动在 CPU 重新获得访问权之前完成清理。
4. 委托给restore_exclusive_pte
实际页表恢复工作由restore_exclusive_pte完成,它重建常规 PTE、恢复反向映射、处理内存锁定,并更新 MMU 缓存。
触发路径
设备独占条目通过SWP_DEVICE_EXCLUSIVE类型的交换项表示。当 CPU 线程访问一个被此类条目占用的地址时,缺页处理路径检测到is_device_exclusive_entry(entry),调用本函数恢复页面。
与 Fork 的交互
设备独占条目在fork()时需要特殊处理。copy_nonpresent_pte检测到设备独占条目后,会调用try_restore_exclusive_pte尝试恢复,因为设备独占 PTE 不能被复制到子进程——子进程不应继承设备的独占状态,逻辑上 fork 时设备独占应当终止。