+{
+ struct address_space *mapping;
+
+ /*
+ * GUP-fast disables IRQs - this prevents IPIs from causing page tables
+ * to disappear from under us, as well as preventing RCU grace periods
+ * from making progress (i.e. implying rcu_read_lock()).
+ *
+ * This means we can rely on the folio remaining stable for all
+ * architectures, both those that set CONFIG_MMU_GATHER_RCU_TABLE_FREE
+ * and those that do not.
+ *
+ * We get the added benefit that given inodes, and thus address_space,
+ * objects are RCU freed, we can rely on the mapping remaining stable
+ * here with no risk of a truncation or similar race.
+ */
+ lockdep_assert_irqs_disabled();
+
+ /*
+ * If no mapping can be found, this implies an anonymous or otherwise
+ * non-file backed folio so in this instance we permit the pin.
+ *
+ * shmem and hugetlb mappings do not require dirty-tracking so we
+ * explicitly whitelist these.
+ *
+ * Other non dirty-tracked folios will be picked up on the slow path.
+ */
+ mapping = folio_mapping(folio);
+ return !mapping || shmem_mapping(mapping) || folio_test_hugetlb(folio);
+}
+
/**
* try_grab_folio() - Attempt to get or pin a folio.
* @page: pointer to page to be grabbed
@@ -123,6 +170,8 @@ static inline struct folio *try_get_folio(struct page *page, int refs)
*/
struct folio *try_grab_folio(struct page *page, int refs, unsigned int flags)
{
+ bool is_longterm = flags & FOLL_LONGTERM;
+
if (unlikely(!(flags & FOLL_PCI_P2PDMA) && is_pci_p2pdma_page(page)))
return NULL;
@@ -136,8 +185,7 @@ struct folio *try_grab_folio(struct page *page, int refs, unsigned int flags)
* right zone, so fail and let the caller fall back to the slow
* path.
*/
- if (unlikely((flags & FOLL_LONGTERM) &&
- !is_longterm_pinnable_page(page)))
+ if (unlikely(is_longterm && !is_longterm_pinnable_page(page)))
return NULL;
/*
@@ -148,6 +196,16 @@ struct folio *try_grab_folio(struct page *page, int refs, unsigned int flags)
if (!folio)
return NULL;
+ /*
+ * Can this folio be safely pinned? We need to perform this
+ * check after the folio is stabilised.
+ */
+ if ((flags & FOLL_WRITE) && is_longterm &&
+ !folio_longterm_write_pin_allowed(folio)) {
+ folio_put_refs(folio, refs);
+ return NULL;
+ }