mmap locking API: convert mmap_sem API comments
[linux-2.6-block.git] / mm / gup.c
index e19ff770eb4c87a0a386da9ed62d4c832c779496..bbb8851f4656eafaff5417bf6ede7f74a5cf01da 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -19,7 +19,6 @@
 #include <linux/sched/mm.h>
 
 #include <asm/mmu_context.h>
-#include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 
 #include "internal.h"
@@ -1250,7 +1249,7 @@ retry:
        }
 
        if (ret & VM_FAULT_RETRY) {
-               down_read(&mm->mmap_sem);
+               mmap_read_lock(mm);
                *unlocked = true;
                fault_flags |= FAULT_FLAG_TRIED;
                goto retry;
@@ -1355,7 +1354,7 @@ retry:
                        break;
                }
 
-               ret = down_read_killable(&mm->mmap_sem);
+               ret = mmap_read_lock_killable(mm);
                if (ret) {
                        BUG_ON(ret > 0);
                        if (!pages_done)
@@ -1390,7 +1389,7 @@ retry:
                 * We must let the caller know we temporarily dropped the lock
                 * and so the critical section protected by it was lost.
                 */
-               up_read(&mm->mmap_sem);
+               mmap_read_unlock(mm);
                *locked = 0;
        }
        return pages_done;
@@ -1426,7 +1425,7 @@ long populate_vma_page_range(struct vm_area_struct *vma,
        VM_BUG_ON(end   & ~PAGE_MASK);
        VM_BUG_ON_VMA(start < vma->vm_start, vma);
        VM_BUG_ON_VMA(end   > vma->vm_end, vma);
-       VM_BUG_ON_MM(!rwsem_is_locked(&mm->mmap_sem), mm);
+       mmap_assert_locked(mm);
 
        gup_flags = FOLL_TOUCH | FOLL_POPULATE | FOLL_MLOCK;
        if (vma->vm_flags & VM_LOCKONFAULT)
@@ -1478,7 +1477,7 @@ int __mm_populate(unsigned long start, unsigned long len, int ignore_errors)
                 */
                if (!locked) {
                        locked = 1;
-                       down_read(&mm->mmap_sem);
+                       mmap_read_lock(mm);
                        vma = find_vma(mm, nstart);
                } else if (nstart >= vma->vm_end)
                        vma = vma->vm_next;
@@ -1510,7 +1509,7 @@ int __mm_populate(unsigned long start, unsigned long len, int ignore_errors)
                ret = 0;
        }
        if (locked)
-               up_read(&mm->mmap_sem);
+               mmap_read_unlock(mm);
        return ret;     /* 0 or negative error code */
 }
 
@@ -1994,19 +1993,19 @@ EXPORT_SYMBOL(get_user_pages);
 /**
  * get_user_pages_locked() is suitable to replace the form:
  *
- *      down_read(&mm->mmap_sem);
+ *      mmap_read_lock(mm);
  *      do_something()
  *      get_user_pages(tsk, mm, ..., pages, NULL);
- *      up_read(&mm->mmap_sem);
+ *      mmap_read_unlock(mm);
  *
  *  to:
  *
  *      int locked = 1;
- *      down_read(&mm->mmap_sem);
+ *      mmap_read_lock(mm);
  *      do_something()
  *      get_user_pages_locked(tsk, mm, ..., pages, &locked);
  *      if (locked)
- *          up_read(&mm->mmap_sem);
+ *          mmap_read_unlock(mm);
  *
  * @start:      starting user address
  * @nr_pages:   number of pages from start to pin
@@ -2035,6 +2034,12 @@ long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
         */
        if (WARN_ON_ONCE(gup_flags & FOLL_LONGTERM))
                return -EINVAL;
+       /*
+        * FOLL_PIN must only be set internally by the pin_user_pages*() APIs,
+        * never directly by the caller, so enforce that:
+        */
+       if (WARN_ON_ONCE(gup_flags & FOLL_PIN))
+               return -EINVAL;
 
        return __get_user_pages_locked(current, current->mm, start, nr_pages,
                                       pages, NULL, locked,
@@ -2045,9 +2050,9 @@ EXPORT_SYMBOL(get_user_pages_locked);
 /*
  * get_user_pages_unlocked() is suitable to replace the form:
  *
- *      down_read(&mm->mmap_sem);
+ *      mmap_read_lock(mm);
  *      get_user_pages(tsk, mm, ..., pages, NULL);
- *      up_read(&mm->mmap_sem);
+ *      mmap_read_unlock(mm);
  *
  *  with:
  *
@@ -2073,11 +2078,11 @@ long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
        if (WARN_ON_ONCE(gup_flags & FOLL_LONGTERM))
                return -EINVAL;
 
-       down_read(&mm->mmap_sem);
+       mmap_read_lock(mm);
        ret = __get_user_pages_locked(current, mm, start, nr_pages, pages, NULL,
                                      &locked, gup_flags | FOLL_TOUCH);
        if (locked)
-               up_read(&mm->mmap_sem);
+               mmap_read_unlock(mm);
        return ret;
 }
 EXPORT_SYMBOL(get_user_pages_unlocked);
@@ -2294,7 +2299,7 @@ pte_unmap:
  * to be special.
  *
  * For a futex to be placed on a THP tail page, get_futex_key requires a
- * __get_user_pages_fast implementation that can pin pages. Thus it's still
+ * get_user_pages_fast_only implementation that can pin pages. Thus it's still
  * useful to have gup_huge_pmd even if we can't operate on ptes.
  */
 static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
@@ -2699,7 +2704,7 @@ static inline void gup_pgd_range(unsigned long addr, unsigned long end,
 
 #ifndef gup_fast_permitted
 /*
- * Check if it's allowed to use __get_user_pages_fast() for the range, or
+ * Check if it's allowed to use get_user_pages_fast_only() for the range, or
  * we need to fall back to the slow version:
  */
 static bool gup_fast_permitted(unsigned long start, unsigned long end)
@@ -2718,11 +2723,11 @@ static int __gup_longterm_unlocked(unsigned long start, int nr_pages,
         * get_user_pages_unlocked() (see comments in that function)
         */
        if (gup_flags & FOLL_LONGTERM) {
-               down_read(&current->mm->mmap_sem);
+               mmap_read_lock(current->mm);
                ret = __gup_longterm_locked(current, current->mm,
                                            start, nr_pages,
                                            pages, NULL, gup_flags);
-               up_read(&current->mm->mmap_sem);
+               mmap_read_unlock(current->mm);
        } else {
                ret = get_user_pages_unlocked(start, nr_pages,
                                              pages, gup_flags);
@@ -2745,7 +2750,7 @@ static int internal_get_user_pages_fast(unsigned long start, int nr_pages,
                return -EINVAL;
 
        if (!(gup_flags & FOLL_FAST_ONLY))
-               might_lock_read(&current->mm->mmap_sem);
+               might_lock_read(&current->mm->mmap_lock);
 
        start = untagged_addr(start) & PAGE_MASK;
        addr = start;
@@ -2811,8 +2816,14 @@ static int internal_get_user_pages_fast(unsigned long start, int nr_pages,
 
        return ret;
 }
-
-/*
+/**
+ * get_user_pages_fast_only() - pin user pages in memory
+ * @start:      starting user address
+ * @nr_pages:   number of pages from start to pin
+ * @gup_flags:  flags modifying pin behaviour
+ * @pages:      array that receives pointers to the pages pinned.
+ *              Should be at least nr_pages long.
+ *
  * Like get_user_pages_fast() except it's IRQ-safe in that it won't fall back to
  * the regular GUP.
  * Note a difference with get_user_pages_fast: this always returns the
@@ -2825,8 +2836,8 @@ static int internal_get_user_pages_fast(unsigned long start, int nr_pages,
  * access can get ambiguous page results. If you call this function without
  * 'write' set, you'd better be sure that you're ok with that ambiguity.
  */
-int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
-                         struct page **pages)
+int get_user_pages_fast_only(unsigned long start, int nr_pages,
+                            unsigned int gup_flags, struct page **pages)
 {
        int nr_pinned;
        /*
@@ -2836,10 +2847,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
         * FOLL_FAST_ONLY is required in order to match the API description of
         * this routine: no fall back to regular ("slow") GUP.
         */
-       unsigned int gup_flags = FOLL_GET | FOLL_FAST_ONLY;
-
-       if (write)
-               gup_flags |= FOLL_WRITE;
+       gup_flags |= FOLL_GET | FOLL_FAST_ONLY;
 
        nr_pinned = internal_get_user_pages_fast(start, nr_pages, gup_flags,
                                                 pages);
@@ -2855,7 +2863,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
 
        return nr_pinned;
 }
-EXPORT_SYMBOL_GPL(__get_user_pages_fast);
+EXPORT_SYMBOL_GPL(get_user_pages_fast_only);
 
 /**
  * get_user_pages_fast() - pin user pages in memory
@@ -2909,9 +2917,6 @@ EXPORT_SYMBOL_GPL(get_user_pages_fast);
  *
  * FOLL_PIN means that the pages must be released via unpin_user_page(). Please
  * see Documentation/core-api/pin_user_pages.rst for further details.
- *
- * This is intended for Case 1 (DIO) in Documentation/core-api/pin_user_pages.rst. It
- * is NOT intended for Case 2 (RDMA: long-term pins).
  */
 int pin_user_pages_fast(unsigned long start, int nr_pages,
                        unsigned int gup_flags, struct page **pages)
@@ -2926,8 +2931,8 @@ int pin_user_pages_fast(unsigned long start, int nr_pages,
 EXPORT_SYMBOL_GPL(pin_user_pages_fast);
 
 /*
- * This is the FOLL_PIN equivalent of __get_user_pages_fast(). Behavior is the
- * same, except that this one sets FOLL_PIN instead of FOLL_GET.
+ * This is the FOLL_PIN equivalent of get_user_pages_fast_only(). Behavior
+ * is the same, except that this one sets FOLL_PIN instead of FOLL_GET.
  *
  * The API rules are the same, too: no negative values may be returned.
  */
@@ -2985,9 +2990,6 @@ EXPORT_SYMBOL_GPL(pin_user_pages_fast_only);
  *
  * FOLL_PIN means that the pages must be released via unpin_user_page(). Please
  * see Documentation/core-api/pin_user_pages.rst for details.
- *
- * This is intended for Case 1 (DIO) in Documentation/core-api/pin_user_pages.rst. It
- * is NOT intended for Case 2 (RDMA: long-term pins).
  */
 long pin_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
                           unsigned long start, unsigned long nr_pages,
@@ -3021,9 +3023,6 @@ EXPORT_SYMBOL(pin_user_pages_remote);
  *
  * FOLL_PIN means that the pages must be released via unpin_user_page(). Please
  * see Documentation/core-api/pin_user_pages.rst for details.
- *
- * This is intended for Case 1 (DIO) in Documentation/core-api/pin_user_pages.rst. It
- * is NOT intended for Case 2 (RDMA: long-term pins).
  */
 long pin_user_pages(unsigned long start, unsigned long nr_pages,
                    unsigned int gup_flags, struct page **pages,
@@ -3055,3 +3054,32 @@ long pin_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
        return get_user_pages_unlocked(start, nr_pages, pages, gup_flags);
 }
 EXPORT_SYMBOL(pin_user_pages_unlocked);
+
+/*
+ * pin_user_pages_locked() is the FOLL_PIN variant of get_user_pages_locked().
+ * Behavior is the same, except that this one sets FOLL_PIN and rejects
+ * FOLL_GET.
+ */
+long pin_user_pages_locked(unsigned long start, unsigned long nr_pages,
+                          unsigned int gup_flags, struct page **pages,
+                          int *locked)
+{
+       /*
+        * FIXME: Current FOLL_LONGTERM behavior is incompatible with
+        * FAULT_FLAG_ALLOW_RETRY because of the FS DAX check requirement on
+        * vmas.  As there are no users of this flag in this call we simply
+        * disallow this option for now.
+        */
+       if (WARN_ON_ONCE(gup_flags & FOLL_LONGTERM))
+               return -EINVAL;
+
+       /* FOLL_GET and FOLL_PIN are mutually exclusive. */
+       if (WARN_ON_ONCE(gup_flags & FOLL_GET))
+               return -EINVAL;
+
+       gup_flags |= FOLL_PIN;
+       return __get_user_pages_locked(current, current->mm, start, nr_pages,
+                                      pages, NULL, locked,
+                                      gup_flags | FOLL_TOUCH);
+}
+EXPORT_SYMBOL(pin_user_pages_locked);