[PATCH net] hinic3: Fix SKB linearization mismatch and silent TX drops
From: Fan Gong <gongfan1@huawei.com>
Date: 2026-08-05 03:46:05
Also in:
linux-doc, lkml
Subsystem:
huawei 3rd gen ethernet driver, networking drivers, the rest · Maintainers:
Fan Gong, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Previously, hinic3_send_one_skb() cached the SKB fragment count before
calling hinic3_tx_offload(). If hinic3_tx_csum() falls back to
skb_checksum_help() for unsupported tunnel packets, the SKB may be
linearized. Continuing to build the TX descriptor with the stale
fragment count leads to a descriptor mismatch, which can trigge
out-of-bounds DMA reads or IOMMU faults.
Furthermore, the old code ignored the return value of skb_checksum_help(),
transmitting corrupted packets with incomplete checksums upon failure. It
also failed to increment drop statistics across various TX error paths,
causing packets to be dropped silently without notifying the user.
Fix this by:
1. Moving the hinic3_tx_offload() call before calculating 'num_sge' to
ensure the correct fragment count is used if the SKB is linearized.
2. Propagating skb_checksum_help() errors and returning
HINIC3_TX_OFFLOAD_INVALID to properly drop the skb.
3. Adding missing statistics increments (dropped, map_frag_err,
unknown_tunnel_pkt, skb_pad_err) across the TX error paths so these
events are correctly reflected in interface statistics.
Fixes: 17fcb3dc12bb ("hinic3: module initialization and tx/rx logic")
Co-developed-by: Teng Peisen <redacted>
Signed-off-by: Teng Peisen <redacted>
Co-developed-by: Wu Di <redacted>
Signed-off-by: Wu Di <redacted>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
---
.../net/ethernet/huawei/hinic3/hinic3_tx.c | 31 ++++++++++++++++---
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c
index 9306bf0020ca..45effdddb434 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c@@ -97,8 +97,12 @@ static int hinic3_tx_map_skb(struct net_device *netdev, struct sk_buff *skb, dma_info[0].dma = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - if (dma_mapping_error(&pdev->dev, dma_info[0].dma)) + if (dma_mapping_error(&pdev->dev, dma_info[0].dma)) { + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.map_frag_err++; + u64_stats_update_end(&txq->txq_stats.syncp); return -EFAULT; + } dma_info[0].len = skb_headlen(skb);
@@ -117,6 +121,9 @@ static int hinic3_tx_map_skb(struct net_device *netdev, struct sk_buff *skb, skb_frag_size(frag), DMA_TO_DEVICE); if (dma_mapping_error(&pdev->dev, dma_info[idx].dma)) { + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.map_frag_err++; + u64_stats_update_end(&txq->txq_stats.syncp); err = -EFAULT; goto err_unmap_page; }
@@ -260,9 +267,11 @@ static int hinic3_tx_csum(struct hinic3_txq *txq, struct hinic3_sq_task *task, if (l4_proto != IPPROTO_UDP || ((struct udphdr *)skb_transport_header(skb))->dest != VXLAN_OFFLOAD_PORT_LE) { + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.unknown_tunnel_pkt++; + u64_stats_update_end(&txq->txq_stats.syncp); /* Unsupported tunnel packet, disable csum offload */ - skb_checksum_help(skb); - return 0; + return skb_checksum_help(skb); } }
@@ -412,6 +421,10 @@ static u32 hinic3_tx_offload(struct sk_buff *skb, struct hinic3_sq_task *task, offload |= HINIC3_TX_OFFLOAD_TSO; } else { tso_cs_en = hinic3_tx_csum(txq, task, skb); + if (tso_cs_en < 0) { + offload = HINIC3_TX_OFFLOAD_INVALID; + return offload; + } if (tso_cs_en) offload |= HINIC3_TX_OFFLOAD_CSUM; }
@@ -539,12 +552,17 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb, int err; if (unlikely(skb->len < MIN_SKB_LEN)) { - if (skb_pad(skb, MIN_SKB_LEN - skb->len)) + if (skb_pad(skb, MIN_SKB_LEN - skb->len)) { + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.skb_pad_err++; + u64_stats_update_end(&txq->txq_stats.syncp); goto err_out; + } skb->len = MIN_SKB_LEN; } + offload = hinic3_tx_offload(skb, &task, &queue_info, txq); num_sge = skb_shinfo(skb)->nr_frags + 1; /* assume normal wqe format + 1 wqebb for task info */ wqebb_cnt = num_sge + 1;
@@ -560,7 +578,6 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb, return NETDEV_TX_BUSY; } - offload = hinic3_tx_offload(skb, &task, &queue_info, txq); if (unlikely(offload == HINIC3_TX_OFFLOAD_INVALID)) { goto err_drop_pkt; } else if (!offload) {
@@ -604,6 +621,10 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb, err_drop_pkt: dev_kfree_skb_any(skb); err_out: + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.dropped++; + u64_stats_update_end(&txq->txq_stats.syncp); + return NETDEV_TX_OK; }
base-commit: 2195424c3da2ef1829a63b807e3a900a90e57d85 -- 2.54.0