unsigned int page_mask;
 };
 
+static inline void sanity_check_pinned_pages(struct page **pages,
+                                            unsigned long npages)
+{
+       if (!IS_ENABLED(CONFIG_DEBUG_VM))
+               return;
+
+       /*
+        * We only pin anonymous pages if they are exclusive. Once pinned, we
+        * can no longer turn them possibly shared and PageAnonExclusive() will
+        * stick around until the page is freed.
+        *
+        * We'd like to verify that our pinned anonymous pages are still mapped
+        * exclusively. The issue with anon THP is that we don't know how
+        * they are/were mapped when pinning them. However, for anon
+        * THP we can assume that either the given page (PTE-mapped THP) or
+        * the head page (PMD-mapped THP) should be PageAnonExclusive(). If
+        * neither is the case, there is certainly something wrong.
+        */
+       for (; npages; npages--, pages++) {
+               struct page *page = *pages;
+               struct folio *folio = page_folio(page);
+
+               if (!folio_test_anon(folio))
+                       continue;
+               if (!folio_test_large(folio) || folio_test_hugetlb(folio))
+                       VM_BUG_ON_PAGE(!PageAnonExclusive(&folio->page), page);
+               else
+                       /* Either a PTE-mapped or a PMD-mapped THP. */
+                       VM_BUG_ON_PAGE(!PageAnonExclusive(&folio->page) &&
+                                      !PageAnonExclusive(page), page);
+       }
+}
+
 /*
  * Return the folio with ref appropriately incremented,
  * or NULL if that failed.
  */
 void unpin_user_page(struct page *page)
 {
+       sanity_check_pinned_pages(&page, 1);
        gup_put_folio(page_folio(page), 1, FOLL_PIN);
 }
 EXPORT_SYMBOL(unpin_user_page);
                return;
        }
 
+       sanity_check_pinned_pages(pages, npages);
        for (i = 0; i < npages; i += nr) {
                folio = gup_folio_next(pages, npages, i, &nr);
                /*
 }
 EXPORT_SYMBOL(unpin_user_page_range_dirty_lock);
 
+static void unpin_user_pages_lockless(struct page **pages, unsigned long npages)
+{
+       unsigned long i;
+       struct folio *folio;
+       unsigned int nr;
+
+       /*
+        * Don't perform any sanity checks because we might have raced with
+        * fork() and some anonymous pages might now actually be shared --
+        * which is why we're unpinning after all.
+        */
+       for (i = 0; i < npages; i += nr) {
+               folio = gup_folio_next(pages, npages, i, &nr);
+               gup_put_folio(folio, nr, FOLL_PIN);
+       }
+}
+
 /**
  * unpin_user_pages() - release an array of gup-pinned pages.
  * @pages:  array of pages to be marked dirty and released.
        if (WARN_ON(IS_ERR_VALUE(npages)))
                return;
 
+       sanity_check_pinned_pages(pages, npages);
        for (i = 0; i < npages; i += nr) {
                folio = gup_folio_next(pages, npages, i, &nr);
                gup_put_folio(folio, nr, FOLL_PIN);
                page = ERR_PTR(-EMLINK);
                goto out;
        }
+
+       VM_BUG_ON_PAGE((flags & FOLL_PIN) && PageAnon(page) &&
+                      !PageAnonExclusive(page), page);
+
        /* try_grab_page() does nothing unless FOLL_GET or FOLL_PIN is set. */
        if (unlikely(!try_grab_page(page, flags))) {
                page = ERR_PTR(-ENOMEM);
         */
        if (gup_flags & FOLL_PIN) {
                if (read_seqcount_retry(¤t->mm->write_protect_seq, seq)) {
-                       unpin_user_pages(pages, nr_pinned);
+                       unpin_user_pages_lockless(pages, nr_pinned);
                        return 0;
+               } else {
+                       sanity_check_pinned_pages(pages, nr_pinned);
                }
        }
        return nr_pinned;