net/sonic: Clear interrupt flags immediately
authorFinn Thain <fthain@telegraphics.com.au>
Wed, 22 Jan 2020 22:07:26 +0000 (09:07 +1100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 23 Jan 2020 20:24:36 +0000 (21:24 +0100)
The chip can change a packet's descriptor status flags at any time.
However, an active interrupt flag gets cleared rather late. This
allows a race condition that could theoretically lose an interrupt.
Fix this by clearing asserted interrupt flags immediately.

Fixes: efcce839360f ("[PATCH] macsonic/jazzsonic network drivers update")
Tested-by: Stan Johnson <userm57@yahoo.com>
Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/natsemi/sonic.c

index 8a7cff5..1109070 100644 (file)
@@ -304,10 +304,11 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
        }
 
        do {
+               SONIC_WRITE(SONIC_ISR, status); /* clear the interrupt(s) */
+
                if (status & SONIC_INT_PKTRX) {
                        netif_dbg(lp, intr, dev, "%s: packet rx\n", __func__);
                        sonic_rx(dev);  /* got packet(s) */
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_PKTRX); /* clear the interrupt */
                }
 
                if (status & SONIC_INT_TXDN) {
@@ -362,7 +363,6 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
                        if (freed_some || lp->tx_skb[entry] == NULL)
                                netif_wake_queue(dev);  /* The ring is no longer full */
                        lp->cur_tx = entry;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_TXDN); /* clear the interrupt */
                }
 
                /*
@@ -372,42 +372,31 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
                        netif_dbg(lp, rx_err, dev, "%s: rx fifo overrun\n",
                                  __func__);
                        lp->stats.rx_fifo_errors++;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RFO); /* clear the interrupt */
                }
                if (status & SONIC_INT_RDE) {
                        netif_dbg(lp, rx_err, dev, "%s: rx descriptors exhausted\n",
                                  __func__);
                        lp->stats.rx_dropped++;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RDE); /* clear the interrupt */
                }
                if (status & SONIC_INT_RBAE) {
                        netif_dbg(lp, rx_err, dev, "%s: rx buffer area exceeded\n",
                                  __func__);
                        lp->stats.rx_dropped++;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_RBAE); /* clear the interrupt */
                }
 
                /* counter overruns; all counters are 16bit wide */
-               if (status & SONIC_INT_FAE) {
+               if (status & SONIC_INT_FAE)
                        lp->stats.rx_frame_errors += 65536;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_FAE); /* clear the interrupt */
-               }
-               if (status & SONIC_INT_CRC) {
+               if (status & SONIC_INT_CRC)
                        lp->stats.rx_crc_errors += 65536;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_CRC); /* clear the interrupt */
-               }
-               if (status & SONIC_INT_MP) {
+               if (status & SONIC_INT_MP)
                        lp->stats.rx_missed_errors += 65536;
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_MP); /* clear the interrupt */
-               }
 
                /* transmit error */
-               if (status & SONIC_INT_TXER) {
+               if (status & SONIC_INT_TXER)
                        if (SONIC_READ(SONIC_TCR) & SONIC_TCR_FU)
                                netif_dbg(lp, tx_err, dev, "%s: tx fifo underrun\n",
                                          __func__);
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_TXER); /* clear the interrupt */
-               }
 
                /* bus retry */
                if (status & SONIC_INT_BR) {
@@ -416,13 +405,8 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id)
                        /* ... to help debug DMA problems causing endless interrupts. */
                        /* Bounce the eth interface to turn on the interrupt again. */
                        SONIC_WRITE(SONIC_IMR, 0);
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_BR); /* clear the interrupt */
                }
 
-               /* load CAM done */
-               if (status & SONIC_INT_LCD)
-                       SONIC_WRITE(SONIC_ISR, SONIC_INT_LCD); /* clear the interrupt */
-
                status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT;
        } while (status);