]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xsk: provide sufficient space in pool->tx_descs
authorMaciej Fijalkowski <maciej.fijalkowski@intel.com>
Sun, 19 Jul 2026 13:56:06 +0000 (15:56 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 24 Jul 2026 22:12:14 +0000 (15:12 -0700)
The temporary Tx descriptor array in an XSK buffer pool is currently
sized from the Tx ring of the socket that creates the pool.

This is insufficient for shared-UMEM Tx. A later socket may have a
larger Tx ring and submit a valid multi-buffer packet containing more
descriptors than the first socket's ring, while still remaining within
the device's xdp_zc_max_segs limit.

A packet-framed batch parser bounded by the temporary array cannot reach
the end-of-packet descriptor in that case. It leaves the packet on the
Tx ring and encounters the same packet on every subsequent attempt,
stalling Tx processing for that socket.

Size the temporary descriptor array to the larger of the first Tx ring
and the device's xdp_zc_max_segs capability. This keeps the array large
enough to inspect one maximum-sized valid packet. Larger shared Tx rings
do not require further resizing, as they can be processed over multiple
batches.

Following commit will actually address the data path side.

Fixes: d5581966040f ("xsk: support ZC Tx multi-buffer in batch API")
Reviewed-by: Jason Xing <kernelxing@tencent.com>
Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Link: https://patch.msgid.link/20260719135609.147823-4-maciej.fijalkowski@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/xsk_buff_pool.h
net/xdp/xsk.c
net/xdp/xsk_buff_pool.c

index ccb3b350001f2408ebf004e6d34ac9617de43931..f5e737a830559bf93524047538f07195c7497d4b 100644 (file)
@@ -102,12 +102,14 @@ struct xsk_buff_pool {
 
 /* AF_XDP core. */
 struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
-                                               struct xdp_umem *umem);
+                                               struct xdp_umem *umem,
+                                               u32 max_segs);
 int xp_assign_dev(struct xsk_buff_pool *pool, struct net_device *dev,
                  u16 queue_id, u16 flags);
 int xp_assign_dev_shared(struct xsk_buff_pool *pool, struct xdp_sock *umem_xs,
                         struct net_device *dev, u16 queue_id);
-int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs);
+int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs,
+                     u32 max_segs);
 void xp_destroy(struct xsk_buff_pool *pool);
 void xp_get_pool(struct xsk_buff_pool *pool);
 bool xp_put_pool(struct xsk_buff_pool *pool);
index 12a845d012f6f0b68e205cb55c8b5bb137df2717..091792d1d82de9a8245670b3c5a93cdae5b6d0be 100644 (file)
@@ -1525,7 +1525,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
                         * and/or device.
                         */
                        xs->pool = xp_create_and_assign_umem(xs,
-                                                            umem_xs->umem);
+                                                            umem_xs->umem,
+                                                            dev->xdp_zc_max_segs);
                        if (!xs->pool) {
                                err = -ENOMEM;
                                sockfd_put(sock);
@@ -1557,7 +1558,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
                         * utilizes
                         */
                        if (xs->tx && !xs->pool->tx_descs) {
-                               err = xp_alloc_tx_descs(xs->pool, xs);
+                               err = xp_alloc_tx_descs(xs->pool, xs,
+                                                       dev->xdp_zc_max_segs);
                                if (err) {
                                        xp_put_pool(xs->pool);
                                        xs->pool = NULL;
@@ -1575,7 +1577,9 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
                goto out_unlock;
        } else {
                /* This xsk has its own umem. */
-               xs->pool = xp_create_and_assign_umem(xs, xs->umem);
+               xs->pool = xp_create_and_assign_umem(xs, xs->umem,
+                                                    dev->xdp_zc_max_segs);
+
                if (!xs->pool) {
                        err = -ENOMEM;
                        goto out_unlock;
index 1f28a9641571e470a8f56017d8d08eae1700dddf..12c9fb29af057abf91f9f708e72090075ffcb263 100644 (file)
@@ -42,9 +42,12 @@ void xp_destroy(struct xsk_buff_pool *pool)
        kvfree(pool);
 }
 
-int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs)
+int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs,
+                     u32 max_segs)
 {
-       pool->tx_descs = kvzalloc_objs(*pool->tx_descs, xs->tx->nentries);
+       u32 nentries = max(xs->tx->nentries, max_segs);
+
+       pool->tx_descs = kvzalloc_objs(*pool->tx_descs, nentries);
        if (!pool->tx_descs)
                return -ENOMEM;
 
@@ -52,7 +55,8 @@ int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs)
 }
 
 struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
-                                               struct xdp_umem *umem)
+                                               struct xdp_umem *umem,
+                                               u32 max_segs)
 {
        bool unaligned = umem->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG;
        struct xsk_buff_pool *pool;
@@ -69,7 +73,7 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
                goto out;
 
        if (xs->tx)
-               if (xp_alloc_tx_descs(pool, xs))
+               if (xp_alloc_tx_descs(pool, xs, max_segs))
                        goto out;
 
        pool->chunk_mask = ~((u64)umem->chunk_size - 1);