Extend the devmem memory-provider path so a knod accelerator can back a
NIC page_pool with accelerator-exported memory (dma-buf), letting the
NIC DMA received packets directly into accelerator memory.

Signed-off-by: Taehee Yoo <[email protected]>
(cherry picked from commit d511a8cb3e229f8f5cf060985880d45bd384db87)
---
 include/net/devmem.h                    |  58 +++++++++++++
 include/net/netmem.h                    |   9 +++
 include/net/page_pool/memory_provider.h |   4 +
 include/net/page_pool/types.h           |  23 +++++-
 net/core/devmem.c                       | 103 +++++++++++++++++++-----
 net/core/devmem.h                       |   7 +-
 net/core/page_pool.c                    |  22 ++++-
 7 files changed, 198 insertions(+), 28 deletions(-)
 create mode 100644 include/net/devmem.h

diff --git a/include/net/devmem.h b/include/net/devmem.h
new file mode 100644
index 000000000000..f1c3895d7833
--- /dev/null
+++ b/include/net/devmem.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Device memory TCP support
+ *
+ * Authors:    Mina Almasry <[email protected]>
+ *             Willem de Bruijn <[email protected]>
+ *             Kaiyuan Zhang <[email protected]>
+ *
+ */
+#ifndef _NET_DEVMEM_H
+#define _NET_DEVMEM_H
+
+#include <linux/dma-direction.h>
+#include <linux/err.h>
+#include <linux/types.h>
+
+struct device;
+struct dma_buf;
+struct dma_buf_attach_ops;
+struct net_device;
+struct net_devmem_dmabuf_binding;
+struct netlink_ext_ack;
+
+#if defined(CONFIG_NET_DEVMEM)
+struct net_devmem_dmabuf_binding *
+__net_devmem_binding_create(struct net_device *dev, struct device *dma_dev,
+                           struct dma_buf *dmabuf,
+                           enum dma_data_direction direction,
+                           const struct dma_buf_attach_ops *importer_ops,
+                           struct netlink_ext_ack *extack);
+int net_devmem_bind_dmabuf_to_queue_direct(struct net_device *dev, u32 rxq_idx,
+                                          struct net_devmem_dmabuf_binding 
*binding);
+void net_devmem_unbind_dmabuf_direct(struct net_devmem_dmabuf_binding 
*binding);
+#else
+static inline struct net_devmem_dmabuf_binding *
+__net_devmem_binding_create(struct net_device *dev, struct device *dma_dev,
+                           struct dma_buf *dmabuf,
+                           enum dma_data_direction direction,
+                           const struct dma_buf_attach_ops *importer_ops,
+                           struct netlink_ext_ack *extack)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline int
+net_devmem_bind_dmabuf_to_queue_direct(struct net_device *dev, u32 rxq_idx,
+                                      struct net_devmem_dmabuf_binding 
*binding)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline void
+net_devmem_unbind_dmabuf_direct(struct net_devmem_dmabuf_binding *binding)
+{
+}
+#endif
+
+#endif /* _NET_DEVMEM_H */
diff --git a/include/net/netmem.h b/include/net/netmem.h
index bccacd21b6c3..3ddfbd37500f 100644
--- a/include/net/netmem.h
+++ b/include/net/netmem.h
@@ -127,6 +127,15 @@ static inline void net_iov_init(struct net_iov *niov,
        niov->type = type;
 }
 
+/* Global page index within the dma-buf, accounting for multi-chunk
+ * scatter-gather layouts where each chunk owner's niovs start at 0.
+ */
+static inline unsigned int net_iov_binding_idx(const struct net_iov *niov)
+{
+       return (net_iov_owner(niov)->base_virtual >> PAGE_SHIFT) +
+              net_iov_idx(niov);
+}
+
 /* netmem */
 
 /**
diff --git a/include/net/page_pool/memory_provider.h 
b/include/net/page_pool/memory_provider.h
index 255ce4cfd975..4b58a9702fb7 100644
--- a/include/net/page_pool/memory_provider.h
+++ b/include/net/page_pool/memory_provider.h
@@ -23,6 +23,10 @@ bool net_mp_niov_set_dma_addr(struct net_iov *niov, 
dma_addr_t addr);
 void net_mp_niov_set_page_pool(struct page_pool *pool, struct net_iov *niov);
 void net_mp_niov_clear_page_pool(struct net_iov *niov);
 
+void page_pool_provider_set_netmem(struct page_pool *pool, netmem_ref netmem,
+                                  dma_addr_t addr);
+void page_pool_clear_pp_info(netmem_ref netmem);
+
 int netif_mp_open_rxq(struct net_device *dev, unsigned int rxq_idx,
                      const struct pp_memory_provider_params *p,
                      struct netlink_ext_ack *extack);
diff --git a/include/net/page_pool/types.h b/include/net/page_pool/types.h
index 03da138722f5..3e866f249768 100644
--- a/include/net/page_pool/types.h
+++ b/include/net/page_pool/types.h
@@ -31,8 +31,16 @@
  */
 #define PP_FLAG_ALLOW_UNREADABLE_NETMEM BIT(3)
 
+/* Driver-managed pool with a directly-supplied memory provider, not bound to a
+ * netdev rx queue. Setting this flag requires page_pool_params.mp_ops and
+ * .mp_priv to both be set.
+ */
+#define PP_FLAG_CUSTOM_MEMORY_PROVIDER BIT(4)
+
 #define PP_FLAG_ALL            (PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV | \
-                                PP_FLAG_SYSTEM_POOL | 
PP_FLAG_ALLOW_UNREADABLE_NETMEM)
+                                PP_FLAG_SYSTEM_POOL | \
+                                PP_FLAG_ALLOW_UNREADABLE_NETMEM | \
+                                PP_FLAG_CUSTOM_MEMORY_PROVIDER)
 
 /* Index limit to stay within PP_DMA_INDEX_BITS for DMA indices */
 #define PP_DMA_INDEX_LIMIT XA_LIMIT(1, BIT(PP_DMA_INDEX_BITS) - 1)
@@ -54,11 +62,11 @@
  * would have to take a slower code path.
  */
 #if PAGE_SIZE >= SZ_64K
-#define PP_ALLOC_CACHE_REFILL  4
+#define PP_ALLOC_CACHE_REFILL  256
 #elif PAGE_SIZE >= SZ_16K
-#define PP_ALLOC_CACHE_REFILL  16
+#define PP_ALLOC_CACHE_REFILL  1024
 #else
-#define PP_ALLOC_CACHE_REFILL  64
+#define PP_ALLOC_CACHE_REFILL  4096
 #endif
 
 #define PP_ALLOC_CACHE_SIZE    (PP_ALLOC_CACHE_REFILL * 2)
@@ -67,6 +75,8 @@ struct pp_alloc_cache {
        netmem_ref cache[PP_ALLOC_CACHE_SIZE];
 };
 
+struct memory_provider_ops;
+
 /**
  * struct page_pool_params - page pool parameters
  * @fast:      params accessed frequently on hotpath
@@ -83,6 +93,9 @@ struct pp_alloc_cache {
  * @queue_idx: queue idx this page_pool is being created for.
  * @flags:     PP_FLAG_DMA_MAP, PP_FLAG_DMA_SYNC_DEV, PP_FLAG_SYSTEM_POOL,
  *             PP_FLAG_ALLOW_UNREADABLE_NETMEM.
+ * @mp_ops:    driver-supplied memory provider for a pool not bound to a
+ *             netdev rx queue (NULL to use rxq->mp_params instead)
+ * @mp_priv:   context passed to @mp_ops
  */
 struct page_pool_params {
        struct_group_tagged(page_pool_params_fast, fast,
@@ -99,6 +112,8 @@ struct page_pool_params {
                struct net_device *netdev;
                unsigned int queue_idx;
                unsigned int    flags;
+               const struct memory_provider_ops *mp_ops;
+               void *mp_priv;
 /* private: used by test code only */
                void (*init_callback)(netmem_ref netmem, void *arg);
                void *init_arg;
diff --git a/net/core/devmem.c b/net/core/devmem.c
index 957d6b96216b..9e21cffc9643 100644
--- a/net/core/devmem.c
+++ b/net/core/devmem.c
@@ -121,12 +121,9 @@ void net_devmem_free_dmabuf(struct net_iov *niov)
        gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE);
 }
 
-void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
+static void
+net_devmem_binding_unpublish(struct net_devmem_dmabuf_binding *binding)
 {
-       struct netdev_rx_queue *rxq;
-       unsigned long xa_idx;
-       unsigned int rxq_idx;
-
        xa_erase(&net_devmem_dmabuf_bindings, binding->id);
 
        /* Ensure no tx net_devmem_lookup_dmabuf() are in flight after the
@@ -136,6 +133,15 @@ void net_devmem_unbind_dmabuf(struct 
net_devmem_dmabuf_binding *binding)
 
        if (binding->list.next)
                list_del(&binding->list);
+}
+
+void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
+{
+       struct netdev_rx_queue *rxq;
+       unsigned long xa_idx;
+       unsigned int rxq_idx;
+
+       net_devmem_binding_unpublish(binding);
 
        xa_for_each(&binding->bound_rxqs, xa_idx, rxq) {
                const struct pp_memory_provider_params mp_params = {
@@ -151,6 +157,47 @@ void net_devmem_unbind_dmabuf(struct 
net_devmem_dmabuf_binding *binding)
        percpu_ref_kill(&binding->ref);
 }
 
+/* Bind/unbind variants for in-kernel offload importers that drive the rx
+ * queue lifecycle themselves. The mp_params are poked directly, without the
+ * tcp-data-split/XDP guards or the queue reconfigure that the netlink control
+ * plane applies through netif_mp_open_rxq()/netif_mp_close_rxq().
+ */
+int net_devmem_bind_dmabuf_to_queue_direct(struct net_device *dev, u32 rxq_idx,
+                                          struct net_devmem_dmabuf_binding 
*binding)
+{
+       struct netdev_rx_queue *rxq;
+       u32 xa_idx;
+       int err;
+
+       rxq = __netif_get_rx_queue(dev, rxq_idx);
+       rxq->mp_params.mp_priv = binding;
+       rxq->mp_params.mp_ops = &dmabuf_devmem_ops;
+
+       err = xa_alloc(&binding->bound_rxqs, &xa_idx, rxq, xa_limit_32b,
+                      GFP_KERNEL);
+       if (err) {
+               rxq->mp_params.mp_priv = NULL;
+               rxq->mp_params.mp_ops = NULL;
+       }
+
+       return err;
+}
+
+void net_devmem_unbind_dmabuf_direct(struct net_devmem_dmabuf_binding *binding)
+{
+       struct netdev_rx_queue *rxq;
+       unsigned long xa_idx;
+
+       net_devmem_binding_unpublish(binding);
+
+       xa_for_each(&binding->bound_rxqs, xa_idx, rxq) {
+               rxq->mp_params.mp_priv = NULL;
+               rxq->mp_params.mp_ops = NULL;
+       }
+
+       percpu_ref_kill(&binding->ref);
+}
+
 int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
                                    struct net_devmem_dmabuf_binding *binding,
                                    struct netlink_ext_ack *extack)
@@ -188,12 +235,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
                       struct netlink_ext_ack *extack)
 {
        struct net_devmem_dmabuf_binding *binding;
-       static u32 id_alloc_next;
-       struct scatterlist *sg;
        struct dma_buf *dmabuf;
-       unsigned int sg_idx, i;
-       unsigned long virtual;
-       int err;
 
        if (!dma_dev) {
                NL_SET_ERR_MSG(extack, "Device doesn't support DMA");
@@ -204,15 +246,39 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
        if (IS_ERR(dmabuf))
                return ERR_CAST(dmabuf);
 
+       binding = __net_devmem_binding_create(dev, dma_dev, dmabuf, direction,
+                                             NULL, extack);
+       if (IS_ERR(binding)) {
+               dma_buf_put(dmabuf);
+               return binding;
+       }
+
+       binding->vdev = vdev;
+       list_add(&binding->list, &priv->bindings);
+
+       return binding;
+}
+
+struct net_devmem_dmabuf_binding *
+__net_devmem_binding_create(struct net_device *dev, struct device *dma_dev,
+                           struct dma_buf *dmabuf,
+                           enum dma_data_direction direction,
+                           const struct dma_buf_attach_ops *importer_ops,
+                           struct netlink_ext_ack *extack)
+{
+       struct net_devmem_dmabuf_binding *binding;
+       static u32 id_alloc_next;
+       struct scatterlist *sg;
+       unsigned int sg_idx, i;
+       unsigned long virtual;
+       int err;
+
        binding = kzalloc_node(sizeof(*binding), GFP_KERNEL,
                               dev_to_node(&dev->dev));
-       if (!binding) {
-               err = -ENOMEM;
-               goto err_put_dmabuf;
-       }
+       if (!binding)
+               return ERR_PTR(-ENOMEM);
 
        binding->dev = dev;
-       binding->vdev = vdev;
        xa_init_flags(&binding->bound_rxqs, XA_FLAGS_ALLOC);
 
        err = percpu_ref_init(&binding->ref,
@@ -226,7 +292,8 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
        binding->dmabuf = dmabuf;
        binding->direction = direction;
 
-       binding->attachment = dma_buf_attach(binding->dmabuf, dma_dev);
+       binding->attachment = dma_buf_dynamic_attach(binding->dmabuf, dma_dev,
+                                                    importer_ops, binding);
        if (IS_ERR(binding->attachment)) {
                err = PTR_ERR(binding->attachment);
                NL_SET_ERR_MSG(extack, "Failed to bind dmabuf to device");
@@ -325,8 +392,6 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
        if (err < 0)
                goto err_free_chunks;
 
-       list_add(&binding->list, &priv->bindings);
-
        return binding;
 
 err_free_chunks:
@@ -344,8 +409,6 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
        percpu_ref_exit(&binding->ref);
 err_free_binding:
        kfree(binding);
-err_put_dmabuf:
-       dma_buf_put(dmabuf);
        return ERR_PTR(err);
 }
 
diff --git a/net/core/devmem.h b/net/core/devmem.h
index 3852a56036cb..6b57837ab454 100644
--- a/net/core/devmem.h
+++ b/net/core/devmem.h
@@ -7,9 +7,10 @@
  *             Kaiyuan Zhang <[email protected]>
  *
  */
-#ifndef _NET_DEVMEM_H
-#define _NET_DEVMEM_H
+#ifndef _NET_CORE_DEVMEM_H
+#define _NET_CORE_DEVMEM_H
 
+#include <net/devmem.h>
 #include <net/netmem.h>
 #include <net/netdev_netlink.h>
 
@@ -240,4 +241,4 @@ net_devmem_iov_binding(const struct net_iov *niov)
 }
 #endif
 
-#endif /* _NET_DEVMEM_H */
+#endif /* _NET_CORE_DEVMEM_H */
diff --git a/net/core/page_pool.c b/net/core/page_pool.c
index 21dc4a9c8714..fd7444943b9f 100644
--- a/net/core/page_pool.c
+++ b/net/core/page_pool.c
@@ -272,7 +272,17 @@ static int page_pool_init(struct page_pool *pool,
 
        xa_init_flags(&pool->dma_mapped, XA_FLAGS_ALLOC1);
 
-       if (pool->slow.flags & PP_FLAG_ALLOW_UNREADABLE_NETMEM) {
+       if (pool->slow.flags & PP_FLAG_CUSTOM_MEMORY_PROVIDER) {
+               /* Driver-managed pool with a directly-supplied memory
+                * provider, not bound to a netdev rx queue.
+                */
+               if (WARN_ON(!pool->slow.mp_ops || !pool->slow.mp_priv)) {
+                       err = -EINVAL;
+                       goto free_ptr_ring;
+               }
+               pool->mp_priv = pool->slow.mp_priv;
+               pool->mp_ops = pool->slow.mp_ops;
+       } else if (pool->slow.flags & PP_FLAG_ALLOW_UNREADABLE_NETMEM) {
                netdev_assert_locked(pool->slow.netdev);
                rxq = __netif_get_rx_queue(pool->slow.netdev,
                                           pool->slow.queue_idx);
@@ -725,6 +735,16 @@ void page_pool_clear_pp_info(netmem_ref netmem)
        netmem_set_pp(netmem, NULL);
 }
 
+void page_pool_provider_set_netmem(struct page_pool *pool, netmem_ref netmem,
+                                  dma_addr_t addr)
+{
+       netmem_to_nmdesc(netmem)->pp_magic = 0;
+       netmem_to_nmdesc(netmem)->pp = NULL;
+       atomic_long_set(&netmem_to_nmdesc(netmem)->pp_ref_count, 0);
+       page_pool_set_pp_info(pool, netmem);
+       page_pool_set_dma_addr_netmem(netmem, addr);
+}
+
 static __always_inline void __page_pool_release_netmem_dma(struct page_pool 
*pool,
                                                           netmem_ref netmem)
 {
-- 
2.43.0


Reply via email to