diff --git a/mm/memory.c b/mm/memory.c index a4d0f744a458..8d71a82462dd 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3462,8 +3462,21 @@ static vm_fault_t remove_device_exclusive_entry(struct vm_fault *vmf) struct vm_area_struct *vma = vmf->vma; struct mmu_notifier_range range; - if (!lock_page_or_retry(page, vma->vm_mm, vmf->flags)) + /* + * We need a reference to lock the page because we don't hold + * the PTL so a racing thread can remove the device-exclusive + * entry and unmap it. If the page is free the entry must + * have been removed already. If it happens to have already + * been re-allocated after being freed all we do is lock and + * unlock it. + */ + if (!get_page_unless_zero(page)) + return 0; + + if (!lock_page_or_retry(page, vma->vm_mm, vmf->flags)) { + put_page(page); return VM_FAULT_RETRY; + } mmu_notifier_range_init_owner(&range, MMU_NOTIFY_EXCLUSIVE, 0, vma, vma->vm_mm, vmf->address & PAGE_MASK, (vmf->address & PAGE_MASK) + PAGE_SIZE, NULL); @@ -3476,6 +3489,7 @@ static vm_fault_t remove_device_exclusive_entry(struct vm_fault *vmf) pte_unmap_unlock(vmf->pte, vmf->ptl); unlock_page(page); + put_page(page); mmu_notifier_invalidate_range_end(&range); return 0;