Commit 5fedabf5 authored by Finn Thain's avatar Finn Thain Committed by David S. Miller
Browse files

net/sonic: Clear interrupt flags immediately



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: efcce839 ("[PATCH] macsonic/jazzsonic network drivers update")
Tested-by: default avatarStan Johnson <userm57@yahoo.com>
Signed-off-by: default avatarFinn Thain <fthain@telegraphics.com.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 865ad2f2
Loading
Loading
Loading
Loading
+6 −22
Original line number Diff line number Diff line
@@ -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);