We can reserve the skb. When sending fails, NETDEV_TX_BUSY or
xskq_prod_reserve fails. As long as skb is successfully generated and
successfully configured, we can reserve skb if we encounter exceptions
later.

Especially when NETDEV_TX_BUSY fails, there is no need to deal with
the problem that xskq_prod_reserve has been updated.

Signed-off-by: Xuan Zhuo <xuanz...@linux.alibaba.com>
---
 include/net/xdp_sock.h |  3 +++
 net/xdp/xsk.c          | 36 +++++++++++++++++++++++++++---------
 2 files changed, 30 insertions(+), 9 deletions(-)

diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
index 4f4e93b..fead0c9 100644
--- a/include/net/xdp_sock.h
+++ b/include/net/xdp_sock.h
@@ -76,6 +76,9 @@ struct xdp_sock {
        struct mutex mutex;
        struct xsk_queue *fq_tmp; /* Only as tmp storage before bind */
        struct xsk_queue *cq_tmp; /* Only as tmp storage before bind */
+
+       struct sk_buff *skb_undone;
+       bool skb_undone_reserve;
 };
 
 #ifdef CONFIG_XDP_SOCKETS
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index e28c682..1051024 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -435,6 +435,19 @@ static int xsk_generic_xmit(struct sock *sk)
        if (xs->queue_id >= xs->dev->real_num_tx_queues)
                goto out;
 
+       if (xs->skb_undone) {
+               if (xs->skb_undone_reserve) {
+                       if (xskq_prod_reserve(xs->pool->cq))
+                               goto out;
+
+                       xs->skb_undone_reserve = false;
+               }
+
+               skb = xs->skb_undone;
+               xs->skb_undone = NULL;
+               goto xmit;
+       }
+
        while (xskq_cons_peek_desc(xs->tx, &desc, xs->pool)) {
                char *buffer;
                u64 addr;
@@ -454,12 +467,7 @@ static int xsk_generic_xmit(struct sock *sk)
                addr = desc.addr;
                buffer = xsk_buff_raw_get_data(xs->pool, addr);
                err = skb_store_bits(skb, 0, buffer, len);
-               /* This is the backpressure mechanism for the Tx path.
-                * Reserve space in the completion queue and only proceed
-                * if there is space in it. This avoids having to implement
-                * any buffering in the Tx path.
-                */
-               if (unlikely(err) || xskq_prod_reserve(xs->pool->cq)) {
+               if (unlikely(err)) {
                        kfree_skb(skb);
                        goto out;
                }
@@ -470,12 +478,22 @@ static int xsk_generic_xmit(struct sock *sk)
                skb_shinfo(skb)->destructor_arg = (void *)(long)desc.addr;
                skb->destructor = xsk_destruct_skb;
 
+               /* This is the backpressure mechanism for the Tx path.
+                * Reserve space in the completion queue and only proceed
+                * if there is space in it. This avoids having to implement
+                * any buffering in the Tx path.
+                */
+               if (xskq_prod_reserve(xs->pool->cq)) {
+                       xs->skb_undone_reserve = true;
+                       xs->skb_undone = skb;
+                       goto out;
+               }
+
+xmit:
                err = __dev_direct_xmit(skb, xs->queue_id);
                if  (err == NETDEV_TX_BUSY) {
                        /* Tell user-space to retry the send */
-                       skb->destructor = sock_wfree;
-                       /* Free skb without triggering the perf drop trace */
-                       consume_skb(skb);
+                       xs->skb_undone = skb;
                        err = -EAGAIN;
                        goto out;
                }
-- 
1.8.3.1

Reply via email to