Commit a308c71b authored by Peter Xu's avatar Peter Xu Committed by Linus Torvalds
Browse files

mm/gup: Remove enfornced COW mechanism



With the more strict (but greatly simplified) page reuse logic in
do_wp_page(), we can safely go back to the world where cow is not
enforced with writes.

This essentially reverts commit 17839856 ("gup: document and work
around 'COW can break either way' issue").  There are some context
differences due to some changes later on around it:

  2170ecfa ("drm/i915: convert get_user_pages() --> pin_user_pages()", 2020-06-03)
  376a34ef ("mm/gup: refactor and de-duplicate gup_fast() code", 2020-06-03)

Some lines moved back and forth with those, but this revert patch should
have striped out and covered all the enforced cow bits anyways.

Suggested-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarPeter Xu <peterx@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1a0cf263
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -599,14 +599,6 @@ static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
				      GFP_KERNEL |
				      __GFP_NORETRY |
				      __GFP_NOWARN);
		/*
		 * Using __get_user_pages_fast() with a read-only
		 * access is questionable. A read-only page may be
		 * COW-broken, and then this might end up giving
		 * the wrong side of the COW..
		 *
		 * We may or may not care.
		 */
		if (pvec) {
			/* defer to worker if malloc fails */
			if (!i915_gem_object_is_readonly(obj))
+5 −35
Original line number Diff line number Diff line
@@ -381,22 +381,13 @@ static int follow_pfn_pte(struct vm_area_struct *vma, unsigned long address,
}

/*
 * FOLL_FORCE or a forced COW break can write even to unwritable pte's,
 * but only after we've gone through a COW cycle and they are dirty.
 * FOLL_FORCE can write to even unwritable pte's, but only
 * after we've gone through a COW cycle and they are dirty.
 */
static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
{
	return pte_write(pte) || ((flags & FOLL_COW) && pte_dirty(pte));
}

/*
 * A (separate) COW fault might break the page the other way and
 * get_user_pages() would return the page from what is now the wrong
 * VM. So we need to force a COW break at GUP time even for reads.
 */
static inline bool should_force_cow_break(struct vm_area_struct *vma, unsigned int flags)
{
	return is_cow_mapping(vma->vm_flags) && (flags & (FOLL_GET | FOLL_PIN));
	return pte_write(pte) ||
		((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));
}

static struct page *follow_page_pte(struct vm_area_struct *vma,
@@ -1075,11 +1066,9 @@ static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
				goto out;
			}
			if (is_vm_hugetlb_page(vma)) {
				if (should_force_cow_break(vma, foll_flags))
					foll_flags |= FOLL_WRITE;
				i = follow_hugetlb_page(mm, vma, pages, vmas,
						&start, &nr_pages, i,
						foll_flags, locked);
						gup_flags, locked);
				if (locked && *locked == 0) {
					/*
					 * We've got a VM_FAULT_RETRY
@@ -1093,10 +1082,6 @@ static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
				continue;
			}
		}

		if (should_force_cow_break(vma, foll_flags))
			foll_flags |= FOLL_WRITE;

retry:
		/*
		 * If we have a pending SIGKILL, don't keep faulting pages and
@@ -2763,19 +2748,6 @@ static int internal_get_user_pages_fast(unsigned long start, int nr_pages,
		return -EFAULT;

	/*
	 * The FAST_GUP case requires FOLL_WRITE even for pure reads,
	 * because get_user_pages() may need to cause an early COW in
	 * order to avoid confusing the normal COW routines. So only
	 * targets that are already writable are safe to do by just
	 * looking at the page tables.
	 *
	 * NOTE! With FOLL_FAST_ONLY we allow read-only gup_fast() here,
	 * because there is no slow path to fall back on. But you'd
	 * better be careful about possible COW pages - you'll get _a_
	 * COW page, but not necessarily the one you intended to get
	 * depending on what COW event happens after this. COW may break
	 * the page copy in a random direction.
	 *
	 * Disable interrupts. The nested form is used, in order to allow
	 * full, general purpose use of this routine.
	 *
@@ -2788,8 +2760,6 @@ static int internal_get_user_pages_fast(unsigned long start, int nr_pages,
	 */
	if (IS_ENABLED(CONFIG_HAVE_FAST_GUP) && gup_fast_permitted(start, end)) {
		unsigned long fast_flags = gup_flags;
		if (!(gup_flags & FOLL_FAST_ONLY))
			fast_flags |= FOLL_WRITE;

		local_irq_save(flags);
		gup_pgd_range(addr, end, fast_flags, pages, &nr_pinned);
+4 −3
Original line number Diff line number Diff line
@@ -1312,12 +1312,13 @@ vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf, pmd_t orig_pmd)
}

/*
 * FOLL_FORCE or a forced COW break can write even to unwritable pmd's,
 * but only after we've gone through a COW cycle and they are dirty.
 * FOLL_FORCE can write to even unwritable pmd's, but only
 * after we've gone through a COW cycle and they are dirty.
 */
static inline bool can_follow_write_pmd(pmd_t pmd, unsigned int flags)
{
	return pmd_write(pmd) || ((flags & FOLL_COW) && pmd_dirty(pmd));
	return pmd_write(pmd) ||
	       ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pmd_dirty(pmd));
}

struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,