From a9adfbf98fb1a39c82e99580e793e76c01d89fa0 Mon Sep 17 00:00:00 2001 From: Julius Bairaktaris Date: Wed, 9 Sep 2026 15:55:15 +0200 Subject: [PATCH] qualcommax: qca_edma: refuse a frame before its preheader is pushed The transmit routine prepends the 32-byte preheader and only then tests whether the descriptor's slot still holds an uncompleted frame. A frame refused there goes back to the qdisc already carrying the preheader, and the requeue pushes a second one: the descriptor names a length that no longer describes the frame, and the headroom the push consumed is gone. Both refusals now precede the push, where the ring-full test already sat. The slot test depends on nothing between the two points, so the accepted path is unchanged. Fixes: bdb0b722b7f0 ("qualcommax: add EDMA driver") Assisted-by: Claude:claude-opus-5 Signed-off-by: Julius Bairaktaris Link: https://github.com/openwrt/openwrt/pull/25084 Signed-off-by: Robert Marko --- .../drivers/net/ethernet/qualcomm/qca_edma.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_edma.c b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_edma.c index ea229eb4436..21ed1d3b94b 100644 --- a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_edma.c +++ b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_edma.c @@ -589,6 +589,16 @@ static netdev_tx_t edma_ring_xmit(struct edma_priv *priv, struct net_device *net return NETDEV_TX_BUSY; } + /* Both refusals come before the preheader is pushed: the qdisc requeues + * the frame as it was handed over, and a second push would prefix it + * twice and hand the hardware a length that no longer describes it. + */ + idx = prod & (txdesc_ring->count - 1); + if (unlikely(txdesc_ring->skb_store[idx])) { + spin_unlock_bh(&priv->tx_lock); + return NETDEV_TX_BUSY; + } + buf_len = skb_headlen(skb); tag_info = skb_ext_find(skb, SKB_EXT_DSA_OOB); @@ -603,12 +613,6 @@ static netdev_tx_t edma_ring_xmit(struct edma_priv *priv, struct net_device *net txph->dst_info = dst_info; - idx = prod & (txdesc_ring->count - 1); - if (unlikely(txdesc_ring->skb_store[idx] != NULL)) { - spin_unlock_bh(&priv->tx_lock); - return NETDEV_TX_BUSY; - } - txdesc_ring->skb_store[idx] = skb; txph->opaque = idx;