[PATCH RFC net-next v4 05/14] xsk: add direct xmit in batch function

Jason Xing
2026-04-15T08:27:34Z

Add batch xmit logic.

Only grabbing the lock and disable bottom half once and sent all
the aggregated packets in one loop. Via skb->list, the already built
skbs can be handled one by one.

Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
include/net/xdp_sock.h | 1 +
net/core/dev.c | 41 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+)

diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
index 2151aab8f0a1..0609e3b04279 100644
--- a/include/net/xdp_sock.h
+++ b/include/net/xdp_sock.h
@@ -141,6 +141,7 @@ struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
struct sk_buff *allocated_skb,
struct xdp_desc *desc);
int xsk_alloc_batch_skb(struct xdp_sock *xs, u32 nb_pkts, u32 nb_descs, int *err);
+int xsk_direct_xmit_batch(struct xdp_sock *xs, struct net_device *dev);

/**
* xsk_tx_metadata_to_compl - Save enough relevant metadata information
diff --git a/net/core/dev.c b/net/core/dev.c
index 4519f0e59beb..e33a2406d8ca 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -163,6 +163,7 @@
#include <net/page_pool/memory_provider.h>
#include <net/rps.h>
#include <linux/phy_link_topology.h>
+#include <net/xdp_sock.h>

#include "dev.h"
#include "devmem.h"
@@ -4893,6 +4894,46 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
}
EXPORT_SYMBOL(__dev_queue_xmit);

+int xsk_direct_xmit_batch(struct xdp_sock *xs, struct net_device *dev)
+{
+ u16 queue_id = xs->queue_id;
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_id);
+ struct sk_buff_head *send_queue = &xs->batch.send_queue;
+ int ret = NETDEV_TX_BUSY;
+ struct sk_buff *skb;
+
+ local_bh_disable();
+ HARD_TX_LOCK(dev, txq, smp_processor_id());
+ while ((skb = __skb_dequeue(send_queue)) != NULL) {
+ struct sk_buff *orig_skb = skb;
+ bool again = false;
+
+ skb = validate_xmit_skb_list(skb, dev, &again);
+ if (skb != orig_skb) {
+ dev_core_stats_tx_dropped_inc(dev);
+ kfree_skb_list(skb);
+ ret = NET_XMIT_DROP;
+ break;
+ }
+
+ if (netif_xmit_frozen_or_drv_stopped(txq)) {
+ __skb_queue_head(send_queue, skb);
+ break;
+ }
+ skb_set_queue_mapping(skb, queue_id);
+ ret = netdev_start_xmit(skb, dev, txq, false);
+ if (ret != NETDEV_TX_OK) {
+ if (ret == NETDEV_TX_BUSY)
+ __skb_queue_head(send_queue, skb);
+ break;
+ }
+ }
+ HARD_TX_UNLOCK(dev, txq);
+ local_bh_enable();
+
+ return ret;
+}
+
int __dev_direct_xmit(struct sk_buff *skb, u16 queue_id)
{
struct net_device *dev = skb->dev;
--
2.41.3