On Tue, Aug 11, 2020 at 11:40 AM Peter Xu <peterx@xxxxxxxxxx> wrote: > > index 206f52b36ffb..c88f773d03af 100644 > --- a/mm/huge_memory.c > +++ b/mm/huge_memory.c > @@ -1296,7 +1296,17 @@ vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf, pmd_t orig_pmd) > if (reuse_swap_page(page, NULL)) { > pmd_t entry; > entry = pmd_mkyoung(orig_pmd); > - entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma); > + entry = pmd_mkdirty(entry); > + if (pmd_uffd_wp(orig_pmd)) > + /* > + * This can happen when an uffd-wp protected page is > + * copied due to enfornced COW. When it happens, we > + * need to keep the uffd-wp bit even after COW, and > + * make sure write bit is kept cleared. > + */ > + entry = pmd_mkuffd_wp(pmd_wrprotect(entry)); > + else > + entry = maybe_pmd_mkwrite(entry, vma); > if (pmdp_set_access_flags(vma, haddr, vmf->pmd, entry, 1)) > update_mmu_cache_pmd(vma, vmf->address, vmf->pmd); > unlock_page(page); > diff --git a/mm/memory.c b/mm/memory.c > index c39a13b09602..b27b555a9df8 100644 > --- a/mm/memory.c > +++ b/mm/memory.c > @@ -2706,7 +2706,17 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf) > flush_cache_page(vma, vmf->address, pte_pfn(vmf->orig_pte)); > entry = mk_pte(new_page, vma->vm_page_prot); > entry = pte_sw_mkyoung(entry); > - entry = maybe_mkwrite(pte_mkdirty(entry), vma); > + entry = pte_mkdirty(entry); > + if (pte_uffd_wp(vmf->orig_pte)) > + /* > + * This can happen when an uffd-wp protected page is > + * copied due to enfornced COW. When it happens, we > + * need to keep the uffd-wp bit even after COW, and > + * make sure write bit is kept cleared. > + */ > + entry = pte_mkuffd_wp(pte_wrprotect(entry)); > + else > + entry = maybe_mkwrite(entry, vma); > /* > * Clear the pte entry and flush it first, before updating the > * pte with the new entry. This will avoid a race condition I think this needs to be cleaned up some way. I realize it's not an exact duplicate (pmd vs pte), but this code is illegible. Maybe just making it a helper inline function (well, two separate ones) with the comment above the function would resolve my "this is very ugly" concerns. > @@ -2900,7 +2910,13 @@ static vm_fault_t do_wp_page(struct vm_fault *vmf) > { > struct vm_area_struct *vma = vmf->vma; > > - if (userfaultfd_pte_wp(vma, *vmf->pte)) { > + /* > + * Userfaultfd-wp only cares about real writes. E.g., enforced COW for > + * read does not count. When that happens, we will do the COW with the > + * UFFD_WP bit inherited from the original PTE/PMD. > + */ > + if ((vmf->flags & FAULT_FLAG_WRITE) && > + userfaultfd_pte_wp(vma, *vmf->pte)) { > pte_unmap_unlock(vmf->pte, vmf->ptl); > return handle_userfault(vmf, VM_UFFD_WP); > } > @@ -4117,7 +4133,14 @@ static inline vm_fault_t create_huge_pmd(struct vm_fault *vmf) > static inline vm_fault_t wp_huge_pmd(struct vm_fault *vmf, pmd_t orig_pmd) > { > if (vma_is_anonymous(vmf->vma)) { > - if (userfaultfd_huge_pmd_wp(vmf->vma, orig_pmd)) > + /* > + * Userfaultfd-wp only cares about real writes. E.g., enforced > + * COW for read does not count. When that happens, we will do > + * the COW with the UFFD_WP bit inherited from the original > + * PTE/PMD. > + */ > + if ((vmf->flags & FAULT_FLAG_WRITE) && > + userfaultfd_huge_pmd_wp(vmf->vma, orig_pmd)) > return handle_userfault(vmf, VM_UFFD_WP); Here again the comment placement could be improved. Particularly in the do_wp_page() case, we have a big and somewhat complex function, and this duplicated boiler-plate makes me worry. Making it a helper function with a comment above would again I think make it more legible. And I think Jann is on the money wrt the follow_page_pte() issue. I think you broke COW break there entirely. That was one of the reasons I did just that "make it use FOLL_WRITE" originally, because it meant that we couldn't have any subtle places we'd missed. Now I wonder if there's any other case of FOLL_WRITE that is missing. Linus