svn commit: r200950 - head/sys/dev/ste

Pyun YongHyeon yongari at FreeBSD.org
Thu Dec 24 17:22:16 UTC 2009


Author: yongari
Date: Thu Dec 24 17:22:15 2009
New Revision: 200950
URL: http://svn.freebsd.org/changeset/base/200950

Log:
  Implement RX interrupt moderation using one-shot timer interrupt.
  Unlike TX interrupt, ST201 does not provide any mechanism to
  suppress RX interrupts. ste(4) can generate more than 70k RX
  interrupts under heavy RX traffics such that these excessive
  interrupts make system useless to process other useful things.
  Maybe this was the major reason why polling support code was
  introduced to ste(4).
  The STE_COUNTDOWN register provides a programmable counter that
  will generate an interrupt upon its expiration. We program
  STE_DMACTL register to use 3.2us clock rate to drive the counter
  register. Whenever ste(4) serves RX interrupt, the driver rearm
  the timer to expire after STE_IM_RX_TIMER_DEFAULT time and disables
  further generation of RX interrupts. This trick seems to work well
  and ste(4) generates less than 8k RX interrupts even under 64 bytes
  UDP torture test. Combined with TX interrupts, the total number of
  interrupts are less than 10k which looks reasonable on heavily
  loaded controller.
  
  The default RX interrupt moderation time is 150us. Users can change
  the value at any time with dev.ste.%d.int_rx_mod sysctl node.
  Setting it 0 effectively disables the RX interrupt moderation
  feature. Now we have both TX/RX interrupt moderation code so remove
  loop of interrupt handler which resulted in sub-optimal performance
  as well as more register accesses.

Modified:
  head/sys/dev/ste/if_ste.c
  head/sys/dev/ste/if_stereg.h

Modified: head/sys/dev/ste/if_ste.c
==============================================================================
--- head/sys/dev/ste/if_ste.c	Thu Dec 24 17:06:54 2009	(r200949)
+++ head/sys/dev/ste/if_ste.c	Thu Dec 24 17:22:15 2009	(r200950)
@@ -659,7 +659,7 @@ ste_intr(void *xsc)
 {
 	struct ste_softc *sc;
 	struct ifnet *ifp;
-	uint16_t status;
+	uint16_t intrs, status;
 
 	sc = xsc;
 	STE_LOCK(sc);
@@ -671,43 +671,67 @@ ste_intr(void *xsc)
 		return;
 	}
 #endif
-
-	/* See if this is really our interrupt. */
-	if (!(CSR_READ_2(sc, STE_ISR) & STE_ISR_INTLATCH)) {
+	/* Reading STE_ISR_ACK clears STE_IMR register. */
+	status = CSR_READ_2(sc, STE_ISR_ACK);
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
 		STE_UNLOCK(sc);
 		return;
 	}
 
-	for (;;) {
-		status = CSR_READ_2(sc, STE_ISR_ACK);
-
-		if (!(status & STE_INTRS))
-			break;
+	intrs = STE_INTRS;
+	if (status == 0xFFFF || (status & intrs) == 0)
+		goto done;
 
-		if (status & STE_ISR_RX_DMADONE)
-			ste_rxeof(sc, -1);
+	if (sc->ste_int_rx_act > 0) {
+		status &= ~STE_ISR_RX_DMADONE;
+		intrs &= ~STE_IMR_RX_DMADONE;
+	}
 
-		if (status & STE_ISR_TX_DMADONE)
+	if ((status & (STE_ISR_SOFTINTR | STE_ISR_RX_DMADONE)) != 0) {
+		ste_rxeof(sc, -1);
+		/*
+		 * The controller has no ability to Rx interrupt
+		 * moderation feature. Receiving 64 bytes frames
+		 * from wire generates too many interrupts which in
+		 * turn make system useless to process other useful
+		 * things. Fortunately ST201 supports single shot
+		 * timer so use the timer to implement Rx interrupt
+		 * moderation in driver. This adds more register
+		 * access but it greatly reduces number of Rx
+		 * interrupts under high network load.
+		 */
+		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 &&
+		    (sc->ste_int_rx_mod != 0)) {
+			if ((status & STE_ISR_RX_DMADONE) != 0) {
+				CSR_WRITE_2(sc, STE_COUNTDOWN,
+				    STE_TIMER_USECS(sc->ste_int_rx_mod));
+				intrs &= ~STE_IMR_RX_DMADONE;
+				sc->ste_int_rx_act = 1;
+			} else {
+				intrs |= STE_IMR_RX_DMADONE;
+				sc->ste_int_rx_act = 0;
+			}
+		}
+	}
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+		if ((status & STE_ISR_TX_DMADONE) != 0)
 			ste_txeof(sc);
-
-		if (status & STE_ISR_TX_DONE)
+		if ((status & STE_ISR_TX_DONE) != 0)
 			ste_txeoc(sc);
-
-		if (status & STE_ISR_STATS_OFLOW)
+		if ((status & STE_ISR_STATS_OFLOW) != 0)
 			ste_stats_update(sc);
-
-		if (status & STE_ISR_HOSTERR) {
-			ste_init_locked(sc);
+		if ((status & STE_ISR_HOSTERR) != 0) {
 			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+			ste_init_locked(sc);
+			STE_UNLOCK(sc);
+			return;
 		}
+		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+			ste_start_locked(ifp);
+done:
+		/* Re-enable interrupts */
+		CSR_WRITE_2(sc, STE_IMR, intrs);
 	}
-
-	/* Re-enable interrupts */
-	CSR_WRITE_2(sc, STE_IMR, STE_INTRS);
-
-	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-		ste_start_locked(ifp);
-
 	STE_UNLOCK(sc);
 }
 
@@ -1538,6 +1562,7 @@ ste_init_rx_list(struct ste_softc *sc)
 	struct ste_list_data *ld;
 	int error, i;
 
+	sc->ste_int_rx_act = 0;
 	cd = &sc->ste_cdata;
 	ld = &sc->ste_ldata;
 	bzero(ld->ste_rx_list, STE_RX_LIST_SZ);
@@ -1684,6 +1709,9 @@ ste_init_locked(struct ste_softc *sc)
 	STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL);
 	STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL);
 	ste_wait(sc);
+	/* Select 3.2us timer. */
+	STE_CLRBIT4(sc, STE_DMACTL, STE_DMACTL_COUNTDOWN_SPEED |
+	    STE_DMACTL_COUNTDOWN_MODE);
 
 	/* Enable receiver and transmitter */
 	CSR_WRITE_2(sc, STE_MACCTL0, 0);
@@ -1696,6 +1724,7 @@ ste_init_locked(struct ste_softc *sc)
 	/* Clear stats counters. */
 	ste_stats_clear(sc);
 
+	CSR_WRITE_2(sc, STE_COUNTDOWN, 0);
 	CSR_WRITE_2(sc, STE_ISR, 0xFFFF);
 #ifdef DEVICE_POLLING
 	/* Disable interrupts if we are polling. */
@@ -1733,6 +1762,7 @@ ste_stop(struct ste_softc *sc)
 	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING|IFF_DRV_OACTIVE);
 
 	CSR_WRITE_2(sc, STE_IMR, 0);
+	CSR_WRITE_2(sc, STE_COUNTDOWN, 0);
 	/* Stop pending DMA. */
 	val = CSR_READ_4(sc, STE_DMACTL);
 	val |= STE_DMACTL_TXDMA_STALL | STE_DMACTL_RXDMA_STALL;
@@ -2105,6 +2135,13 @@ ste_sysctl_node(struct ste_softc *sc)
 	ctx = device_get_sysctl_ctx(sc->ste_dev);
 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ste_dev));
 
+	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "int_rx_mod",
+	    CTLFLAG_RW, &sc->ste_int_rx_mod, 0, "ste RX interrupt moderation");
+	/* Pull in device tunables. */
+	sc->ste_int_rx_mod = STE_IM_RX_TIMER_DEFAULT;
+	resource_int_value(device_get_name(sc->ste_dev),
+	    device_get_unit(sc->ste_dev), "int_rx_mod", &sc->ste_int_rx_mod);
+
 	tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
 	    NULL, "STE statistics");
 	parent = SYSCTL_CHILDREN(tree);

Modified: head/sys/dev/ste/if_stereg.h
==============================================================================
--- head/sys/dev/ste/if_stereg.h	Thu Dec 24 17:06:54 2009	(r200949)
+++ head/sys/dev/ste/if_stereg.h	Thu Dec 24 17:22:15 2009	(r200950)
@@ -285,7 +285,8 @@
 
 #define STE_INTRS				\
 	(STE_IMR_RX_DMADONE|STE_IMR_TX_DMADONE|	\
-	STE_IMR_TX_DONE|STE_IMR_HOSTERR)
+	STE_IMR_TX_DONE|STE_IMR_SOFTINTR|	\
+	STE_IMR_HOSTERR)
 
 #define STE_ISR_INTLATCH		0x0001
 #define STE_ISR_HOSTERR			0x0002
@@ -349,6 +350,13 @@
 #define STE_PHYCTL_SPEEDSTAT		0x40
 #define STE_PHYCTL_LINKSTAT		0x80
 
+#define	STE_TIMER_TICKS			32
+#define	STE_TIMER_USECS(x)		((x * 10) / STE_TIMER_TICKS)
+
+#define	STE_IM_RX_TIMER_MIN		0
+#define	STE_IM_RX_TIMER_MAX		209712
+#define	STE_IM_RX_TIMER_DEFAULT		150
+
 /*
  * EEPROM offsets.
  */
@@ -570,6 +578,8 @@ struct ste_softc {
 #define	STE_FLAG_LINK		0x8000
 	int			ste_if_flags;
 	int			ste_timer;
+	int			ste_int_rx_act;
+	int			ste_int_rx_mod;
 	struct ste_list_data	ste_ldata;
 	struct ste_chain_data	ste_cdata;
 	struct callout		ste_callout;


More information about the svn-src-head mailing list