xsk: support mbuf on ZC RX
authorMaciej Fijalkowski <maciej.fijalkowski@intel.com>
Wed, 19 Jul 2023 13:24:08 +0000 (15:24 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 19 Jul 2023 16:56:49 +0000 (09:56 -0700)
Given that skb_shared_info relies on skb_frag_t, in order to support
xskb chaining, introduce xdp_buff_xsk::xskb_list_node and
xsk_buff_pool::xskb_list.

This is needed so ZC drivers can add frags as xskb nodes which will make
it possible to handle it both when producing AF_XDP Rx descriptors as
well as freeing/recycling all the frags that a single frame carries.

Speaking of latter, update xsk_buff_free() to take care of list nodes.
For the former (adding as frags), introduce xsk_buff_add_frag() for ZC
drivers usage that is going to be used to add a frag to xskb list from
pool.

xsk_buff_get_frag() will be utilized by XDP_TX and, on contrary, will
return xdp_buff.

One of the previous patches added a wrapper for ZC Rx so implement xskb
list walk and production of Rx descriptors there.

On bind() path, bail out if socket wants to use ZC multi-buffer but
underlying netdev does not support it.

Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Link: https://lore.kernel.org/r/20230719132421.584801-12-maciej.fijalkowski@intel.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/net/xdp_sock_drv.h
include/net/xsk_buff_pool.h
net/xdp/xsk.c
net/xdp/xsk_buff_pool.c

index 0d34cdb5567d41e9f5a959448dcb75e1ed01d322..1f6fc8c7a84c6c68166f1c231e8afec5998ed228 100644 (file)
@@ -108,10 +108,45 @@ static inline bool xsk_buff_can_alloc(struct xsk_buff_pool *pool, u32 count)
 static inline void xsk_buff_free(struct xdp_buff *xdp)
 {
        struct xdp_buff_xsk *xskb = container_of(xdp, struct xdp_buff_xsk, xdp);
+       struct list_head *xskb_list = &xskb->pool->xskb_list;
+       struct xdp_buff_xsk *pos, *tmp;
 
+       if (likely(!xdp_buff_has_frags(xdp)))
+               goto out;
+
+       list_for_each_entry_safe(pos, tmp, xskb_list, xskb_list_node) {
+               list_del(&pos->xskb_list_node);
+               xp_free(pos);
+       }
+
+       xdp_get_shared_info_from_buff(xdp)->nr_frags = 0;
+out:
        xp_free(xskb);
 }
 
+static inline void xsk_buff_add_frag(struct xdp_buff *xdp)
+{
+       struct xdp_buff_xsk *frag = container_of(xdp, struct xdp_buff_xsk, xdp);
+
+       list_add_tail(&frag->xskb_list_node, &frag->pool->xskb_list);
+}
+
+static inline struct xdp_buff *xsk_buff_get_frag(struct xdp_buff *first)
+{
+       struct xdp_buff_xsk *xskb = container_of(first, struct xdp_buff_xsk, xdp);
+       struct xdp_buff *ret = NULL;
+       struct xdp_buff_xsk *frag;
+
+       frag = list_first_entry_or_null(&xskb->pool->xskb_list,
+                                       struct xdp_buff_xsk, xskb_list_node);
+       if (frag) {
+               list_del(&frag->xskb_list_node);
+               ret = &frag->xdp;
+       }
+
+       return ret;
+}
+
 static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size)
 {
        xdp->data = xdp->data_hard_start + XDP_PACKET_HEADROOM;
@@ -265,6 +300,15 @@ static inline void xsk_buff_free(struct xdp_buff *xdp)
 {
 }
 
+static inline void xsk_buff_add_frag(struct xdp_buff *xdp)
+{
+}
+
+static inline struct xdp_buff *xsk_buff_get_frag(struct xdp_buff *first)
+{
+       return NULL;
+}
+
 static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size)
 {
 }
index 4dcca163e076ed5fe86c516258255a25c9d600f3..b0bdff26fc882ccafe8bdd3c2727366198eabb94 100644 (file)
@@ -29,6 +29,7 @@ struct xdp_buff_xsk {
        struct xsk_buff_pool *pool;
        u64 orig_addr;
        struct list_head free_list_node;
+       struct list_head xskb_list_node;
 };
 
 #define XSK_CHECK_PRIV_TYPE(t) BUILD_BUG_ON(sizeof(t) > offsetofend(struct xdp_buff_xsk, cb))
@@ -54,6 +55,7 @@ struct xsk_buff_pool {
        struct xdp_umem *umem;
        struct work_struct work;
        struct list_head free_list;
+       struct list_head xskb_list;
        u32 heads_cnt;
        u16 queue_id;
 
index ba755fed37507685d407da847da40f1915e096c9..4f1e0599146e2cde9dea5907feda8c9b136540d2 100644 (file)
@@ -155,8 +155,32 @@ static int __xsk_rcv_zc(struct xdp_sock *xs, struct xdp_buff_xsk *xskb, u32 len,
 static int xsk_rcv_zc(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len)
 {
        struct xdp_buff_xsk *xskb = container_of(xdp, struct xdp_buff_xsk, xdp);
+       u32 frags = xdp_buff_has_frags(xdp);
+       struct xdp_buff_xsk *pos, *tmp;
+       struct list_head *xskb_list;
+       u32 contd = 0;
+       int err;
+
+       if (frags)
+               contd = XDP_PKT_CONTD;
 
-       return __xsk_rcv_zc(xs, xskb, len, 0);
+       err = __xsk_rcv_zc(xs, xskb, len, contd);
+       if (err || likely(!frags))
+               goto out;
+
+       xskb_list = &xskb->pool->xskb_list;
+       list_for_each_entry_safe(pos, tmp, xskb_list, xskb_list_node) {
+               if (list_is_singular(xskb_list))
+                       contd = 0;
+               len = pos->xdp.data_end - pos->xdp.data;
+               err = __xsk_rcv_zc(xs, pos, len, contd);
+               if (err)
+                       return err;
+               list_del(&pos->xskb_list_node);
+       }
+
+out:
+       return err;
 }
 
 static void *xsk_copy_xdp_start(struct xdp_buff *from)
index 26f6d304451e9e310ab3f120eb5d494d4c05f841..b3f7b310811edfa492b81fe026859dd17017df8d 100644 (file)
@@ -86,6 +86,7 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
        pool->umem = umem;
        pool->addrs = umem->addrs;
        INIT_LIST_HEAD(&pool->free_list);
+       INIT_LIST_HEAD(&pool->xskb_list);
        INIT_LIST_HEAD(&pool->xsk_tx_list);
        spin_lock_init(&pool->xsk_tx_list_lock);
        spin_lock_init(&pool->cq_lock);
@@ -99,6 +100,7 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
                xskb->pool = pool;
                xskb->xdp.frame_sz = umem->chunk_size - umem->headroom;
                INIT_LIST_HEAD(&xskb->free_list_node);
+               INIT_LIST_HEAD(&xskb->xskb_list_node);
                if (pool->unaligned)
                        pool->free_heads[i] = xskb;
                else
@@ -187,6 +189,11 @@ int xp_assign_dev(struct xsk_buff_pool *pool,
                goto err_unreg_pool;
        }
 
+       if (netdev->xdp_zc_max_segs == 1 && (flags & XDP_USE_SG)) {
+               err = -EOPNOTSUPP;
+               goto err_unreg_pool;
+       }
+
        bpf.command = XDP_SETUP_XSK_POOL;
        bpf.xsk.pool = pool;
        bpf.xsk.queue_id = queue_id;