Merge branch 'net-stmmac-fix-interrupt-coalescing'

Russell King says:

====================
net: stmmac: fix interrupt coalescing

While cleaning up the descriptor handling, I noticed that the accounting
of transmit "packets" for interrupt coalescing was buggy in that it
takes the difference of the two indexes into the circular list of
transmit discriptors and merely subtracts one from the other without
regard for the indexes wrapping.

This can result in a negative number or very large positive number
which would have the effect of either reducing tx_q->tx_count_frames
or making that very large.

Either way, the result is numerically incorrect, and could trigger
interrupts or not trigger interrupts when required.

This series converts stmmac to use the circ_buf helpers, and then fixes
this problem.
====================

Link: https://patch.msgid.link/aZ1o2dmfpeiubCik@shell.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-02-25 19:12:36 -08:00
commit 23a611b9b3

View File

@ -14,6 +14,7 @@
https://bugzilla.stlinux.com/
*******************************************************************************/
#include <linux/circ_buf.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
@ -355,14 +356,9 @@ static void print_pkt(unsigned char *buf, int len)
static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue)
{
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
u32 avail;
if (tx_q->dirty_tx > tx_q->cur_tx)
avail = tx_q->dirty_tx - tx_q->cur_tx - 1;
else
avail = priv->dma_conf.dma_tx_size - tx_q->cur_tx + tx_q->dirty_tx - 1;
return avail;
return CIRC_SPACE(tx_q->cur_tx, tx_q->dirty_tx,
priv->dma_conf.dma_tx_size);
}
/**
@ -373,14 +369,9 @@ static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue)
static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)
{
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
u32 dirty;
if (rx_q->dirty_rx <= rx_q->cur_rx)
dirty = rx_q->cur_rx - rx_q->dirty_rx;
else
dirty = priv->dma_conf.dma_rx_size - rx_q->dirty_rx + rx_q->cur_rx;
return dirty;
return CIRC_CNT(rx_q->cur_rx, rx_q->dirty_rx,
priv->dma_conf.dma_rx_size);
}
static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)
@ -4525,7 +4516,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
tx_q->tx_skbuff_dma[tx_q->cur_tx].buf_type = STMMAC_TXBUF_T_SKB;
/* Manage tx mitigation */
tx_packets = (tx_q->cur_tx + 1) - first_tx;
tx_packets = CIRC_CNT(tx_q->cur_tx + 1, first_tx,
priv->dma_conf.dma_tx_size);
tx_q->tx_count_frames += tx_packets;
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)
@ -4584,8 +4576,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
/* If we only have one entry used, then the first entry is the last
* segment.
*/
is_last_segment = ((tx_q->cur_tx - first_entry) &
(priv->dma_conf.dma_tx_size - 1)) == 1;
is_last_segment = CIRC_CNT(tx_q->cur_tx, first_entry,
priv->dma_conf.dma_tx_size) == 1;
/* Complete the first descriptor before granting the DMA */
stmmac_prepare_tso_tx_desc(priv, first, 1, proto_hdr_len, 0, 1,
@ -4803,7 +4795,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
* This approach takes care about the fragments: desc is the first
* element in case of no SG.
*/
tx_packets = (entry + 1) - first_tx;
tx_packets = CIRC_CNT(entry + 1, first_tx, priv->dma_conf.dma_tx_size);
tx_q->tx_count_frames += tx_packets;
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)