* [net-next v9 07/10] net: bnxt: Implement software USO
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Implement bnxt_sw_udp_gso_xmit() using the core tso_dma_map API and
the pre-allocated TX inline buffer for per-segment headers.
The xmit path:
1. Calls tso_start() to initialize TSO state
2. Stack-allocates a tso_dma_map and calls tso_dma_map_init() to
DMA-map the linear payload and all frags upfront.
3. For each segment:
- Copies and patches headers via tso_build_hdr() into the
pre-allocated tx_inline_buf (DMA-synced per segment)
- Counts payload BDs via tso_dma_map_count()
- Emits long BD (header) + ext BD + payload BDs
- Payload BDs use tso_dma_map_next() which yields (dma_addr,
chunk_len, mapping_len) tuples.
Header BDs set dma_unmap_len=0 since the inline buffer is pre-allocated
and unmapped only at ring teardown.
Completion state is updated by calling tso_dma_map_completion_save() for
the last segment.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joe Damato <joe@dama.to>
---
v9:
- Added inline slot check to prevent possible overwriting of in-flight
headers (suggested by AI).
- Set TX_BD_FLAGS_IP_CKSUM conditionally on !tso.ipv6 (suggested by AI).
v8:
- Zero csum fields on per-segment header copy after tso_build_hdr()
instead of on the original skb, avoiding the need for skb_cow_head, as
suggested by Eric Dumazet.
v7:
- Dropped Pavan's Reviewed-by as some changes were made.
- Updated struct bnxt_sw_tx_bd to embed a tso_dma_map_completion_state
struct for tracking completion state.
- Dropped an unnecessary slot check.
- Eliminated an ugly looking ternary to simplify the code.
- Call tso_dma_map_completion_save to update completion state.
v6:
- Addressed Paolo's feedback where the IOVA API could fail transiently,
leaving stale state in iova_state. Fix this by always copying the state,
noting that dma_iova_try_alloc is called unconditionally in the
tso_dma_map_init function (via tso_dma_iova_try), which zeroes the state
even if the API can't be used.
- Since this was a very minor change, I retained Pavan's Reviewed-by.
v5:
- Added __maybe_unused to last_unmap_len and last_unmap_addr to silence a
build warning when CONFIG_NEED_DMA_MAP_STATE is disabled. No functional
changes.
- Added Pavan's Reviewed-by.
v4:
- Fixed the early return issue Pavan pointed out when num_segs <= 1; use the
drop label instead of returning.
v3:
- Added iova_state and iova_total_len to struct bnxt_sw_tx_bd.
- Stores iova_state on the last segment's tx_buf during xmit.
rfcv2:
- set the unmap len on the last descriptor, so that when completions fire
only the last completion unmaps the region.
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 3 +
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c | 214 ++++++++++++++++++
2 files changed, 217 insertions(+)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 6b38b84924e0..fe50576ae525 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -11,6 +11,8 @@
#ifndef BNXT_H
#define BNXT_H
+#include <net/tso.h>
+
#define DRV_MODULE_NAME "bnxt_en"
/* DO NOT CHANGE DRV_VER_* defines
@@ -899,6 +901,7 @@ struct bnxt_sw_tx_bd {
u16 rx_prod;
u16 txts_prod;
};
+ struct tso_dma_map_completion_state sw_gso_cstate;
};
#define BNXT_SW_GSO_MID 1
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
index b296769ee4fe..0d4a59aae88e 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
@@ -19,11 +19,225 @@
#include "bnxt.h"
#include "bnxt_gso.h"
+static u32 bnxt_sw_gso_lhint(unsigned int len)
+{
+ if (len <= 512)
+ return TX_BD_FLAGS_LHINT_512_AND_SMALLER;
+ else if (len <= 1023)
+ return TX_BD_FLAGS_LHINT_512_TO_1023;
+ else if (len <= 2047)
+ return TX_BD_FLAGS_LHINT_1024_TO_2047;
+ else
+ return TX_BD_FLAGS_LHINT_2048_AND_LARGER;
+}
+
netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
struct netdev_queue *txq,
struct sk_buff *skb)
{
+ unsigned int last_unmap_len __maybe_unused = 0;
+ dma_addr_t last_unmap_addr __maybe_unused = 0;
+ struct bnxt_sw_tx_bd *last_unmap_buf = NULL;
+ unsigned int hdr_len, mss, num_segs;
+ struct pci_dev *pdev = bp->pdev;
+ unsigned int total_payload;
+ struct tso_dma_map map;
+ u32 vlan_tag_flags = 0;
+ int i, bds_needed;
+ struct tso_t tso;
+ u16 prod, slots;
+ u16 cfa_action;
+ __le32 csum;
+
+ hdr_len = tso_start(skb, &tso);
+ mss = skb_shinfo(skb)->gso_size;
+ total_payload = skb->len - hdr_len;
+ num_segs = DIV_ROUND_UP(total_payload, mss);
+
+ if (unlikely(num_segs <= 1))
+ goto drop;
+
+ /* Upper bound on the number of descriptors needed.
+ *
+ * Each segment uses 1 long BD + 1 ext BD + payload BDs, which is
+ * at most num_segs + nr_frags (each frag boundary crossing adds at
+ * most 1 extra BD).
+ */
+ bds_needed = 3 * num_segs + skb_shinfo(skb)->nr_frags + 1;
+
+ if (unlikely(bnxt_tx_avail(bp, txr) < bds_needed)) {
+ netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
+ bp->tx_wake_thresh);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* BD backpressure alone cannot prevent overwriting in-flight
+ * headers in the inline buffer. Check slot availability directly.
+ */
+ slots = txr->tx_inline_prod - txr->tx_inline_cons;
+ slots = BNXT_SW_USO_MAX_SEGS - slots;
+
+ if (unlikely(slots < num_segs)) {
+ netif_txq_try_stop(txq, slots, num_segs);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (unlikely(tso_dma_map_init(&map, &pdev->dev, skb, hdr_len)))
+ goto drop;
+
+ cfa_action = bnxt_xmit_get_cfa_action(skb);
+ if (skb_vlan_tag_present(skb)) {
+ vlan_tag_flags = TX_BD_CFA_META_KEY_VLAN |
+ skb_vlan_tag_get(skb);
+ if (skb->vlan_proto == htons(ETH_P_8021Q))
+ vlan_tag_flags |= 1 << TX_BD_CFA_META_TPID_SHIFT;
+ }
+
+ csum = cpu_to_le32(TX_BD_FLAGS_TCP_UDP_CHKSUM);
+ if (!tso.ipv6)
+ csum |= cpu_to_le32(TX_BD_FLAGS_IP_CKSUM);
+
+ prod = txr->tx_prod;
+
+ for (i = 0; i < num_segs; i++) {
+ unsigned int seg_payload = min_t(unsigned int, mss,
+ total_payload - i * mss);
+ u16 slot = (txr->tx_inline_prod + i) &
+ (BNXT_SW_USO_MAX_SEGS - 1);
+ struct bnxt_sw_tx_bd *tx_buf;
+ unsigned int mapping_len;
+ dma_addr_t this_hdr_dma;
+ unsigned int chunk_len;
+ unsigned int offset;
+ dma_addr_t dma_addr;
+ struct tx_bd *txbd;
+ struct udphdr *uh;
+ void *this_hdr;
+ int bd_count;
+ bool last;
+ u32 flags;
+
+ last = (i == num_segs - 1);
+ offset = slot * TSO_HEADER_SIZE;
+ this_hdr = txr->tx_inline_buf + offset;
+ this_hdr_dma = txr->tx_inline_dma + offset;
+
+ tso_build_hdr(skb, this_hdr, &tso, seg_payload, last);
+
+ /* Zero stale csum fields copied from the original skb;
+ * HW offload recomputes from scratch.
+ */
+ uh = this_hdr + skb_transport_offset(skb);
+ uh->check = 0;
+ if (!tso.ipv6) {
+ struct iphdr *iph = this_hdr + skb_network_offset(skb);
+
+ iph->check = 0;
+ }
+
+ dma_sync_single_for_device(&pdev->dev, this_hdr_dma,
+ hdr_len, DMA_TO_DEVICE);
+
+ bd_count = tso_dma_map_count(&map, seg_payload);
+
+ tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
+ txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
+
+ tx_buf->skb = skb;
+ tx_buf->nr_frags = bd_count;
+ tx_buf->is_push = 0;
+ tx_buf->is_ts_pkt = 0;
+
+ dma_unmap_addr_set(tx_buf, mapping, this_hdr_dma);
+ dma_unmap_len_set(tx_buf, len, 0);
+
+ if (last) {
+ tx_buf->is_sw_gso = BNXT_SW_GSO_LAST;
+ tso_dma_map_completion_save(&map, &tx_buf->sw_gso_cstate);
+ } else {
+ tx_buf->is_sw_gso = BNXT_SW_GSO_MID;
+ }
+
+ flags = (hdr_len << TX_BD_LEN_SHIFT) |
+ TX_BD_TYPE_LONG_TX_BD |
+ TX_BD_CNT(2 + bd_count);
+
+ flags |= bnxt_sw_gso_lhint(hdr_len + seg_payload);
+
+ txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
+ txbd->tx_bd_haddr = cpu_to_le64(this_hdr_dma);
+ txbd->tx_bd_opaque = SET_TX_OPAQUE(bp, txr, prod,
+ 2 + bd_count);
+
+ prod = NEXT_TX(prod);
+ bnxt_init_ext_bd(bp, txr, prod, csum,
+ vlan_tag_flags, cfa_action);
+
+ /* set dma_unmap_len on the LAST BD touching each
+ * region. Since completions are in-order, the last segment
+ * completes after all earlier ones, so the unmap is safe.
+ */
+ while (tso_dma_map_next(&map, &dma_addr, &chunk_len,
+ &mapping_len, seg_payload)) {
+ prod = NEXT_TX(prod);
+ txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
+ tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
+
+ txbd->tx_bd_haddr = cpu_to_le64(dma_addr);
+ dma_unmap_addr_set(tx_buf, mapping, dma_addr);
+ dma_unmap_len_set(tx_buf, len, 0);
+ tx_buf->skb = NULL;
+ tx_buf->is_sw_gso = 0;
+
+ if (mapping_len) {
+ if (last_unmap_buf) {
+ dma_unmap_addr_set(last_unmap_buf,
+ mapping,
+ last_unmap_addr);
+ dma_unmap_len_set(last_unmap_buf,
+ len,
+ last_unmap_len);
+ }
+ last_unmap_addr = dma_addr;
+ last_unmap_len = mapping_len;
+ }
+ last_unmap_buf = tx_buf;
+
+ flags = chunk_len << TX_BD_LEN_SHIFT;
+ txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
+ txbd->tx_bd_opaque = 0;
+
+ seg_payload -= chunk_len;
+ }
+
+ txbd->tx_bd_len_flags_type |=
+ cpu_to_le32(TX_BD_FLAGS_PACKET_END);
+
+ prod = NEXT_TX(prod);
+ }
+
+ if (last_unmap_buf) {
+ dma_unmap_addr_set(last_unmap_buf, mapping, last_unmap_addr);
+ dma_unmap_len_set(last_unmap_buf, len, last_unmap_len);
+ }
+
+ txr->tx_inline_prod += num_segs;
+
+ netdev_tx_sent_queue(txq, skb->len);
+
+ WRITE_ONCE(txr->tx_prod, prod);
+ /* Sync BDs before doorbell */
+ wmb();
+ bnxt_db_write(bp, &txr->tx_db, prod);
+
+ if (unlikely(bnxt_tx_avail(bp, txr) <= bp->tx_wake_thresh))
+ netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
+ bp->tx_wake_thresh);
+
+ return NETDEV_TX_OK;
+
+drop:
dev_kfree_skb_any(skb);
dev_core_stats_tx_dropped_inc(bp->dev);
return NETDEV_TX_OK;
--
2.52.0
^ permalink raw reply related
* [net-next v9 08/10] net: bnxt: Add SW GSO completion and teardown support
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Update __bnxt_tx_int and bnxt_free_one_tx_ring_skbs to handle SW GSO
segments:
- MID segments: adjust tx_pkts/tx_bytes accounting and skip skb free
(the skb is shared across all segments and freed only once)
- LAST segments: call tso_dma_map_complete() to tear down the IOVA
mapping if one was used. On the fallback path, payload DMA unmapping
is handled by the existing per-BD dma_unmap_len walk.
Both MID and LAST completions advance tx_inline_cons to release the
segment's inline header slot back to the ring.
is_sw_gso is initialized to zero, so the new code paths are not run.
Add logic for feature advertisement and guardrails for ring sizing.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joe Damato <joe@dama.to>
---
v9:
- Always allocate header buffer for non-HW-USO NICs. Avoids a possible
NULL deref if USO is toggled off and the device is brought down, up,
and USO is re-enabled (suggested by AI).
- Adjust bnxt_min_tx_desc_cnt to take a feature parameter. This is needed
to prevent stale features from being examined (suggested by AI).
v7:
- Dropped Pavan's Reviewed-by because some changes were made.
- Added helper bnxt_min_tx_desc_cnt to avoid repeated code computing
descriptor counts.
- Updated to use tso_dma_map_complete helper instead of calling the DMA
IOVA API directly.
v5:
- Added Pavan's Reviewed-by. No functional changes.
v3:
- completion paths updated to use DMA IOVA APIs to teardown mappings.
rfcv2:
- Update the shared header buffer consumer on TX completion.
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 70 ++++++++++++++++---
.../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 19 ++++-
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h | 9 +++
3 files changed, 87 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 74968ca1f4e2..73e692ae2253 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -74,6 +74,8 @@
#include "bnxt_debugfs.h"
#include "bnxt_coredump.h"
#include "bnxt_hwmon.h"
+#include "bnxt_gso.h"
+#include <net/tso.h>
#define BNXT_TX_TIMEOUT (5 * HZ)
#define BNXT_DEF_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_HW | \
@@ -817,12 +819,13 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
bool rc = false;
while (RING_TX(bp, cons) != hw_cons) {
- struct bnxt_sw_tx_bd *tx_buf;
+ struct bnxt_sw_tx_bd *tx_buf, *head_buf;
struct sk_buff *skb;
bool is_ts_pkt;
int j, last;
tx_buf = &txr->tx_buf_ring[RING_TX(bp, cons)];
+ head_buf = tx_buf;
skb = tx_buf->skb;
if (unlikely(!skb)) {
@@ -869,6 +872,20 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
DMA_TO_DEVICE, 0);
}
}
+
+ if (unlikely(head_buf->is_sw_gso)) {
+ txr->tx_inline_cons++;
+ if (head_buf->is_sw_gso == BNXT_SW_GSO_LAST) {
+ tso_dma_map_complete(&pdev->dev,
+ &head_buf->sw_gso_cstate);
+ } else {
+ tx_pkts--;
+ tx_bytes -= skb->len;
+ skb = NULL;
+ }
+ head_buf->is_sw_gso = 0;
+ }
+
if (unlikely(is_ts_pkt)) {
if (BNXT_CHIP_P5(bp)) {
/* PTP worker takes ownership of the skb */
@@ -3412,6 +3429,7 @@ static void bnxt_free_one_tx_ring_skbs(struct bnxt *bp,
for (i = 0; i < max_idx;) {
struct bnxt_sw_tx_bd *tx_buf = &txr->tx_buf_ring[i];
+ struct bnxt_sw_tx_bd *head_buf = tx_buf;
struct sk_buff *skb;
int j, last;
@@ -3464,7 +3482,17 @@ static void bnxt_free_one_tx_ring_skbs(struct bnxt *bp,
DMA_TO_DEVICE, 0);
}
}
- dev_kfree_skb(skb);
+ if (head_buf->is_sw_gso) {
+ txr->tx_inline_cons++;
+ if (head_buf->is_sw_gso == BNXT_SW_GSO_LAST) {
+ tso_dma_map_complete(&pdev->dev,
+ &head_buf->sw_gso_cstate);
+ } else {
+ skb = NULL;
+ }
+ }
+ if (skb)
+ dev_kfree_skb(skb);
}
netdev_tx_reset_queue(netdev_get_tx_queue(bp->dev, idx));
}
@@ -3990,9 +4018,9 @@ static void bnxt_free_tx_inline_buf(struct bnxt_tx_ring_info *txr,
txr->tx_inline_size = 0;
}
-static int __maybe_unused bnxt_alloc_tx_inline_buf(struct bnxt_tx_ring_info *txr,
- struct pci_dev *pdev,
- unsigned int size)
+static int bnxt_alloc_tx_inline_buf(struct bnxt_tx_ring_info *txr,
+ struct pci_dev *pdev,
+ unsigned int size)
{
txr->tx_inline_buf = kmalloc(size, GFP_KERNEL);
if (!txr->tx_inline_buf)
@@ -4095,6 +4123,13 @@ static int bnxt_alloc_tx_rings(struct bnxt *bp)
sizeof(struct tx_push_bd);
txr->data_mapping = cpu_to_le64(mapping);
}
+ if (!(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) {
+ rc = bnxt_alloc_tx_inline_buf(txr, pdev,
+ BNXT_SW_USO_MAX_SEGS *
+ TSO_HEADER_SIZE);
+ if (rc)
+ return rc;
+ }
qidx = bp->tc_to_qidx[j];
ring->queue_id = bp->q_info[qidx].queue_id;
spin_lock_init(&txr->xdp_tx_lock);
@@ -4633,10 +4668,13 @@ static int bnxt_init_rx_rings(struct bnxt *bp)
static int bnxt_init_tx_rings(struct bnxt *bp)
{
+ netdev_features_t features;
u16 i;
+ features = bp->dev->features;
+
bp->tx_wake_thresh = max_t(int, bp->tx_ring_size / 2,
- BNXT_MIN_TX_DESC_CNT);
+ bnxt_min_tx_desc_cnt(bp, features));
for (i = 0; i < bp->tx_nr_rings; i++) {
struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
@@ -13835,6 +13873,11 @@ static netdev_features_t bnxt_fix_features(struct net_device *dev,
if ((features & NETIF_F_NTUPLE) && !bnxt_rfs_capable(bp, false))
features &= ~NETIF_F_NTUPLE;
+ if ((features & NETIF_F_GSO_UDP_L4) &&
+ !(bp->flags & BNXT_FLAG_UDP_GSO_CAP) &&
+ bp->tx_ring_size < 2 * BNXT_SW_USO_MAX_DESCS)
+ features &= ~NETIF_F_GSO_UDP_L4;
+
if ((bp->flags & BNXT_FLAG_NO_AGG_RINGS) || bp->xdp_prog)
features &= ~(NETIF_F_LRO | NETIF_F_GRO_HW);
@@ -13880,6 +13923,9 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features)
int rc = 0;
bool re_init = false;
+ bp->tx_wake_thresh = max_t(int, bp->tx_ring_size / 2,
+ bnxt_min_tx_desc_cnt(bp, features));
+
flags &= ~BNXT_FLAG_ALL_CONFIG_FEATS;
if (features & NETIF_F_GRO_HW)
flags |= BNXT_FLAG_GRO;
@@ -16905,8 +16951,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM |
NETIF_F_GSO_PARTIAL | NETIF_F_RXHASH |
NETIF_F_RXCSUM | NETIF_F_GRO;
- if (bp->flags & BNXT_FLAG_UDP_GSO_CAP)
- dev->hw_features |= NETIF_F_GSO_UDP_L4;
+ dev->hw_features |= NETIF_F_GSO_UDP_L4;
if (BNXT_SUPPORTS_TPA(bp))
dev->hw_features |= NETIF_F_LRO;
@@ -16939,8 +16984,15 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->priv_flags |= IFF_UNICAST_FLT;
netif_set_tso_max_size(dev, GSO_MAX_SIZE);
- if (bp->tso_max_segs)
+ if (!(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) {
+ u16 max_segs = BNXT_SW_USO_MAX_SEGS;
+
+ if (bp->tso_max_segs)
+ max_segs = min_t(u16, max_segs, bp->tso_max_segs);
+ netif_set_tso_max_segs(dev, max_segs);
+ } else if (bp->tso_max_segs) {
netif_set_tso_max_segs(dev, bp->tso_max_segs);
+ }
dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
NETDEV_XDP_ACT_RX_SG;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index 6826bf762d26..9ded88196bb4 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -33,6 +33,7 @@
#include "bnxt_xdp.h"
#include "bnxt_ptp.h"
#include "bnxt_ethtool.h"
+#include "bnxt_gso.h"
#include "bnxt_nvm_defs.h" /* NVRAM content constant and structure defs */
#include "bnxt_fw_hdr.h" /* Firmware hdr constant and structure defs */
#include "bnxt_coredump.h"
@@ -852,12 +853,18 @@ static int bnxt_set_ringparam(struct net_device *dev,
u8 tcp_data_split = kernel_ering->tcp_data_split;
struct bnxt *bp = netdev_priv(dev);
u8 hds_config_mod;
+ int rc;
if ((ering->rx_pending > BNXT_MAX_RX_DESC_CNT) ||
(ering->tx_pending > BNXT_MAX_TX_DESC_CNT) ||
(ering->tx_pending < BNXT_MIN_TX_DESC_CNT))
return -EINVAL;
+ if ((dev->features & NETIF_F_GSO_UDP_L4) &&
+ !(bp->flags & BNXT_FLAG_UDP_GSO_CAP) &&
+ ering->tx_pending < 2 * BNXT_SW_USO_MAX_DESCS)
+ return -EINVAL;
+
hds_config_mod = tcp_data_split != dev->cfg->hds_config;
if (tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_DISABLED && hds_config_mod)
return -EINVAL;
@@ -882,9 +889,17 @@ static int bnxt_set_ringparam(struct net_device *dev,
bp->tx_ring_size = ering->tx_pending;
bnxt_set_ring_params(bp);
- if (netif_running(dev))
- return bnxt_open_nic(bp, false, false);
+ if (netif_running(dev)) {
+ rc = bnxt_open_nic(bp, false, false);
+ if (rc)
+ return rc;
+ }
+ /* ring size changes may affect features (SW USO requires a minimum
+ * ring size), so recalculate features to ensure the correct features
+ * are blocked/available.
+ */
+ netdev_update_features(dev);
return 0;
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
index f01e8102dcd7..3bee626a6303 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
@@ -23,6 +23,15 @@
*/
#define BNXT_SW_USO_MAX_DESCS (3 * BNXT_SW_USO_MAX_SEGS + MAX_SKB_FRAGS + 1)
+static inline int bnxt_min_tx_desc_cnt(struct bnxt *bp,
+ netdev_features_t features)
+{
+ if (!(bp->flags & BNXT_FLAG_UDP_GSO_CAP) &&
+ (features & NETIF_F_GSO_UDP_L4))
+ return BNXT_SW_USO_MAX_DESCS;
+ return BNXT_MIN_TX_DESC_CNT;
+}
+
netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
struct netdev_queue *txq,
--
2.52.0
^ permalink raw reply related
* [net-next v9 09/10] net: bnxt: Dispatch to SW USO
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Wire in the SW USO path added in preceding commits when hardware USO is
not possible.
When a GSO skb with SKB_GSO_UDP_L4 arrives and the NIC lacks HW USO
capability, redirect to bnxt_sw_udp_gso_xmit() which handles software
segmentation into individual UDP frames submitted directly to the TX
ring.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: Joe Damato <joe@dama.to>
---
v5:
- Added Pavan's Reviewed-by. No functional changes.
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 73e692ae2253..68cad2951fe0 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -508,6 +508,11 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
#endif
+ if (skb_is_gso(skb) &&
+ (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) &&
+ !(bp->flags & BNXT_FLAG_UDP_GSO_CAP))
+ return bnxt_sw_udp_gso_xmit(bp, txr, txq, skb);
+
free_size = bnxt_tx_avail(bp, txr);
if (unlikely(free_size < skb_shinfo(skb)->nr_frags + 2)) {
/* We must have raced with NAPI cleanup */
--
2.52.0
^ permalink raw reply related
* [net-next v9 10/10] selftests: drv-net: Add USO test
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Shuah Khan
Cc: horms, michael.chan, pavan.chebbi, linux-kernel, leon, Joe Damato,
linux-kselftest
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Add a simple test for USO. Tests both ipv4 and ipv6 with several full
segments and a partial segment.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joe Damato <joe@dama.to>
---
v9:
- Use UDP-LISTEN instead of UDP-RECV in socat receiver (suggested by AI).
- Fixed stale docstring.
- Removed unused return value.
v7:
- Dropped Pavan's Reviewed-by as there were changes.
- Update to use ksft_variants with a generator and a parameterized test_uso
function.
- Save original USO state and restore it at the end of the test.
- Replace sleep with cfg.wait_hw_stats_settle
- Use a socat receiver and check tx stats locally instead of rx on the
remote.
v5:
- Added Pavan's Reviewed-by. No functional changes.
v4:
- Fix python linter issues (unused imports, docstring, etc).
rfcv2:
- new in rfcv2
tools/testing/selftests/drivers/net/Makefile | 1 +
tools/testing/selftests/drivers/net/uso.py | 103 +++++++++++++++++++
2 files changed, 104 insertions(+)
create mode 100755 tools/testing/selftests/drivers/net/uso.py
diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index 7c7fa75b80c2..335c2ce4b9ab 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -21,6 +21,7 @@ TEST_PROGS := \
ring_reconfig.py \
shaper.py \
stats.py \
+ uso.py \
xdp.py \
# end of TEST_PROGS
diff --git a/tools/testing/selftests/drivers/net/uso.py b/tools/testing/selftests/drivers/net/uso.py
new file mode 100755
index 000000000000..6d61e56cab3c
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/uso.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""Test USO
+
+Sends large UDP datagrams with UDP_SEGMENT and verifies that the peer
+receives the expected total payload and that the NIC transmitted at least
+the expected number of segments.
+"""
+import random
+import socket
+import string
+
+from lib.py import ksft_run, ksft_exit, KsftSkipEx
+from lib.py import ksft_eq, ksft_ge, ksft_variants, KsftNamedVariant
+from lib.py import NetDrvEpEnv
+from lib.py import bkg, defer, ethtool, ip, rand_port, wait_port_listen
+
+# python doesn't expose this constant, so we need to hardcode it to enable UDP
+# segmentation for large payloads
+UDP_SEGMENT = 103
+
+
+def _send_uso(cfg, ipver, mss, total_payload, port):
+ if ipver == "4":
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ dst = (cfg.remote_addr_v["4"], port)
+ else:
+ sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+ dst = (cfg.remote_addr_v["6"], port)
+
+ sock.setsockopt(socket.IPPROTO_UDP, UDP_SEGMENT, mss)
+ payload = ''.join(random.choice(string.ascii_lowercase)
+ for _ in range(total_payload))
+ sock.sendto(payload.encode(), dst)
+ sock.close()
+
+
+def _get_tx_packets(cfg):
+ stats = ip(f"-s link show dev {cfg.ifname}", json=True)[0]
+ return stats['stats64']['tx']['packets']
+
+
+def _test_uso(cfg, ipver, mss, total_payload):
+ cfg.require_ipver(ipver)
+ cfg.require_cmd("socat", remote=True)
+
+ features = ethtool(f"-k {cfg.ifname}", json=True)
+ uso_was_on = features[0]["tx-udp-segmentation"]["active"]
+
+ try:
+ ethtool(f"-K {cfg.ifname} tx-udp-segmentation on")
+ except Exception as exc:
+ raise KsftSkipEx(
+ "Device does not support tx-udp-segmentation") from exc
+ if not uso_was_on:
+ defer(ethtool, f"-K {cfg.ifname} tx-udp-segmentation off")
+
+ expected_segs = (total_payload + mss - 1) // mss
+
+ port = rand_port(stype=socket.SOCK_DGRAM)
+ rx_cmd = f"socat -{ipver} -T 2 -u UDP-LISTEN:{port},reuseport STDOUT"
+
+ tx_before = _get_tx_packets(cfg)
+
+ with bkg(rx_cmd, host=cfg.remote, exit_wait=True) as rx:
+ wait_port_listen(port, proto="udp", host=cfg.remote)
+ _send_uso(cfg, ipver, mss, total_payload, port)
+
+ ksft_eq(len(rx.stdout), total_payload,
+ comment=f"Received {len(rx.stdout)}B, expected {total_payload}B")
+
+ cfg.wait_hw_stats_settle()
+
+ tx_after = _get_tx_packets(cfg)
+ tx_delta = tx_after - tx_before
+
+ ksft_ge(tx_delta, expected_segs,
+ comment=f"Expected >= {expected_segs} tx packets, got {tx_delta}")
+
+
+def _uso_variants():
+ for ipver in ["4", "6"]:
+ yield KsftNamedVariant(f"v{ipver}_partial", ipver, 1400, 1400 * 10 + 500)
+ yield KsftNamedVariant(f"v{ipver}_exact", ipver, 1400, 1400 * 5)
+
+
+@ksft_variants(_uso_variants())
+def test_uso(cfg, ipver, mss, total_payload):
+ """Send a USO datagram and verify the peer receives the expected segments."""
+ _test_uso(cfg, ipver, mss, total_payload)
+
+
+def main() -> None:
+ """Run USO tests."""
+ with NetDrvEpEnv(__file__) as cfg:
+ ksft_run([test_uso],
+ args=(cfg, ))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v3 11/15] media: qcom: Switch to generic PAS TZ APIs
From: Trilok Soni @ 2026-04-07 22:14 UTC (permalink / raw)
To: Sumit Garg, Jorge Ramirez, vikash.garodia
Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
konradybcio, robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo,
lumag, abhinav.kumar, jesszhan0024, marijn.suijten, airlied,
simona, dikshita.agarwal, bod, mchehab, elder, andrew+netdev,
davem, edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
mukesh.ojha, pavan.kondeti, tonyh, vignesh.viswanathan,
srinivas.kandagatla, amirreza.zarrabi, jens.wiklander, op-tee,
apurupa, skare, harshal.dev, linux-kernel, Sumit Garg
In-Reply-To: <adOcMsk8a_Clb4WZ@sumit-xelite>
On 4/6/2026 4:42 AM, Sumit Garg wrote:
> Hi Jorge,
>
> On Fri, Apr 03, 2026 at 11:37:07AM +0200, Jorge Ramirez wrote:
>> On 27/03/26 18:40:39, Sumit Garg wrote:
>>> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>>>
>>> Switch qcom media client drivers over to generic PAS TZ APIs. Generic PAS
>>> TZ service allows to support multiple TZ implementation backends like QTEE
>>> based SCM PAS service, OP-TEE based PAS service and any further future TZ
>>> backend service.
>>
>> OP-TEE based PAS service relies on the linux driver to configure the
>> iommu (just as it is done on the no_tz case). This generic patch does
>> not cover that requirement.
>
> That's exactly the reason why the kodiak EL2 dtso disables venus by
> default in patch #1 due to missing IOMMU configuration.
>
>>
>> Because of that, it is probably better if the commit message doesnt
>> mention OP-TEE and instead maybe indicate that PAS wll support TEEs that
>> implement the same restrictions that QTEE (ie, iommu configuration).
>
> The scope for this patch is to just adopt the generic PAS layer without
> affecting the client functionality.
>
>>
>> I can send an RFC for OP-TEE support based on the integration work being
>> carried out here [1]
>
> @Vikash may know better details about support for IOMMU configuration
> for venus since it's a generic functionality missing when Linux runs in
> EL2 whether it's with QTEE or OP-TEE.
>
> However, feel free to propose your work to initiate discussions again.
Vikas and team depends on some of the IOMMU patches to get accepted
before they enable the EL2 venus support. Please reach out to him
and Prakash Gupta at Qualcomm.
>
>>
>> [1] https://github.com/OP-TEE/optee_os/pull/7721#discussion_r3016923507
>
> -Sumit
^ permalink raw reply
* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Pawan Gupta @ 2026-04-07 22:27 UTC (permalink / raw)
To: Jim Mattson
Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
David Laight, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
David Ahern, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, John Fastabend, Stanislav Fomichev, Hao Luo,
Paolo Bonzini, Jonathan Corbet, linux-kernel, kvm, Asit Mallick,
Tao Zhang, bpf, netdev, linux-doc, chao.gao
In-Reply-To: <CALMp9eTK0o7Z7-oTB8ohvmoh-vy-Y2qjdUvbqD6HaEhOEPZmhw@mail.gmail.com>
On Tue, Apr 07, 2026 at 01:53:24PM -0700, Jim Mattson wrote:
> On Tue, Apr 7, 2026 at 12:11 PM Pawan Gupta
> <pawan.kumar.gupta@linux.intel.com> wrote:
> >
> > On Tue, Apr 07, 2026 at 11:40:57AM -0700, Jim Mattson wrote:
> > > My proposal is as follows:
> > >
> > > 1. The (advanced) hypervisor can advertise to the guest (via CPUID bit
> > > or MSR bit) that the short BHB clearing sequence is adequate. This may
> > > mean either that the VM will only be hosted on pre-Alder Lake hardware
> > > or that the hypervisor will set BHI_DIS_S behind the back of the
> > > guest. Presumably, this bit would not be reported if BHI_CTRL is
> > > advertised to the guest.
> > > 2. If the guest sees this bit, then it can use the short sequence. If
> > > it doesn't see this bit, it must use the long sequence.
> >
> > Thats a good middle ground. Let me check with folks internally what they
> > think about defining a new software-only bit.
> >
> > Third case, for a guest that doesn't want BHI_DIS_S, userspace should be
> > allowed to override setting BHI_DIS_S. Then this proposed bit can indicate
> > that long sequence is required.
>
> That case can be handled by the paravirtual mitigation MSRs, right?
Yes. But, that was the part that received the most pushback.
^ permalink raw reply
* Re: [PATCH net-next v1] r8169: implement get_module functions for rtl8127atf
From: Fabio Baltieri @ 2026-04-07 22:44 UTC (permalink / raw)
To: Andrew Lunn
Cc: Heiner Kallweit, nic_swsd, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King - ARM Linux, netdev, linux-kernel
In-Reply-To: <368d3f30-b4af-4548-a4b9-f8e4e4f9a4cb@lunn.ch>
On Sun, Apr 05, 2026 at 12:46:41AM +0200, Andrew Lunn wrote:
> > Hi Fabio, thanks for the patch. Being able to access the SFP I2C bus is an
> > important step towards future phylink/SFP support.
> >
> > @Russell/@Andrew
> > I'm not really familiar with the phylink and sfp code. And I would like to have
> > the functionality being implemented here reusable once we do further steps
> > towards phylink support in r8169. So how shall the code be modeled, which
> > components are needed?
> > - Shall the SFP I2C bus be modeled as a struct i2c_adapter?
>
> Yes, that would be best. Call i2c_add_adapter() to add it to the I2C
> core. The SFP code in drivers/net/phy can then make use of it.
Hey Andrew, thanks for the pointers, I was able to instantiate the dw
i2c controller, same as txgbe/txgbe_phy.c really.
For some reasons I had to revert 560072246088, was getting a "spurious
STOP detected" but I'll follow up on that separately. Also find an issue
in the txgbe driver (I think?), sent
https://lore.kernel.org/netdev/20260405222013.5347-1-fabio.baltieri@gmail.com/
for it.
> > - Shall we (partially?) implement a struct sfp, so that functions like
> > sfp_module_eeprom() can be used (implicitly)?
> >
> > I assume this patch includes logic which duplicates what is available in
> > phylink/sfp already. I'd a appreciate any hints you can provide.
>
> No. phylink etc will end up populating netdev->sfp_bus, and then
> get_module_eeprom_by_page() should then just make the module work in
> ethtool.
>
> The interesting bit if gluing it all together, without DT. But
> txgbe_phy.c should be something you can copy from.
>
> Does the out of tree driver give access to GPIOs connected to the SFP
> cage pins? Ideally you want those as well, for loss of signal,
> transmitter disable, knowing when a module has been inserted into the
> cage, etc.
It doesn't, but I was able to find them anyway, just dumping registers
randomly. :-) It looks like that is a bit more than "Ideally", the sfp
driver probes happily with no gpios defined but then it doesn't have
any ways to detect module changes (insertion/removal), it doesn't even
go into polling mode but even then it seems like it doesn't do detection
without a detect pin. I think the sfp probe function could be a bit more
defensive to inoperable configurations.
Anyway I was able to make detection and los work, so I have some code
with gpio, i2c and the sfp module detect and showing up in hwmon, and
sitting in waitdev state.
>
> And you will need a PCS driver.
>
> But first step is probably to work with the existing Base-T devices
> and convert the driver to phylink.
Ok I was going to ask how to connect the one above with the ethtool
handlers, so you are saying that first the whole driver needs to be
converted to use phylink/pcs and then that should work automatically-ish
right?
Can I send the current code I have as an RFC in the meantime to get some
early feedback? And then I guess it'll have to be reworked on top of the
phylink stuff (which I did not try to implement).
It's a pretty substantial amount of boilerplate code so I suspect Heiner
may want to refactor things about it, feels like it could live in its
own file.
Thanks for the help so far,
Fabio
^ permalink raw reply
* Re: [PATCH net] ice: fix VF queue configuration with low MTU values
From: Jacob Keller @ 2026-04-07 22:50 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez, intel-wired-lan
Cc: netdev, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Aleksandr Loktionov, Michal Swiatkowski, Dave Ertman,
Michal Kubiak, stable
In-Reply-To: <20260406145641.1020623-1-jtornosm@redhat.com>
On 4/6/2026 7:56 AM, Jose Ignacio Tornos Martinez wrote:
> The ice driver's VF queue configuration validation rejects
> databuffer_size values below 1024 bytes, which prevents VFs from
> using MTU values below 871 bytes.
>
> The iavf driver calculates databuffer_size based on the MTU using:
> databuffer_size = ALIGN(MTU + LIBETH_RX_LL_LEN, 128)
>
> where LIBETH_RX_LL_LEN = 26 (ETH_HLEN + 2*VLAN_HLEN + ETH_FCS_LEN).
>
> For MTU values below 871:
> MTU 870: 870 + 26 = 896, aligned to 128 = 896 (< 1024, rejected)
> MTU 871: 871 + 26 = 897, aligned to 128 = 1024 (>= 1024, accepted)
>
> The 1024-byte minimum seems unnecessarily restrictive, because the hardware
> supports databuffer_size as low as 128 bytes (the alignment boundary),
> which should allow MTU values down to the standard minimum of 68 bytes.
>
> I haven't found the reason why the limit was configured in the commit
> 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message"), so
> with no more information and since it is working, change the minimum
> databuffer_size validation from 1024 to 128 bytes to allow standard low
> MTU values while still preventing invalid configurations.
>
I dug through some of our internal history and found that there was no
justification on why 1024 was chosen.
I agree with your assessment that the value of 128 makes the most sense
as it is the actual hardware minimum. I wonder if we used to always use
data buffer sizes of 1024 before the conversion to libeth. Either way, I
think it makes sense to allow smaller buffers since the modern iAVF will
request them.
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
> Fixes: 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message")
> cc: stable@vger.kernel.org
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> ---
> drivers/net/ethernet/intel/ice/virt/queues.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c
> index f73d5a3e83d4..31be2f76181c 100644
> --- a/drivers/net/ethernet/intel/ice/virt/queues.c
> +++ b/drivers/net/ethernet/intel/ice/virt/queues.c
> @@ -840,7 +840,7 @@ int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
>
> if (qpi->rxq.databuffer_size != 0 &&
> (qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
> - qpi->rxq.databuffer_size < 1024))
> + qpi->rxq.databuffer_size < 128))
> goto error_param;
>
> ring->rx_buf_len = qpi->rxq.databuffer_size;
^ permalink raw reply
* Re: [PATCH v5 0/9] driver core: Fix some race conditions
From: Danilo Krummrich @ 2026-04-07 22:58 UTC (permalink / raw)
To: Douglas Anderson, m.szyprowski, Robin Murphy
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Alexey Kardashevskiy, Johan Hovold, Eric Dumazet, Leon Romanovsky,
Christoph Hellwig, maz, Alexander Lobakin, Saravana Kannan,
Andrew Morton, Frank.Li, Jason Gunthorpe, alex, alexander.stein,
andre.przywara, andrew, andrew, andriy.shevchenko, aou, ardb,
astewart, bhelgaas, brgl, broonie, catalin.marinas, chleroy,
davem, david, devicetree, dmaengine, driver-core, gbatra,
gregory.clement, hkallweit1, iommu, jirislaby, joel, joro, kees,
kevin.brodsky, kuba, lenb, lgirdwood, linux-acpi,
linux-arm-kernel, linux-aspeed, linux-cxl, linux-kernel,
linux-mips, linux-mm, linux-pci, linux-riscv, linux-serial,
linux-snps-arc, linux-usb, linux, linuxppc-dev, maddy, mani,
miko.lenczewski, mpe, netdev, npiggin, osalvador, oupton, pabeni,
palmer, peter.ujfalusi, peterz, pjw, robh, sebastian.hesselbarth,
tglx, tsbogend, vgupta, vkoul, will, willy, yangyicong,
yeoreum.yun
In-Reply-To: <20260406232444.3117516-1-dianders@chromium.org>
On Tue Apr 7, 2026 at 1:22 AM CEST, Douglas Anderson wrote:
Applied to driver-core-testing, thanks!
> Douglas Anderson (9):
> driver core: Don't let a device probe until it's ready
> driver core: Replace dev->can_match with dev_can_match()
> driver core: Replace dev->dma_iommu with dev_dma_iommu()
> driver core: Replace dev->dma_skip_sync with dev_dma_skip_sync()
> driver core: Replace dev->dma_ops_bypass with dev_dma_ops_bypass()
> driver core: Replace dev->state_synced with dev_state_synced()
> driver core: Replace dev->dma_coherent with dev_dma_coherent()
[ Since all DEV_FLAG_DMA_COHERENT accessors are exposed unconditionally,
also drop the CONFIG guards around dev_assign_dma_coherent() in
device_initialize() to ensure a correct default value. - Danilo ]
> driver core: Replace dev->of_node_reused with dev_of_node_reused()
> driver core: Replace dev->offline + ->offline_disabled with accessors
^ permalink raw reply
* Re: [PATCH net-next v5 00/10] Decouple receive and transmit enablement in team driver
From: Marc Harvey @ 2026-04-07 23:06 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Jiri Pirko, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Shuah Khan, Simon Horman, netdev, linux-kernel,
linux-kselftest
In-Reply-To: <CANkEMg=DOiwj-ZbTLGC3oXaQuPM=sGOjg09CTXNpguCL5dh7Ug@mail.gmail.com>
On Mon, Apr 6, 2026 at 10:04 PM Marc Harvey <marcharvey@google.com> wrote:
>
> On Mon, Apr 6, 2026 at 7:44 AM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Mon, 06 Apr 2026 03:03:36 +0000 Marc Harvey wrote:
> > > Allow independent control over receive and transmit enablement states
> > > for aggregated ports in the team driver.
> > >
> > > The motivation is that IEE 802.3ad LACP "independent control" can't
> > > be implemented for the team driver currently. This was added to the
> > > bonding driver in commit 240fd405528b ("bonding: Add independent
> > > control state machine").
> > >
> > > This series also has a few patches that add tests to show that the old
> > > coupled enablement still works and that the new decoupled enablement
> > > works as intended (4, 5, and 10).
> > >
> > > There are three patches with small fixes as well, with the goal of
> > > making the final decoupling patch clearer (1, 2, and 3).
> >
> > activebackup:
> >
> > TAP version 13
> > 1..1
> > # overriding timeout to 2400
> > # selftests: drivers/net/team: teamd_activebackup.sh
> > # Setting up two-link aggregation for runner activebackup
> > # Teamd version is: teamd 1.32
> > # Conf files are /tmp/tmp.ydjNK9Um7H and /tmp/tmp.xZuc3cWbN0
> > # This program is not intended to be run as root.
> > # This program is not intended to be run as root.
> > # Created team devices
> > # Teamd PIDs are 21457 and 21461
> > # exec of "ip link set eth0 up" failed: No such file or directory
> > # exec of "ip link set eth0 up" failed: No such file or directory
> > # exec of "ip link set eth1 up" failed: No such file or directory
> > # exec of "ip link set eth1 up" failed: No such file or directory
> > # PING fd00::2 (fd00::2) 56 data bytes
> > # 64 bytes from fd00::2: icmp_seq=1 ttl=64 time=0.753 ms
> > #
> > # --- fd00::2 ping statistics ---
> > # 1 packets transmitted, 1 received, 0% packet loss, time 0ms
> > # rtt min/avg/max/mdev = 0.753/0.753/0.753/0.000 msPacket count for test_team2 was 0
> > # Waiting for eth0 in ns2-lZ0gqd to stop receiving
> > # Packet count for eth0 was 0Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Waiting for eth1 in ns2-lZ0gqd to stop receiving
> > # Packet count for eth1 was 0Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # TEST: teamd active backup runner test [FAIL]
> > # Traffic did not reach team interface in NS2.
> > # Tearing down two-link aggregation
> > # Failed to kill daemon: Timer expired
> > # Failed to kill daemon: Timer expired
> > # Sending sigkill to teamd for test_team1
> > # rm: cannot remove '/var/run/teamd/test_team1.pid': No such file or directory
> > # rm: cannot remove '/var/run/teamd/test_team1.sock': No such file or directory
> > # Sending sigkill to teamd for test_team2
> > # rm: cannot remove '/var/run/teamd/test_team2.pid': No such file or directory
> > # rm: cannot remove '/var/run/teamd/test_team2.sock': No such file or directory
> > not ok 1 selftests: drivers/net/team: teamd_activebackup.sh # exit=1
> >
> >
> > transmit_failover:
> >
> > TAP version 13
> > 1..1
> > # overriding timeout to 2400
> > # selftests: drivers/net/team: transmit_failover.sh
> > # Error: ipv6: address not found.
> > # Setting team in ns2-yxjiUo to mode roundrobin
> > # Error: ipv6: address not found.
> > # Setting team in ns1-Jht6kA to mode broadcast
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # TEST: Failover of 'broadcast' test [FAIL]
> > # eth0 not transmitting when both links enabled
> > # Setting team in ns1-Jht6kA to mode roundrobin
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # TEST: Failover of 'roundrobin' test [FAIL]
> > # eth0 not transmitting when both links enabled
> > # Setting team in ns1-Jht6kA to mode random
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # TEST: Failover of 'random' test [FAIL]
> > # eth0 not transmitting when both links enabled
> > not ok 1 selftests: drivers/net/team: transmit_failover.sh # exit=1
> > --
> > pw-bot: cr
>
> Apologies for all of the test failures. Before sending this revision,
> I ran each test thousands of times and observed no failures, so I
> thought the flakiness would be resolved.
>
> No matter what I try, I can't recreate either issue on my end. I've
> tried building with the exact config from one of the test runs
> (https://netdev-ctrl.bots.linux.dev/logs/vmksft/bonding/results/590921/).
> I've tried stressing the VM according to
> https://github.com/linux-netdev/nipa/wiki/How-to-run-netdev-selftests-CI-style#reproducing-unstable-tests
> (this makes the tests time out, but I can still see traffic). I've
> tried using the netdev-testing/net-next-2026-04-06--09-00 kernel
> source. I've tried in nested and unnested virtual machines. I've also
> tried running multiple test instances in parallel, but nothing
> recreates the issues. The issues seem related to tcpdump, but without
> reproducing them, I can only guess. Any suggestions for running the
> tests exactly as the CI does would be greatly appreciated.
>
> - Marc
Thank you very much to kuniyu@google.com, who figured out how to
recreate the issue on Fedora. Fedora's /etc/services maps TCP port
1234 to the "search-agent" service (normal), which tcpdump then uses
to text-replace port numbers in its output. So the tests were looking
for ${ip_address}.1234, but tcpdump was spitting out
${ip_address}.search_agent. What is strange is that the test already
uses tcpdump's "-n" option: "Don't convert addresses (i.e., host
addresses, port numbers, etc.) to names."
It turns out that Fedora has a patched version of tcpdump that
separates the normal "-n" option into two options! "-n" handles host
addresses, and "-nn" handles port and protocol numbers. The tcpdump
invocation used by the selftests only uses "-n". What's stranger is
that passing "-nn" to tcpdump is actually portable, because under the
hood it is treated as a counter, with or without the Fedora patch:
https://github.com/the-tcpdump-group/tcpdump/blob/master/tcpdump.c#L1915
(thanks again to Kuniyuki for discovering this).
For v6, I will just change the TCP port to one that is not used by a
service, and will make the tcpdump helper function in the
net/forwarding lib use "-nn" instead of "-n".
- Marc
^ permalink raw reply
* Re: [net-next v9 07/10] net: bnxt: Implement software USO
From: Joe Damato @ 2026-04-07 23:23 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon
In-Reply-To: <20260407220313.3990909-8-joe@dama.to>
On Tue, Apr 07, 2026 at 03:03:03PM -0700, Joe Damato wrote:
[...]
> v9:
> - Added inline slot check to prevent possible overwriting of in-flight
> headers (suggested by AI).
[...]
> netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
> struct bnxt_tx_ring_info *txr,
> struct netdev_queue *txq,
> struct sk_buff *skb)
> {
[...]
> +
> + /* BD backpressure alone cannot prevent overwriting in-flight
> + * headers in the inline buffer. Check slot availability directly.
> + */
> + slots = txr->tx_inline_prod - txr->tx_inline_cons;
> + slots = BNXT_SW_USO_MAX_SEGS - slots;
> +
> + if (unlikely(slots < num_segs)) {
> + netif_txq_try_stop(txq, slots, num_segs);
> + return NETDEV_TX_BUSY;
This is the check I added. AI says this is wrong and netdev_queues.h says:
* @get_desc must be a formula or a function call, it must always
* return up-to-date information when evaluated!
which I obviously failed to do, so I'm pretty sure I got this wrong.
^ permalink raw reply
* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Jim Mattson @ 2026-04-07 23:27 UTC (permalink / raw)
To: Pawan Gupta
Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
David Laight, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
David Ahern, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, John Fastabend, Stanislav Fomichev, Hao Luo,
Paolo Bonzini, Jonathan Corbet, linux-kernel, kvm, Asit Mallick,
Tao Zhang, bpf, netdev, linux-doc, chao.gao
In-Reply-To: <20260407222738.lrartp6evfp7yhti@desk>
On Tue, Apr 7, 2026 at 3:27 PM Pawan Gupta
<pawan.kumar.gupta@linux.intel.com> wrote:
>
> On Tue, Apr 07, 2026 at 01:53:24PM -0700, Jim Mattson wrote:
> > On Tue, Apr 7, 2026 at 12:11 PM Pawan Gupta
> > <pawan.kumar.gupta@linux.intel.com> wrote:
> > >
> > > On Tue, Apr 07, 2026 at 11:40:57AM -0700, Jim Mattson wrote:
> > > > My proposal is as follows:
> > > >
> > > > 1. The (advanced) hypervisor can advertise to the guest (via CPUID bit
> > > > or MSR bit) that the short BHB clearing sequence is adequate. This may
> > > > mean either that the VM will only be hosted on pre-Alder Lake hardware
> > > > or that the hypervisor will set BHI_DIS_S behind the back of the
> > > > guest. Presumably, this bit would not be reported if BHI_CTRL is
> > > > advertised to the guest.
> > > > 2. If the guest sees this bit, then it can use the short sequence. If
> > > > it doesn't see this bit, it must use the long sequence.
> > >
> > > Thats a good middle ground. Let me check with folks internally what they
> > > think about defining a new software-only bit.
> > >
> > > Third case, for a guest that doesn't want BHI_DIS_S, userspace should be
> > > allowed to override setting BHI_DIS_S. Then this proposed bit can indicate
> > > that long sequence is required.
> >
> > That case can be handled by the paravirtual mitigation MSRs, right?
>
> Yes. But, that was the part that received the most pushback.
What is your proposed BHI_DIS_S override mechanism, then?
^ permalink raw reply
* Re: [PATCH v10 net-next 2/5] psp: add new netlink cmd for dev-assoc and dev-disassoc
From: Wei Wang @ 2026-04-07 23:36 UTC (permalink / raw)
To: Daniel Zahka
Cc: netdev, Jakub Kicinski, Willem de Bruijn, David Wei, Andrew Lunn,
David S . Miller, Eric Dumazet, Simon Horman, Wei Wang
In-Reply-To: <2758ac3a-50dc-451a-990a-93e4db9d4bd6@gmail.com>
On Mon, Apr 6, 2026 at 6:27 AM Daniel Zahka <daniel.zahka@gmail.com> wrote:
>
>
> On 4/5/26 1:58 AM, Wei Wang wrote:
> > From: Wei Wang <weibunny@fb.com>
> >
> > The main purpose of this cmd is to be able to associate a
> > non-psp-capable device (e.g. veth or netkit) with a psp device.
> > One use case is if we create a pair of veth/netkit, and assign 1 end
> > inside a netns, while leaving the other end within the default netns,
> > with a real PSP device, e.g. netdevsim or a physical PSP-capable NIC.
> > With this command, we could associate the veth/netkit inside the netns
> > with PSP device, so the virtual device could act as PSP-capable device
> > to initiate PSP connections, and performs PSP encryption/decryption on
> > the real PSP device.
> >
> > Signed-off-by: Wei Wang <weibunny@fb.com>
> > ---
> > Documentation/netlink/specs/psp.yaml | 67 +++++-
> > include/net/psp/types.h | 15 ++
> > include/uapi/linux/psp.h | 13 ++
> > net/psp/psp-nl-gen.c | 32 +++
> > net/psp/psp-nl-gen.h | 2 +
> > net/psp/psp_main.c | 20 ++
> > net/psp/psp_nl.c | 319 ++++++++++++++++++++++++++-
> > 7 files changed, 457 insertions(+), 11 deletions(-)
> >
> ...
> >
> > +/**
> > + * Admin version of psp_device_get_locked() where it returns psd only if
> > + * current netns is the same as psd->main_netdev's netns.
> > + */
> > int psp_device_get_locked_admin(const struct genl_split_ops *ops,
> > struct sk_buff *skb, struct genl_info *info)
> > {
> > return __psp_device_get_locked(ops, skb, info, true);
> > }
> >
> > +/**
> > + * Non-admin version of psp_device_get_locked() where it returns psd in netns
> > + * for not only psd->main_netdev but all netdevs in psd->assoc_dev_list.
> > + */
> > int psp_device_get_locked(const struct genl_split_ops *ops,
> > struct sk_buff *skb, struct genl_info *info)
> > {
> > @@ -103,11 +179,74 @@ psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
> > sockfd_put(socket);
> > }
>
>
> There's a warning that these comments have the kdoc open sequence, but
> are not proper kdoc comments.
>
> > +
> > static int
> > psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
> > const struct genl_info *info)
> > {
> > + struct net *cur_net;
> > void *hdr;
> > + int err;
> > +
> > + cur_net = genl_info_net(info);
> > +
> > + /* Skip this device if we're in an associated netns but have no
> > + * associated devices in cur_net
> > + */
> > + if (cur_net != dev_net(psd->main_netdev) &&
> > + !psp_has_assoc_dev_in_ns(psd, cur_net))
> > + return 0;
> >
>
>
> Is this branch dead code given we either arrived here via
> psp_dev_check_access(), or psp_nl_build_dev_ntf() which should only use
> associated netns's?
>
Yes. But should we keep this check to prevent future usage of this
function to misbehave?
> >
> > +int psp_nl_dev_assoc_doit(struct sk_buff *skb, struct genl_info *info)
> > +{
> > + struct psp_dev *psd = info->user_ptr[0];
> > + struct psp_assoc_dev *psp_assoc_dev;
> > + struct net_device *assoc_dev;
> > + struct sk_buff *rsp;
> > + u32 assoc_ifindex;
> > + struct net *net;
> > + int nsid;
> > +
> > + if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_IFINDEX))
> > + return -EINVAL;
> > +
> > + if (info->attrs[PSP_A_DEV_NSID]) {
> > + nsid = nla_get_s32(info->attrs[PSP_A_DEV_NSID]);
> > +
> > + net = get_net_ns_by_id(genl_info_net(info), nsid);
> > + if (!net) {
> > + NL_SET_BAD_ATTR(info->extack,
> > + info->attrs[PSP_A_DEV_NSID]);
> > + return -EINVAL;
> > + }
> > + } else {
> > + net = get_net(genl_info_net(info));
> > + }
> > +
> > + psp_assoc_dev = kzalloc(sizeof(*psp_assoc_dev), GFP_KERNEL);
> > + if (!psp_assoc_dev) {
> > + put_net(net);
> > + return -ENOMEM;
> > + }
> > +
> > + assoc_ifindex = nla_get_u32(info->attrs[PSP_A_DEV_IFINDEX]);
> > + assoc_dev = netdev_get_by_index(net, assoc_ifindex,
> > + &psp_assoc_dev->dev_tracker,
> > + GFP_KERNEL);
> > + if (!assoc_dev) {
> > + put_net(net);
> > + kfree(psp_assoc_dev);
> > + NL_SET_BAD_ATTR(info->extack, info->attrs[PSP_A_DEV_IFINDEX]);
> > + return -ENODEV;
> > + }
> > +
> > + /* Check if device is already associated with a PSP device */
> > + if (cmpxchg(&assoc_dev->psp_dev, NULL, RCU_INITIALIZER(psd))) {
> > + NL_SET_ERR_MSG(info->extack,
> > + "Device already associated with a PSP device");
> > + netdev_put(assoc_dev, &psp_assoc_dev->dev_tracker);
> > + put_net(net);
> > + kfree(psp_assoc_dev);
> > + return -EBUSY;
> > + }
> > +
> > + psp_assoc_dev->assoc_dev = assoc_dev;
> > + rsp = psp_nl_reply_new(info);
> > + if (!rsp) {
> > + rcu_assign_pointer(assoc_dev->psp_dev, NULL);
> > + netdev_put(assoc_dev, &psp_assoc_dev->dev_tracker);
> > + put_net(net);
> > + kfree(psp_assoc_dev);
> > + return -ENOMEM;
> > + }
> > +
> > + list_add_tail(&psp_assoc_dev->dev_list, &psd->assoc_dev_list);
> > +
> > + put_net(net);
> > +
> > + psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF);
> > +
> > + return psp_nl_reply_send(rsp, info);
> > +}
> > +
>
>
> This function could probably benefit from a goto style cleanup chain,
> given the overlapping set of actions to unwind at each error.
>
Ack. Updating in the new version.
> >
> > int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
> > @@ -320,7 +617,9 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
> >
> > psd = psp_dev_get_for_sock(socket->sk);
> > if (psd) {
> > + mutex_lock(&psd->lock);
> > err = psp_dev_check_access(psd, genl_info_net(info), false);
> > + mutex_unlock(&psd->lock);
>
>
> This looks like a "TOCTOU" issue on the mutable assoc_dev_list, but I
> think it ends up being a benign race.
Yea. Will address in the next version.
>
>
> > if (err) {
> > psp_dev_put(psd);
> > psd = NULL;
>
>
> Some minor comments, but otherwise:
>
> Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com>
>
>
^ permalink raw reply
* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Dave Hansen @ 2026-04-07 23:41 UTC (permalink / raw)
To: Jim Mattson, Pawan Gupta
Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
David Laight, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
David Ahern, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, John Fastabend, Stanislav Fomichev, Hao Luo,
Paolo Bonzini, Jonathan Corbet, linux-kernel, kvm, Asit Mallick,
Tao Zhang, bpf, netdev, linux-doc, chao.gao
In-Reply-To: <CALMp9eQjSqwnvJz4JVzYpMkkTiucSJtW48zC4Hj9GBiUhOH-Eg@mail.gmail.com>
On 4/7/26 16:27, Jim Mattson wrote:
> What is your proposed BHI_DIS_S override mechanism, then?
Let me make sure I get this right. The desire is to:
1. Have hypervisors lie to guests about the CPU they are running on (for
the benefit of large/diverse migration pools)
2. Have guests be allowed to boot with BHI_DIS_S for performance
3. Have apps in those guests that care about security to opt back in to
BHI_DIS_S for themselves?
^ permalink raw reply
* Re: [PATCH net-next v1] r8169: implement get_module functions for rtl8127atf
From: Andrew Lunn @ 2026-04-07 23:50 UTC (permalink / raw)
To: Fabio Baltieri
Cc: Heiner Kallweit, nic_swsd, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King - ARM Linux, netdev, linux-kernel
In-Reply-To: <adWIy9d9VgDha_ub@google.com>
> > Yes, that would be best. Call i2c_add_adapter() to add it to the I2C
> > core. The SFP code in drivers/net/phy can then make use of it.
>
> Hey Andrew, thanks for the pointers, I was able to instantiate the dw
> i2c controller, same as txgbe/txgbe_phy.c really.
Cool.
> For some reasons I had to revert 560072246088, was getting a "spurious
> STOP detected" but I'll follow up on that separately.
I2C transfers need to be multiples of 2 bytes, in order for the HWMON
to work correctly. You need to read the 16 bits in a single
operation. If they get chopped up into multiple transfers they become
unreliable. So it would be good to get to the bottom of this.
> Anyway I was able to make detection and los work, so I have some code
> with gpio, i2c and the sfp module detect and showing up in hwmon, and
> sitting in waitdev state.
Nice.
> >
> > And you will need a PCS driver.
> >
> > But first step is probably to work with the existing Base-T devices
> > and convert the driver to phylink.
>
> Ok I was going to ask how to connect the one above with the ethtool
> handlers, so you are saying that first the whole driver needs to be
> converted to use phylink/pcs and then that should work automatically-ish
> right?
Yes, once phylink is coordinating all the parts, accessing the module
via ethtool should just start working.
> Can I send the current code I have as an RFC in the meantime to get some
> early feedback?
Sure.
> It's a pretty substantial amount of boilerplate code so I suspect Heiner
> may want to refactor things about it, feels like it could live in its
> own file.
How much is identical to the txgbe? I wounder if it makes sense to add
some helpers which can be shared by both drivers?
Andrew
^ permalink raw reply
* Re: [PATCH v10 net-next 3/5] psp: add a new netdev event for dev unregister
From: Wei Wang @ 2026-04-08 0:01 UTC (permalink / raw)
To: Daniel Zahka
Cc: netdev, Jakub Kicinski, Willem de Bruijn, David Wei, Andrew Lunn,
David S . Miller, Eric Dumazet, Simon Horman, Wei Wang
In-Reply-To: <c502c837-6f01-4854-bd6b-e81aba940dc8@gmail.com>
On Mon, Apr 6, 2026 at 7:12 AM Daniel Zahka <daniel.zahka@gmail.com> wrote:
>
>
> On 4/5/26 1:58 AM, Wei Wang wrote:
> > From: Wei Wang <weibunny@fb.com>
> >
> > Add a new netdev event for dev unregister and handle the removal of this
> > dev from psp->assoc_dev_list, upon the first dev-assoc operation.
> >
> > Signed-off-by: Wei Wang <weibunny@fb.com>
> > ---
> ...
> > +static bool psp_notifier_registered;
> > +
> > +/**
> > + * psp_attach_netdev_notifier() - register netdev notifier on first use
> > + *
> > + * Register the netdevice notifier when the first device association
> > + * is created. In many installations no associations will be created and
> > + * the notifier won't be needed.
> > + *
> > + * Must be called without psd->lock held, due to lock ordering:
> > + * rtnl_lock -> psd->lock (the notifier callback runs under rtnl_lock
> > + * and takes psd->lock).
> > + */
> > +int psp_attach_netdev_notifier(void)
> > +{
> > + int err = 0;
> > +
> > + if (READ_ONCE(psp_notifier_registered))
> > + return 0;
> > +
> > + mutex_lock(&psp_devs_lock);
> > + if (!psp_notifier_registered) {
> > + err = register_netdevice_notifier(&psp_netdev_notifier);
> > + if (!err)
> > + WRITE_ONCE(psp_notifier_registered, true);
> > + }
> > + mutex_unlock(&psp_devs_lock);
> > +
> > + return err;
> > +}
> > +
>
>
> This looks like an abuse of the psp_devs_lock to provide mutual
> exclusion on psp_notifier_registered, which has the undesirable property
> of establishing a lock dependency from psp_devs_lock -> rtnl_lock.
> Perhaps a dedicated mutex would be cleaner.
>
Ack. Will address in the next version.
> >
> > +/**
> > + * Non-admin version of psp_device_get_locked() + psp_attach_netdev_notifier()
> > + * only used for dev-assoc.
> > + */
> > +int psp_device_get_locked_dev_assoc(const struct genl_split_ops *ops,
> > + struct sk_buff *skb, struct genl_info *info)
>
>
> kdoc open, but not a kdoc comment.
Ack.
>
> Minor comments, otherwise:
>
> Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com>
>
^ permalink raw reply
* Re: [PATCH net-next 13/13] netfilter: ctnetlink: restrict expectfn to helper
From: Pablo Neira Ayuso @ 2026-04-08 0:05 UTC (permalink / raw)
To: Florian Westphal; +Cc: netdev
In-Reply-To: <adUUTtWT8ITs83It@strlen.de>
On Tue, Apr 07, 2026 at 04:27:26PM +0200, Florian Westphal wrote:
> Florian Westphal <fw@strlen.de> wrote:
> > list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
> > - if (!strcmp(cur->name, name)) {
> > + if ((cur->helper && !strcmp(cur->helper, helper)) ||
> > + !strcmp(cur->name, name)) {
>
> Sigh, I don't know why I did not see this earlier. It looks wrong.
>
> Should this be:
>
> if ((cur->helper && strcmp(cur->helper, helper))
> continue; // skip, name doesn't match
>
> if (!strcmp(cur->name, name)) {
> ...
>
> as is, this restriction has no effect in case the requested
> name matches?
>
> AI suggests
>
> if ((cur->helper && !strcmp(cur->helper, helper)) &&
> !strcmp(cur->name, name)) {
>
> ... but i think thats bogus too. What to do?
>
> Send v2 or do you want to followup later?
Keep it back, not urgent.
I should have withdraw this patch, I wanted to use an enum instead of
strings in v2.
Sorry.
^ permalink raw reply
* Re: [PATCH net-next v5 00/10] Decouple receive and transmit enablement in team driver
From: Marc Harvey @ 2026-04-08 0:12 UTC (permalink / raw)
To: Jiri Pirko
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Shuah Khan, Simon Horman, netdev, linux-kernel,
linux-kselftest
In-Reply-To: <utlz3oebjyd4lqp5pd4qdbth2kywxo4hrlrnwcxsx3br76fwfg@vy3tx5f3lv32>
On Tue, Apr 7, 2026 at 4:55 AM Jiri Pirko <jiri@resnulli.us> wrote:
>
> Mon, Apr 06, 2026 at 05:03:36AM +0200, marcharvey@google.com wrote:
> >Allow independent control over receive and transmit enablement states
> >for aggregated ports in the team driver.
> >
> >The motivation is that IEE 802.3ad LACP "independent control" can't
> >be implemented for the team driver currently. This was added to the
> >bonding driver in commit 240fd405528b ("bonding: Add independent
> >control state machine").
> >
> >This series also has a few patches that add tests to show that the old
> >coupled enablement still works and that the new decoupled enablement
> >works as intended (4, 5, and 10).
> >
> >There are three patches with small fixes as well, with the goal of
> >making the final decoupling patch clearer (1, 2, and 3).
>
> Looks fine to me now. Do you have libteam/teamd counterpart?
I don't see a need for this to be used in any of the teamd runners.
Libteam should support this out of the box, since the options are
identified over netlink by their string names. The options.sh test
uses teamnl, which uses libteam, to set the new options.
^ permalink raw reply
* [PATCH net] tcp: update window_clamp when SO_RCVBUF is set
From: Jakub Kicinski @ 2026-04-08 0:14 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, Jakub Kicinski,
ncardwell, kuniyu, willemb, dsahern, quic_subashab, quic_stranche
Commit under Fixes moved recomputing the window clamp to
tcp_measure_rcv_mss() (when scaling_ratio changes).
I suspect it missed the fact that we don't recompute the clamp
when rcvbuf is set. Until scaling_ratio changes we are
stuck with the old window clamp which may be based on
the small initial buffer. scaling_ratio may never change.
Inspired by Eric's recent commit d1361840f8c5 ("tcp: fix
SO_RCVLOWAT and RCVBUF autotuning") plumb the user action
thru to TCP and have it update the clamp.
A smaller fix would be to just have tcp_rcvbuf_grow()
adjust the clamp even if SOCK_RCVBUF_LOCK is set.
But IIUC this is what we were trying to get away from
in the first place.
Fixes: a2cbb1603943 ("tcp: Update window clamping condition")
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: ncardwell@google.com
CC: kuniyu@google.com
CC: willemb@google.com
CC: dsahern@kernel.org
CC: quic_subashab@quicinc.com
CC: quic_stranche@quicinc.com
---
include/linux/net.h | 1 +
include/net/tcp.h | 1 +
net/core/sock.c | 9 +++++++++
net/ipv4/af_inet.c | 1 +
net/ipv4/tcp.c | 5 +++++
net/ipv6/af_inet6.c | 1 +
6 files changed, 18 insertions(+)
diff --git a/include/linux/net.h b/include/linux/net.h
index a8e818de95b3..ca6a7bc5c9ae 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -223,6 +223,7 @@ struct proto_ops {
int (*sendmsg_locked)(struct sock *sk, struct msghdr *msg,
size_t size);
int (*set_rcvlowat)(struct sock *sk, int val);
+ void (*set_rcvbuf)(struct sock *sk, int val);
};
#define DECLARE_SOCKADDR(type, dst, src) \
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 6156d1d068e1..b9db447892dd 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -516,6 +516,7 @@ void tcp_syn_ack_timeout(const struct request_sock *req);
int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
int flags);
int tcp_set_rcvlowat(struct sock *sk, int val);
+void tcp_set_rcvbuf(struct sock *sk, int val);
int tcp_set_window_clamp(struct sock *sk, int val);
static inline void
diff --git a/net/core/sock.c b/net/core/sock.c
index fdaf66e6dc18..f3a186376bc5 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -973,6 +973,8 @@ EXPORT_SYMBOL(sock_set_keepalive);
static void __sock_set_rcvbuf(struct sock *sk, int val)
{
+ struct socket *sock = sk->sk_socket;
+
/* Ensure val * 2 fits into an int, to prevent max_t() from treating it
* as a negative value.
*/
@@ -990,6 +992,13 @@ static void __sock_set_rcvbuf(struct sock *sk, int val)
* we actually used in getsockopt is the most desirable behavior.
*/
WRITE_ONCE(sk->sk_rcvbuf, max_t(int, val * 2, SOCK_MIN_RCVBUF));
+
+ if (sock) {
+ const struct proto_ops *ops = READ_ONCE(sock->ops);
+
+ if (ops->set_rcvbuf)
+ ops->set_rcvbuf(sk, sk->sk_rcvbuf);
+ }
}
void sock_set_rcvbuf(struct sock *sk, int val)
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index f98e46ae3e30..0e62032e76b1 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1091,6 +1091,7 @@ const struct proto_ops inet_stream_ops = {
.compat_ioctl = inet_compat_ioctl,
#endif
.set_rcvlowat = tcp_set_rcvlowat,
+ .set_rcvbuf = tcp_set_rcvbuf,
};
EXPORT_SYMBOL(inet_stream_ops);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index e57eaffc007a..1a494d18c5fd 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1858,6 +1858,11 @@ int tcp_set_rcvlowat(struct sock *sk, int val)
return 0;
}
+void tcp_set_rcvbuf(struct sock *sk, int val)
+{
+ tcp_set_window_clamp(sk, tcp_win_from_space(sk, val));
+}
+
#ifdef CONFIG_MMU
static const struct vm_operations_struct tcp_vm_ops = {
};
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index ee341a8254bf..0a88b376141d 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -690,6 +690,7 @@ const struct proto_ops inet6_stream_ops = {
.compat_ioctl = inet6_compat_ioctl,
#endif
.set_rcvlowat = tcp_set_rcvlowat,
+ .set_rcvbuf = tcp_set_rcvbuf,
};
EXPORT_SYMBOL_GPL(inet6_stream_ops);
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v2] net: bcmasp: Switch to page pool for RX path
From: Florian Fainelli @ 2026-04-08 0:18 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Justin Chen, Vikas Gupta,
Rajashekar Hudumula, Bhargava Marreddy, Arnd Bergmann,
Markus Blöchl, Heiner Kallweit, Fernando Fernandez Mancera,
open list, open list:BROADCOM ASP 2.0 ETHERNET DRIVER,
Nicolai Buchwitz
This shows an improvement of 1.9% in reducing the CPU cycles and data
cache misses.
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
Changes in v2:
- addressed Nicolai's comments by setting the .netdev and .napi members
and dropped the useless comment and .dma_dir initialization
drivers/net/ethernet/broadcom/Kconfig | 1 +
drivers/net/ethernet/broadcom/asp2/bcmasp.h | 8 +-
.../net/ethernet/broadcom/asp2/bcmasp_intf.c | 125 +++++++++++++++---
.../ethernet/broadcom/asp2/bcmasp_intf_defs.h | 4 +
4 files changed, 115 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig
index dd164acafd01..4287edc7ddd6 100644
--- a/drivers/net/ethernet/broadcom/Kconfig
+++ b/drivers/net/ethernet/broadcom/Kconfig
@@ -272,6 +272,7 @@ config BCMASP
depends on OF
select PHYLIB
select MDIO_BCM_UNIMAC
+ select PAGE_POOL
help
This configuration enables the Broadcom ASP 2.0 Ethernet controller
driver which is present in Broadcom STB SoCs such as 72165.
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.h b/drivers/net/ethernet/broadcom/asp2/bcmasp.h
index 29cd87335ec8..8c8ffaeadc79 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp.h
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.h
@@ -6,6 +6,7 @@
#include <linux/phy.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <uapi/linux/ethtool.h>
+#include <net/page_pool/helpers.h>
#define ASP_INTR2_OFFSET 0x1000
#define ASP_INTR2_STATUS 0x0
@@ -298,16 +299,19 @@ struct bcmasp_intf {
void __iomem *rx_edpkt_cfg;
void __iomem *rx_edpkt_dma;
int rx_edpkt_index;
- int rx_buf_order;
struct bcmasp_desc *rx_edpkt_cpu;
dma_addr_t rx_edpkt_dma_addr;
dma_addr_t rx_edpkt_dma_read;
dma_addr_t rx_edpkt_dma_valid;
- /* RX buffer prefetcher ring*/
+ /* Streaming RX data ring (RBUF_4K mode) */
void *rx_ring_cpu;
dma_addr_t rx_ring_dma;
dma_addr_t rx_ring_dma_valid;
+ int rx_buf_order;
+
+ /* Page pool for recycling RX SKB data pages */
+ struct page_pool *rx_page_pool;
struct napi_struct rx_napi;
struct bcmasp_res res;
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
index b368ec2fea43..ec63f50a849e 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <net/ip.h>
#include <net/ipv6.h>
+#include <net/page_pool/helpers.h>
#include "bcmasp.h"
#include "bcmasp_intf_defs.h"
@@ -482,10 +483,14 @@ static int bcmasp_rx_poll(struct napi_struct *napi, int budget)
struct bcmasp_desc *desc;
struct sk_buff *skb;
dma_addr_t valid;
+ struct page *page;
void *data;
u64 flags;
u32 len;
+ /* Hardware advances DMA_VALID as it writes each descriptor
+ * (RBUF_4K streaming mode); software chases with rx_edpkt_dma_read.
+ */
valid = rx_edpkt_dma_rq(intf, RX_EDPKT_DMA_VALID) + 1;
if (valid == intf->rx_edpkt_dma_addr + DESC_RING_SIZE)
valid = intf->rx_edpkt_dma_addr;
@@ -493,12 +498,12 @@ static int bcmasp_rx_poll(struct napi_struct *napi, int budget)
while ((processed < budget) && (valid != intf->rx_edpkt_dma_read)) {
desc = &intf->rx_edpkt_cpu[intf->rx_edpkt_index];
- /* Ensure that descriptor has been fully written to DRAM by
- * hardware before reading by the CPU
+ /* Ensure the descriptor has been fully written to DRAM by
+ * the hardware before the CPU reads it.
*/
rmb();
- /* Calculate virt addr by offsetting from physical addr */
+ /* Locate the packet data inside the streaming ring buffer. */
data = intf->rx_ring_cpu +
(DESC_ADDR(desc->buf) - intf->rx_ring_dma);
@@ -524,19 +529,38 @@ static int bcmasp_rx_poll(struct napi_struct *napi, int budget)
len = desc->size;
- skb = napi_alloc_skb(napi, len);
- if (!skb) {
+ /* Allocate a page pool page as the SKB data area so the
+ * kernel can recycle it efficiently after the packet is
+ * consumed, avoiding repeated slab allocations.
+ */
+ page = page_pool_dev_alloc_pages(intf->rx_page_pool);
+ if (!page) {
u64_stats_update_begin(&stats->syncp);
u64_stats_inc(&stats->rx_dropped);
u64_stats_update_end(&stats->syncp);
intf->mib.alloc_rx_skb_failed++;
+ goto next;
+ }
+ skb = napi_build_skb(page_address(page), PAGE_SIZE);
+ if (!skb) {
+ u64_stats_update_begin(&stats->syncp);
+ u64_stats_inc(&stats->rx_dropped);
+ u64_stats_update_end(&stats->syncp);
+ intf->mib.alloc_rx_skb_failed++;
+ page_pool_recycle_direct(intf->rx_page_pool, page);
goto next;
}
+ /* Reserve headroom then copy the full descriptor payload
+ * (hardware prepends a 2-byte alignment pad at the start).
+ */
+ skb_reserve(skb, NET_SKB_PAD);
skb_put(skb, len);
memcpy(skb->data, data, len);
+ skb_mark_for_recycle(skb);
+ /* Skip the 2-byte hardware alignment pad. */
skb_pull(skb, 2);
len -= 2;
if (likely(intf->crc_fwd)) {
@@ -558,6 +582,7 @@ static int bcmasp_rx_poll(struct napi_struct *napi, int budget)
u64_stats_update_end(&stats->syncp);
next:
+ /* Return this portion of the streaming ring buffer to HW. */
rx_edpkt_cfg_wq(intf, (DESC_ADDR(desc->buf) + desc->size),
RX_EDPKT_RING_BUFFER_READ);
@@ -661,12 +686,31 @@ static void bcmasp_adj_link(struct net_device *dev)
phy_print_status(phydev);
}
-static int bcmasp_alloc_buffers(struct bcmasp_intf *intf)
+static struct page_pool *
+bcmasp_rx_page_pool_create(struct bcmasp_intf *intf)
+{
+ struct page_pool_params pp_params = {
+ .order = 0,
+ .flags = 0,
+ .pool_size = NUM_4K_BUFFERS,
+ .nid = NUMA_NO_NODE,
+ .dev = &intf->parent->pdev->dev,
+ .napi = &intf->rx_napi,
+ .netdev = intf->ndev,
+ .offset = 0,
+ .max_len = PAGE_SIZE,
+ };
+
+ return page_pool_create(&pp_params);
+}
+
+static int bcmasp_alloc_rx_buffers(struct bcmasp_intf *intf)
{
struct device *kdev = &intf->parent->pdev->dev;
struct page *buffer_pg;
+ int ret;
- /* Alloc RX */
+ /* Contiguous streaming ring that hardware writes packet data into. */
intf->rx_buf_order = get_order(RING_BUFFER_SIZE);
buffer_pg = alloc_pages(GFP_KERNEL, intf->rx_buf_order);
if (!buffer_pg)
@@ -675,13 +719,55 @@ static int bcmasp_alloc_buffers(struct bcmasp_intf *intf)
intf->rx_ring_cpu = page_to_virt(buffer_pg);
intf->rx_ring_dma = dma_map_page(kdev, buffer_pg, 0, RING_BUFFER_SIZE,
DMA_FROM_DEVICE);
- if (dma_mapping_error(kdev, intf->rx_ring_dma))
- goto free_rx_buffer;
+ if (dma_mapping_error(kdev, intf->rx_ring_dma)) {
+ ret = -ENOMEM;
+ goto free_ring_pages;
+ }
+
+ /* Page pool for SKB data areas (copy targets, not DMA buffers). */
+ intf->rx_page_pool = bcmasp_rx_page_pool_create(intf);
+ if (IS_ERR(intf->rx_page_pool)) {
+ ret = PTR_ERR(intf->rx_page_pool);
+ intf->rx_page_pool = NULL;
+ goto free_ring_dma;
+ }
+
+ return 0;
+
+free_ring_dma:
+ dma_unmap_page(kdev, intf->rx_ring_dma, RING_BUFFER_SIZE,
+ DMA_FROM_DEVICE);
+free_ring_pages:
+ __free_pages(buffer_pg, intf->rx_buf_order);
+ return ret;
+}
+
+static void bcmasp_reclaim_rx_buffers(struct bcmasp_intf *intf)
+{
+ struct device *kdev = &intf->parent->pdev->dev;
+
+ page_pool_destroy(intf->rx_page_pool);
+ intf->rx_page_pool = NULL;
+ dma_unmap_page(kdev, intf->rx_ring_dma, RING_BUFFER_SIZE,
+ DMA_FROM_DEVICE);
+ __free_pages(virt_to_page(intf->rx_ring_cpu), intf->rx_buf_order);
+}
+
+static int bcmasp_alloc_buffers(struct bcmasp_intf *intf)
+{
+ struct device *kdev = &intf->parent->pdev->dev;
+ int ret;
+
+ /* Alloc RX */
+ ret = bcmasp_alloc_rx_buffers(intf);
+ if (ret)
+ return ret;
intf->rx_edpkt_cpu = dma_alloc_coherent(kdev, DESC_RING_SIZE,
- &intf->rx_edpkt_dma_addr, GFP_KERNEL);
+ &intf->rx_edpkt_dma_addr,
+ GFP_KERNEL);
if (!intf->rx_edpkt_cpu)
- goto free_rx_buffer_dma;
+ goto free_rx_buffers;
/* Alloc TX */
intf->tx_spb_cpu = dma_alloc_coherent(kdev, DESC_RING_SIZE,
@@ -701,11 +787,8 @@ static int bcmasp_alloc_buffers(struct bcmasp_intf *intf)
free_rx_edpkt_dma:
dma_free_coherent(kdev, DESC_RING_SIZE, intf->rx_edpkt_cpu,
intf->rx_edpkt_dma_addr);
-free_rx_buffer_dma:
- dma_unmap_page(kdev, intf->rx_ring_dma, RING_BUFFER_SIZE,
- DMA_FROM_DEVICE);
-free_rx_buffer:
- __free_pages(buffer_pg, intf->rx_buf_order);
+free_rx_buffers:
+ bcmasp_reclaim_rx_buffers(intf);
return -ENOMEM;
}
@@ -717,9 +800,7 @@ static void bcmasp_reclaim_free_buffers(struct bcmasp_intf *intf)
/* RX buffers */
dma_free_coherent(kdev, DESC_RING_SIZE, intf->rx_edpkt_cpu,
intf->rx_edpkt_dma_addr);
- dma_unmap_page(kdev, intf->rx_ring_dma, RING_BUFFER_SIZE,
- DMA_FROM_DEVICE);
- __free_pages(virt_to_page(intf->rx_ring_cpu), intf->rx_buf_order);
+ bcmasp_reclaim_rx_buffers(intf);
/* TX buffers */
dma_free_coherent(kdev, DESC_RING_SIZE, intf->tx_spb_cpu,
@@ -738,7 +819,7 @@ static void bcmasp_init_rx(struct bcmasp_intf *intf)
/* Make sure channels are disabled */
rx_edpkt_cfg_wl(intf, 0x0, RX_EDPKT_CFG_ENABLE);
- /* Rx SPB */
+ /* Streaming data ring: hardware writes raw packet bytes here. */
rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_READ);
rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_WRITE);
rx_edpkt_cfg_wq(intf, intf->rx_ring_dma, RX_EDPKT_RING_BUFFER_BASE);
@@ -747,7 +828,9 @@ static void bcmasp_init_rx(struct bcmasp_intf *intf)
rx_edpkt_cfg_wq(intf, intf->rx_ring_dma_valid,
RX_EDPKT_RING_BUFFER_VALID);
- /* EDPKT */
+ /* EDPKT descriptor ring: hardware fills descriptors pointing into
+ * the streaming ring buffer above (RBUF_4K mode).
+ */
rx_edpkt_cfg_wl(intf, (RX_EDPKT_CFG_CFG0_RBUF_4K <<
RX_EDPKT_CFG_CFG0_DBUF_SHIFT) |
(RX_EDPKT_CFG_CFG0_64_ALN <<
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf_defs.h b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf_defs.h
index af7418348e81..0318f257452a 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf_defs.h
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf_defs.h
@@ -246,6 +246,10 @@
((((intf)->channel - 6) * 0x14) + 0xa2000)
#define RX_SPB_TOP_BLKOUT 0x00
+/*
+ * Number of 4 KB pages that make up the contiguous RBUF_4K streaming ring
+ * and the page pool used as copy-target SKB data areas.
+ */
#define NUM_4K_BUFFERS 32
#define RING_BUFFER_SIZE (PAGE_SIZE * NUM_4K_BUFFERS)
--
2.34.1
^ permalink raw reply related
* [PATCH net] eth: fbnic: Use wake instead of start
From: Mohsin Bashir @ 2026-04-08 0:24 UTC (permalink / raw)
To: netdev
Cc: alexanderduyck, alok.a.tiwari, andrew+netdev, davem, dg573847474,
edumazet, horms, jacob.e.keller, kernel-team, kuba, lee,
linux-kernel, mohsin.bashr, pabeni
From: Mohsin Bashir <hmohsin@meta.com>
fbnic_up() calls netif_tx_start_all_queues(), which only clears
__QUEUE_STATE_DRV_XOFF. If qdisc backlog has accumulated on any TX
queue before the reconfiguration (e.g. ring resize via ethtool -G),
start does not call __netif_schedule() to kick the qdisc, so the
pending backlog is never drained and the queue stalls.
Switch to netif_tx_wake_all_queues(), which clears DRV_XOFF and also
calls __netif_schedule() on every queue, ensuring any backlog that
built up before the down/up cycle is promptly dequeued.
Fixes: bc6107771bb4 ("eth: fbnic: Allocate a netdevice and napi vectors with queues")
Signed-off-by: Mohsin Bashir <hmohsin@meta.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/meta/fbnic/fbnic_pci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
index 3fa9d1910daa..8f331358c972 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
@@ -139,7 +139,7 @@ void fbnic_up(struct fbnic_net *fbn)
/* Enable Tx/Rx processing */
fbnic_napi_enable(fbn);
- netif_tx_start_all_queues(fbn->netdev);
+ netif_tx_wake_all_queues(fbn->netdev);
fbnic_service_task_start(fbn);
--
2.52.0
^ permalink raw reply related
* [PATCH net-next 0/2] tcp: rehash onto different ECMP path on retransmit timeout
From: Neil Spring @ 2026-04-08 0:28 UTC (permalink / raw)
To: netdev
Cc: edumazet, ncardwell, kuniyu, davem, dsahern, kuba, pabeni, horms,
shuah, linux-kselftest
I configured an ECMP route across two local interfaces, expecting
autoflowlabel to adapt when one of those interfaces is blocked in
reaching the destination. However, it did not. This affected SYN,
SYN/ACK, and established traffic.
I was able to make this work as I expected it to by calling
sk_dst_reset() at times when txhash was updated, and propagating
sk_txhash into fl6->mp_hash so fib6_select_path() uses the socket's
current hash for ECMP selection.
I expected autoflowlabel to apply to local ECMP routes, and think
it should, but am open to feedback if this isn't the right way to
do it.
Patch 1 has the kernel changes; patch 2 adds a selftest exercising
SYN, SYN/ACK, and established connection rehash over a two-path
ECMP topology. The selftest retries a SYN 26 times, so has a tiny
(~3e-8) probability of false failure if repeatedly unlucky with ECMP
path selection.
Neil Spring (2):
tcp: rehash onto different ECMP path on retransmit timeout
selftests: net: add ECMP rehash test
net/ipv4/tcp_input.c | 4 +-
net/ipv4/tcp_minisocks.c | 9 +
net/ipv4/tcp_plb.c | 1 +
net/ipv4/tcp_timer.c | 1 +
net/ipv6/inet6_connection_sock.c | 8 +
tools/testing/selftests/net/Makefile | 1 +
tools/testing/selftests/net/ecmp_rehash.sh | 354 +++++++++++++++++++++
7 files changed, 377 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/net/ecmp_rehash.sh
--
2.52.0
^ permalink raw reply
* [PATCH net-next 1/2] tcp: rehash onto different ECMP path on retransmit timeout
From: Neil Spring @ 2026-04-08 0:28 UTC (permalink / raw)
To: netdev
Cc: edumazet, ncardwell, kuniyu, davem, dsahern, kuba, pabeni, horms,
shuah, linux-kselftest
In-Reply-To: <20260408002802.2448424-1-ntspring@meta.com>
Add sk_dst_reset() alongside sk_rethink_txhash() in the RTO, PLB,
and spurious-retrans paths so that the next transmit triggers a fresh
route lookup. Propagate sk_txhash into fl6->mp_hash in
inet6_csk_route_req() and inet6_csk_route_socket() so
fib6_select_path() uses the socket's current hash for ECMP selection.
The ir_iif update in tcp_check_req() covers both IPv4 and IPv6
because it was cleaner than gating on address family; IPv4 is
otherwise unaltered, and not having autoflowlabel in IPv4 means
I wouldn't expect a new path on timeout.
It is possible that PLB does not need this (that there are other
methods of reacting to local congestion); I added the sk_dst_reset
for consistency.
Signed-off-by: Neil Spring <ntspring@meta.com>
---
net/ipv4/tcp_input.c | 4 +++-
net/ipv4/tcp_minisocks.c | 9 +++++++++
net/ipv4/tcp_plb.c | 1 +
net/ipv4/tcp_timer.c | 1 +
net/ipv6/inet6_connection_sock.c | 8 ++++++++
5 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 7171442c3ed7..3d42ab45066c 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5014,8 +5014,10 @@ static void tcp_rcv_spurious_retrans(struct sock *sk,
skb->protocol == htons(ETH_P_IPV6) &&
(tcp_sk(sk)->inet_conn.icsk_ack.lrcv_flowlabel !=
ntohl(ip6_flowlabel(ipv6_hdr(skb)))) &&
- sk_rethink_txhash(sk))
+ sk_rethink_txhash(sk)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPDUPLICATEDATAREHASH);
+ sk_dst_reset(sk);
+ }
/* Save last flowlabel after a spurious retrans. */
tcp_save_lrcv_flowlabel(sk, skb);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 199f0b579e89..ef4b3771e9d8 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -750,6 +750,15 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* Reset timer after retransmitting SYNACK, similar to
* the idea of fast retransmit in recovery.
*/
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == AF_INET6)
+ inet_rsk(req)->ir_iif = tcp_v6_iif(skb);
+ else
+#endif
+ inet_rsk(req)->ir_iif =
+ inet_request_bound_dev_if(sk, skb);
+
if (!tcp_oow_rate_limited(sock_net(sk), skb,
LINUX_MIB_TCPACKSKIPPEDSYNRECV,
&tcp_rsk(req)->last_oow_ack_time)) {
diff --git a/net/ipv4/tcp_plb.c b/net/ipv4/tcp_plb.c
index 68ccdb9a5412..d7cc00a58e53 100644
--- a/net/ipv4/tcp_plb.c
+++ b/net/ipv4/tcp_plb.c
@@ -79,6 +79,7 @@ void tcp_plb_check_rehash(struct sock *sk, struct tcp_plb_state *plb)
return;
sk_rethink_txhash(sk);
+ sk_dst_reset(sk);
plb->consec_cong_rounds = 0;
tcp_sk(sk)->plb_rehash++;
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPLBREHASH);
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index ea99988795e7..acc22fc532c2 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -299,6 +299,7 @@ static int tcp_write_timeout(struct sock *sk)
if (sk_rethink_txhash(sk)) {
tp->timeout_rehash++;
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTIMEOUTREHASH);
+ sk_dst_reset(sk);
}
return 0;
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 37534e116899..2fe753bb38b4 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -48,6 +48,11 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk,
fl6->flowi6_uid = sk_uid(sk);
security_req_classify_flow(req, flowi6_to_flowi_common(fl6));
+ if (req->num_retrans)
+ fl6->mp_hash = jhash_1word(req->num_retrans,
+ (__force u32)ireq->ir_rmt_port)
+ >> 1;
+
if (!dst) {
dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p);
if (IS_ERR(dst))
@@ -70,6 +75,9 @@ struct dst_entry *inet6_csk_route_socket(struct sock *sk,
fl6->saddr = np->saddr;
fl6->flowlabel = np->flow_label;
IP6_ECN_flow_xmit(sk, fl6->flowlabel);
+
+ if (sk->sk_txhash)
+ fl6->mp_hash = sk->sk_txhash >> 1;
fl6->flowi6_oif = sk->sk_bound_dev_if;
fl6->flowi6_mark = sk->sk_mark;
fl6->fl6_sport = inet->inet_sport;
--
2.52.0
^ permalink raw reply related
* [PATCH net-next 2/2] selftests: net: add ECMP rehash test
From: Neil Spring @ 2026-04-08 0:28 UTC (permalink / raw)
To: netdev
Cc: edumazet, ncardwell, kuniyu, davem, dsahern, kuba, pabeni, horms,
shuah, linux-kselftest
In-Reply-To: <20260408002802.2448424-1-ntspring@meta.com>
Add ecmp_rehash.sh to exercise TCP ECMP path re-selection on
retransmission timeout. Three tests cover client SYN rehash, server
SYN/ACK rehash, and midstream RTO rehash of an established connection
over a two-path ECMP topology with one leg blocked by tc.
The SYN test retries 26 times, so has a false negative probability
of ~(1/2)^25 ≈ 3e-8.
Signed-off-by: Neil Spring <ntspring@meta.com>
---
tools/testing/selftests/net/Makefile | 1 +
tools/testing/selftests/net/ecmp_rehash.sh | 354 +++++++++++++++++++++
2 files changed, 355 insertions(+)
create mode 100755 tools/testing/selftests/net/ecmp_rehash.sh
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 6bced3ed798b..acc61a51d7e2 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -25,6 +25,7 @@ TEST_PROGS := \
cmsg_time.sh \
double_udp_encap.sh \
drop_monitor_tests.sh \
+ ecmp_rehash.sh \
fcnal-ipv4.sh \
fcnal-ipv6.sh \
fcnal-other.sh \
diff --git a/tools/testing/selftests/net/ecmp_rehash.sh b/tools/testing/selftests/net/ecmp_rehash.sh
new file mode 100755
index 000000000000..a062c0b51fd6
--- /dev/null
+++ b/tools/testing/selftests/net/ecmp_rehash.sh
@@ -0,0 +1,354 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test ECMP path re-selection on TCP retransmission timeout.
+#
+# Two namespaces connected by two parallel veth pairs with a 2-way ECMP
+# route. When a TCP path is blocked (via tc drop), RTO triggers
+# sk_rethink_txhash() + sk_dst_reset(), causing the next route lookup
+# to select the other ECMP path.
+#
+# False negative: ~(1/2)^25 ≈ 3e-8. With tcp_syn_retries=6 (~127 s
+# timeout) and tcp_syn_linear_timeouts=20 there are roughly 26
+# independent rehash attempts, each choosing one of 2 paths uniformly.
+
+source lib.sh
+
+SUBNETS=(a b)
+PORT=9900
+
+ALL_TESTS="
+ test_ecmp_rto_rehash
+ test_ecmp_synack_rehash
+ test_ecmp_midstream_rehash
+"
+
+link_tx_packets_get()
+{
+ local ns=$1; shift
+ local dev=$1; shift
+
+ ip netns exec "$ns" cat "/sys/class/net/$dev/statistics/tx_packets"
+}
+
+# Return the number of packets matched by the tc filter action on a device.
+# When tc drops packets via "action drop", the device's tx_packets is not
+# incremented (packet never reaches veth_xmit), but the tc action maintains
+# its own counter.
+tc_filter_pkt_count()
+{
+ local ns=$1; shift
+ local dev=$1; shift
+
+ ip netns exec "$ns" tc -s filter show dev "$dev" parent 1: 2>/dev/null |
+ awk '/Sent .* pkt/ { for (i=1;i<=NF;i++) if ($i=="pkt") { print $(i-1); exit } }'
+}
+
+# Read TcpTimeoutRehash counter from /proc/net/netstat in a namespace.
+# This counter increments in tcp_write_timeout() on every RTO that triggers
+# sk_rethink_txhash().
+get_timeout_rehash_count()
+{
+ local ns=$1; shift
+
+ ip netns exec "$ns" awk '
+ /^TcpExt:/ {
+ if (!h) { split($0, n); h=1 }
+ else {
+ split($0, v)
+ for (i in n)
+ if (n[i] == "TcpTimeoutRehash") print v[i]
+ }
+ }
+ ' /proc/net/netstat
+}
+
+# Block TCP (IPv6 next-header = 6) egress, allowing ICMPv6 through.
+block_tcp()
+{
+ local ns=$1; shift
+ local dev=$1; shift
+
+ ip netns exec "$ns" tc qdisc add dev "$dev" root handle 1: prio
+ ip netns exec "$ns" tc filter add dev "$dev" parent 1: \
+ protocol ipv6 prio 1 u32 match u8 0x06 0xff at 6 action drop
+}
+
+unblock_tcp()
+{
+ local ns=$1; shift
+ local dev=$1; shift
+
+ ip netns exec "$ns" tc qdisc del dev "$dev" root 2>/dev/null
+}
+
+# Return success when both devices have dropped at least one TCP packet.
+both_devs_attempted()
+{
+ local ns=$1; shift
+ local dev0=$1; shift
+ local dev1=$1; shift
+
+ local c0 c1
+ c0=$(tc_filter_pkt_count "$ns" "$dev0")
+ c1=$(tc_filter_pkt_count "$ns" "$dev1")
+ [ "${c0:-0}" -ge 1 ] && [ "${c1:-0}" -ge 1 ]
+}
+
+setup()
+{
+ setup_ns NS1 NS2
+
+ local ns
+ for ns in "$NS1" "$NS2"; do
+ ip netns exec "$ns" sysctl -qw net.ipv6.conf.all.accept_dad=0
+ ip netns exec "$ns" sysctl -qw net.ipv6.conf.default.accept_dad=0
+ ip netns exec "$ns" sysctl -qw net.ipv6.conf.all.forwarding=1
+ ip netns exec "$ns" sysctl -qw net.core.txrehash=1
+ done
+
+ local i sub
+ for i in 0 1; do
+ sub=${SUBNETS[$i]}
+ ip link add "veth${i}a" type veth peer name "veth${i}b"
+ ip link set "veth${i}a" netns "$NS1"
+ ip link set "veth${i}b" netns "$NS2"
+ ip -n "$NS1" addr add "fd00:${sub}::1/64" dev "veth${i}a"
+ ip -n "$NS2" addr add "fd00:${sub}::2/64" dev "veth${i}b"
+ ip -n "$NS1" link set "veth${i}a" up
+ ip -n "$NS2" link set "veth${i}b" up
+ done
+
+ ip -n "$NS1" addr add fd00:ff::1/128 dev lo
+ ip -n "$NS2" addr add fd00:ff::2/128 dev lo
+
+ # Allow many SYN retries at 1-second intervals (linear, no
+ # exponential backoff) so the rehash test has enough attempts
+ # to exercise both ECMP paths deterministically.
+ ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_syn_retries=6
+ ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_syn_linear_timeouts=20
+
+ ip -n "$NS1" -6 route add fd00:ff::2/128 \
+ nexthop via fd00:a::2 dev veth0a \
+ nexthop via fd00:b::2 dev veth1a
+
+ ip -n "$NS2" -6 route add fd00:ff::1/128 \
+ nexthop via fd00:a::1 dev veth0b \
+ nexthop via fd00:b::1 dev veth1b
+
+ for i in 0 1; do
+ sub=${SUBNETS[$i]}
+ ip netns exec "$NS1" \
+ ping -6 -c1 -W5 "fd00:${sub}::2" &>/dev/null
+ ip netns exec "$NS2" \
+ ping -6 -c1 -W5 "fd00:${sub}::1" &>/dev/null
+ done
+
+ if ! ip netns exec "$NS1" ping -6 -c1 -W5 fd00:ff::2 &>/dev/null; then
+ echo "Basic connectivity check failed"
+ return $ksft_skip
+ fi
+}
+
+# Block ALL paths, start a connection, wait until SYNs have been dropped
+# on both interfaces (proving rehash steered the SYN to a new path), then
+# unblock so the connection completes.
+test_ecmp_rto_rehash()
+{
+ RET=0
+
+ block_tcp "$NS1" veth0a
+ defer unblock_tcp "$NS1" veth0a
+ block_tcp "$NS1" veth1a
+ defer unblock_tcp "$NS1" veth1a
+
+ ip netns exec "$NS2" socat \
+ "TCP6-LISTEN:$PORT,bind=[fd00:ff::2],reuseaddr,fork" \
+ EXEC:"echo ESTABLISH_OK" &
+ defer kill_process $!
+
+ wait_local_port_listen "$NS2" $PORT tcp
+
+ local rehash_before
+ rehash_before=$(get_timeout_rehash_count "$NS1")
+
+ # Start the connection in the background; it will retry SYNs at
+ # 1-second intervals until an unblocked path is found.
+ ip netns exec "$NS1" bash -c \
+ "echo test | socat - \
+ 'TCP6:[fd00:ff::2]:$PORT,bind=[fd00:ff::1],connect-timeout=60'" \
+ >"/tmp/ecmp_rto_$$" 2>&1 &
+ local client_pid=$!
+ defer kill_process $client_pid
+
+ # Wait until both paths have seen at least one dropped SYN.
+ # This proves sk_rethink_txhash() rehashed the connection from
+ # one ECMP path to the other.
+ slowwait 30 both_devs_attempted "$NS1" veth0a veth1a
+ check_err $? "SYNs did not appear on both paths (rehash not working)"
+ if [ $RET -ne 0 ]; then
+ log_test "ECMP RTO rehash: establish with blocked paths"
+ return
+ fi
+
+ # Unblock both paths and let the next SYN retransmit succeed.
+ unblock_tcp "$NS1" veth0a
+ unblock_tcp "$NS1" veth1a
+
+ local rc=0
+ wait $client_pid || rc=$?
+
+ local result
+ result=$(cat "/tmp/ecmp_rto_$$" 2>/dev/null)
+ rm -f "/tmp/ecmp_rto_$$"
+
+ if [ $rc -ne 0 ] || [[ "$result" != *"ESTABLISH_OK"* ]]; then
+ check_err 1 "connection failed after unblocking: $result"
+ fi
+
+ local rehash_after
+ rehash_after=$(get_timeout_rehash_count "$NS1")
+ if [ "$rehash_after" -le "$rehash_before" ]; then
+ check_err 1 "TcpTimeoutRehash counter did not increment"
+ fi
+
+ log_test "ECMP RTO rehash: establish with blocked paths"
+}
+
+# Block the server's return paths so SYN/ACKs are dropped. The client
+# retransmits SYNs at 1-second intervals; each duplicate SYN arriving at
+# the server updates ir_iif to match the new arrival interface, so the
+# retransmitted SYN/ACK routes back via the interface the SYN arrived on.
+test_ecmp_synack_rehash()
+{
+ RET=0
+ local port=$((PORT + 2))
+
+ block_tcp "$NS2" veth0b
+ defer unblock_tcp "$NS2" veth0b
+ block_tcp "$NS2" veth1b
+ defer unblock_tcp "$NS2" veth1b
+
+ ip netns exec "$NS2" socat \
+ "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr,fork" \
+ EXEC:"echo SYNACK_OK" &
+ defer kill_process $!
+
+ wait_local_port_listen "$NS2" $port tcp
+
+ # Start the connection; SYNs reach the server (client egress is
+ # open) but SYN/ACKs are dropped on the server's return path.
+ ip netns exec "$NS1" bash -c \
+ "echo test | socat - \
+ 'TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1],connect-timeout=60'" \
+ >"/tmp/ecmp_synack_$$" 2>&1 &
+ local client_pid=$!
+ defer kill_process $client_pid
+
+ # Wait until both server-side interfaces have dropped at least
+ # one SYN/ACK, proving the server rehashed its return path.
+ slowwait 30 both_devs_attempted "$NS2" veth0b veth1b
+ check_err $? "SYN/ACKs did not appear on both return paths"
+ if [ $RET -ne 0 ]; then
+ log_test "ECMP SYN/ACK rehash: blocked return path"
+ return
+ fi
+
+ # Unblock and let the connection complete.
+ unblock_tcp "$NS2" veth0b
+ unblock_tcp "$NS2" veth1b
+
+ local rc=0
+ wait $client_pid || rc=$?
+
+ local result
+ result=$(cat "/tmp/ecmp_synack_$$" 2>/dev/null)
+ rm -f "/tmp/ecmp_synack_$$"
+
+ if [ $rc -ne 0 ] || [[ "$result" != *"SYNACK_OK"* ]]; then
+ check_err 1 "connection failed after unblocking: $result"
+ fi
+
+ log_test "ECMP SYN/ACK rehash: blocked return path"
+}
+
+# Establish a data transfer with both paths open, then block the
+# active path. Verify the transfer continues via rehash and that
+# TcpTimeoutRehash incremented.
+test_ecmp_midstream_rehash()
+{
+ RET=0
+ local port=$((PORT + 1))
+
+ ip netns exec "$NS2" socat -u \
+ "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr" - >/dev/null &
+ defer kill_process $!
+
+ wait_local_port_listen "$NS2" $port tcp
+
+ local base_tx0 base_tx1
+ base_tx0=$(link_tx_packets_get "$NS1" veth0a)
+ base_tx1=$(link_tx_packets_get "$NS1" veth1a)
+
+ ip netns exec "$NS1" bash -c "
+ for i in \$(seq 1 40); do
+ dd if=/dev/zero bs=10k count=1 2>/dev/null
+ sleep 0.25
+ done | timeout 60 socat - 'TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1]'
+ " &>/dev/null &
+ local client_pid=$!
+ defer kill_process $client_pid
+
+ busywait $BUSYWAIT_TIMEOUT until_counter_is \
+ ">= $((base_tx0 + base_tx1 + 20))" \
+ link_tx_packets_total "$NS1"
+ check_err $? "no TX activity detected"
+ if [ $RET -ne 0 ]; then
+ log_test "ECMP midstream rehash: block active path"
+ return
+ fi
+
+ # Find the active path and block it.
+ local cur0 cur1 active_idx
+ cur0=$(link_tx_packets_get "$NS1" veth0a)
+ cur1=$(link_tx_packets_get "$NS1" veth1a)
+ if [ $((cur0 - base_tx0)) -ge $((cur1 - base_tx1)) ]; then
+ active_idx=0
+ else
+ active_idx=1
+ fi
+
+ local rehash_before
+ rehash_before=$(get_timeout_rehash_count "$NS1")
+
+ block_tcp "$NS1" "veth${active_idx}a"
+ defer unblock_tcp "$NS1" "veth${active_idx}a"
+
+ local rc=0
+ wait $client_pid || rc=$?
+
+ check_err $rc "data transfer failed after blocking veth${active_idx}a"
+
+ local rehash_after
+ rehash_after=$(get_timeout_rehash_count "$NS1")
+ if [ "$rehash_after" -le "$rehash_before" ]; then
+ check_err 1 "TcpTimeoutRehash counter did not increment"
+ fi
+
+ log_test "ECMP midstream rehash: block active path"
+}
+
+link_tx_packets_total()
+{
+ local ns=$1; shift
+
+ echo $(( $(link_tx_packets_get "$ns" veth0a) +
+ $(link_tx_packets_get "$ns" veth1a) ))
+}
+
+require_command socat
+
+trap cleanup_all_ns EXIT
+setup || exit $?
+tests_run
+exit $EXIT_STATUS
--
2.52.0
^ permalink raw reply related
* Re: [PATCH net-next v2 3/4] bpf-timestamp: keep track of the skb when wait_for_space occurs
From: Jason Xing @ 2026-04-08 0:35 UTC (permalink / raw)
To: Willem de Bruijn
Cc: davem, edumazet, kuba, pabeni, horms, willemb, martin.lau, netdev,
bpf, Jason Xing, Yushan Zhou
In-Reply-To: <willemdebruijn.kernel.1b49845d3acc7@gmail.com>
On Wed, Apr 8, 2026 at 5:17 AM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> Jason Xing wrote:
> > On Tue, Apr 7, 2026 at 11:33 AM Jason Xing <kerneljasonxing@gmail.com> wrote:
> > >
> > > On Mon, Apr 6, 2026 at 10:37 PM Willem de Bruijn
> > > <willemdebruijn.kernel@gmail.com> wrote:
> > > >
> > > > Jason Xing wrote:
> > > > > On Mon, Apr 6, 2026 at 10:28 AM Willem de Bruijn
> > > > > <willemdebruijn.kernel@gmail.com> wrote:
> > > > > >
> > > > > > Jason Xing wrote:
> > > > > > > From: Jason Xing <kernelxing@tencent.com>
> > > > > > >
> > > > > > > The patch is the 1/2 part of push-level granularity feature.
> > > > > > >
> > > > > > > Tag the skb in tcp_sendmsg_locked() when wait_for_space occurs even
> > > > > > > though it might not carry the last byte of the sendmsg.
> > > > > > >
> > > > > > > Prior to the patch, BPF timestamping cannot cover this case:
> > > > > > > The following steps reproduce this:
> > > > > > > 1) skb A is the current last skb before entering wait_for_space process
> > > > > > > 2) tcp_push() pushes A without any tag
> > > > > > > 3) A is transmitted from TCP to driver without putting any skb carrying
> > > > > > > timestamps in the error queue, like SCHED, DRV/HARDWARE.
> > > > > > > 4) sk_stream_wait_memory() sleeps for a while and then returns with an
> > > > > > > error code. Note that the socket lock is released.
> > > > > > > 5) skb A finally gets acked and removed from the rtx queue.
> > > > > > > 6) continue with the rest of tcp_sendmsg_locked(): it will jump to(goto)
> > > > > > > 'do_error' label and then 'out' label.
> > > > > > > 7) at this moment, skb A turns out to be the last one in this send
> > > > > > > syscall, and miss the following tcp_bpf_tx_timestamp() opportunity
> > > > > > > before the final tcp_push()
> > > > > > > 8) BPF script fails to see any timestamps this time
> > > > > > >
> > > > > > > Signed-off-by: Yushan Zhou <katrinzhou@tencent.com>
> > > > > > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > > > > > ---
> > > > > > > net/ipv4/tcp.c | 4 +++-
> > > > > > > 1 file changed, 3 insertions(+), 1 deletion(-)
> > > > > > >
> > > > > > > diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> > > > > > > index c603b90057f6..7d030a11d004 100644
> > > > > > > --- a/net/ipv4/tcp.c
> > > > > > > +++ b/net/ipv4/tcp.c
> > > > > > > @@ -1400,9 +1400,11 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
> > > > > > > wait_for_space:
> > > > > > > set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
> > > > > > > tcp_remove_empty_skb(sk);
> > > > > > > - if (copied)
> > > > > > > + if (copied) {
> > > > > > > + tcp_bpf_tx_timestamp(sk);
> > > > > > > tcp_push(sk, flags & ~MSG_MORE, mss_now,
> > > > > > > TCP_NAGLE_PUSH, size_goal);
> > > > > >
> > > > > > Now the number of skbs that will be tracked will be unpredictable,
> > > > > > varying based on memory pressure.
> > > > >
> > > > > Right, I put some effort into writing a selftests to check how many
> > > > > push functions get called at one time and failed to do so.
> > > > >
> > > > > >
> > > > > > That sounds hard to use to me. Especially if these extra pushes
> > > > > > cannot be identified as such.
> > > > > >
> > > > > > Perhaps if all skbs from the same sendmsg call can be identified,
> > > > > > that would help explain pattern in data resulting from these
> > > > > > uncommon extra data points.
> > > > >
> > > > > You meant move tcp_bpf_tx_timestamp before tcp_skb_entail()? That is
> > > > > close to packet basis without considering fragmentation of skb :)
> > > >
> > > > No, I meant somehow in the notification having a way to identify all
> > > > the skbs belonging to the same sendmsg call, to allow filtering on
> > > > that. But I also don't immediately see how to do that (without adding
> > > > yet another counter say).
> > >
> > > If we don't build the relationship between skb and sendmsg (just like
> > > the SENDMSG sock option), we will have no clue on how to calculate. If
> > > we only take care of the skb from the view of the syscall layer, it's
> > > fine by moving tcp_bpf_tx_timestamp() before tcp_skb_entail(). But in
> > > terms of per skb even generated beneath TCP due to gso/tso, there is
> > > only one way to correlate: adding an additional member in the skb
> > > structure to store its sendmsg time. This discussion is only suitable
> > > for use cases like net_timestamping.
> > >
> > > Well, my key point is that, I have to admit, the above (including
> > > existing bpf script net_timestamping) is a less effective way which
> > > definitely harms the performance because of the extremely frequent
> > > look-up process. It's not suitable for 7x24 observability in
> > > production. What we've done internally is make the kernel layer as
> > > lightweight/easy as possible and let the timestamping feature throw
> > > each record into a ring buffer that the application can read, sort and
> > > calculate. This arch survives the performance. But that's simply what
> > > the design of the kernel module is, given the fast deployment in
> > > production. I suppose in the future we could build a userspace tool
> > > like blktrace to monitor efficiently instead of the selftest sample.
> > > Honestly I don't like look-up action.
> > >
> > > Since we're modifying the kernel, how about adding a new member to
> > > record sendmsg time which bpf script is able to read. The whole
> > > scenario looks like this:
> > > 1) in tcp_sendmsg_locked(), record the sendmsg time for each skb
> > > 2) in either tso_fragment() or tcp_gso_tstamp(), each new skb will get
> > > a copy of its original skb
> > > 3) in each stage, bpf script reads the skb's sendmsg time and the
> > > current time, and then effortlessly do the math.
> > >
> > > At this point, what I had in mind is we have two options:
> > > 1) only handle the skb from the view of the send syscall layer, which
> > > is, for sure, very simple but not thorough.
> > > 2) stick to a pure authentic packet basis, then adding a new member
> > > seems inevitable. so the question would be where to add? The space of
> > > the skb structure is very precious :(
> >
> > Finding a suitable place to put this timestamp is really hard. IIRC,
> > we can't expand the size of struct skb_shared_info so easily since
> > it's a global effect.
> >
> > I'm wondering if we can turn the per-packet mode into a non-compatible
> > feature by reusing 'u32 tskey' to store a microsecond timestamp of
> > sendmsg.
>
> Agreed that an extra field is hard. We should avoid that.
Avoiding adding a new one makes the whole work extremely hard. I'm
wondering since we have hwtstamp in shared info, why not add a
software one for timestamping use? Then, we would support more
different protocols in more different stages in a finer grain, which
is a big coarse picture in my mind.
Adding a software bit will completely reduce the whole complexity and
be very easy to use. Would you expect to see a draft by adding such a
bit first?
Or just like I mentioned, repurposing tskey seems an alternative,
which, however, makes the new feature incompatible.
>
> If the purpose is to group skbs by sendmsg call (e.g., to filter out
> all but the last one), it is probably also unnecessary.
>
> From a process PoV, since the process knows the sendmsg len and each
> skb has a tskey in byte offset, it can correlate the skb with a given
> sendmsg buffer.
>
> The BPF program is under control of a third-party admin. So that does
> not follow directly. But it can be passed additional metadata.
>
> I thought about passing the offset of the skb from the start of the
> sendmsg buffer to identify all consecutive skbs for a sendmsg call,
> as each new buffer will start with an skb with offset 0 ..
>
> .. but that won't work as there is no guarantee that a sendmsg call
> will not append to an existing outstanding skb.
Right. TCP is way too complex and we indeed see some tough issues when
trying to deploy the feature. So my humble take is to make the design
as simple as possible.
>
> Anyway, the general idea is to pass to the BPF program through
> bpf_skops_tx_timestamping some relevant signal , without having to
> expand either skb or sk itself.
>
> I hear you on that measuring every skb is too frequent. But is calling
> the BPF program and letting it decide whether to measure too? BPF
> program invocation itself should be cheap.
Oh, I was clear enough. Sorry. I meant tracing per skb is definitely
an awesome way to go. My ultimate goal is to do so. Instead of letting
people implement various fine grained bpf progs, we can provide a very
easy/understandable/efficient approach with more samples. It should be
very beneficial.
>
> If per-push is preferable, with a filter ability like the above, it
> seems more useful to me already.
Push-level is a compromise plan. Packet-level is what I always pursue :)
The current series has this ability: the bpf prog noticed it's a
SENDMSG sock option and will selectively call
bpf_sock_ops_enable_tx_tstamp() to do so. Only by calling
bpf_sock_ops_enable_tx_tstamp() could the skb be tracked.
Thanks,
Jason
^ 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