bnxt_en: Pad short SW USO segments to BNXT_MIN_PKT_SIZE

Each SW USO segment is hdr_len + seg_payload bytes. With IPv4, hdr_len
is 42 bytes, so a segment carrying less than 10 bytes of UDP payload is
sent shorter than BNXT_MIN_PKT_SIZE. This happens with a short last
segment (e.g. gso_size 1400 and 1405 bytes of payload) or with a
gso_size below 10 bytes.

Fix the short last segment in bnxt_sw_udp_gso_xmit(): zero the pad
bytes after the header in the segment's inline header slot and add a
pad BD pointing at them after the payload BDs. Only the last segment
can need padding, and its pad BD fits within the existing bound in
bds_needed and BNXT_SW_USO_MAX_DESCS, since payload BDs are at most
num_segs + nr_frags.

If gso_size itself is too small, every segment would need a pad BD,
exceeding the descriptor bound. This case is unlikely, so
bnxt_features_check() clears GSO features for these skbs and the stack
segments them instead; bnxt_start_xmit() then pads each segment.

Reviewed-by: Michael Chan <michael.chan@broadcom.com>
Signed-off-by: Joe Damato <joe@dama.to>
Link: https://patch.msgid.link/20261009215426.2377946-3-joe@dama.to
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Joe Damato 2026-10-09 14:54:24 -07:00 committed by Paolo Abeni
parent 70f63dc608
commit b0f7f9c878
3 changed files with 53 additions and 2 deletions

View File

@ -14268,6 +14268,18 @@ static netdev_features_t bnxt_features_check(struct sk_buff *skb,
u8 *l4_proto;
features = vlan_features_check(skb, features);
/* SW USO pads at most the last segment; let the stack segment skbs
* where every segment would be shorter than BNXT_MIN_PKT_SIZE.
*/
if (skb_is_gso(skb) &&
(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) &&
!(bp->flags & BNXT_FLAG_UDP_GSO_CAP) &&
bnxt_sw_gso_pad_len(skb_transport_offset(skb) +
sizeof(struct udphdr),
skb_shinfo(skb)->gso_size))
features &= ~NETIF_F_GSO_MASK;
switch (vlan_get_protocol(skb)) {
case htons(ETH_P_IP):
if (!skb->encapsulation)

View File

@ -82,11 +82,19 @@ int bnxt_sw_udp_gso_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
if (unlikely(num_segs <= 1))
goto drop;
/* bnxt_features_check() makes the stack segment these, but paths
* that skip validate_xmit_skb() (e.g. generic XDP_TX of a GRO'd
* skb) can still get here. Padding every segment would need more
* BDs than bds_needed reserves below, so drop instead.
*/
if (unlikely(bnxt_sw_gso_pad_len(hdr_len, mss)))
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).
* most 1 extra BD). The last segment may need 1 pad BD.
*/
bds_needed = 3 * num_segs + skb_shinfo(skb)->nr_frags + 1;
@ -133,12 +141,14 @@ int bnxt_sw_udp_gso_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
dma_addr_t dma_addr;
struct tx_bd *txbd;
struct udphdr *uh;
unsigned int pad;
void *this_hdr;
int bd_count;
bool last;
u32 flags;
last = (i == num_segs - 1);
pad = bnxt_sw_gso_pad_len(hdr_len, seg_payload);
offset = slot * TSO_HEADER_SIZE;
this_hdr = txr->tx_inline_buf + offset;
this_hdr_dma = txr->tx_inline_dma + offset;
@ -156,10 +166,18 @@ int bnxt_sw_udp_gso_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
iph->check = 0;
}
/* Pad bytes follow the header in the inline slot. Zero them
* so stale header bytes from an earlier packet are not sent.
*/
if (pad)
memset(this_hdr + hdr_len, 0, pad);
dma_sync_single_for_device(&pdev->dev, this_hdr_dma,
hdr_len, DMA_TO_DEVICE);
hdr_len + pad, DMA_TO_DEVICE);
bd_count = tso_dma_map_count(&map, seg_payload);
if (pad)
bd_count++;
tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
@ -222,6 +240,12 @@ int bnxt_sw_udp_gso_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
seg_payload -= chunk_len;
}
if (pad) {
prod = NEXT_TX(prod);
txbd = bnxt_sw_gso_data_bd(bp, txr, prod,
this_hdr_dma + hdr_len, pad);
}
txbd->tx_bd_len_flags_type |=
cpu_to_le32(TX_BD_FLAGS_PACKET_END);

View File

@ -19,10 +19,25 @@
* Each segment: 1 long BD + 1 ext BD + payload BDs.
* Total payload BDs across all segs <= num_segs + nr_frags (each frag
* boundary crossing adds at most 1 extra BD).
* The last segment may need 1 pad BD (see bnxt_sw_gso_pad_len()).
* So: 3 * max_segs + MAX_SKB_FRAGS + 1 = 3 * 64 + 17 + 1 = 210.
*/
#define BNXT_SW_USO_MAX_DESCS (3 * BNXT_SW_USO_MAX_SEGS + MAX_SKB_FRAGS + 1)
/* Bytes of padding needed to bring a segment carrying @seg_payload bytes
* up to BNXT_MIN_PKT_SIZE, or 0 if no padding is needed.
*/
static inline unsigned int bnxt_sw_gso_pad_len(unsigned int hdr_len,
unsigned int seg_payload)
{
unsigned int len = hdr_len + seg_payload;
if (len < BNXT_MIN_PKT_SIZE)
return BNXT_MIN_PKT_SIZE - len;
else
return 0;
}
static inline u16 bnxt_inline_avail(struct bnxt_tx_ring_info *txr)
{
return BNXT_SW_USO_MAX_SEGS -