* [PATCH net v3 2/5] net: mana: Init gf_stats_work before potential error paths in probe
From: Erni Sri Satya Vennela @ 2026-04-15 8:09 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, ernis, ssengar, dipayanroy, gargaditya,
shirazsaleem, kees, kotaranov, leon, shacharr, stephen,
linux-hyperv, netdev, linux-kernel
In-Reply-To: <20260415080944.732901-1-ernis@linux.microsoft.com>
Move INIT_DELAYED_WORK(gf_stats_work) to before mana_create_eq(),
while keeping schedule_delayed_work() at its original location.
Previously, if any function between mana_create_eq() and the
INIT_DELAYED_WORK call failed, mana_probe() would call mana_remove()
which unconditionally calls cancel_delayed_work_sync(gf_stats_work)
in __flush_work() or debug object warnings with
CONFIG_DEBUG_OBJECTS_WORK enabled.
Fixes: be4f1d67ec56 ("net: mana: Add standard counter rx_missed_errors")
Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
---
Changes in v3:
* No change
Changes in v2:
* Apply the patch in net instead of net-next.
---
drivers/net/ethernet/microsoft/mana/mana_en.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index e3e4b6de6668..468ed60a8a00 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3635,6 +3635,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
INIT_WORK(&ac->link_change_work, mana_link_state_handle);
}
+ INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
+
err = mana_create_eq(ac);
if (err) {
dev_err(dev, "Failed to create EQs: %d\n", err);
@@ -3709,7 +3711,6 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
err = add_adev(gd, "eth");
- INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
schedule_delayed_work(&ac->gf_stats_work, MANA_GF_STATS_PERIOD);
out:
--
2.34.1
^ permalink raw reply related
* [PATCH net v3 3/5] net: mana: Guard mana_remove against double invocation
From: Erni Sri Satya Vennela @ 2026-04-15 8:09 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, ernis, ssengar, dipayanroy, gargaditya,
shirazsaleem, kees, kotaranov, leon, shacharr, stephen,
linux-hyperv, netdev, linux-kernel
In-Reply-To: <20260415080944.732901-1-ernis@linux.microsoft.com>
If PM resume fails (e.g., mana_attach() returns an error), mana_probe()
calls mana_remove(), which tears down the device and sets
gd->gdma_context = NULL and gd->driver_data = NULL.
However, a failed resume callback does not automatically unbind the
driver. When the device is eventually unbound, mana_remove() is invoked
a second time. Without a NULL check, it dereferences gc->dev with
gc == NULL, causing a kernel panic.
Add an early return if gdma_context or driver_data is NULL so the second
invocation is harmless. Move the dev = gc->dev assignment after the
guard so it cannot dereference NULL.
Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)")
Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
---
Changes in v3:
* Add this patch to the patchset
---
drivers/net/ethernet/microsoft/mana/mana_en.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 468ed60a8a00..ce1b7ec46a27 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3731,11 +3731,16 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
struct gdma_context *gc = gd->gdma_context;
struct mana_context *ac = gd->driver_data;
struct mana_port_context *apc;
- struct device *dev = gc->dev;
+ struct device *dev;
struct net_device *ndev;
int err;
int i;
+ if (!gc || !ac)
+ return;
+
+ dev = gc->dev;
+
disable_work_sync(&ac->link_change_work);
cancel_delayed_work_sync(&ac->gf_stats_work);
--
2.34.1
^ permalink raw reply related
* [PATCH net v3 4/5] net: mana: Don't overwrite port probe error with add_adev result
From: Erni Sri Satya Vennela @ 2026-04-15 8:09 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, ernis, ssengar, dipayanroy, gargaditya,
shirazsaleem, kees, kotaranov, leon, shacharr, stephen,
linux-hyperv, netdev, linux-kernel
In-Reply-To: <20260415080944.732901-1-ernis@linux.microsoft.com>
In mana_probe(), if mana_probe_port() fails for any port, the error
is stored in 'err' and the loop breaks. However, the subsequent
unconditional 'err = add_adev(gd, "eth")' overwrites this error.
If add_adev() succeeds, mana_probe() returns success despite ports
being left in a partially initialized state (ac->ports[i] == NULL).
Only call add_adev() when there is no prior error, so the probe
correctly fails and triggers mana_remove() cleanup.
Fixes: ced82fce77e9 ("net: mana: Probe rdma device in mana driver")
Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
---
Changes in v3:
* Fix inaccurate comments.
Changes in v2:
* Apply the patch in net instead of net-next.
---
drivers/net/ethernet/microsoft/mana/mana_en.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index ce1b7ec46a27..39b18577fb51 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3680,10 +3680,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
if (!resuming) {
for (i = 0; i < ac->num_ports; i++) {
err = mana_probe_port(ac, i, &ac->ports[i]);
- /* we log the port for which the probe failed and stop
- * probes for subsequent ports.
- * Note that we keep running ports, for which the probes
- * were successful, unless add_adev fails too
+ /* Log the port for which the probe failed, stop probing
+ * subsequent ports, and skip add_adev.
+ * mana_remove() will clean up already-probed ports.
*/
if (err) {
dev_err(dev, "Probe Failed for port %d\n", i);
@@ -3697,10 +3696,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
enable_work(&apc->queue_reset_work);
err = mana_attach(ac->ports[i]);
rtnl_unlock();
- /* we log the port for which the attach failed and stop
- * attach for subsequent ports
- * Note that we keep running ports, for which the attach
- * were successful, unless add_adev fails too
+ /* Log the port for which the attach failed, stop
+ * attaching subsequent ports, and skip add_adev.
+ * mana_remove() will clean up already-attached ports.
*/
if (err) {
dev_err(dev, "Attach Failed for port %d\n", i);
@@ -3709,7 +3707,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
}
}
- err = add_adev(gd, "eth");
+ if (!err)
+ err = add_adev(gd, "eth");
schedule_delayed_work(&ac->gf_stats_work, MANA_GF_STATS_PERIOD);
--
2.34.1
^ permalink raw reply related
* [PATCH net v3 5/5] net: mana: Fix EQ leak in mana_remove on NULL port
From: Erni Sri Satya Vennela @ 2026-04-15 8:09 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, ernis, ssengar, dipayanroy, gargaditya,
shirazsaleem, kees, kotaranov, leon, shacharr, stephen,
linux-hyperv, netdev, linux-kernel
In-Reply-To: <20260415080944.732901-1-ernis@linux.microsoft.com>
In mana_remove(), when a NULL port is encountered in the port iteration
loop, 'goto out' skips the mana_destroy_eq(ac) call, leaking the event
queues allocated earlier by mana_create_eq().
This can happen when mana_probe_port() fails for port 0, leaving
ac->ports[0] as NULL. On driver unload or error cleanup, mana_remove()
hits the NULL entry and jumps past mana_destroy_eq().
Change 'goto out' to 'break' so the for-loop exits normally and
mana_destroy_eq() is always reached. Remove the now-unreferenced out:
label.
Fixes: 1e2d0824a9c3 ("net: mana: Add support for EQ sharing")
Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
---
Changes in v3;
* Update Fixes tag to appropriate commit id.
Changes in v2:
* Apply the patch in net instead of net-next.
---
drivers/net/ethernet/microsoft/mana/mana_en.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 39b18577fb51..98e2fcc797ca 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3752,7 +3752,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
if (!ndev) {
if (i == 0)
dev_err(dev, "No net device to remove\n");
- goto out;
+ break;
}
apc = netdev_priv(ndev);
@@ -3783,7 +3783,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
}
mana_destroy_eq(ac);
-out:
+
if (ac->per_port_queue_reset_wq) {
destroy_workqueue(ac->per_port_queue_reset_wq);
ac->per_port_queue_reset_wq = NULL;
--
2.34.1
^ permalink raw reply related
* Re: [Intel-wired-lan] [PATCH net-next v3 2/2] e1000e: limit endianness conversion to boundary words
From: Dahan, AvigailX @ 2026-04-15 8:14 UTC (permalink / raw)
To: Agalakov Daniil, Tony Nguyen
Cc: Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, intel-wired-lan, netdev,
linux-kernel, lvc-project, Daniil Iskhakov, Roman Razov
In-Reply-To: <20260401120919.282668-3-ade@amicon.ru>
On 01/04/2026 15:08, Agalakov Daniil wrote:
> [Why]
> In e1000_set_eeprom(), the eeprom_buff is allocated to hold a range of
> words. However, only the boundary words (the first and the last) are
> populated from the EEPROM if the write request is not word-aligned.
> The words in the middle of the buffer remain uninitialized because they
> are intended to be completely overwritten by the new data via memcpy().
>
> The previous implementation had a loop that performed le16_to_cpus()
> on the entire buffer. This resulted in endianness conversion being
> performed on uninitialized memory for all interior words.
>
> Fix this by converting the endianness only for the boundary words
> immediately after they are successfully read from the EEPROM.
>
> Found by Linux Verification Center (linuxtesting.org) with SVACE.
>
> Co-developed-by: Iskhakov Daniil <dish@amicon.ru>
> Signed-off-by: Iskhakov Daniil <dish@amicon.ru>
> Signed-off-by: Agalakov Daniil <ade@amicon.ru>
> ---
> v3:
> - Reverted to v1's "check-then-convert" logic: the return value of
> e1000_read_nvm() is now checked before performing le16_to_cpus().
> - Removed the redundant full-buffer loops that caused double endianness
> conversion in v2.
>
> v2:
> - Split from the original bugfix series and targeted at 'net-next'.
> - Removed the Fixes: tag; limiting the conversion scope is an
> improvement to avoid unnecessary processing of uninitialized memory.
> - Improved commit description for clarity.
> - Note on e1000e: this driver already contains the necessary return
> value checks for EEPROM reads, so only the endianness conversion
> cleanup is included for e1000e.
>
> drivers/net/ethernet/intel/e1000e/ethtool.c | 19 ++++++++++++-------
> 1 file changed, 12 insertions(+), 7 deletions(-)
>
Tested-by: Avigail Dahan <avigailx.dahan@intel.com>
^ permalink raw reply
* Re: [PATCH net] net: pse-pd: fix out-of-bounds bitmap access in pse_isr() on 32-bit
From: Kory Maincent @ 2026-04-15 8:22 UTC (permalink / raw)
To: Oleksij Rempel
Cc: Jakub Kicinski, netdev, linux-kernel, Carlo Szelinsky,
thomas.petazzoni, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni
In-Reply-To: <ad6LqkKrXwNJfN0B@pengutronix.de>
On Tue, 14 Apr 2026 20:47:06 +0200
Oleksij Rempel <o.rempel@pengutronix.de> wrote:
> Hi Kory,
>
> On Tue, Apr 14, 2026 at 05:13:30PM +0200, Kory Maincent wrote:
> > @@ -1340,6 +1341,11 @@ int devm_pse_irq_helper(struct pse_controller_dev
> > *pcdev, int irq, if (!h->notifs)
> > return -ENOMEM;
> >
> > + h->notifs_mask = devm_kcalloc(dev, BITS_TO_LONGS(pcdev->nr_lines),
> > + sizeof(*h->notifs_mask),
> > GFP_KERNEL);
>
> May be better devm_bitmap_zalloc() instead of devm_kcalloc()?
Oh didn't know this one. Yes that's better. I will send v2 with it.
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply
* Re: [PATCH net v1] net/mlx5: Fix HCA caps leak on notifier init failure
From: Cosmin Ratiu @ 2026-04-15 8:26 UTC (permalink / raw)
To: Saeed Mahameed, prathameshdeshpande7@gmail.com, Carolina Jubran,
leon@kernel.org
Cc: Tariq Toukan, kuba@kernel.org, netdev@vger.kernel.org,
linux-rdma@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260415005022.34764-1-prathameshdeshpande7@gmail.com>
On Wed, 2026-04-15 at 01:49 +0100, Prathamesh Deshpande wrote:
> mlx5_mdev_init() allocates HCA caps via mlx5_hca_caps_alloc() before
> calling mlx5_notifiers_init(). If notifier initialization fails, the
> error path jumps to err_hca_caps and skips mlx5_hca_caps_free(),
> leaking
> allocated caps.
>
> Add a dedicated unwind label for notifier-init failure that frees HCA
> caps before continuing the existing cleanup sequence.
>
> Fixes: b6b03097f982 ("net/mlx5: Initialize events outside devlink
> lock")
Thank you for the fix, LGTM!
> Signed-off-by: Prathamesh Deshpande <prathameshdeshpande7@gmail.com>
Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
^ permalink raw reply
* [PATCH RFC net-next v4 00/14] xsk: batch xmit in copy mode
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
From: Jason Xing <kernelxing@tencent.com>
Greetings, everyone. This is the batch feature series. Even though
net-next is closed, I would appreciate any feedbacks and suggestions
on this! Many thanks!
Bottom line up front: it improves the performance by 88.2% stably.
# Background
This series is focused on the performance improvement in copy mode. As
observed in the physical servers, there are much room left to ramp up
the transmission for copy mode, compared to zerocopy mode.
Even though we can apply zerocopy to achieve a much better performance,
some limitations are still there especially for virtio and veth cases
due to the implementation in the host. In the real world, hundreds and
thousands of hosts like at Tencent still don't support zerocopy mode
for VMs, so copy mode is the only way we can resort to. Being general
is its strong advantage.
Zerocopy has a good function name xskq_cons_read_desc_batch() which
reads descriptors in batch and then sends them out at a time, rather
than just read and send the descriptor one by one in a loop. Similar
batch ideas can be seen from classic mechanisms like GSO/GRO which
also try to handle as many packets as they can at one time. So the
motivation and idea of the series actually originated from them.
# AF_PACKET Comparison
Looking back to the initial design and implementation of AF_XDP, it's
not hard to find the big difference it made is to speed up the
transmission when zerocopy mode is enabled. So the conclusion is that
zerocopy mode of AF_XDP outperforms AF_PACKET that still uses copy mode.
As to the whole logic of copy mode for both of them, they looks quite
similar, especially when application using AF_PACKET sets
PACKET_QDISC_BYPASS option. Digging into the details of AF_PACKET, we
can find the implementation is comparatively heavy which can also be
proved by the real test as shown below. The numbers of AF_PACKET test
are a little bit lower.
# Batch Mode
At the current moment, I consider copy mode of AF_XDP as a half bypass
mechanism to some extent in comparison with the well known bypass
mechanism like DPDK. To avoid much consumption in kernel as much as
possible, then the batch xmit is proposed to aggregate descriptors in a
certain small group and then read/allocate/build/send them in individual
loops.
Applications are allowed to use setsockopt to enlarge the default value.
Please note that since memory allocation can be time consuming and heavy
due to lack of memory that results in complicated memory reclaim, it
might not be that good to hold one descriptor for too long, which brings
high latency for one skb.
# Experiments
Tested on ixgbe at 10Gb/sec with the following settings:
1. mitigations off
2. ethtool -G enp2s0f1 tx 512
3. sysctl -w net.core.skb_defer_max=0
4. sysctl -w net.core.wmem_max=21299200 and sndbuf is the same value
5. XDP_MAX_TX_SKB_BUDGET 512
taskset -c 1 ./xdpsock -i enp2s0f1 -t -S -s 64
copy mode(before): 1,801,007 pps (baseline)
AF_PACKET: 1,375,808 pps (-23.6%)
zc mode: 13,333,593 pps (+640.3%)
batch mode(batch 1): 1,976,821 pps (+9.8%)
batch mode(batch 64): 3,389,704 pps (+88.2%)
batch mode(batch 256): 3,387,563 pps (+88.0%)
---
RFC v4
Link: https://lore.kernel.org/all/20251021131209.41491-1-kerneljasonxing@gmail.com/
1. fix a few bugs in v3
2. add a few optimizations
The series is built on top of commit 2ce8a41113ed (net: hsr: emit
notification for PRP slave2 changed hw addr on port deletion). Since the
changes compared to v3 are too many, please review the series from scratch.
Thanks!
v3
Link: https://lore.kernel.org/all/20250825135342.53110-1-kerneljasonxing@gmail.com/
1. I retested and got different test numbers. Previous test is not that
right because my env has two NUMA nodes and only the first one has a
faster speed.
2. To achieve a stable performance result, the development and
evaluation are also finished in physical servers just like the numbers
that I share.
3. I didn't use pool->tx_descs because sockets can share the same umem
pool.
3. Use skb list to chain the allocated and built skbs to send.
5. Add AF_PACKET test numbers.
V2
Link: https://lore.kernel.org/all/20250811131236.56206-1-kerneljasonxing@gmail.com/
1. add xmit.more sub-feature (Jesper)
2. add kmem_cache_alloc_bulk (Jesper and Maciej)
Jason Xing (14):
xsk: introduce XDP_GENERIC_XMIT_BATCH setsockopt
xsk: extend xsk_build_skb() to support passing an already allocated
skb
xsk: add xsk_alloc_batch_skb() to build skbs in batch
xsk: cache data buffers to avoid frequently calling kmalloc_reserve
xsk: add direct xmit in batch function
xsk: support dynamic xmit.more control for batch xmit
xsk: try to skip validating skb list in xmit path
xsk: rename nb_pkts to nb_descs in xsk_tx_peek_release_desc_batch
xsk: extend xskq_cons_read_desc_batch to count nb_pkts
xsk: extend xsk_cq_reserve_locked() to reserve n slots
xsk: support batch xmit main logic
xsk: separate read-mostly and write-heavy fields in xsk_buff_pool
xsk: retire old xmit path in copy mode
xsk: optimize xsk_build_skb for batch copy-mode fast path
Documentation/networking/af_xdp.rst | 17 ++
include/net/xdp_sock.h | 17 ++
include/net/xsk_buff_pool.h | 10 +-
include/uapi/linux/if_xdp.h | 1 +
net/core/dev.c | 49 +++++
net/core/skbuff.c | 152 +++++++++++++++
net/xdp/xsk.c | 279 ++++++++++++++++++++--------
net/xdp/xsk_queue.h | 40 +++-
tools/include/uapi/linux/if_xdp.h | 1 +
9 files changed, 473 insertions(+), 93 deletions(-)
--
2.41.3
^ permalink raw reply
* [PATCH RFC net-next v4 01/14] xsk: introduce XDP_GENERIC_XMIT_BATCH setsockopt
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
Add a new socket option to provide an alternative to achieve a higher
overall throughput with the rest of series applied. As the corresponding
documentataion I added says, it might increase the latency because the
heavy allocation cannot be avoided especially when the shortage of
memory occurs. So this patch don't turn this feature as default.
Add generic_xmit_batch to tertermine how many descriptors are handled
at one time. It shouldn't be larger than max_tx_budget or smaller than
one that is the default value (disabling batch mode).
Introduce skb_cache when setting setsockopt with xs->mutex protection to
store newly allocated skbs at one time.
Introduce desc_cache to temporarily cache what descriptors the xsk is
about to send each round.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
Documentation/networking/af_xdp.rst | 17 +++++++++++
include/net/xdp_sock.h | 7 +++++
include/uapi/linux/if_xdp.h | 1 +
net/xdp/xsk.c | 47 +++++++++++++++++++++++++++++
tools/include/uapi/linux/if_xdp.h | 1 +
5 files changed, 73 insertions(+)
diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst
index 50d92084a49c..7a8d219efe71 100644
--- a/Documentation/networking/af_xdp.rst
+++ b/Documentation/networking/af_xdp.rst
@@ -447,6 +447,23 @@ mode to allow application to tune the per-socket maximum iteration for
better throughput and less frequency of send syscall.
Allowed range is [32, xs->tx->nentries].
+XDP_GENERIC_XMIT_BATCH
+----------------------
+
+It provides an option that allows application to use batch xmit in the copy
+mode. Batch process tries to allocate a certain number skbs through bulk
+mechanism first and then initialize them and finally send them out at one
+time.
+It applies efficient bulk allocation/deallocation function, avoid frequently
+grabbing/releasing a few locks (like cache lock and queue lock), minimizing
+triggering IRQs from the driver side, which generally gain the overall
+performance improvement as observed by xdpsock benchmark.
+Potential side effect is that it might increase the latency of per packet
+due to memory allocation that is unavoidable and time-consuming.
+Setting a relatively large value of batch size could benifit for scenarios
+like bulk transmission. The maximum value shouldn't be larger than
+xs->max_tx_budget.
+
XDP_STATISTICS getsockopt
-------------------------
diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
index 23e8861e8b25..965cab9a0465 100644
--- a/include/net/xdp_sock.h
+++ b/include/net/xdp_sock.h
@@ -45,6 +45,12 @@ struct xsk_map {
struct xdp_sock __rcu *xsk_map[];
};
+struct xsk_batch {
+ u32 generic_xmit_batch;
+ struct sk_buff **skb_cache;
+ struct xdp_desc *desc_cache;
+};
+
struct xdp_sock {
/* struct sock must be the first member of struct xdp_sock */
struct sock sk;
@@ -89,6 +95,7 @@ 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 xsk_batch batch;
};
/*
diff --git a/include/uapi/linux/if_xdp.h b/include/uapi/linux/if_xdp.h
index 23a062781468..44cb72cd328e 100644
--- a/include/uapi/linux/if_xdp.h
+++ b/include/uapi/linux/if_xdp.h
@@ -80,6 +80,7 @@ struct xdp_mmap_offsets {
#define XDP_STATISTICS 7
#define XDP_OPTIONS 8
#define XDP_MAX_TX_SKB_BUDGET 9
+#define XDP_GENERIC_XMIT_BATCH 10
struct xdp_umem_reg {
__u64 addr; /* Start of packet data area */
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index 6149f6a79897..6122db8606fe 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -1218,6 +1218,16 @@ static void xsk_delete_from_maps(struct xdp_sock *xs)
}
}
+static void xsk_batch_reset(struct xsk_batch *batch, struct sk_buff **skbs,
+ struct xdp_desc *descs, unsigned int size)
+{
+ kfree(batch->skb_cache);
+ kvfree(batch->desc_cache);
+ batch->skb_cache = skbs;
+ batch->desc_cache = descs;
+ batch->generic_xmit_batch = size;
+}
+
static int xsk_release(struct socket *sock)
{
struct sock *sk = sock->sk;
@@ -1247,6 +1257,7 @@ static int xsk_release(struct socket *sock)
xskq_destroy(xs->tx);
xskq_destroy(xs->fq_tmp);
xskq_destroy(xs->cq_tmp);
+ xsk_batch_reset(&xs->batch, NULL, NULL, 0);
sock_orphan(sk);
sock->sk = NULL;
@@ -1588,6 +1599,42 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,
WRITE_ONCE(xs->max_tx_budget, budget);
return 0;
}
+ case XDP_GENERIC_XMIT_BATCH:
+ {
+ struct xsk_buff_pool *pool = xs->pool;
+ struct xsk_batch *batch = &xs->batch;
+ struct xdp_desc *descs;
+ struct sk_buff **skbs;
+ unsigned int size;
+ int ret = 0;
+
+ if (optlen != sizeof(size))
+ return -EINVAL;
+ if (copy_from_sockptr(&size, optval, sizeof(size)))
+ return -EFAULT;
+ if (size == batch->generic_xmit_batch)
+ return 0;
+ if (!size || size > xs->max_tx_budget || !pool)
+ return -EACCES;
+
+ mutex_lock(&xs->mutex);
+ skbs = kmalloc(size * sizeof(struct sk_buff *), GFP_KERNEL);
+ if (!skbs) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ descs = kvcalloc(size, sizeof(struct xdp_desc), GFP_KERNEL);
+ if (!descs) {
+ kfree(skbs);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ xsk_batch_reset(batch, skbs, descs, size);
+out:
+ mutex_unlock(&xs->mutex);
+ return ret;
+ }
default:
break;
}
diff --git a/tools/include/uapi/linux/if_xdp.h b/tools/include/uapi/linux/if_xdp.h
index 23a062781468..44cb72cd328e 100644
--- a/tools/include/uapi/linux/if_xdp.h
+++ b/tools/include/uapi/linux/if_xdp.h
@@ -80,6 +80,7 @@ struct xdp_mmap_offsets {
#define XDP_STATISTICS 7
#define XDP_OPTIONS 8
#define XDP_MAX_TX_SKB_BUDGET 9
+#define XDP_GENERIC_XMIT_BATCH 10
struct xdp_umem_reg {
__u64 addr; /* Start of packet data area */
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 02/14] xsk: extend xsk_build_skb() to support passing an already allocated skb
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
To avoid reinvent the wheel, the patch provides a way to let batch
feature reuse xsk_build_skb() as the rest process of the whole
initialization just after the skb is allocated.
The original xsk_build_skb() itself allocates a new skb by calling
sock_alloc_send_skb whether in copy mode or zerocopy mode. Add a new
parameter allocated skb to let other callers to pass an already
allocated skb to support later xmit batch feature. It replaces the
previous allocation of memory function with a bulk one.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
include/net/xdp_sock.h | 3 +++
net/xdp/xsk.c | 23 ++++++++++++++++-------
2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
index 965cab9a0465..90c709fd1239 100644
--- a/include/net/xdp_sock.h
+++ b/include/net/xdp_sock.h
@@ -133,6 +133,9 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp);
int __xsk_map_redirect(struct xdp_sock *xs, struct xdp_buff *xdp);
void __xsk_map_flush(struct list_head *flush_list);
INDIRECT_CALLABLE_DECLARE(void xsk_destruct_skb(struct sk_buff *));
+struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
+ struct sk_buff *allocated_skb,
+ struct xdp_desc *desc);
/**
* xsk_tx_metadata_to_compl - Save enough relevant metadata information
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index 6122db8606fe..ecd5b9c424b8 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -718,6 +718,7 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
}
static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
+ struct sk_buff *allocated_skb,
struct xdp_desc *desc)
{
struct xsk_buff_pool *pool = xs->pool;
@@ -734,7 +735,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
if (!skb) {
hr = max(NET_SKB_PAD, L1_CACHE_ALIGN(xs->dev->needed_headroom));
- skb = sock_alloc_send_skb(&xs->sk, hr, 1, &err);
+ if (!allocated_skb)
+ skb = sock_alloc_send_skb(&xs->sk, hr, 1, &err);
+ else
+ skb = allocated_skb;
if (unlikely(!skb))
return ERR_PTR(err);
@@ -799,15 +803,16 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
return skb;
}
-static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
- struct xdp_desc *desc)
+struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
+ struct sk_buff *allocated_skb,
+ struct xdp_desc *desc)
{
struct net_device *dev = xs->dev;
struct sk_buff *skb = xs->skb;
int err;
if (dev->priv_flags & IFF_TX_SKB_NO_LINEAR) {
- skb = xsk_build_skb_zerocopy(xs, desc);
+ skb = xsk_build_skb_zerocopy(xs, allocated_skb, desc);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
skb = NULL;
@@ -822,8 +827,12 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
if (!skb) {
hr = max(NET_SKB_PAD, L1_CACHE_ALIGN(dev->needed_headroom));
- tr = dev->needed_tailroom;
- skb = sock_alloc_send_skb(&xs->sk, hr + len + tr, 1, &err);
+ if (!allocated_skb) {
+ tr = dev->needed_tailroom;
+ skb = sock_alloc_send_skb(&xs->sk, hr + len + tr, 1, &err);
+ } else {
+ skb = allocated_skb;
+ }
if (unlikely(!skb))
goto free_err;
@@ -943,7 +952,7 @@ static int __xsk_generic_xmit(struct sock *sk)
goto out;
}
- skb = xsk_build_skb(xs, &desc);
+ skb = xsk_build_skb(xs, NULL, &desc);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
if (err != -EOVERFLOW)
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 03/14] xsk: add xsk_alloc_batch_skb() to build skbs in batch
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
Support allocating and building skbs in batch.
There are three steps for one batched allocation:
1. Reserve the skb and count the skb->truesize. It provides a way
that for later patch to speed up small data transmission by
diminishing the impact of kmalloc_reserve().
2. Add the total of truesize to sk_wmem_alloc at one time. The load and
store of sk_wmem_alloc is time-consuming, so this batch process makes
it gain the performance improvement.
3. Copy data and then finish initialization of each skb.
This patch uses kmem_cache_alloc_bulk() to complete the batch allocation
which relies on the global common cache 'net_hotdata.skbuff_cache'. Use
a xsk standalone skb cache (namely, xs->skb_cache) to store allocated
skbs instead of resorting to napi_alloc_cache that was designed for
softirq condition.
After allocating memory for each of skbs, in a 'for' loop, the patch
borrows part of __alloc_skb() to initialize skb and then calls
xsk_build_skb() to complete the rest of initialization process, like
copying data and stuff. To achieve a better result, the allocation
function only uses the function we need to keep it super clean, like
skb_set_owner_w() that is simplified into two lines of codes.
Add batch.send_queue and use the skb->list to make skbs into one chain
so that they can be easily sent which is shown in the subsequent patches.
In terms of freeing skbs process, napi_consume_skb() in the tx completion
would put the skb into global cache 'net_hotdata.skbuff_cache' that
implements the deferred freeing skb feature to avoid freeing skb one
by one to improve the performance.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
include/net/xdp_sock.h | 3 +
net/core/skbuff.c | 121 +++++++++++++++++++++++++++++++++++++++++
net/xdp/xsk.c | 7 +++
3 files changed, 131 insertions(+)
diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
index 90c709fd1239..84f0aee3fb10 100644
--- a/include/net/xdp_sock.h
+++ b/include/net/xdp_sock.h
@@ -47,8 +47,10 @@ struct xsk_map {
struct xsk_batch {
u32 generic_xmit_batch;
+ unsigned int skb_count;
struct sk_buff **skb_cache;
struct xdp_desc *desc_cache;
+ struct sk_buff_head send_queue;
};
struct xdp_sock {
@@ -136,6 +138,7 @@ INDIRECT_CALLABLE_DECLARE(void xsk_destruct_skb(struct sk_buff *));
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);
/**
* xsk_tx_metadata_to_compl - Save enough relevant metadata information
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 4045d7c484a1..f29cecacd8bb 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -83,6 +83,7 @@
#include <net/psp/types.h>
#include <net/dropreason.h>
#include <net/xdp_sock.h>
+#include <net/xsk_buff_pool.h>
#include <linux/uaccess.h>
#include <trace/events/skb.h>
@@ -647,6 +648,126 @@ static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node,
return obj;
}
+#ifdef CONFIG_XDP_SOCKETS
+int xsk_alloc_batch_skb(struct xdp_sock *xs, u32 nb_pkts, u32 nb_descs, int *err)
+{
+ struct xsk_batch *batch = &xs->batch;
+ struct xdp_desc *descs = batch->desc_cache;
+ struct sk_buff **skbs = batch->skb_cache;
+ u32 alloc_descs, base_len, wmem, sndbuf;
+ gfp_t gfp_mask = xs->sk.sk_allocation;
+ u32 skb_count = batch->skb_count;
+ struct net_device *dev = xs->dev;
+ unsigned int total_truesize = 0;
+ struct sk_buff *skb = NULL;
+ int node = NUMA_NO_NODE;
+ u32 i = 0, j, k = 0;
+ bool need_alloc;
+ u8 *data;
+
+ base_len = max(NET_SKB_PAD, L1_CACHE_ALIGN(dev->needed_headroom));
+ if (!(dev->priv_flags & IFF_TX_SKB_NO_LINEAR))
+ base_len += dev->needed_tailroom;
+
+ if (xs->skb)
+ nb_pkts--;
+
+ if (skb_count >= nb_pkts)
+ goto alloc_data;
+
+ skb_count += kmem_cache_alloc_bulk(net_hotdata.skbuff_cache,
+ gfp_mask,
+ nb_pkts - skb_count,
+ (void **)&skbs[skb_count]);
+ if (skb_count < nb_pkts)
+ nb_pkts = skb_count;
+
+alloc_data:
+ /*
+ * Phase 1: Allocate data buffers and initialize SKBs.
+ * Pre-scan descriptors to determine packet boundaries, so we can
+ * batch the sk_wmem_alloc charge in Phase 2.
+ */
+ need_alloc = !xs->skb;
+ wmem = sk_wmem_alloc_get(&xs->sk);
+ sndbuf = READ_ONCE(xs->sk.sk_sndbuf);
+ for (j = 0; j < nb_descs; j++) {
+ if (need_alloc) {
+ u32 size = base_len;
+
+ if (!(dev->priv_flags & IFF_TX_SKB_NO_LINEAR))
+ size += descs[j].len;
+
+ if (i >= nb_pkts) {
+ *err = -EAGAIN;
+ break;
+ }
+
+ if (wmem + size + total_truesize > sndbuf) {
+ *err = -EAGAIN;
+ break;
+ }
+
+ skb = skbs[skb_count - 1 - i];
+ skbuff_clear(skb);
+ data = kmalloc_reserve(&size, gfp_mask, node, skb);
+ if (unlikely(!data)) {
+ *err = -ENOBUFS;
+ break;
+ }
+ __finalize_skb_around(skb, data, size);
+ /* Replace skb_set_owner_w() with the following */
+ skb->sk = &xs->sk;
+ skb->destructor = sock_wfree;
+ total_truesize += skb->truesize;
+ i++;
+ need_alloc = false;
+ }
+ if (!xp_mb_desc(&descs[j]))
+ need_alloc = true;
+ }
+ alloc_descs = j;
+
+ /*
+ * Phase 2: Batch charge sk_wmem_alloc.
+ * One refcount_add() replaces N per-SKB skb_set_owner_w() calls,
+ * which gains much performance improvement.
+ */
+ if (total_truesize)
+ refcount_add(total_truesize, &xs->sk.sk_wmem_alloc);
+
+ /* Phase 3: Build SKBs with packet data */
+ for (j = 0; j < alloc_descs; j++) {
+ if (!xs->skb) {
+ skb = skbs[skb_count - 1 - k];
+ k++;
+ }
+
+ skb = xsk_build_skb(xs, skb, &descs[j]);
+ if (IS_ERR(skb)) {
+ *err = PTR_ERR(skb);
+ break;
+ }
+
+ if (xp_mb_desc(&descs[j])) {
+ xs->skb = skb;
+ continue;
+ }
+
+ xs->skb = NULL;
+ __skb_queue_tail(&batch->send_queue, skb);
+ }
+
+ /* Phase 4: Reclaim unused allocated SKBs */
+ while (k < i)
+ kfree_skb(skbs[skb_count - 1 - k++]);
+
+ batch->skb_count = skb_count - i;
+
+ return j;
+}
+#endif
+
/* Allocate a new skbuff. We do this ourselves so we can fill in a few
* 'private' fields and also do memory statistics to find all the
* [BEEP] leaks.
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index ecd5b9c424b8..f97bc9cf9b9a 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -25,6 +25,7 @@
#include <linux/vmalloc.h>
#include <net/xdp_sock_drv.h>
#include <net/busy_poll.h>
+#include <net/hotdata.h>
#include <net/netdev_lock.h>
#include <net/netdev_rx_queue.h>
#include <net/xdp.h>
@@ -1230,10 +1231,15 @@ static void xsk_delete_from_maps(struct xdp_sock *xs)
static void xsk_batch_reset(struct xsk_batch *batch, struct sk_buff **skbs,
struct xdp_desc *descs, unsigned int size)
{
+ if (batch->skb_count)
+ kmem_cache_free_bulk(net_hotdata.skbuff_cache,
+ batch->skb_count,
+ (void **)batch->skb_cache);
kfree(batch->skb_cache);
kvfree(batch->desc_cache);
batch->skb_cache = skbs;
batch->desc_cache = descs;
+ batch->skb_count = 0;
batch->generic_xmit_batch = size;
}
@@ -1946,6 +1952,7 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,
INIT_LIST_HEAD(&xs->map_list);
spin_lock_init(&xs->map_list_lock);
+ __skb_queue_head_init(&xs->batch.send_queue);
mutex_lock(&net->xdp.lock);
sk_add_node_rcu(sk, &net->xdp.list);
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 04/14] xsk: cache data buffers to avoid frequently calling kmalloc_reserve
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
It's beneficial for small data transmission.
Replace per-SKB kmalloc_reserve() with on-demand bulk allocation from
skb_small_head_cache for small packets.
Add a persistent per-socket data buffer cache (batch.data_cache /
batch.data_count) that survives across batch cycles, similar to how
batch.send_queue caches built SKBs.
Inside the Phase-1 per-descriptor loop, when a small packet needs a
data buffer and the cache is empty, a single kmem_cache_alloc_bulk()
refills it with generic_xmit_batch objects. Subsequent small packets
pop directly from the cache. Large packets bypass the cache entirely
and fall back to kmalloc_reserve(). Unused buffers remain in the
cache for the next batch.
I observed that kmalloc_reserve() consumes nearly 40% which seems
unavoidable at the first glance, thinking adding the bulk mechanism
should contribute to the performance. That's the motivation of this
patch. Now, the feature gives us around 10% improvement.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
include/net/xdp_sock.h | 2 ++
net/core/skbuff.c | 27 ++++++++++++++++++++++-----
net/xdp/xsk.c | 24 ++++++++++++++++++++----
3 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
index 84f0aee3fb10..2151aab8f0a1 100644
--- a/include/net/xdp_sock.h
+++ b/include/net/xdp_sock.h
@@ -51,6 +51,8 @@ struct xsk_batch {
struct sk_buff **skb_cache;
struct xdp_desc *desc_cache;
struct sk_buff_head send_queue;
+ unsigned int data_count;
+ void **data_cache;
};
struct xdp_sock {
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index f29cecacd8bb..5726b1566b2b 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -661,9 +661,11 @@ int xsk_alloc_batch_skb(struct xdp_sock *xs, u32 nb_pkts, u32 nb_descs, int *err
unsigned int total_truesize = 0;
struct sk_buff *skb = NULL;
int node = NUMA_NO_NODE;
+ void **dc = batch->data_cache;
+ unsigned int dc_count = batch->data_count;
u32 i = 0, j, k = 0;
bool need_alloc;
- u8 *data;
+ void *data;
base_len = max(NET_SKB_PAD, L1_CACHE_ALIGN(dev->needed_headroom));
if (!(dev->priv_flags & IFF_TX_SKB_NO_LINEAR))
@@ -683,6 +685,13 @@ int xsk_alloc_batch_skb(struct xdp_sock *xs, u32 nb_pkts, u32 nb_descs, int *err
nb_pkts = skb_count;
alloc_data:
+ if (dc_count < nb_pkts && !(gfp_mask & KMALLOC_NOT_NORMAL_BITS))
+ dc_count += kmem_cache_alloc_bulk(
+ net_hotdata.skb_small_head_cache,
+ gfp_mask | __GFP_NOMEMALLOC | __GFP_NOWARN,
+ batch->generic_xmit_batch - dc_count,
+ &dc[dc_count]);
+
/*
* Phase 1: Allocate data buffers and initialize SKBs.
* Pre-scan descriptors to determine packet boundaries, so we can
@@ -710,10 +719,17 @@ int xsk_alloc_batch_skb(struct xdp_sock *xs, u32 nb_pkts, u32 nb_descs, int *err
skb = skbs[skb_count - 1 - i];
skbuff_clear(skb);
- data = kmalloc_reserve(&size, gfp_mask, node, skb);
- if (unlikely(!data)) {
- *err = -ENOBUFS;
- break;
+ if (dc_count &&
+ SKB_HEAD_ALIGN(size) <= SKB_SMALL_HEAD_CACHE_SIZE) {
+ data = dc[--dc_count];
+ size = SKB_SMALL_HEAD_CACHE_SIZE;
+ } else {
+ data = kmalloc_reserve(&size, gfp_mask,
+ node, skb);
+ if (unlikely(!data)) {
+ *err = -ENOBUFS;
+ break;
+ }
}
__finalize_skb_around(skb, data, size);
/* Replace skb_set_owner_w() with the following */
@@ -762,6 +778,7 @@ int xsk_alloc_batch_skb(struct xdp_sock *xs, u32 nb_pkts, u32 nb_descs, int *err
while (k < i)
kfree_skb(skbs[skb_count - 1 - k++]);
+ batch->data_count = dc_count;
batch->skb_count = skb_count - i;
return j;
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index f97bc9cf9b9a..7a6991bc19a8 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -1229,14 +1229,22 @@ static void xsk_delete_from_maps(struct xdp_sock *xs)
}
static void xsk_batch_reset(struct xsk_batch *batch, struct sk_buff **skbs,
- struct xdp_desc *descs, unsigned int size)
-{
+ struct xdp_desc *descs, void **data,
+ unsigned int size)
+{
+ if (batch->data_count)
+ kmem_cache_free_bulk(net_hotdata.skb_small_head_cache,
+ batch->data_count,
+ batch->data_cache);
+ kfree(batch->data_cache);
if (batch->skb_count)
kmem_cache_free_bulk(net_hotdata.skbuff_cache,
batch->skb_count,
(void **)batch->skb_cache);
kfree(batch->skb_cache);
kvfree(batch->desc_cache);
+ batch->data_cache = data;
+ batch->data_count = 0;
batch->skb_cache = skbs;
batch->desc_cache = descs;
batch->skb_count = 0;
@@ -1272,7 +1280,7 @@ static int xsk_release(struct socket *sock)
xskq_destroy(xs->tx);
xskq_destroy(xs->fq_tmp);
xskq_destroy(xs->cq_tmp);
- xsk_batch_reset(&xs->batch, NULL, NULL, 0);
+ xsk_batch_reset(&xs->batch, NULL, NULL, NULL, 0);
sock_orphan(sk);
sock->sk = NULL;
@@ -1620,6 +1628,7 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,
struct xsk_batch *batch = &xs->batch;
struct xdp_desc *descs;
struct sk_buff **skbs;
+ void **data;
unsigned int size;
int ret = 0;
@@ -1638,14 +1647,21 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,
ret = -ENOMEM;
goto out;
}
+ data = kmalloc_array(size, sizeof(void *), GFP_KERNEL);
+ if (!data) {
+ kfree(skbs);
+ ret = -ENOMEM;
+ goto out;
+ }
descs = kvcalloc(size, sizeof(struct xdp_desc), GFP_KERNEL);
if (!descs) {
+ kfree(data);
kfree(skbs);
ret = -ENOMEM;
goto out;
}
- xsk_batch_reset(batch, skbs, descs, size);
+ xsk_batch_reset(batch, skbs, descs, data, size);
out:
mutex_unlock(&xs->mutex);
return ret;
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 05/14] xsk: add direct xmit in batch function
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
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
^ permalink raw reply related
* [PATCH RFC net-next v4 06/14] xsk: support dynamic xmit.more control for batch xmit
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
Only set xmit.more false for the last skb.
In theory, only making xmit.more false for the last packets to be
sent in each round can bring much benefit like avoid triggering too
many irqs.
Compared to the numbers for batch mode, a huge improvement (26%) can
be seen on i40e/ixgbe driver since the cost of triggering irqs is
expensive.
Suggested-by: Jesper Dangaard Brouer <hawk@kernel.org>
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
net/core/dev.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index e33a2406d8ca..a6abd621a7f3 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4901,6 +4901,7 @@ int xsk_direct_xmit_batch(struct xdp_sock *xs, struct net_device *dev)
struct sk_buff_head *send_queue = &xs->batch.send_queue;
int ret = NETDEV_TX_BUSY;
struct sk_buff *skb;
+ bool more = true;
local_bh_disable();
HARD_TX_LOCK(dev, txq, smp_processor_id());
@@ -4920,8 +4921,12 @@ int xsk_direct_xmit_batch(struct xdp_sock *xs, struct net_device *dev)
__skb_queue_head(send_queue, skb);
break;
}
+
+ if (!skb_peek(send_queue))
+ more = false;
+
skb_set_queue_mapping(skb, queue_id);
- ret = netdev_start_xmit(skb, dev, txq, false);
+ ret = netdev_start_xmit(skb, dev, txq, more);
if (ret != NETDEV_TX_OK) {
if (ret == NETDEV_TX_BUSY)
__skb_queue_head(send_queue, skb);
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 07/14] xsk: try to skip validating skb list in xmit path
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
This patch moves the SG check ahead, which is the only place we need to
handle very carefully because either in xsk_build_skb_zerocopy() or in
multi-buffer mode nr_frags (in skb_needs_linearize()) is used[1].
In most cases, for xsk, it's totally not needed to validate and check
the skb in validate_xmit_skb_list() that adds numerous checks in the
extremely hot path. In this kind of workload, even the overhead of
mathematical operations is not trivial.
Performance-wise, I run './xdpsock -i enp2s0f0np0 -t -S -s 64' on 1Gb/sec
ixgbe driver to verify. It stably goes up by 5.48%
[1]: https://lore.kernel.org/all/20251125115754.46793-1-kerneljasonxing@gmail.com/
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
net/core/dev.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index a6abd621a7f3..aa38993b9dd4 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4899,6 +4899,7 @@ 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;
+ bool need_validate = !(dev->features & NETIF_F_SG);
int ret = NETDEV_TX_BUSY;
struct sk_buff *skb;
bool more = true;
@@ -4906,15 +4907,17 @@ int xsk_direct_xmit_batch(struct xdp_sock *xs, struct net_device *dev)
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 (unlikely(need_validate)) {
+ 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)) {
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 08/14] xsk: rename nb_pkts to nb_descs in xsk_tx_peek_release_desc_batch
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
Rename the last parameter to nb_descs for more accurate naming. Next
patch will add a real nb_pkts parameter to help copy mode count how
many pakcets are needed.
No functional change here.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
net/xdp/xsk.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index 7a6991bc19a8..6cd2e58e170c 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -485,16 +485,16 @@ EXPORT_SYMBOL(xsk_tx_peek_desc);
static u32 xsk_tx_peek_release_fallback(struct xsk_buff_pool *pool, u32 max_entries)
{
struct xdp_desc *descs = pool->tx_descs;
- u32 nb_pkts = 0;
+ u32 nb_descs = 0;
- while (nb_pkts < max_entries && xsk_tx_peek_desc(pool, &descs[nb_pkts]))
- nb_pkts++;
+ while (nb_descs < max_entries && xsk_tx_peek_desc(pool, &descs[nb_descs]))
+ nb_descs++;
xsk_tx_release(pool);
- return nb_pkts;
+ return nb_descs;
}
-u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts)
+u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_descs)
{
struct xdp_sock *xs;
@@ -502,16 +502,16 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts)
if (!list_is_singular(&pool->xsk_tx_list)) {
/* Fallback to the non-batched version */
rcu_read_unlock();
- return xsk_tx_peek_release_fallback(pool, nb_pkts);
+ return xsk_tx_peek_release_fallback(pool, nb_descs);
}
xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock, tx_list);
if (!xs) {
- nb_pkts = 0;
+ nb_descs = 0;
goto out;
}
- nb_pkts = xskq_cons_nb_entries(xs->tx, nb_pkts);
+ nb_descs = xskq_cons_nb_entries(xs->tx, nb_descs);
/* This is the backpressure mechanism for the Tx path. Try to
* reserve space in the completion queue for all packets, but
@@ -519,23 +519,23 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts)
* packets. This avoids having to implement any buffering in
* the Tx path.
*/
- nb_pkts = xskq_prod_nb_free(pool->cq, nb_pkts);
- if (!nb_pkts)
+ nb_descs = xskq_prod_nb_free(pool->cq, nb_descs);
+ if (!nb_descs)
goto out;
- nb_pkts = xskq_cons_read_desc_batch(xs->tx, pool, nb_pkts);
- if (!nb_pkts) {
+ nb_descs = xskq_cons_read_desc_batch(xs->tx, pool, nb_descs);
+ if (!nb_descs) {
xs->tx->queue_empty_descs++;
goto out;
}
__xskq_cons_release(xs->tx);
- xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_pkts);
+ xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_descs);
xs->sk.sk_write_space(&xs->sk);
out:
rcu_read_unlock();
- return nb_pkts;
+ return nb_descs;
}
EXPORT_SYMBOL(xsk_tx_peek_release_desc_batch);
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 09/14] xsk: extend xskq_cons_read_desc_batch to count nb_pkts
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
Add a new parameter nb_pkts to count how many packets are needed
practically by copy mode with the help of XDP_PKT_CONTD option.
Add descs to provide a way to pass xs->desc_cache to store the
descriptors for copy mode.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
net/xdp/xsk_queue.h | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h
index ec08d9c102b1..354f6fe86893 100644
--- a/net/xdp/xsk_queue.h
+++ b/net/xdp/xsk_queue.h
@@ -263,12 +263,12 @@ static inline void parse_desc(struct xsk_queue *q, struct xsk_buff_pool *pool,
parsed->mb = xp_mb_desc(desc);
}
-static inline
-u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
- u32 max)
+static inline u32
+__xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
+ struct xdp_desc *descs, u32 max, u32 *nb_pkts,
+ u32 max_segs)
{
u32 cached_cons = q->cached_cons, nb_entries = 0;
- struct xdp_desc *descs = pool->tx_descs;
u32 total_descs = 0, nr_frags = 0;
/* track first entry, if stumble upon *any* invalid descriptor, rewind
@@ -288,9 +288,11 @@ u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
if (likely(!parsed.mb)) {
total_descs += (nr_frags + 1);
nr_frags = 0;
+ if (nb_pkts)
+ (*nb_pkts)++;
} else {
nr_frags++;
- if (nr_frags == pool->xdp_zc_max_segs) {
+ if (nr_frags == max_segs) {
nr_frags = 0;
break;
}
@@ -304,6 +306,14 @@ u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
return total_descs;
}
+static inline u32
+xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
+ u32 max)
+{
+ return __xskq_cons_read_desc_batch(q, pool, pool->tx_descs, max,
+ NULL, pool->xdp_zc_max_segs);
+}
+
/* Functions for consumers */
static inline void __xskq_cons_release(struct xsk_queue *q)
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 10/14] xsk: extend xsk_cq_reserve_locked() to reserve n slots
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
Previously it only reserves one slot. The patch extends it to n to cover
the batch mode.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
net/xdp/xsk.c | 12 ++++++++----
net/xdp/xsk_queue.h | 12 +++++++-----
2 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index 6cd2e58e170c..c26e26cb4dda 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -546,12 +546,17 @@ static int xsk_wakeup(struct xdp_sock *xs, u8 flags)
return dev->netdev_ops->ndo_xsk_wakeup(dev, xs->queue_id, flags);
}
-static int xsk_cq_reserve_locked(struct xsk_buff_pool *pool)
+/* The function tries to reserve as many descs as possible. If there
+ * is no single slot to allocate, return zero. Otherwise, return how
+ * many slots are available, even though it might stop reserving at
+ * certain point.
+ */
+static int xsk_cq_reserve_locked(struct xsk_buff_pool *pool, u32 n)
{
int ret;
spin_lock(&pool->cq->cq_cached_prod_lock);
- ret = xskq_prod_reserve(pool->cq);
+ ret = xskq_prod_reserve(pool->cq, n);
spin_unlock(&pool->cq->cq_cached_prod_lock);
return ret;
@@ -947,8 +952,7 @@ static int __xsk_generic_xmit(struct sock *sk)
* if there is space in it. This avoids having to implement
* any buffering in the Tx path.
*/
- err = xsk_cq_reserve_locked(xs->pool);
- if (err) {
+ if (!xsk_cq_reserve_locked(xs->pool, 1)) {
err = -EAGAIN;
goto out;
}
diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h
index 354f6fe86893..34cc07d6115e 100644
--- a/net/xdp/xsk_queue.h
+++ b/net/xdp/xsk_queue.h
@@ -413,14 +413,16 @@ static inline void xskq_prod_cancel_n(struct xsk_queue *q, u32 cnt)
q->cached_prod -= cnt;
}
-static inline int xskq_prod_reserve(struct xsk_queue *q)
+static inline int xskq_prod_reserve(struct xsk_queue *q, u32 n)
{
- if (xskq_prod_is_full(q))
- return -ENOSPC;
+ u32 nr_free = xskq_prod_nb_free(q, n);
+
+ if (!nr_free)
+ return 0;
/* A, matches D */
- q->cached_prod++;
- return 0;
+ q->cached_prod += nr_free;
+ return nr_free;
}
static inline int xskq_prod_reserve_addr(struct xsk_queue *q, u64 addr)
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 11/14] xsk: support batch xmit main logic
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
This function __xsk_generic_xmit_batch() is the core function in batches
xmit, implement a batch version of __xsk_generic_xmit().
The whole logic is divided into sections:
1. check if we have enough available slots in tx ring and completion
ring.
2. read descriptors from tx ring into pool->tx_descs in batches
3. reserve enough slots in completion ring to avoid backpressure
4. allocate and build skbs in batches
5. send all the possible packets in batches at one time
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
net/xdp/xsk.c | 116 ++++++++++++++++++++++++++++++++++++++++++++
net/xdp/xsk_queue.h | 8 +++
2 files changed, 124 insertions(+)
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index c26e26cb4dda..e1ad2ac2b39a 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -920,6 +920,122 @@ struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
return ERR_PTR(err);
}
+static int __xsk_generic_xmit_batch(struct xdp_sock *xs)
+{
+ struct xsk_buff_pool *pool = xs->pool;
+ struct xsk_batch *batch = &xs->batch;
+ struct xdp_desc *descs = batch->desc_cache;
+ struct net_device *dev = xs->dev;
+ u32 max_batch, max_budget;
+ bool sent_frame = false;
+ struct sk_buff *skb;
+ u32 cons_descs;
+ int err = 0;
+ u32 i = 0;
+
+ mutex_lock(&xs->mutex);
+
+ /* Since we dropped the RCU read lock, the socket state might have changed. */
+ if (unlikely(!xsk_is_bound(xs))) {
+ err = -ENXIO;
+ goto out;
+ }
+
+ if (xs->queue_id >= dev->real_num_tx_queues) {
+ err = -ENXIO;
+ goto out;
+ }
+
+ if (unlikely(!netif_running(dev) || !netif_carrier_ok(dev))) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ max_budget = READ_ONCE(xs->max_tx_budget);
+ max_batch = batch->generic_xmit_batch;
+
+ for (i = 0; i < max_budget; i += cons_descs) {
+ u32 nb_pkts = 0;
+ u32 nb_descs;
+
+ nb_descs = min(max_batch, max_budget - i);
+ nb_descs = xskq_cons_nb_entries(xs->tx, nb_descs);
+ if (!nb_descs)
+ goto out;
+
+ /* This is the backpressure mechanism for the Tx path. Try to
+ * reserve space in the completion queue for all packets, but
+ * if there are fewer slots available, just process that many
+ * packets. This avoids having to implement any buffering in
+ * the Tx path.
+ */
+ nb_descs = xsk_cq_reserve_locked(pool, nb_descs);
+ if (!nb_descs) {
+ err = -EAGAIN;
+ goto out;
+ }
+
+ cons_descs = xskq_cons_read_desc_batch_copy(xs->tx, pool, descs,
+ nb_descs, &nb_pkts);
+ if (cons_descs < nb_descs) {
+ u32 delta = nb_descs - cons_descs;
+
+ xsk_cq_cancel_locked(pool, delta);
+ xs->tx->queue_empty_descs += delta;
+ if (!cons_descs) {
+ err = -EAGAIN;
+ goto out;
+ }
+ nb_descs = cons_descs;
+ }
+
+ cons_descs = xsk_alloc_batch_skb(xs, nb_pkts, nb_descs, &err);
+ /* Return 'nb_descs - cons_descs' number of descs to the
+ * pool if the batch allocation partially fails
+ */
+ if (cons_descs < nb_descs) {
+ xskq_cons_cancel_n(xs->tx, nb_descs - cons_descs);
+ xsk_cq_cancel_locked(pool, nb_descs - cons_descs);
+ }
+
+ if (!skb_queue_empty(&batch->send_queue)) {
+ int err_xmit;
+
+ err_xmit = xsk_direct_xmit_batch(xs, dev);
+ if (err_xmit == NETDEV_TX_BUSY)
+ err = -EAGAIN;
+ else if (err_xmit == NET_XMIT_DROP)
+ err = -EBUSY;
+
+ sent_frame = true;
+ }
+
+ if (err)
+ goto out;
+ }
+
+ /* Maximum budget of descriptors have been consumed */
+ if (xskq_has_descs(xs->tx))
+ err = -EAGAIN;
+
+out:
+ if (xs->skb)
+ xsk_drop_skb(xs->skb);
+
+ /* If send_queue has more pending skbs, we must to clear
+ * the rest of them.
+ */
+ while ((skb = __skb_dequeue(&batch->send_queue)) != NULL) {
+ xskq_cons_cancel_n(xs->tx, xsk_get_num_desc(skb));
+ xsk_consume_skb(skb);
+ }
+ if (sent_frame)
+ __xsk_tx_release(xs);
+
+ mutex_unlock(&xs->mutex);
+ return err;
+}
+
static int __xsk_generic_xmit(struct sock *sk)
{
struct xdp_sock *xs = xdp_sk(sk);
diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h
index 34cc07d6115e..c3b97c6f2910 100644
--- a/net/xdp/xsk_queue.h
+++ b/net/xdp/xsk_queue.h
@@ -314,6 +314,14 @@ xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
NULL, pool->xdp_zc_max_segs);
}
+static inline u32
+xskq_cons_read_desc_batch_copy(struct xsk_queue *q, struct xsk_buff_pool *pool,
+ struct xdp_desc *descs, u32 max, u32 *nb_pkts)
+{
+ return __xskq_cons_read_desc_batch(q, pool, descs, max,
+ nb_pkts, MAX_SKB_FRAGS);
+}
+
/* Functions for consumers */
static inline void __xskq_cons_release(struct xsk_queue *q)
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 12/14] xsk: separate read-mostly and write-heavy fields in xsk_buff_pool
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
perf c2c profiling of the AF_XDP generic-copy batch TX path reveals
that ~45% of all cache-line contention (HITM) comes from a single
cacheline inside struct xsk_buff_pool.
The sendmsg CPU reads pool geometry fields (addrs, chunk_size,
headroom, tx_metadata_len, etc.) in the validate and build hot
path, while the NAPI TX-completion CPU writes cq_prod_lock (via
xsk_destruct_skb -> xsk_cq_submit_addr_locked) and
cached_need_wakeup (via xsk_set/clear_tx_need_wakeup) on the same
cacheline—classic false sharing.
This adds one extra cacheline (64 bytes) to the per-pool allocation
but eliminates cross-CPU false sharing between the TX sendmsg and
TX completion paths.
This reorganization improves overall performance by 5-6%, which can
be captured by xdpsock.
After this, the only one hotpot is 6% refcount process, which has
already been batched to minimize the impact in the series.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
include/net/xsk_buff_pool.h | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h
index ccb3b350001f..b1b11e3aa273 100644
--- a/include/net/xsk_buff_pool.h
+++ b/include/net/xsk_buff_pool.h
@@ -73,23 +73,27 @@ struct xsk_buff_pool {
u64 addrs_cnt;
u32 free_list_cnt;
u32 dma_pages_cnt;
- u32 free_heads_cnt;
+
+ /* Read-mostly fields */
u32 headroom;
u32 chunk_size;
u32 chunk_shift;
u32 frame_len;
u32 xdp_zc_max_segs;
u8 tx_metadata_len; /* inherited from umem */
- u8 cached_need_wakeup;
bool uses_need_wakeup;
bool unaligned;
bool tx_sw_csum;
void *addrs;
+
+ /* Write-heavy fields */
/* Mutual exclusion of the completion ring in the SKB mode.
* Protect: NAPI TX thread and sendmsg error paths in the SKB
* destructor callback.
*/
- spinlock_t cq_prod_lock;
+ spinlock_t cq_prod_lock ____cacheline_aligned_in_smp;
+ u8 cached_need_wakeup;
+ u32 free_heads_cnt;
struct xdp_buff_xsk *free_heads[];
};
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 13/14] xsk: retire old xmit path in copy mode
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
Add a new helper xsk_init_batch() used in xsk_create() with the default
value 1.
Obsolete __xsk_generic_xmit.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
net/xdp/xsk.c | 151 +++++++++++++-------------------------------------
1 file changed, 37 insertions(+), 114 deletions(-)
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index e1ad2ac2b39a..be341290e42c 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -1036,101 +1036,14 @@ static int __xsk_generic_xmit_batch(struct xdp_sock *xs)
return err;
}
-static int __xsk_generic_xmit(struct sock *sk)
-{
- struct xdp_sock *xs = xdp_sk(sk);
- bool sent_frame = false;
- struct xdp_desc desc;
- struct sk_buff *skb;
- u32 max_batch;
- int err = 0;
-
- mutex_lock(&xs->mutex);
-
- /* Since we dropped the RCU read lock, the socket state might have changed. */
- if (unlikely(!xsk_is_bound(xs))) {
- err = -ENXIO;
- goto out;
- }
-
- if (xs->queue_id >= xs->dev->real_num_tx_queues)
- goto out;
-
- max_batch = READ_ONCE(xs->max_tx_budget);
- while (xskq_cons_peek_desc(xs->tx, &desc, xs->pool)) {
- if (max_batch-- == 0) {
- err = -EAGAIN;
- goto out;
- }
-
- /* 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 (!xsk_cq_reserve_locked(xs->pool, 1)) {
- err = -EAGAIN;
- goto out;
- }
-
- skb = xsk_build_skb(xs, NULL, &desc);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- if (err != -EOVERFLOW)
- goto out;
- err = 0;
- continue;
- }
-
- xskq_cons_release(xs->tx);
-
- if (xp_mb_desc(&desc)) {
- xs->skb = skb;
- continue;
- }
-
- err = __dev_direct_xmit(skb, xs->queue_id);
- if (err == NETDEV_TX_BUSY) {
- /* Tell user-space to retry the send */
- xskq_cons_cancel_n(xs->tx, xsk_get_num_desc(skb));
- xsk_consume_skb(skb);
- err = -EAGAIN;
- goto out;
- }
-
- /* Ignore NET_XMIT_CN as packet might have been sent */
- if (err == NET_XMIT_DROP) {
- /* SKB completed but not sent */
- err = -EBUSY;
- xs->skb = NULL;
- goto out;
- }
-
- sent_frame = true;
- xs->skb = NULL;
- }
-
- if (xskq_has_descs(xs->tx)) {
- if (xs->skb)
- xsk_drop_skb(xs->skb);
- xskq_cons_release(xs->tx);
- }
-
-out:
- if (sent_frame)
- __xsk_tx_release(xs);
-
- mutex_unlock(&xs->mutex);
- return err;
-}
-
static int xsk_generic_xmit(struct sock *sk)
{
+ struct xdp_sock *xs = xdp_sk(sk);
int ret;
/* Drop the RCU lock since the SKB path might sleep. */
rcu_read_unlock();
- ret = __xsk_generic_xmit(sk);
+ ret = __xsk_generic_xmit_batch(xs);
/* Reaquire RCU lock before going into common code. */
rcu_read_lock();
@@ -1626,6 +1539,34 @@ struct xdp_umem_reg_v1 {
__u32 headroom;
};
+static int xsk_init_batch(struct xsk_batch *batch, unsigned int size)
+{
+ struct xdp_desc *descs;
+ struct sk_buff **skbs;
+ void **data;
+
+ skbs = kmalloc(size * sizeof(struct sk_buff *), GFP_KERNEL);
+ if (!skbs)
+ return -ENOMEM;
+
+ data = kmalloc_array(size, sizeof(void *), GFP_KERNEL);
+ if (!data) {
+ kfree(skbs);
+ return -ENOMEM;
+ }
+
+ descs = kvcalloc(size, sizeof(struct xdp_desc), GFP_KERNEL);
+ if (!descs) {
+ kfree(data);
+ kfree(skbs);
+ return -ENOMEM;
+ }
+
+ xsk_batch_reset(batch, skbs, descs, data, size);
+
+ return 0;
+}
+
static int xsk_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
@@ -1746,9 +1687,6 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,
{
struct xsk_buff_pool *pool = xs->pool;
struct xsk_batch *batch = &xs->batch;
- struct xdp_desc *descs;
- struct sk_buff **skbs;
- void **data;
unsigned int size;
int ret = 0;
@@ -1762,27 +1700,7 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname,
return -EACCES;
mutex_lock(&xs->mutex);
- skbs = kmalloc(size * sizeof(struct sk_buff *), GFP_KERNEL);
- if (!skbs) {
- ret = -ENOMEM;
- goto out;
- }
- data = kmalloc_array(size, sizeof(void *), GFP_KERNEL);
- if (!data) {
- kfree(skbs);
- ret = -ENOMEM;
- goto out;
- }
- descs = kvcalloc(size, sizeof(struct xdp_desc), GFP_KERNEL);
- if (!descs) {
- kfree(data);
- kfree(skbs);
- ret = -ENOMEM;
- goto out;
- }
-
- xsk_batch_reset(batch, skbs, descs, data, size);
-out:
+ ret = xsk_init_batch(batch, size);
mutex_unlock(&xs->mutex);
return ret;
}
@@ -2056,6 +1974,7 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,
{
struct xdp_sock *xs;
struct sock *sk;
+ int ret;
if (!ns_capable(net->user_ns, CAP_NET_RAW))
return -EPERM;
@@ -2071,6 +1990,11 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,
if (!sk)
return -ENOBUFS;
+ xs = xdp_sk(sk);
+ ret = xsk_init_batch(&xs->batch, 1);
+ if (ret)
+ return ret;
+
sock->ops = &xsk_proto_ops;
sock_init_data(sock, sk);
@@ -2081,7 +2005,6 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,
sock_set_flag(sk, SOCK_RCU_FREE);
- xs = xdp_sk(sk);
xs->state = XSK_READY;
xs->max_tx_budget = TX_BATCH_SIZE;
mutex_init(&xs->mutex);
--
2.41.3
^ permalink raw reply related
* [PATCH RFC net-next v4 14/14] xsk: optimize xsk_build_skb for batch copy-mode fast path
From: Jason Xing @ 2026-04-15 8:26 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
john.fastabend
Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260415082654.21026-1-kerneljasonxing@gmail.com>
From: Jason Xing <kernelxing@tencent.com>
Three targeted optimizations for the batch copy-mode TX hot path:
Replace skb_store_bits() with memcpy() for single-buffer first-desc
path. After skb_reserve() + skb_put(), the SKB is freshly allocated
with all data in the linear area and no frags, so skb_store_bits()
degenerates to memcpy(skb->data, buffer, len) but carries unnecessary
function call overhead, offset validation, and frag iteration logic.
Inline UMEM address computation in Phase 3 and pass the pre-computed
buffer pointer to xsk_build_skb(), avoiding the per-packet non-inlined
xp_raw_get_data() (EXPORT_SYMBOL) call chain:
xsk_buff_raw_get_data -> xp_raw_get_data -> __xp_raw_get_addr +
__xp_raw_get_data.
In the batch loop the pool->addrs and pool->unaligned are invariant,
so we cache them once and compute each buffer address inline.
Prefetch the *next* descriptor's UMEM data buffer at the top of the
Phase 3 loop, hiding the memory latency of the upcoming memcpy.
It improves 3-4% performance stably.
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
include/net/xdp_sock.h | 3 ++-
net/core/skbuff.c | 18 ++++++++++++++++--
net/xdp/xsk.c | 15 ++++++---------
3 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
index 0609e3b04279..5e05236c7fba 100644
--- a/include/net/xdp_sock.h
+++ b/include/net/xdp_sock.h
@@ -139,7 +139,8 @@ void __xsk_map_flush(struct list_head *flush_list);
INDIRECT_CALLABLE_DECLARE(void xsk_destruct_skb(struct sk_buff *));
struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
struct sk_buff *allocated_skb,
- struct xdp_desc *desc);
+ struct xdp_desc *desc,
+ void *buffer);
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);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 5726b1566b2b..bef5270e6332 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -752,14 +752,28 @@ int xsk_alloc_batch_skb(struct xdp_sock *xs, u32 nb_pkts, u32 nb_descs, int *err
if (total_truesize)
refcount_add(total_truesize, &xs->sk.sk_wmem_alloc);
- /* Phase 3: Build SKBs with packet data */
+ /* Phase 3: Build SKBs with packet data. */
+ struct xsk_buff_pool *pool = xs->pool;
+ void *pool_addrs = pool->addrs;
+ bool unaligned = pool->unaligned;
+
for (j = 0; j < alloc_descs; j++) {
+ u64 addr = descs[j].addr;
+ void *buffer;
+
+ if (unaligned)
+ addr = xp_unaligned_add_offset_to_addr(addr);
+ buffer = pool_addrs + addr;
+
+ if (j + 1 < alloc_descs)
+ prefetch(pool_addrs + descs[j + 1].addr);
+
if (!xs->skb) {
skb = skbs[skb_count - 1 - k];
k++;
}
- skb = xsk_build_skb(xs, skb, &descs[j]);
+ skb = xsk_build_skb(xs, skb, &descs[j], buffer);
if (IS_ERR(skb)) {
*err = PTR_ERR(skb);
break;
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index be341290e42c..3bf81b838075 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -811,7 +811,8 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
struct sk_buff *allocated_skb,
- struct xdp_desc *desc)
+ struct xdp_desc *desc,
+ void *buffer)
{
struct net_device *dev = xs->dev;
struct sk_buff *skb = xs->skb;
@@ -825,11 +826,10 @@ struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
goto free_err;
}
} else {
- u32 hr, tr, len;
- void *buffer;
+ u32 hr, tr, len = desc->len;
- buffer = xsk_buff_raw_get_data(xs->pool, desc->addr);
- len = desc->len;
+ if (!buffer)
+ buffer = xsk_buff_raw_get_data(xs->pool, desc->addr);
if (!skb) {
hr = max(NET_SKB_PAD, L1_CACHE_ALIGN(dev->needed_headroom));
@@ -844,10 +844,7 @@ struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
skb_reserve(skb, hr);
skb_put(skb, len);
-
- err = skb_store_bits(skb, 0, buffer, len);
- if (unlikely(err))
- goto free_err;
+ memcpy(skb->data, buffer, len);
xsk_skb_init_misc(skb, xs, desc->addr);
if (desc->options & XDP_TX_METADATA) {
--
2.41.3
^ permalink raw reply related
* [PATCH v12 net-next 00/11] nbl driver for Nebulamatrix NICs
From: illusion.wang @ 2026-04-15 8:29 UTC (permalink / raw)
To: netdev; +Cc: illusion.wang, open list
In-Reply-To: <20260415033608.2438-2-illusion.wang@nebula-matrix.com>
This patch series represents the first phase. We plan to integrate it in
two phases: the first phase covers mailbox and chip configuration,
while the second phase involves net dev configuration.
Together, they will provide basic PF-based Ethernet port transmission and
reception capabilities.
After that, we will consider other features, such as ethtool support,
flow management, adminq messaging, VF support, debugfs support, etc.
changes v11->v12
Link to v10:https://lore.kernel.org/netdev/20260408093739.56001-1-illusion.wang@nebula-matrix.com/
AI review issues
changes v10->v11
Link to v10:https://lore.kernel.org/netdev/20260401022318.28550-1-illusion.wang@nebula-matrix.com/
1.Issues found by Mohsin
2.AI review issues
changes v9->v10
Link to v9:https://lore.kernel.org/netdev/20260325040048.2313-1-illusion.wang@nebula-matrix.com/
1.Issues found by Jakub
2.AI review issue
changes v8->v9
Link to v8:https://lore.kernel.org/netdev/20260317034533.5600-1-illusion.wang@nebula-matrix.com/
1.Issues found by Jakub
2.AI review issue
Changes v7→v8
Link to v7:https://lore.kernel.org/netdev/20260310120959.22015-1-illusion.wang@nebula-matrix.com/
1.Issues found by Paolo
Changes v6->v7
Link to v6:https://lore.kernel.org/netdev/20260306033451.5196-1-illusion.wang@nebula-matrix.com/
1.Issue found by Jakub
2.AI review issue
Changes v5->v6
Link to V5:https://lore.kernel.org/netdev/20260226073840.3222-1-illusion.wang@nebula-matrix.com/
1.put all standard linux includes files the .c file which needs it & others
--Andrew
2.AI review issue
Changes v4->v5
Link to V4:https://lore.kernel.org/netdev/20260206021608.85381-1-illusion.wang@nebula-matrix.com/
1.change nbl_core to nbl & change ** pointers to *pointers & others
--Andrew
2.AI review issue
Changes v3->v4
Link to v3: https://lore.kernel.org/netdev/20260123011804.31263-1-illusion.wang@nebula-matrix.com
1.cut down to part of a mini driver(mailbox and chip init)
--Jakub Kicinski Simon Horman(some sort of staged approached)
2.modify issues found by ai.
3. Reverse Christmas tree/nbl_err/devm_kfree/remove some macros/
void type to real type/others
--Andrew Lunn
4.change deprecated pci_enable_msix_range to pci_alloc_irq_vectors
5.delete service layer
6.the style of kconfig---Randy Dunlap
7.add to Documentation/networking/device_drivers/ethernet/index.rst
--Simon Horman
Changes v2 →v3
Link to v2: https://lore.kernel.org/netdev/20260109100146.63569-1-illusion.wang@nebula-matrix.com/
1.cut down to a mini driver:
delete vf support
use promisc mode to cut down flow management
drop patch15 in v2
delete adminq msg
delete abnormal handling
delete some unimportant interfaces
2.modify issues found by ai review
Changes v1->v2
Link to v1: https://lore.kernel.org/netdev/20251223035113.31122-1-illusion.wang@nebula-matrix.com/
1.Format Issues and Compilation Issues
- Paolo Abeni
2.add sysfs patch and drop coexisting patch
- Andrew Lunn
3.delete some unimportant ndo operations
4.add machine generated headers patch
5.Modify the issues found in patch1-2 and apply the same fixes to other
patches
6.modify issues found by nipa
illusion.wang (11):
net/nebula-matrix: add minimum nbl build framework
net/nebula-matrix: add our driver architecture
net/nebula-matrix: add chip related definitions
net/nebula-matrix: channel msg value and msg struct
net/nebula-matrix: add channel layer
net/nebula-matrix: add common resource implementation
net/nebula-matrix: add intr resource implementation
net/nebula-matrix: add vsi resource implementation
net/nebula-matrix: add Dispatch layer implementation
net/nebula-matrix: add common/ctrl dev init/reinit operation
net/nebula-matrix: add common dev start/stop operation
.../device_drivers/ethernet/index.rst | 1 +
.../ethernet/nebula-matrix/nbl.rst | 27 +
MAINTAINERS | 10 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/nebula-matrix/Kconfig | 34 +
drivers/net/ethernet/nebula-matrix/Makefile | 6 +
.../net/ethernet/nebula-matrix/nbl/Makefile | 16 +
.../nbl/nbl_channel/nbl_channel.c | 843 +++++
.../nbl/nbl_channel/nbl_channel.h | 163 +
.../nebula-matrix/nbl/nbl_common/nbl_common.c | 214 ++
.../nebula-matrix/nbl/nbl_common/nbl_common.h | 33 +
.../net/ethernet/nebula-matrix/nbl/nbl_core.h | 59 +
.../nebula-matrix/nbl/nbl_core/nbl_dev.c | 441 +++
.../nebula-matrix/nbl/nbl_core/nbl_dev.h | 58 +
.../nebula-matrix/nbl/nbl_core/nbl_dispatch.c | 487 +++
.../nebula-matrix/nbl/nbl_core/nbl_dispatch.h | 56 +
.../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c | 797 +++++
.../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h | 493 +++
.../nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.c | 2901 +++++++++++++++++
.../nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.h | 11 +
.../nbl_hw_leonis/nbl_resource_leonis.c | 264 ++
.../nbl_hw_leonis/nbl_resource_leonis.h | 10 +
.../nebula-matrix/nbl/nbl_hw/nbl_hw_reg.h | 68 +
.../nebula-matrix/nbl/nbl_hw/nbl_interrupt.c | 246 ++
.../nebula-matrix/nbl/nbl_hw/nbl_interrupt.h | 12 +
.../nebula-matrix/nbl/nbl_hw/nbl_resource.c | 118 +
.../nebula-matrix/nbl/nbl_hw/nbl_resource.h | 116 +
.../nebula-matrix/nbl/nbl_hw/nbl_vsi.c | 51 +
.../nebula-matrix/nbl/nbl_hw/nbl_vsi.h | 11 +
.../nbl/nbl_include/nbl_def_channel.h | 362 ++
.../nbl/nbl_include/nbl_def_common.h | 80 +
.../nbl/nbl_include/nbl_def_dev.h | 16 +
.../nbl/nbl_include/nbl_def_dispatch.h | 42 +
.../nbl/nbl_include/nbl_def_hw.h | 54 +
.../nbl/nbl_include/nbl_def_resource.h | 37 +
.../nbl/nbl_include/nbl_include.h | 79 +
.../nbl/nbl_include/nbl_product_base.h | 19 +
.../net/ethernet/nebula-matrix/nbl/nbl_main.c | 329 ++
39 files changed, 8566 insertions(+)
create mode 100644 Documentation/networking/device_drivers/ethernet/nebula-matrix/nbl.rst
create mode 100644 drivers/net/ethernet/nebula-matrix/Kconfig
create mode 100644 drivers/net/ethernet/nebula-matrix/Makefile
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/Makefile
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_reg.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_product_base.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
--
2.47.3
^ permalink raw reply
* Re: [PATCH iwl-next 1/1] igc: add support for forcing link speed without autonegotiation
From: Abdul Rahim, Faizal @ 2026-04-15 8:37 UTC (permalink / raw)
To: Simon Horman, khai.wen.tan
Cc: anthony.l.nguyen, przemyslaw.kitszel, andrew+netdev, davem,
edumazet, kuba, pabeni, intel-wired-lan, netdev, linux-kernel,
faizal.abdul.rahim, hector.blanco.alcaine, hong.aun.looi,
khai.wen.tan
In-Reply-To: <20260413145215.718532-1-horms@kernel.org>
On 13/4/2026 10:52 pm, Simon Horman wrote:
> From: 'Simon Horman' <horms@kernel.org>
>
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> ---
> igc: add support for forcing link speed without autonegotiation
>
> This commit allows forcing 10/100 Mb/s link speed and duplex settings via
> ethtool when autonegotiation is disabled. It configures the MAC CTRL and
> PHY registers to bypass autonegotiation and apply the requested settings.
>
>> diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
>> index 0122009bedd03..e14771532dad8 100644
>> --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
>> +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
> [ ... ]
>> +static int igc_handle_autoneg_disabled(struct net_device *netdev, u32 speed,
>> + u8 duplex)
>> +{
>> + struct igc_adapter *adapter = netdev_priv(netdev);
>> + struct igc_mac_info *mac = &adapter->hw.mac;
>> + enum igc_forced_speed_duplex forced_speed_duplex;
>>
>> + switch (speed) {
>> + case SPEED_10:
>> + forced_speed_duplex = (duplex == DUPLEX_FULL) ? IGC_FORCED_10F : IGC_FORCED_10H;
>> + break;
>> + case SPEED_100:
>> + forced_speed_duplex = (duplex == DUPLEX_FULL) ? IGC_FORCED_100F : IGC_FORCED_100H;
>> + break;
>
> Does forcing a half-duplex configuration here also require setting flow
> control to none (e.g., hw->fc.requested_mode = igc_fc_none)?
>
>> diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c
>> index 7ac6637f8db73..16b0f2db06896 100644
>> --- a/drivers/net/ethernet/intel/igc/igc_mac.c
>> +++ b/drivers/net/ethernet/intel/igc/igc_mac.c
>> @@ -452,7 +452,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw)
>> * so we had to force link. In this case, we need to force the
>> * configuration of the MAC to match the "fc" parameter.
>> */
>> - if (mac->autoneg_failed)
>> + if (mac->autoneg_failed || !mac->autoneg_enabled)
>> ret_val = igc_force_mac_fc(hw);
>
> If mac->autoneg_enabled is false, igc_force_mac_fc() will configure the MAC
> using the existing hw->fc.current_mode. If current_mode defaults to full flow
> control, this will enable PAUSE frames.
>
> Since autonegotiation is disabled, the MII status check further down in this
> same function will fail and return early:
>
> drivers/net/ethernet/intel/igc/igc_mac.c:igc_config_fc_after_link_up() {
> ...
> if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
> hw_dbg("Copper PHY and Auto Neg has not completed.\n");
> goto out;
> }
> ...
> }
>
> This early return skips the half-duplex safety check at the end of
> igc_config_fc_after_link_up() which would normally clear the flow control
> settings:
>
> drivers/net/ethernet/intel/igc/igc_mac.c:igc_config_fc_after_link_up() {
> ...
> if (duplex == HALF_DUPLEX)
> hw->fc.current_mode = igc_fc_none;
>
> ret_val = igc_force_mac_fc(hw);
> ...
> }
>
> Does this leave the MAC incorrectly configured to send and receive PAUSE
> frames on a half-duplex link, violating the IEEE 802.3 specification?
Hi Simon,
Thanks for the review.
Yes, you're right, I missed that interaction.
I’ll update the code to also set the following in
igc_handle_autoneg_disabled() when forcing half‑duplex:
hw->fc.requested_mode = igc_fc_none
I’ll test it and, if everything looks good, send out v2.
Thanks again!
^ permalink raw reply
* Re: [PATCH v2] vsock/virtio: fix accept queue count leak on transport mismatch
From: Luigi Leonardi @ 2026-04-15 8:44 UTC (permalink / raw)
To: Dudu Lu; +Cc: netdev, stefanha, sgarzare, mst, jasowang
In-Reply-To: <20260413131409.19022-1-phx0fer@gmail.com>
On Mon, Apr 13, 2026 at 09:14:09PM +0800, Dudu Lu wrote:
>virtio_transport_recv_listen() calls sk_acceptq_added() before
>vsock_assign_transport(). If vsock_assign_transport() fails or
>selects a different transport, the error path returns without
>calling sk_acceptq_removed(), permanently incrementing
>sk_ack_backlog.
>
>After approximately backlog+1 such failures, sk_acceptq_is_full()
>returns true, causing the listener to reject all new connections.
>
>Fix by moving sk_acceptq_added() to after the transport validation,
>matching the pattern used by vmci_transport and hyperv_transport.
>
>Fixes: c0cfa2d8a788 ("vsock: add multi-transports support")
>Signed-off-by: Dudu Lu <phx0fer@gmail.com>
>---
You forgot to add the `net` tag to the patch.
Other than that, the code LGTM and testing passed.
Reviewed-by: Luigi Leonardi <leonardi@redhat.com>
> net/vmw_vsock/virtio_transport_common.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
>diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
>index 8a9fb23c6e85..e01d983488e5 100644
>--- a/net/vmw_vsock/virtio_transport_common.c
>+++ b/net/vmw_vsock/virtio_transport_common.c
>@@ -1560,8 +1560,6 @@ virtio_transport_recv_listen(struct sock *sk, struct sk_buff *skb,
> return -ENOMEM;
> }
>
>- sk_acceptq_added(sk);
>-
> lock_sock_nested(child, SINGLE_DEPTH_NESTING);
>
> child->sk_state = TCP_ESTABLISHED;
>@@ -1583,6 +1581,7 @@ virtio_transport_recv_listen(struct sock *sk, struct sk_buff *skb,
> return ret;
> }
>
>+ sk_acceptq_added(sk);
> if (virtio_transport_space_update(child, skb))
> child->sk_write_space(child);
>
>--
>2.39.3 (Apple Git-145)
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox