Skip to content

Commit 5a6a044

Browse files
Niklas Casseldavem330
authored andcommitted
net: stmmac: fix broken dma_interrupt handling for multi-queues
There is nothing that says that number of TX queues == number of RX queues. E.g. the ARTPEC-6 SoC has 2 TX queues and 1 RX queue. This code is obviously wrong: for (chan = 0; chan < tx_channel_count; chan++) { struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; priv->rx_queue has size MTL_MAX_RX_QUEUES, so this will send an uninitialized napi_struct to __napi_schedule(), causing us to crash in net_rx_action(), because napi_struct->poll is zero. [12846.759880] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [12846.768014] pgd = (ptrval) [12846.770742] [00000000] *pgd=39ec7831, *pte=00000000, *ppte=00000000 [12846.777023] Internal error: Oops: 80000007 [#1] PREEMPT SMP ARM [12846.782942] Modules linked in: [12846.785998] CPU: 0 PID: 161 Comm: dropbear Not tainted 4.15.0-rc2-00285-gf5fb5f2f39a7 #36 [12846.794177] Hardware name: Axis ARTPEC-6 Platform [12846.798879] task: (ptrval) task.stack: (ptrval) [12846.803407] PC is at 0x0 [12846.805942] LR is at net_rx_action+0x274/0x43c [12846.810383] pc : [<00000000>] lr : [<80bff064>] psr: 200e0113 [12846.816648] sp : b90d9ae8 ip : b90d9ae8 fp : b90d9b44 [12846.821871] r10: 00000008 r9 : 0013250e r8 : 00000100 [12846.827094] r7 : 0000012c r6 : 00000000 r5 : 00000001 r4 : bac84900 [12846.833619] r3 : 00000000 r2 : b90d9b08 r1 : 00000000 r0 : bac84900 Since each DMA channel can be used for rx and tx simultaneously, the current code should probably be rewritten so that napi_struct is embedded in a new struct stmmac_channel. That way, stmmac_poll() can call stmmac_tx_clean() on just the tx queue where we got the IRQ, instead of looping through all tx queues. This is also how the xgbe driver does it (another driver for this IP). Fixes: c22a3f4 ("net: stmmac: adding multiple napi mechanism") Signed-off-by: Niklas Cassel <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2e8d243 commit 5a6a044

File tree

1 file changed

+46
-8
lines changed

1 file changed

+46
-8
lines changed

drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,22 +1997,60 @@ static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
19971997
static void stmmac_dma_interrupt(struct stmmac_priv *priv)
19981998
{
19991999
u32 tx_channel_count = priv->plat->tx_queues_to_use;
2000-
int status;
2000+
u32 rx_channel_count = priv->plat->rx_queues_to_use;
2001+
u32 channels_to_check = tx_channel_count > rx_channel_count ?
2002+
tx_channel_count : rx_channel_count;
20012003
u32 chan;
2004+
bool poll_scheduled = false;
2005+
int status[channels_to_check];
2006+
2007+
/* Each DMA channel can be used for rx and tx simultaneously, yet
2008+
* napi_struct is embedded in struct stmmac_rx_queue rather than in a
2009+
* stmmac_channel struct.
2010+
* Because of this, stmmac_poll currently checks (and possibly wakes)
2011+
* all tx queues rather than just a single tx queue.
2012+
*/
2013+
for (chan = 0; chan < channels_to_check; chan++)
2014+
status[chan] = priv->hw->dma->dma_interrupt(priv->ioaddr,
2015+
&priv->xstats,
2016+
chan);
20022017

2003-
for (chan = 0; chan < tx_channel_count; chan++) {
2004-
struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan];
2018+
for (chan = 0; chan < rx_channel_count; chan++) {
2019+
if (likely(status[chan] & handle_rx)) {
2020+
struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan];
20052021

2006-
status = priv->hw->dma->dma_interrupt(priv->ioaddr,
2007-
&priv->xstats, chan);
2008-
if (likely((status & handle_rx)) || (status & handle_tx)) {
20092022
if (likely(napi_schedule_prep(&rx_q->napi))) {
20102023
stmmac_disable_dma_irq(priv, chan);
20112024
__napi_schedule(&rx_q->napi);
2025+
poll_scheduled = true;
2026+
}
2027+
}
2028+
}
2029+
2030+
/* If we scheduled poll, we already know that tx queues will be checked.
2031+
* If we didn't schedule poll, see if any DMA channel (used by tx) has a
2032+
* completed transmission, if so, call stmmac_poll (once).
2033+
*/
2034+
if (!poll_scheduled) {
2035+
for (chan = 0; chan < tx_channel_count; chan++) {
2036+
if (status[chan] & handle_tx) {
2037+
/* It doesn't matter what rx queue we choose
2038+
* here. We use 0 since it always exists.
2039+
*/
2040+
struct stmmac_rx_queue *rx_q =
2041+
&priv->rx_queue[0];
2042+
2043+
if (likely(napi_schedule_prep(&rx_q->napi))) {
2044+
stmmac_disable_dma_irq(priv, chan);
2045+
__napi_schedule(&rx_q->napi);
2046+
}
2047+
break;
20122048
}
20132049
}
2050+
}
20142051

2015-
if (unlikely(status & tx_hard_error_bump_tc)) {
2052+
for (chan = 0; chan < tx_channel_count; chan++) {
2053+
if (unlikely(status[chan] & tx_hard_error_bump_tc)) {
20162054
/* Try to bump up the dma threshold on this failure */
20172055
if (unlikely(priv->xstats.threshold != SF_DMA_MODE) &&
20182056
(tc <= 256)) {
@@ -2029,7 +2067,7 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
20292067
chan);
20302068
priv->xstats.threshold = tc;
20312069
}
2032-
} else if (unlikely(status == tx_hard_error)) {
2070+
} else if (unlikely(status[chan] == tx_hard_error)) {
20332071
stmmac_tx_err(priv, chan);
20342072
}
20352073
}

0 commit comments

Comments
 (0)