| From cc4e2e1423906c4fb1cec7617d151da37f5b62a1 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 7 Apr 2022 19:16:59 +0300 |
| Subject: net: macb: Restart tx only if queue pointer is lagging |
| |
| From: Tomas Melin <tomas.melin@vaisala.com> |
| |
| [ Upstream commit 5ad7f18cd82cee8e773d40cc7a1465a526f2615c ] |
| |
| commit 4298388574da ("net: macb: restart tx after tx used bit read") |
| added support for restarting transmission. Restarting tx does not work |
| in case controller asserts TXUBR interrupt and TQBP is already at the end |
| of the tx queue. In that situation, restarting tx will immediately cause |
| assertion of another TXUBR interrupt. The driver will end up in an infinite |
| interrupt loop which it cannot break out of. |
| |
| For cases where TQBP is at the end of the tx queue, instead |
| only clear TX_USED interrupt. As more data gets pushed to the queue, |
| transmission will resume. |
| |
| This issue was observed on a Xilinx Zynq-7000 based board. |
| During stress test of the network interface, |
| driver would get stuck on interrupt loop within seconds or minutes |
| causing CPU to stall. |
| |
| Signed-off-by: Tomas Melin <tomas.melin@vaisala.com> |
| Tested-by: Claudiu Beznea <claudiu.beznea@microchip.com> |
| Reviewed-by: Claudiu Beznea <claudiu.beznea@microchip.com> |
| Link: https://lore.kernel.org/r/20220407161659.14532-1-tomas.melin@vaisala.com |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/net/ethernet/cadence/macb_main.c | 8 ++++++++ |
| 1 file changed, 8 insertions(+) |
| |
| diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c |
| index 480d2ca369e6..002a374f197b 100644 |
| --- a/drivers/net/ethernet/cadence/macb_main.c |
| +++ b/drivers/net/ethernet/cadence/macb_main.c |
| @@ -1378,6 +1378,7 @@ static void macb_tx_restart(struct macb_queue *queue) |
| unsigned int head = queue->tx_head; |
| unsigned int tail = queue->tx_tail; |
| struct macb *bp = queue->bp; |
| + unsigned int head_idx, tbqp; |
| |
| if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) |
| queue_writel(queue, ISR, MACB_BIT(TXUBR)); |
| @@ -1385,6 +1386,13 @@ static void macb_tx_restart(struct macb_queue *queue) |
| if (head == tail) |
| return; |
| |
| + tbqp = queue_readl(queue, TBQP) / macb_dma_desc_get_size(bp); |
| + tbqp = macb_adj_dma_desc_idx(bp, macb_tx_ring_wrap(bp, tbqp)); |
| + head_idx = macb_adj_dma_desc_idx(bp, macb_tx_ring_wrap(bp, head)); |
| + |
| + if (tbqp == head_idx) |
| + return; |
| + |
| macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); |
| } |
| |
| -- |
| 2.35.1 |
| |