Add NODE_PRIVATE_CAP_LTPIN so a node can opt back into gup pins.

An opted-in node is then pinned exactly like ordinary memory, or
migrated and pinned if in ZONE_MOVABLE.

Signed-off-by: Gregory Price <[email protected]>
---
 include/linux/node_private.h | 29 +++++++++++++++++++++++++++++
 mm/gup.c                     |  3 ++-
 mm/internal.h                |  9 +++++----
 3 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/include/linux/node_private.h b/include/linux/node_private.h
index 5c3e070ed0deb..6f568bc0d46c7 100644
--- a/include/linux/node_private.h
+++ b/include/linux/node_private.h
@@ -17,6 +17,7 @@ struct page;
 #define NODE_PRIVATE_CAP_HOTUNPLUG     (1UL << 2)      /* allow hot-unplug */
 #define NODE_PRIVATE_CAP_DEMOTION      (1UL << 3)      /* allow tiering 
demotion */
 #define NODE_PRIVATE_CAP_NUMA_BALANCING        (1UL << 4)      /* allow NUMA 
balancing */
+#define NODE_PRIVATE_CAP_LTPIN         (1UL << 5)      /* allow GUP pins */
 
 /**
  * struct node_private - Per-node container for N_MEMORY_PRIVATE nodes
@@ -167,6 +168,29 @@ static inline bool node_allows_numa_balancing(int nid)
        return ret;
 }
 
+/**
+ * node_allows_ltpin - may a folio on this node be long-term GUP-pinned?
+ * @nid: the node to test
+ *
+ * Opted-out private nodes cause longterm pins to outright fail regardless
+ * of ZONE placement (NORMAL would allow, MOVABLE would migrate first).
+ *
+ * Opted-in private nodes allow longterm pins to operate normally.
+ */
+static inline bool node_allows_ltpin(int nid)
+{
+       struct node_private *np;
+       bool ret;
+
+       if (!node_state(nid, N_MEMORY_PRIVATE))
+               return true;
+       rcu_read_lock();
+       np = rcu_dereference(NODE_DATA(nid)->node_private);
+       ret = np && (np->caps & NODE_PRIVATE_CAP_LTPIN);
+       rcu_read_unlock();
+       return ret;
+}
+
 #else /* !CONFIG_NUMA */
 
 static inline bool folio_is_private_node(struct folio *folio)
@@ -209,6 +233,11 @@ static inline bool node_allows_numa_balancing(int nid)
        return true;
 }
 
+static inline bool node_allows_ltpin(int nid)
+{
+       return true;
+}
+
 #endif /* CONFIG_NUMA */
 
 #if defined(CONFIG_NUMA) && defined(CONFIG_MEMORY_HOTPLUG)
diff --git a/mm/gup.c b/mm/gup.c
index a7d4de223785c..1e313aa590bc1 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -547,7 +547,8 @@ static struct folio *try_grab_folio_fast(struct page *page, 
int refs,
        /*
         * Can't do FOLL_LONGTERM + FOLL_PIN gup fast path if not in a
         * right zone, so fail and let the caller fall back to the slow
-        * path.  Fail for private-node folios here so slow path rejects.
+        * path.  Fail for non-opted-in private node folios here so the
+        * slow path can reject the pin entirely.
         */
        if (unlikely((flags & FOLL_LONGTERM) &&
                     !folio_allows_longterm_pin(folio))) {
diff --git a/mm/internal.h b/mm/internal.h
index 01ab8b32b0bd8..9621eb127c28c 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -146,19 +146,20 @@ static inline bool folio_allows_numa_balance(struct folio 
*folio)
 static inline bool folio_allows_longterm_pin(struct folio *folio)
 {
        return folio_is_longterm_pinnable(folio) &&
-              !folio_is_private_node(folio);
+              node_allows_ltpin(folio_nid(folio));
 }
 
 /*
  * folio_longterm_pin_forbidden() - must a longterm pin of this folio fail
  * outright (neither pinned in place nor migrated off the node)?
  *
- * True for any folio on a private node: such memory can be neither pinned
- * nor migrated, so the pin must be rejected with the folio left in place.
+ * True only for a folio on a private node that did not opt into longterm
+ * pinning (NODE_PRIVATE_CAP_LTPIN); node_allows_ltpin() is true for ordinary
+ * nodes and for opted-in private nodes, so this never trips them.
  */
 static inline bool folio_longterm_pin_forbidden(struct folio *folio)
 {
-       return folio_is_private_node(folio);
+       return !node_allows_ltpin(folio_nid(folio));
 }
 
 /*
-- 
2.53.0-Meta


Reply via email to