mm/gup: reliable R/O long-term pinning in COW mappings
[sfrench/cifs-2.6.git] / include / linux / mm.h
index 686879dbb0bd1c1481e38fbd2436cb19e3f59d38..d8363ac34a7c2448ee8f223e7c73c58d0d944822 100644 (file)
@@ -3149,8 +3149,12 @@ static inline int vm_fault_to_errno(vm_fault_t vm_fault, int foll_flags)
  * Must be called with the (sub)page that's actually referenced via the
  * page table entry, which might not necessarily be the head page for a
  * PTE-mapped THP.
+ *
+ * If the vma is NULL, we're coming from the GUP-fast path and might have
+ * to fallback to the slow path just to lookup the vma.
  */
-static inline bool gup_must_unshare(unsigned int flags, struct page *page)
+static inline bool gup_must_unshare(struct vm_area_struct *vma,
+                                   unsigned int flags, struct page *page)
 {
        /*
         * FOLL_WRITE is implicitly handled correctly as the page table entry
@@ -3163,8 +3167,25 @@ static inline bool gup_must_unshare(unsigned int flags, struct page *page)
         * Note: PageAnon(page) is stable until the page is actually getting
         * freed.
         */
-       if (!PageAnon(page))
-               return false;
+       if (!PageAnon(page)) {
+               /*
+                * We only care about R/O long-term pining: R/O short-term
+                * pinning does not have the semantics to observe successive
+                * changes through the process page tables.
+                */
+               if (!(flags & FOLL_LONGTERM))
+                       return false;
+
+               /* We really need the vma ... */
+               if (!vma)
+                       return true;
+
+               /*
+                * ... because we only care about writable private ("COW")
+                * mappings where we have to break COW early.
+                */
+               return is_cow_mapping(vma->vm_flags);
+       }
 
        /* Paired with a memory barrier in page_try_share_anon_rmap(). */
        if (IS_ENABLED(CONFIG_HAVE_FAST_GUP))